Uboot 2017.01 SPL中的image_loader

来源:互联网 发布:淘宝优惠券派发 编辑:程序博客网 时间:2024/05/19 06:49

    • 概述
    • 原型
    • 使用
    • 定义
    • 总结

概述:

有些厂商的MCU的启动过程是:ROM code > SPL > uboot。也就是在Uboot启动前还需要一个SPL来进行一系列的初始化工作,而SPL和UBoot有什么区别呢?我认为最大的区别是:SPL对镜像大小是否敏感,不能超过指定大小,并且其运行环境是在片内RAM中
于是,我们在ROM code阶段后,就是SPL阶段了。而SPL阶段初始化完外部SDRAM后,此时就有环境来去运行我们Uboot了。这个时候需要加载Uboot镜像,在SPL中就是使用image_loader 来实现的。


原型:

struct spl_image_loader {#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT    const char *name;#endif    uint boot_device;    /**     * load_image() - Load an SPL image     *     * @spl_image: place to put image information     * @bootdev: describes the boot device to load from     */    int (*load_image)(struct spl_image_info *spl_image,              struct spl_boot_device *bootdev);};

所有的image_loader都是一个spl_image_loader结构体。其中有一个关键函数 int load_image函数。该函数不同的设备有不同的实现,而这个函数就是用来加载在该设备下的image。

那么,我们是怎么定义一个image_loader的呢?
首先我们要看,在Uboot中,是怎么找到并使用这个image_loader的。
先用,再来去分析它的机制


使用:

在SPL代码的最后阶段就是去加载内核镜像或者加载Uboot镜像。
首先是在spl.c中的board_init_r函数中调用boot_from_devices()函数来进行加载镜像。

void board_init_r(){    .....       if (boot_from_devices(&spl_image, spl_boot_list,                  ARRAY_SIZE(spl_boot_list))) {        puts("SPL: failed to boot from all boot devices\n");        hang();    }    ......}

进入boot_from_devices查看:

static int boot_from_devices(struct spl_image_info *spl_image,                 u32 spl_boot_list[], int count){    int i;    for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {        struct spl_image_loader *loader;        loader = spl_ll_find_loader(spl_boot_list[i]);#if defined(CONFIG_SPL_SERIAL_SUPPORT) && defined(CONFIG_SPL_LIBCOMMON_SUPPORT)        if (loader)            printf("Trying to boot from %s", loader->name);        else            puts("SPL: Unsupported Boot Device!\n");#endif        if (loader && !spl_load_image(spl_image, loader))            return 0;    }    return -ENODEV;}

根据函数的名称,我们就可以猜测到,spl_ll_find_loader 函数就是用来寻找相应的image_loader的。

那么让我们继续进入spl_ll_find_loader函数,看看这里面是什么鬼东西。

static struct spl_image_loader *spl_ll_find_loader(uint boot_device){    struct spl_image_loader *drv =        ll_entry_start(struct spl_image_loader, spl_image_loader);    const int n_ents =        ll_entry_count(struct spl_image_loader, spl_image_loader);    struct spl_image_loader *entry;    for (entry = drv; entry != drv + n_ents; entry++) {        if (boot_device == entry->boot_device)            return entry;    }    /* Not found */    return NULL;}

首先我们来分析下第一行。
drv的值是通过ll_entry_start宏进行获取的。现在我们先不管这个宏是干嘛的。
让我们来接着看下去:
n_ents是通过ll_entry_count宏,进行判断的。我们可以通过这个函数名初步判断,这个是获取image_loader的数量。

最后,就到了我们一个for循环,我们先进行猜测,这个函数是干嘛的。
这个函数的目标就是获取一个对应设备的的image_loader。那么肯定会涉及到对每个image_loader进行遍历比较。而比较的一个关键点就是,image_loader的一个成员变量boot_device。
那么根据我们的猜测来去分析下面的for循环可能就能有着事半功倍的效果。

首先,我们的for循环开始,
entry 等于我们一开始通过ll_entry_start宏获取的一个image_loader。我们可以判断,这是drv是第一个image_loader。
然后判断为true条件就是entry 不等于 第一个image_loader加上所有的image_loader的总数。
并且,在for循环里面,进行对比的正是通过boot_device进行对比。

这都验证了我们的猜测。现在基本上这个函数的功能我们已经摸清了。但是,我们仍然有一个十分关键的地方没有摸清楚。

那就是,我们是在哪里定义各个设备对应的image_loader的呢?只有找到这个,我们才能够知道,SPL是来加载镜像的。

定义

要判断各个设备对应的的Image_loader是怎么工作,那么久必须要找到他们定义它的地方。那么我们应该怎么找到呢?
这个时候之前没有分析的宏就能够派上用场了ll_entry_start。让我们来看看,Uboot它自己是怎么找到各个设备的image_loader的呢。

首先,让我们来看看ll_entry_start的原型。

#define ll_entry_start(_type, _list)                    \({                                  \    static char start[0] __aligned(4) __attribute__((unused,    \        section(".u_boot_list_2_"#_list"_1")));         \    (_type *)&start;                        \})

这个宏,它定义了一个只有0个char的数组start。而这个start数组,指向的是一个section的地址:”.u_boot_list_2_”#_list”_1”。把这个展开就是.u_boot_list_2_spl_image_loader_1。也就是说,我们的image_loader是保存在uboot镜像中的u_boot_list_2_spl_image_loader_1段中。

于是struct spl_image_loader *drv就是等于,struct spl_image_loader *drv = u_boot_list_2_spl_image_loader_1段的起始地址。

其实看到这里,后面的代码细节就没有必要看了。我们可以通过这个section来去寻找在哪里定义了各个设备的image_loader。

通过进一步的分析,我们可以知道,实际上定义这个section并进行添加的是SPL_LOAD_IMAGE_METHOD这个宏。我们在uboot的源码中进行查找,不出所料我们找到了,

SPL_LOAD_IMAGE_METHOD("MMC1", 0, BOOT_DEVICE_MMC1, spl_mmc_load_image);SPL_LOAD_IMAGE_METHOD("MMC2", 0, BOOT_DEVICE_MMC2, spl_mmc_load_image);SPL_LOAD_IMAGE_METHOD("MMC2_2", 0, BOOT_DEVICE_MMC2_2, spl_mmc_load_image);

现在,我们就得到了,在mmc设备上,SPL是如何加载镜像的。是通过spl_mmc_load_image函数进行加载。
函数原型如下:

int spl_mmc_load_image(struct spl_image_info *spl_image,               struct spl_boot_device *bootdev){    struct mmc *mmc = NULL;    u32 boot_mode;    int err = 0;    __maybe_unused int part;    err = spl_mmc_find_device(&mmc, bootdev->boot_device);    if (err)        return err;    err = mmc_init(mmc);    if (err) {#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT        printf("spl: mmc init failed with error: %d\n", err);#endif        return err;    }    boot_mode = spl_boot_mode(bootdev->boot_device);    err = -EINVAL;    switch (boot_mode) {    case MMCSD_MODE_EMMCBOOT:            /*             * We need to check what the partition is configured to.             * 1 and 2 match up to boot0 / boot1 and 7 is user data             * which is the first physical partition (0).             */            part = (mmc->part_config >> 3) & PART_ACCESS_MASK;            if (part == 7)                part = 0;            if (CONFIG_IS_ENABLED(MMC_TINY))                err = mmc_switch_part(mmc, part);            else                err = blk_dselect_hwpart(mmc_get_blk_desc(mmc), part);            if (err) {#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT                puts("spl: mmc partition switch failed\n");#endif                return err;            }            /* Fall through */    case MMCSD_MODE_RAW:        debug("spl: mmc boot mode: raw\n");        if (!spl_start_uboot()) {            err = mmc_load_image_raw_os(spl_image, mmc);            if (!err)                return err;        }        err = mmc_load_image_raw_partition(spl_image, mmc,            CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION);        if (!err)            return err;#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR        err = mmc_load_image_raw_sector(spl_image, mmc,            CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);        if (!err)            return err;#endif        /* If RAW mode fails, try FS mode. */    case MMCSD_MODE_FS:        debug("spl: mmc boot mode: fs\n");        err = spl_mmc_do_fs_boot(spl_image, mmc);        if (!err)            return err;        break;#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT    default:        puts("spl: mmc: wrong boot mode\n");#endif    }    return err;}

总结

在Uboot中,经常使用的到一个技术就是将一部分的数据保存在特定的段中。

原创粉丝点击