STM32
直播中

程成

12年用户 502经验值
私信 关注
[问答]

u-boot是如何实现跳转到Kernel的


u-boot是如何实现跳转到Kernel的?有哪些基本步骤?

回帖(1)

石径

2021-11-30 15:59:45
    经过前面的四篇文章的学习,相信我们对u-boot 这个东西已经很熟悉了,
接下来,我们要看看u-boot最核心最重要的一部分: 以hi3516dv300为例,看看如何实现跳转到Kernel。


  1. misc_init_r() 配置自启动命令

  在 hi3516dv300 板子中,是有提前配置自启动参数的,配置 的函数就是 misc_init_r()。
misc_init_r() 是配置在 init_sequence_r[] 中的,由board_init_r() 解析调用运行。
  在misc_init_r() 主要作用就是,配置gd->fdt_blob中的bootcmd为"mmc read 0x0 0x80000000 0x800 0x4800; bootm 0x80000000",
最重要的其实就是 bootm 0x80000000,说明 kernel 内核是从 0x80000000 地址开始运行的。
  好,我们先来看看 misc_init_r() 的作用吧。
  

  • 配置环境变量bootargs的内容,保存在g_bootArgsStr 中
  • 配置启动参数,如果要进入 recovery则修改启动参数,否则不变
  • 设置启动参数bootargs ,及启动命令bootcmd= mmc read 0x0 0x80000000 0x800 0x4800; bootm 0x80000000


static init_fnc_t init_sequence_r[] = {
#ifdef CONFIG_MISC_INIT_R
        misc_init_r,                /* miscellaneous platform-dependent init */
#endif
}


# devicehisiliconthird_partyubootu-boot-2020.01boardhisiliconhi3516dv300hi3516dv300.c
int misc_init_r(void)
{
    const char cmdBuf[] = "mmc read 0x0 0x80000000 0x800 0x4800; bootm 0x80000000";
        env_set("verify", "n");
        // 1. 配置环境变量bootargs的内容,保存在g_bootArgsStr 中
    if (EmmcInitParam() == -1) {
        return 0;
    }
   
    // 2. 配置启动参数,如果要进入 recovery则修改启动参数,否则不变
    ChangeBootArgs();
    ==============>
    +        char *emmcBootArgs = env_get("bootargs");
    +        int emmcBootArgsLen = strlen(emmcBootArgs);
    +        char *initIndex = strstr(emmcBootArgs, "init");
    +        char *blkIndex = strstr(emmcBootArgs, "blkdevparts");
    +        if (!g_isRecovery) {                 // hos
        +        memset(g_bootArgsStr, 0, ARG_SZ);
        +        memcpy(g_bootArgsStr, emmcBootArgs, emmcBootArgsLen);
        +        printf("@@@ bootArgs final from emmc = %sn", g_bootArgsStr);
        +    } else {
        +        printf("@@@ bootArgs final from misc = %sn", g_bootArgsStr);
        +    }
    <==============
   
        // 3. 设置启动参数bootargs ,及启动命令bootcmd= mmc read 0x0 0x80000000 0x800 0x4800; bootm 0x80000000
    env_set("bootargs", g_bootArgsStr);
    env_set("bootcmd", cmdBuf);
    return 0;
}

  1.1 环境变量 bootargs,拼凑 cmdline信息

  在 EmmcInitParam() 中主要作用是配置环境变量bootargs的内容,保存在g_bootArgsStr 中,我们来看看它的工作做了啥:
  


  • 从mmcdev 中读取5个扇区的内容,即512byte x 5=2.5M的内容
  • 如果是 recovery模式,则往mmcblk0写入defaultUpdaterStr 及 updaterHead 内容


        const char defaultUpdaterStr[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M "
            "clk_ignore_unused androidboot.selinux=permissive skip_initramfs "
            "rootdelay=10 init=/updaterinit root=/dev/mmcblk0p3 rootfstype=ext4 rw blkdevparts=mmcblk0:1M(boot),"
            "15M(kernel),20M(updater),1M(misc),3307M(system),256M(vendor),-(userdata)";
        const char updaterHead[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M clk_ignore_unused "
            "androidboot.selinux=permissive skip_initramfs "
            "rootdelay=10 init=/updaterinit root=/dev/mmcblk0p3 rootfstype=ext4 rw blkdevparts=";


如果是正常启动kernel,则往mmcblk0写入defaultRebootStr 及 rebootHead 内容
        const char defaultRebootStr[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M "
                "clk_ignore_unused androidboot.selinux=permissive skip_initramfs rootdelay=10 init=/init root=/dev/mmcblk0p5 "
                "rootfstype=ext4 rw blkdevparts=mmcblk0:1M(boot),15M(kernel),20M(updater),"
                "1M(misc),3307M(system),256M(vendor),-(userdata)";
        const char rebootHead[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M "
                "clk_ignore_unused androidboot.selinux=permissive skip_initramfs rootdelay=10 init=/init "
                "root=/dev/mmcblk0p5 rootfstype=ext4 rw blkdevparts=";


最终末尾写入block2 的内容 ,即最开始读取的 5 个扇区的内容


# devicehisiliconthird_partyubootu-boot-2020.01boardhisiliconhi3516dv300hi3516dv300.c
char g_bootArgsStr[ARG_SZ];                // #define ARG_SZ 1000


static int EmmcInitParam()              // get "boot_updater" string in misc,then set env
{
    const char rebootHead[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M "
        "clk_ignore_unused androidboot.selinux=permissive skip_initramfs rootdelay=10 init=/init "
        "root=/dev/mmcblk0p5 rootfstype=ext4 rw blkdevparts=";
    const char defaultRebootStr[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M "
        "clk_ignore_unused androidboot.selinux=permissive skip_initramfs rootdelay=10 init=/init root=/dev/mmcblk0p5 "
        "rootfstype=ext4 rw blkdevparts=mmcblk0:1M(boot),15M(kernel),20M(updater),"
        "1M(misc),3307M(system),256M(vendor),-(userdata)";
    const char updaterHead[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M clk_ignore_unused "
        "androidboot.selinux=permissive skip_initramfs "
        "rootdelay=10 init=/updaterinit root=/dev/mmcblk0p3 rootfstype=ext4 rw blkdevparts=";
    const char defaultUpdaterStr[] = "mem=640M console=ttyAMA0,115200 mmz=anonymous,0,0xA8000000,384M "
        "clk_ignore_unused androidboot.selinux=permissive skip_initramfs "
        "rootdelay=10 init=/updaterinit root=/dev/mmcblk0p3 rootfstype=ext4 rw blkdevparts=mmcblk0:1M(boot),"
        "15M(kernel),20M(updater),1M(misc),3307M(system),256M(vendor),-(userdata)";
    // 1. 从mmcdev 中读取5个扇区的内容 ,即512byte x 5=2.5M的内容
    char block2[EMMC_SECTOR_SIZE*EMMC_SECTOR_CNT];
    BlkDevRead(block2, MISC_LOCATION*(M_1/EMMC_SECTOR_SIZE), EMMC_SECTOR_CNT);
    =============>
    +        int devNo = env_get_ulong("mmcdev", NUM_BASE, 0);
    +    mmc = MmcBlkDevInit(devNo);
    +    return MmcBlkRead(mmc, buffer, blk, cnt);
    <============


    struct UpdateMessage *p = (struct UpdateMessage *)block2;
    block2[MAX_COMMAND_SIZE-1] = block2[MAX_COMMAND_SIZE+MAX_UPDATE_SIZE-1] =block2[EMMC_SECTOR_SIZE*EMMC_SECTOR_CNT-1] = 0;
    p->command[0] = p->command[0] == ((char)-1) ? 0 : p->command[0];
    p->update[0] = p->update[0] == ((char)-1) ? 0 : p->update[0];
    block2[EMMC_SECTOR_SIZE * (EMMC_SECTOR_CNT - 1)] = block2[EMMC_SECTOR_SIZE * (EMMC_SECTOR_CNT - 1)] ==
        (char)-1 ? 0 : block2[EMMC_SECTOR_SIZE * (EMMC_SECTOR_CNT - 1)];


    g_isRecovery = memcmp(p->command, "boot_updater", UPDATE_BOOT_LENGTH) ? 0 : 1;
    unsigned int partStrLen = strlen(&block2[EMMC_SECTOR_SIZE*(EMMC_SECTOR_CNT - 1)]);


        // 2. 如果是 recovery模式,则往mmcblk0写入defaultUpdaterStr 及 updaterHead 内容
        // 3. 如果是正常启动kernel,则往mmcblk0写入defaultRebootStr 及 rebootHead 内容
    if (memcmp(&block2[EMMC_SECTOR_SIZE*(EMMC_SECTOR_CNT-1)], "mmcblk0", MMC_LENGTH)) {
        if (g_isRecovery) {
            memcpy(g_bootArgsStr, defaultUpdaterStr, strlen(defaultUpdaterStr) + 1);
        } else {
            memcpy(g_bootArgsStr, defaultRebootStr, strlen(defaultRebootStr) + 1);
        }
    } else {
        if (g_isRecovery) {
            memcpy(g_bootArgsStr, updaterHead, strlen(updaterHead) + 1);
        } else {
            memcpy(g_bootArgsStr, rebootHead, strlen(rebootHead) + 1);
        }
        memcpy(g_bootArgsStr + strlen(g_bootArgsStr), &block2[EMMC_SECTOR_SIZE * (EMMC_SECTOR_CNT - 1)],
            partStrLen + 1);
    }
    printf("@@@ g_isRecovery = %dn", g_isRecovery);
    printf("@@@ bootArgs from misc       = %sn", g_bootArgsStr);


    return g_isRecovery;
}
  1.2 环境变量 bootcmd

  const char cmdBuf[] = "mmc read 0x0 0x80000000 0x800 0x4800; bootm 0x80000000";
env_set("bootcmd", cmdBuf);
  可以看出,在hi3516dv300 板子上,kernel 是从0x80000000 地址开始启动的。
  
  2. bootm 0x80000000

  可以看出,系统启动时,调用的是bootm 命令,传参0x80000000, 最终执行的是 do_bootm() 函数,我们来看看它主要的工作。
  


  • 如果使能了 Security boot功能,则对镜像进行安全检查
  • 判断是否有额外的子命令需要运行
  • 以hi3516dv300 为例,它的启动命令为: bootm 0x80000000,那此处的argv=0x0x80000000


# devicehisiliconthird_partyubootu-boot-2020.01cmdbootm.c


U_BOOT_CMD(bootm,        CONFIG_SYS_MAXARGS,        1,        do_bootm,"boot application image from memory", bootm_help_text);


int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
        // 1. 如果使能了 Security boot功能,则对镜像进行安全检查
#if defined(CONFIG_OHOS_SEC_BOOT_SUPPORT)
    if (CONFIG_OHOS_SEC_BOOT_ENABLE) {
        int ret = check_security_boot(CONFIG_OHOS_X509_BIN_START_ADDR);
        if (ret) {
            printf("## Check security boot falied, sig_addr:%#Xn", CONFIG_OHOS_X509_BIN_START_ADDR);
            while (1);
        }
    }
#endif
        // 2. 判断是否有额外的子命令需要运行
        /* determine if we have a sub command */
        argc--; argv++;
        if (argc > 0) {
                char *endp;
                simple_strtoul(argv[0], &endp, 16);
                if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
                        return do_bootm_subcommand(cmdtp, flag, argc, argv);
        }
        // 3. 开始运行 bootm的命令,注意此处 images是用于保存头信息结构体
        return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START|BOOTM_STATE_FINDOS|BOOTM_STATE_FINDOTHER |
                BOOTM_STATE_LOADOS|BOOTM_STATE_RAMDISK|BOOTM_STATE_OS_CMDLINE|BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
                BOOTM_STATE_OS_GO, &images, 1);
}

  2.1 do_bootm_states()

  


  • 获得 kernel 头信息,起始地址0x80000000,及长度, img parameters, 根据kernel 镜像类型来配置不同的信息, 映射 kernel 镜像地址保存在images.os.start 中, bootm 0x80000000 中的 0x80000000 就是这被传给img_addr 的。
  • 如果images.os.type == IH_TYPE_MULTI的话,说明有多个镜像,查找是否存在ramdisk镜像,device tree镜像,fpga镜像,loadables 等
  • 拷贝ramdisk镜像
  • 拷贝device tree镜像
  • 获得kernel os 的启动函数入口, 它会根据传入的 os 值返回对应os类型的启动函数,在boot_os数组中,定义了各种类型的OS的启动函数,如,我们当前是linux,则传入的os=1,返回的函数是 do_bootm_linux。

  
# devicehisiliconthird_partyubootu-boot-2020.01configshi3516dv300_emmc_smp_defconfig
CONFIG_BOOTM_LINUX=y
CONFIG_BOOTM_NETBSD=y
# CONFIG_BOOTM_OPENRTOS is not set
# CONFIG_BOOTM_OSE is not set
CONFIG_BOOTM_PLAN9=y
CONFIG_BOOTM_RTEMS=y
CONFIG_BOOTM_VXWORKS=y
CONFIG_CMD_ELF=y


我们根据宏控处理下boot_os
# devicehisiliconthird_partyubootu-boot-2020.01commonbootm_os.c
static boot_os_fn *boot_os[] = {
        [IH_OS_U_BOOT] = do_bootm_standalone,
        [IH_OS_LINUX] = do_bootm_linux,
        [IH_OS_NETBSD] = do_bootm_netbsd,
        [IH_OS_RTEMS] = do_bootm_rtems,
        [IH_OS_PLAN9] = do_bootm_plan9,
        [IH_OS_VXWORKS] = do_bootm_vxworks,
        [IH_OS_QNX] = do_bootm_qnxelf,
};


调用 boot_fn (do_bootm_linux) 函数,开始BOOTM_STATE_OS_PREP 预加载
正式跳转linux 不再返回, 调用的还是boot_fn (do_bootm_linux) 函数, 标志传参BOOTM_STATE_OS_GO
# devicehisiliconthird_partyubootu-boot-2020.01commonbootm.c
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
                    int states, bootm_headers_t *images, int boot_progress)
{
        boot_os_fn *boot_fn;
        int ret = 0, need_boot_fn;


        images->state |= states;
        /* Work through the states and see how far we get. We stop on any error.*/
        if (states & BOOTM_STATE_START)
                ret = bootm_start(cmdtp, flag, argc, argv);
                ---------------->
                +        images.verify = env_get_yesno("verify");
                +        bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");
                +        images.state = BOOTM_STATE_START;
                <-----------------       
        // 1. 获得 kernel 头信息,起始地址0x80000000,及长度, img parameters, 根据kernel 镜像类型来配置不同的信息, 映射 kernel 镜像地址保存在images.os.start 中
        if (!ret && (states & BOOTM_STATE_FINDOS))
                ret = bootm_find_os(cmdtp, flag, argc, argv);
                ---------------->       
                +        os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, &images, &images.os.image_start, &images.os.image_len);
                +                -------->//bootm 0x80000000 中的 0x80000000 就是这被传给img_addr 的。
                +                        // 定义在 # devicehisiliconthird_partyubootu-boot-2020.01includeconfigshi3516dv300.h
                +                        img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0],&fit_uname_config,&fit_uname_kernel);
                +                                        ----->  return kernel_addr = simple_strtoul(img_addr, NULL, 16);
                +                        buf = map_sysmem(img_addr, 0);
                +        // 映射 kernel 镜像地址
                +        images.os.start = map_to_sysmem(os_hdr);
                <-----------------       
        // 2. 如果images.os.type == IH_TYPE_MULTI的话,说明有多个镜像,查找是否存在ramdisk镜像,device tree镜像,fpga镜像,loadables 等
        if (!ret && (states & BOOTM_STATE_FINDOTHER))
                ret = bootm_find_other(cmdtp, flag, argc, argv);
       
        // 3. 开始加载kernel OS, 加载前先禁止中断
        /* Load the OS */
        if (!ret && (states & BOOTM_STATE_LOADOS)) {
                iflag = bootm_disable_interrupts();
                ret = bootm_load_os(images, 0);
        }


        // 3. 拷贝ramdisk镜像  /* Relocate the ramdisk */
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
        if (!ret && (states & BOOTM_STATE_RAMDISK)) {
                ulong rd_len = images->rd_end - images->rd_start;


                ret = boot_ramdisk_high(&images->lmb, images->rd_start,rd_len, &images->initrd_start, &images->initrd_end);
                if (!ret) {
                        env_set_hex("initrd_start", images->initrd_start);
                        env_set_hex("initrd_end", images->initrd_end);
                }
        }
#endif
        // 4. 拷贝device tree镜像
#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB)
        if (!ret && (states & BOOTM_STATE_FDT)) {
                boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
                ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,&images->ft_len);
        }
#endif
        // 5. 获得kernel os 的启动函数入口
        /* From now on, we need the OS boot function */
        boot_fn = bootm_os_get_boot_func(images->os.os);
        need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
                                        BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
       
        //        6. 调用 boot_fn (do_bootm_linux) 函数,开始BOOTM_STATE_OS_PREP 预加载
        /* Call various other states that are not generally used */
        if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
                ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
        if (!ret && (states & BOOTM_STATE_OS_BD_T))
                ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
        if (!ret && (states & BOOTM_STATE_OS_PREP)) {
                if (images->os.os == IH_OS_LINUX)
                        fixup_silent_linux();
                ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
        }
       
        //        7. 正式跳转linux 不再返回, 调用的还是boot_fn (do_bootm_linux) 函数, 标志传参BOOTM_STATE_OS_GO
        /* Now run the OS! We hope this doesn't return */
        if (!ret && (states & BOOTM_STATE_OS_GO))
                ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,images, boot_fn);
                --------->
                        boot_fn(BOOTM_STATE_OS_GO, argc, argv, images);


        return ret;
}
  2.2 linux OS启动函数boot_fn 、 do_bootm_linux()

  在前面,分别调用boot_fn ,传参BOOTM_STATE_OS_PREP 和 BOOTM_STATE_OS_GO,主要工作为:
  


  • 配置 uboot中需要传给kernel 的所有信息的地址,可以看出,其实u-boot传消息给kernel,就是共享内存方式,
    将这些信息的入口地址保存在0x54410001 起始的地址中,信息包括:SERIAL, CMDLINE, REVISION, MEM,INITRD。
  • 正式开始跳转kernel 不再返回


# devicehisiliconthird_partyubootu-boot-2020.01archarmlibbootm.c
/* Main Entry point for arm bootm implementation
*
* Modeled after the powerpc implementation
* DIFFERENCE: Instead of calling prep and go at the end
* they are called if subcommand is equal 0.
*/
int do_bootm_linux(int flag, int argc, char * const argv[],
                   bootm_headers_t *images)
{
        // 1. 配置 uboot中需要传给kernel 的所有信息的地址,包括,SERIAL, CMDLINE, REVISION, MEM,INITRD。
        if (flag & BOOTM_STATE_OS_PREP) {
                boot_prep_linux(images);
                =======================>
                +        char *commandline = env_get("bootargs");
                +        setup_start_tag(gd->bd);                                        // #define ATAG_CORE        0x54410001
                +        setup_serial_tag(¶ms);                                        // #define ATAG_SERIAL        0x54410006
                +        setup_commandline_tag(gd->bd, commandline);        // #define ATAG_CMDLINE        0x54410009
                +        setup_revision_tag(¶ms);                                // #define ATAG_REVISION        0x54410007
                +        setup_memory_tags(gd->bd);                                        // #define ATAG_MEM                        0x54410002
                +        setup_initrd_tag(gd->bd, images->initrd_start,images->initrd_end);        // #define ATAG_INITRD2        0x54420005
                +        setup_board_tags(¶ms);
                +        setup_end_tag(gd->bd);                                                // #define ATAG_NONE        0x00000000
                +        board_prep_linux(images);
                <=======================
                return 0;
        }
        // 2. 正式开始跳转kernel 不再返回
        if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
                boot_jump_linux(images, flag);
                return 0;
        }
        boot_prep_linux(images);
        boot_jump_linux(images, flag);
        return 0;
}
  2.3 boot_jump_linux()

  在64位系统中的,通过 armv8_switch_to_el2来跳转,
其中可以看出,是在汇编层面直接通过 br x4 跳转到 (u64)images->ep 的地址开始运行,也就 实现了 u-boot 向kernel 的跳转。
而在32位系统中则相对简单,直接 kernel_entry(0, machid, r2); 在C层面以函数调用方式跳转。
  
# devicehisiliconthird_partyubootu-boot-2020.01archarmcpuarmv8transition.S
.pushsection .text.armv8_switch_to_el2, "ax"
ENTRY(armv8_switch_to_el2)
        switch_el x6, 1f, 0f, 0f
0:
        cmp x5, #ES_TO_AARCH64
        b.eq 2f
        /* When loading 32-bit kernel, it will jump to secure firmware again, and never return.*/
        bl armv8_el2_to_aarch32
2:
        /* x4 is kernel entry point or switch_to_el1 if CONFIG_ARMV8_SWITCH_TO_EL1 is defined.
        When running in EL2 now, jump to the address saved in x4.*/
        br x4
1:        armv8_switch_to_el2_m x4, x5, x6
ENDPROC(armv8_switch_to_el2)


static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
        void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,void *res2);
        int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
        // 1. 获得 kernel 镜像的入口地址
        kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,void *res2))images->ep;


        debug("## Transferring control to Linux (at address %lx)...n",(ulong) kernel_entry);
        bootstage_mark(BOOTSTAGE_ID_RUN_OS);
        announce_and_cleanup(fake);
        if (!fake) {
                do_nonsec_virt_switch();
                update_os_arch_secondary_cores(images->os.arch);
                if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && (images->os.arch == IH_ARCH_ARM))
                        armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number, (u64)images->ft_addr, 0,    (u64)images->ep, ES_TO_AARCH32);
                else
                        armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,  images->ep,   ES_TO_AARCH64);
        }
#else
        unsigned long machid = gd->bd->bi_arch_number;
        char *s;
        void (*kernel_entry)(int zero, int arch, uint params);
        unsigned long r2;
        int fake = (flag & BOOTM_STATE_OS_FAKE_GO);


        kernel_entry = (void (*)(int, int, uint))images->ep;
        ulong addr = (ulong)kernel_entry | 1;
        kernel_entry = (void *)addr;
        s = env_get("machid");
        if (s) {
                if (strict_strtoul(s, 16, &machid) < 0) {
                        debug("strict_strtoul failed!n");
                        return;
                }
                printf("Using machid 0x%lx from environmentn", machid);
        }


        debug("## Transferring control to Linux (at address %08lx)"
                "...n", (ulong) kernel_entry);
        bootstage_mark(BOOTSTAGE_ID_RUN_OS);
        announce_and_cleanup(fake);


        if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
                r2 = (unsigned long)images->ft_addr;
        else
                r2 = gd->bd->bi_boot_params;


        if (!fake) {
                if (armv7_boot_nonsec()) {
                        armv7_init_nonsec();
                        secure_ram_addr(_do_nonsec_entry)(kernel_entry, 0, machid, r2);
                } else
                        kernel_entry(0, machid, r2);
        }
#endif
}


好,至此,CPU指针跳转到了kernel OS,U-boot方面的代码分析也到此为止了,
下章我们开始分析Harmony OS 的kernel了,加油!
  
  3. 鸿蒙Kernel镜像 uImage分析

  前面的在 do_bootm_states() 中,我们通过boot_get_kernel()来获得kernel的头信息,那我们现在打开一个kernel镜像,来瞧瞧这些头信息到底是啥?
  进入我们编译好的harmony 的out目录,找到 kernel 镜像uImage:

  

  

通过如下命令实现以16进制查看 uImage:
vim -b uImage
:%!xxd
  打开uImage ok,如下:

  

  

  结合image_header 结构体:

/*
* Legacy format image header,
* all data in network byte order (aka natural aka bigendian).
*/
typedef struct image_header {
        uint32_t        ih_magic;        /* Image Header Magic Number        */
        uint32_t        ih_hcrc;        /* Image Header CRC Checksum        */
        uint32_t        ih_time;        /* Image Creation Timestamp        */
        uint32_t        ih_size;        /* Image Data Size                */
        uint32_t        ih_load;        /* Data         Load  Address                */
        uint32_t        ih_ep;                /* Entry Point Address                */
        uint32_t        ih_dcrc;        /* Image Data CRC Checksum        */
        uint8_t                ih_os;                /* Operating System                */
        uint8_t                ih_arch;        /* CPU architecture                */
        uint8_t                ih_type;        /* Image Type                        */
        uint8_t                ih_comp;        /* Compression Type                */
        uint8_t                ih_name[IH_NMLEN];        /* Image Name                */
} image_header_t;


我们可以知道 :


ih_magic = 0x27051956


ih_hcrc = 0xea737f06


ih_time = 0x60c4882c = (10进制) 1,623,492,652 转换成时间为 2021-06-12 18:10:52




ih_size = 0x004940be = (10进制) 4,800,702
-rw-rw-r-- 1 ciellee ciellee 4800766 Jun 12 03:11 uImage
我们看到 uImage大小为4800766,比ih_size多64个字节,这是为什么呢?
其实啊,是因为此处的 ih_size 是不包含头信息的,头信息image_header占用内存为64字节,4800702(ih_size) + 64(header) =4800766,这就对上了。


ih_load = 0x80008000


ih_ep = 0x80008000
kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,void *res2))images->ep;


ih_dcrc = 0xb574b5a4


ih_os = 0x05
boot_fn = bootm_os_get_boot_func(images->os.os);
此处 os = 0x05,那看来,我们前面分析有点问题,我们之前默认它跑的是do_bootm_linux,
看来,在鸿蒙OS中,我们应该跑的是do_bootm_vxworks。


# devicehisiliconthird_partyubootu-boot-2020.01commonbootm_os.c
static boot_os_fn *boot_os[] = {
        [IH_OS_U_BOOT] = do_bootm_standalone,
        [IH_OS_LINUX] = do_bootm_linux,
        [IH_OS_NETBSD] = do_bootm_netbsd,
        [IH_OS_RTEMS] = do_bootm_rtems,
        [IH_OS_PLAN9] = do_bootm_plan9,
        [IH_OS_VXWORKS] = do_bootm_vxworks,
        [IH_OS_QNX] = do_bootm_qnxelf,
};


ih_arch = 0x02
此入 arch = 0x02 ,我们来看下,还确实,对应着IH_ARCH_ARM
# devicehisiliconthird_partyubootu-boot-2020.01includeimage.h
enum {
        IH_ARCH_INVALID                = 0,        /* Invalid CPU        */
        IH_ARCH_ALPHA,                        /* Alpha        */
        IH_ARCH_ARM,                        /* ARM                */
        IH_ARCH_I386,                        /* Intel x86        */
        IH_ARCH_IA64,                        /* IA64                */
        IH_ARCH_MIPS,                        /* MIPS                */
        IH_ARCH_MIPS64,                        /* MIPS         64 Bit */
        IH_ARCH_PPC,                        /* PowerPC        */
        IH_ARCH_S390,                        /* IBM S390        */
        IH_ARCH_SH,                        /* SuperH        */
        IH_ARCH_SPARC,                        /* Sparc        */
        IH_ARCH_SPARC64,                /* Sparc 64 Bit */
        IH_ARCH_M68K,                        /* M68K                */
        IH_ARCH_NIOS,                        /* Nios-32        */
        IH_ARCH_MICROBLAZE,                /* MicroBlaze   */
        IH_ARCH_NIOS2,                        /* Nios-II        */
        IH_ARCH_BLACKFIN,                /* Blackfin        */
        IH_ARCH_AVR32,                        /* AVR32        */
        IH_ARCH_ST200,                        /* STMicroelectronics ST200  */
        IH_ARCH_SANDBOX,                /* Sandbox architecture (test only) */
        IH_ARCH_NDS32,                        /* ANDES Technology - NDS32  */
        IH_ARCH_OPENRISC,                /* OpenRISC 1000  */
        IH_ARCH_ARM64,                        /* ARM64        */
        IH_ARCH_ARC,                        /* Synopsys DesignWare ARC */
        IH_ARCH_X86_64,                        /* AMD x86_64, Intel and Via */
        IH_ARCH_XTENSA,                        /* Xtensa        */
        IH_ARCH_RISCV,                        /* RISC-V */


        IH_ARCH_COUNT,
};


ih_type = 0x02
此入type = 0x2,对应的就是IH_TYPE_KERNEL,对上了
# devicehisiliconthird_partyubootu-boot-2020.01includeimage.h
enum {
        IH_TYPE_INVALID                = 0,        /* Invalid Image                */
        IH_TYPE_STANDALONE,                /* Standalone Program                */
        IH_TYPE_KERNEL,                        /* OS Kernel Image                */
        IH_TYPE_RAMDISK,                /* RAMDisk Image                */
        IH_TYPE_MULTI,                        /* Multi-File Image                */
        IH_TYPE_FIRMWARE,                /* Firmware Image                */
        IH_TYPE_SCRIPT,                        /* Script file                        */
        IH_TYPE_FILESYSTEM,                /* Filesystem Image (any type)        */
        IH_TYPE_FLATDT,                        /* Binary Flat Device Tree Blob        */
        IH_TYPE_KWBIMAGE,                /* Kirkwood Boot Image                */
        IH_TYPE_IMXIMAGE,                /* Freescale IMXBoot Image        */
        IH_TYPE_UBLIMAGE,                /* Davinci UBL Image                */
        IH_TYPE_OMAPIMAGE,                /* TI OMAP Config Header Image        */
        IH_TYPE_AISIMAGE,                /* TI Davinci AIS Image                */
        /* OS Kernel Image, can run from any load address */
        IH_TYPE_KERNEL_NOLOAD,
        IH_TYPE_PBLIMAGE,                /* Freescale PBL Boot Image        */
        IH_TYPE_MXSIMAGE,                /* Freescale MXSBoot Image        */
        IH_TYPE_GPIMAGE,                /* TI Keystone GPHeader Image        */
        IH_TYPE_ATMELIMAGE,                /* ATMEL ROM bootable Image        */
        IH_TYPE_SOCFPGAIMAGE,                /* Altera SOCFPGA CV/AV Preloader */
        IH_TYPE_X86_SETUP,                /* x86 setup.bin Image                */
        IH_TYPE_LPC32XXIMAGE,                /* x86 setup.bin Image                */
        IH_TYPE_LOADABLE,                /* A list of typeless images        */
        IH_TYPE_RKIMAGE,                /* Rockchip Boot Image                */
        IH_TYPE_RKSD,                        /* Rockchip SD card                */
        IH_TYPE_RKSPI,                        /* Rockchip SPI image                */
        IH_TYPE_ZYNQIMAGE,                /* Xilinx Zynq Boot Image */
        IH_TYPE_ZYNQMPIMAGE,                /* Xilinx ZynqMP Boot Image */
        IH_TYPE_ZYNQMPBIF,                /* Xilinx ZynqMP Boot Image (bif) */
        IH_TYPE_FPGA,                        /* FPGA Image */
        IH_TYPE_VYBRIDIMAGE,        /* VYBRID .vyb Image */
        IH_TYPE_TEE,            /* Trusted Execution Environment OS Image */
        IH_TYPE_FIRMWARE_IVT,                /* Firmware Image with HABv4 IVT */
        IH_TYPE_PMMC,            /* TI Power Management Micro-Controller Firmware */
        IH_TYPE_STM32IMAGE,                /* STMicroelectronics STM32 Image */
        IH_TYPE_SOCFPGAIMAGE_V1,        /* Altera SOCFPGA A10 Preloader        */
        IH_TYPE_MTKIMAGE,                /* MediaTek BootROM loadable Image */
        IH_TYPE_IMX8MIMAGE,                /* Freescale IMX8MBoot Image        */
        IH_TYPE_IMX8IMAGE,                /* Freescale IMX8Boot Image        */
        IH_TYPE_COPRO,                        /* Coprocessor Image for remoteproc*/


        IH_TYPE_COUNT,                        /* Number of image types */
};


ih_comp = 0x00;
ih_name = 0x 4c69 6e75 782d 342e 3139 2e31 3535 = Linux-4.19.155




4. boot_fn do_bootm_vxworks()
通过分析编译出来的Kernel 镜像,我们得知ih_os = 0x05 ,结合宏控定义,我们知道了boot_os数组内容如下:


# devicehisiliconthird_partyubootu-boot-2020.01commonbootm_os.c
static boot_os_fn *boot_os[] = {
        [IH_OS_U_BOOT] = do_bootm_standalone,
        [IH_OS_LINUX] = do_bootm_linux,
        [IH_OS_NETBSD] = do_bootm_netbsd,
        [IH_OS_RTEMS] = do_bootm_rtems,
        [IH_OS_PLAN9] = do_bootm_plan9,
        [IH_OS_VXWORKS] = do_bootm_vxworks,
        [IH_OS_QNX] = do_bootm_qnxelf,
};


而0x05对应的启动函数是 do_bootm_vxworks(),由此可以前面我们猜测它走 do_bootm_linux 还是有点问题的。


那我们应补充分析下 do_bootm_vxworks()的工作流程,看看和 do_bootm_linux有什么不一样。


# devicehisiliconthird_partyubootu-boot-2020.01archriscvlibbootm.c
int do_bootm_vxworks(int flag, int argc, char * const argv[],
                     bootm_headers_t *images)
{
        return do_bootm_linux(flag, argc, argv, images);
}


  进入代码,发现do_bootm_vxworks 完全就是对 do_bootm_linux 的重新封装, emmmmmmmmmm,好吧!
  该做晚饭吃了,本文就写到这吧。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分