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卡格式化,包括黑色显示未使用部分,如果只是格式建立的分区是消灭不了的。(备注:水平欠佳,如有欠缺欢迎纠正)
这里写图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值