1. 程式人生 > >u-boot sdfuse命令燒錄分析----從SD卡載入核心

u-boot sdfuse命令燒錄分析----從SD卡載入核心

在u-boot移植過程中,由於u-boot燒錄在SD卡中,因此老是載入核心失敗,是什麼原因呢?在載入核心的列印資訊中有這樣類似的資訊:

reading kernel.. 1120, 10240 
MMC read: dev # 1, block # 1120, count 10240 ...10240 blocks read: OK
completed
reading RFS.. 11360, 2048 
MMC read: dev # 1, block # 11360, count 2048 ...2048 blocks read: OK

資訊描述的意思是從MMC 裝置1(這裡是指SD卡)的1120塊中去讀取核心,從11360塊中讀取ramdisk,但由於此時的SD卡中相應的位置並沒有寫入kernel 和ramdisk,因此在後續的載入時會失敗。那麼可以怎麼解決呢?這裡使用兩種方式,一種是讓u-boot從eMMC中去讀取kernel 和 ramdisk ,另一種是燒錄kernel 和 ramdisk 到SD卡中,這裡主要記錄的是第二種方式的修改記錄,對於第一種方式,相較之下比較簡單,只要修改預設從eMMC中讀取就可以了。

1、u-boot 使用sdfuse 命令燒錄的實現過程

u-boot中燒錄的映象的命令格式是:

sdfuse flash [bootloader|kernel|ramdisk|system] xx.bin/img

當使用sdfuse進行燒錄時,會呼叫 common/cmd_fastboot.c –> do_sdfuse()函式進行命令引數解析和處理;do_sdfuse() 函式會呼叫set_partition_table_sdmmc() 建立一個分割槽表,下面是do_sdfuse()部分程式碼:

int do_sdfuse (cmd_tbl_t *cmdtp, int
flag, int argc, char *argv[]) { …… if (set_partition_table_sdmmc()) return 1; …… /* 根據引數解析,判斷進入哪種處理方式 */ if ((argc == 2) && !strcmp(argv[1], "info")){…… }else if ((argc == 2) && !strcmp(argv[1], "flashall")){…… }else if ((argc == 4) && !strcmp
(argv[1], "flash")) { if (update_from_sd(argv[2], argv[3])) goto err_sdfuse; ret = 0; } else if ((argc == 3) && !strcmp(argv[1], "erase")) { …… } …… }

do_sdfuse 函式通過呼叫update_from_sd()函式從SD卡中讀取映象進行燒錄,update_from_sd()的部分程式碼:

static int update_from_sd (char *part, char *file)
{
    int ret = 1;
    /* 讀取映象 */
    if (file != NULL){
        ……
        sprintf(filename, "%s%s", CFG_FASTBOOT_SDFUSE_DIR, file);
        offset = CFG_FASTBOOT_TRANSFER_BUFFER;
        count = 0;
        size = file_fat_read (filename, (unsigned char *) offset, count);
        if (size == -1) {
            printf("Failed to read %s\n", filename);
            return 1;
        }
        download_size = 0;  // should be 0
        download_bytes = size;
    }
    else{
        download_size = 0;  // should be 0
        download_bytes = 0;
    }
    {
        char command[32];
        if (download_bytes == 0)
            sprintf(command, "%s:%s", "erase", part);
        else
            sprintf(command, "%s:%s", "flash", part);
        ret = rx_handler(command, sizeof(command));
    }
    return ret;
}

rx_handler()函式中實現操作方式的判斷:根據 “rx_handler(command, sizeof(command));”的一個引數進行分類判斷,這裡解析到傳遞的第一個引數是“flash”:

static int rx_handler (const unsigned char *buffer, unsigned int buffer_size)
{
    ……
    if (download_size){……}
    else{
        ……
        if (memcmp(cmdbuf, "flash:", 6) == 0){
            ……
            /* 根據啟動方式:oneNand、MMC*/
            /* 如果是 oneNAND */
            呼叫函式:write_to_ptn()
            /* 如果是 MMC */
            呼叫函式:write_to_ptn_sdmmc()
            ……
        }
        ……
    }

使用的板子是外接eMMC的,因此是呼叫write_to_ptn_sdmmc()函式;write_to_ptn_sdmmc()函式中實現的功能是判斷通過哪種命令方式來將映象寫到MMC中:mmc命令 或 movi命令,如何判斷使用哪種方式來寫映象呢?在set_partition_table_sdmmc()函式中為每一分割槽都定義了一個flag,通過這個flag來判斷寫入的方式。

static int write_to_ptn_sdmmc(struct fastboot_ptentry *ptn, unsigned int addr, unsigned int size)
{
    ……
    if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_USE_MMC_CMD)
    {
        argv[2] = device;
        argv[3] = buffer;
        argv[4] = start;
        argv[5] = length;
        ……
        if (check_compress_ext4((char*)addr,ptn->length) != 0) {
            ret = do_mmcops(NULL, 0, 6, argv);
        } 
        else {
            ret = write_compressed_ext4((char*)addr,
                    ptn->start / CFG_FASTBOOT_SDMMC_BLOCKSIZE);
        }
    }
    else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_USE_MOVI_CMD){
        ……
        /* 將燒錄映象描述名修改為分割槽名,例如 ramdisk -> rootfs*/
        if(!strcmp(ptn->name, "ramdisk ")){
            strncpy(part, "rootfs", 7);
            argv[4] = length;
            ……
        }
        ……
        ret = do_movi(NULL, 0, argc, argv);
    }
    ……
}

system映象是通過 mmc命令來燒寫的,這裡會通過在函式write_compressed_ext4()中呼叫write_raw_chunk函式來執行mmc命令:

int write_raw_chunk(char* data, unsigned int sector, unsigned int sector_size) 
{
    char run_cmd[64];
    int err ;
    ext4_printf("write raw data in %d size %d \n", sector, sector_size);
    sprintf(run_cmd,"mmc write %s 0x%x 0x%x 0x%x", dev_number_write ? "1" : "0",
            (int)data, sector, sector_size);
    err = run_command(run_cmd, 0);

    return (1-err); //mj
}

kernel 、ramdisk、u-boot都是使用movi 命令燒錄的,也就是呼叫“do_movi(NULL, 0, argc, argv);”,執行movi命令就是通過do_movi函式( common/cmd_movi.c)來解析的,函式主要實現使用movi命令時是讀/寫 到eMMC 還是 SD卡:

int do_movi(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
    ……
    /* 判斷是哪種方式啟動、將該啟動方式的裝置設定為當前裝置 */
    if(OmPin == BOOT_EMMC441){
        boot_dev = 0;       
    }
    else if(OmPin == BOOT_MMCSD){
        //emmc exist..
        if(strcmp(mmc->name, "S5P_MSHC4") == 0){
            boot_dev = 1;   
        }
        else{
            boot_dev = 0; 
        }
    }
    else{
        boot_dev = 0; 
        printf("[ERROR]Undefined booting mode\n");
    }
    /* 根據attribute 來確定從哪個分割槽讀/寫; attribute在分割槽表中定義 */
    if (attribute == 0x2) {
        ……
        printf("%s bootloader.. %ld, %ld ", rw ? "writing":"reading",
                start_blk, blkcnt);
        sprintf(run_cmd,"mmc %s 1 0x%lx 0x%lx 0x%lx", //0-->1
                rw ? "write":"read",
                addr, start_blk, blkcnt);
        run_command(run_cmd, 0);
        return 1;
    }   
    /* u-boot r/w for emmc*/
    if (attribute == 0x20) {
        ……
        printf("%s bootloader.. %ld, %ld \n", rw ? "writing":"reading",
                start_blk, blkcnt);
        ……
        sprintf(run_cmd,"mmc %s 0 0x%lx 0x%lx 0x%lx",
                rw ? "write":"read",
                addr, start_blk, blkcnt);
        run_command(run_cmd, 0);
        ……
        return 1;
    }
    if (attribute == 0x4) {
        ……
        printf("%s kernel.. %ld, %ld ", rw ? "writing" : "reading",
                start_blk, blkcnt);
        if (1 == fuse_by_fastboot)
            sprintf(run_cmd, "mmc %s %s 0x%lx 0x%lx 0x%lx",
                    rw ? "write" : "read", dev_number_write ? "1" : "0",
                    addr, start_blk, blkcnt);
        else {
            sprintf(run_cmd,"mmc %s %d 0x%lx 0x%lx 0x%lx",rw ? "write":"read",boot_dev,
                        addr, start_blk, blkcnt);
        }
        run_command(run_cmd, 0);
        printf("completed\n");
        return 1;
    }
    ……
}

do_movi函式呼叫run_command(run_cmd, 0)函式,執行命令解析函式,執行“mmc”命令,mmc的執行函式是:do_mmcops(),do_mmcops函式實現從MMC中讀資料,或者寫資料到MMC中。

2、從SD卡中讀寫映象

通過上面的分析,已經大概知道了u-boot讀寫映象的過程,因此接下要實現從SD卡中讀寫映象功能就有思路了。只需要在實現燒錄的時候進行判斷燒錄到哪個分割槽就可以,這裡修改後實現了將kernel、ramdisk、system燒錄到啟動的那個MMC裝置上,將u-boot始終燒錄到eMMC中。在do_movi函式中讀寫u-boot、kernel、ramdisk映象執行的mmc命令可以分析出,u-boot的讀寫裝置是寫死的,而kernel、ramdisk卻是可以通過判斷來決定操作哪個MMC裝置。

/* bootloader 第二個引數寫死的:0 或者 1*/
sprintf(run_cmd,"mmc %s 0 0x%lx 0x%lx 0x%lx",
                rw ? "write":"read",
                addr, start_blk, blkcnt);
/* kernel、ramdisk 類似*/
if (1 == fuse_by_fastboot)
            sprintf(run_cmd, "mmc %s %s 0x%lx 0x%lx 0x%lx",
                    rw ? "write" : "read", dev_number_write ? "1" : "0",
                    addr, start_blk, blkcnt);

因此只需要根據當前的啟動方式判斷啟動裝置,並將fuse_by_fastboot設定為1即可實現將kernel、ramdisk燒錄到SD卡中,do_movi()如下修改(+表示新增):

+ fuse_by_fastboot = 1if(OmPin == BOOT_EMMC441){
        boot_dev = 0;       
    +   dev_number_write = 0;
    }
    else if(OmPin == BOOT_MMCSD){
        //emmc exist..
        if(strcmp(mmc->name, "S5P_MSHC4") == 0){
            boot_dev = 1;   
        }
        else{
            boot_dev = 0; 
        }
        +   dev_number_write = 1;
    }
    else{
    +   dev_number_write = 0;
        boot_dev = 0; 
        printf("[ERROR]Undefined booting mode\n");
    }

將system燒錄到SD卡,需要在執行movi命令之前根據啟動方式進行判斷當前的啟動裝置,write_raw_chunk函式做如下改動(+表示新增)

int write_raw_chunk(char* data, unsigned int sector, unsigned int sector_size) 
{
    char run_cmd[64];
    int err ;
+ if(OmPin == BOOT_EMMC441){
+       dev_number_write = 0;
+   }
+   else if(OmPin == BOOT_MMCSD){
+           dev_number_write = 1;
+   }
+   else{
+       dev_number_write = 0;
+       printf("[ERROR]Undefined booting mode\n");
+   }

}

至此修改完成,可以實現將kernel、ramdisk、system燒寫到SD卡中,或者從SD卡中讀取。實現這樣的目的是通過PC檢視SD卡,可以更加直觀的看到分割槽的分佈和映象燒錄的哪去了的問題。解決了明明格式化了分割槽,為什麼燒錄的映象除了system映象,其他的映象還是沒有被消滅掉,還正常的在eMMC中躺著,這是因為只是格式化了設定的分割槽,並沒有對u-boot中的虛擬分割槽(暫時這樣理解的)進行格式化,下圖就中說明了,使用fdisk -c 1 300 300 300 對SD卡進行分割槽後記憶體分佈,還剩兩個部分的記憶體空間,而我們的u-boot 的虛擬分割槽就位於頭部的塊中,這是因為在燒錄時是從第一個塊開始的。那麼如果想要消滅MMC中的kernel、u-boot、ramdisk,就應該將正SD卡格式化,包括黑色顯示未使用部分,如果只是格式建立的分割槽是消滅不了的。(備註:水平欠佳,如有欠缺歡迎糾正) 這裡寫圖片描述