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 = 1;
if(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卡格式化,包括黑色顯示未使用部分,如果只是格式建立的分割槽是消滅不了的。(備註:水平欠佳,如有欠缺歡迎糾正)