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中,经常使用的到一个技术就是将一部分的数据保存在特定的段中。
- Uboot 2017.01 SPL中的image_loader
- uboot spl
- uboot SPL
- Image_loader
- uboot SPL Overview
- uboot SPL Overview
- AM335x uboot spl分析
- AM335x uboot spl分析
- uboot SPL Overview
- AM335x uboot spl分析
- AM335x uboot spl分析
- uboot spl分析
- AM335x uboot spl分析
- AM335x uboot spl分析
- AM335x uboot spl分析
- 【续】AM335x uboot spl分析
- uboot SPL Overview
- uboot SPL Overview
- 动态规划、记忆化搜索:HDU1505-City Game(记忆化搜索)
- MyBatis入门基础(一)
- springboot (1) 快速搭建简单项目
- arcengine栅格渲染(栅格重分类) IRasterClassifyColorRampRenderer
- php 去掉最大值,最小值,求出平均值(简洁代码)
- Uboot 2017.01 SPL中的image_loader
- 幻想乡系列-----例大祭
- 归一化的意义笔记
- 进制转换
- HTTP 请求方式: GET和POST的比较
- iframe标签里面的页面元素只读
- JAVA设计模式初探之装饰者模式
- Hadoop好友推荐系统-HDFS的文件上传和下载
- hdu 2838 Cow Sorting(树状数组)