从零移植uboot 2017 到nuc970(第五天)
2017-03-02 18:39
537 查看
今天接着看b_i_r,首先看到结构体
我首先可以说 linker-g..是个链接时候确定的东西,那么可以模糊理解,spl_image_loader结构体
继续往下,这是初步的认识,后面一定会越来越清楚实现。
那么最后 spl_ll_find_loader返回的是上述的结构体的地址,而且该结构体的具体数值是ll_entry_start中给于的先不着急解决上面的问题,有个函数更加重要,一直要找到到底哪个函数搬移了代码到ddr这是关键。
debug("loaded - jumping to U-Boot...");
spl_board_prepare_for_boot(); 该函数是空函数,nothing jump_to_image_no_args(&spl_image);最后一个函数
其实这个规则很简单,只要记着ifdef 只负责检查变量是否为empty,而不expend 而在/include/configs/xxx.h中只是定义,但是make config后会有生成的文件,里面会把定义的宏 变成xxx =y的模式,也就是autoconfg.mk文件里面。从这个makefile得知 CONFIG_SPL_BUILD CONFIG_SPL_FRAMEWORK 是需要同时配置的,但是我不使用spl_nand支持,所以我先只要这两个定义。之后从主makefile找什么地方include这个spl的makefile
# KBUILD_SRC is set on invocation of make in OBJ directory
# KBUILD_SRC is not intended to be used by the regular user (for now)
这里我死活找不到kbuild_src的出处,看样子是make 作为cml 传进去的,那么后面的解释,我就当在说我吧。那么srctree,objtree,src,obj都等于.也就是uboot的主目录了。
今天我自己分析就到这里,下面还要去查阅相关的lds的语法知识,之后去单单编译 uboot-spl.bin了
struct spl_image_info { const char *name; u8 os; u32 load_addr;// 这个很有意思 用uboot 命令去加载kernel的时候也会涉及到这两个名词,load_addr,entry_point u32 entry_point; u32 size; u32 flags; };首先也确定了这里spl_image 可能是kernel,也可能是uboot本身(非spl)spl_board_init();这个函数先进去看看 首先根据名字是板级初始化,一些老代码:
void nand_boot(void) { struct nand_chip nand_chip; nand_info_t nand_info; __attribute__((noreturn)) void (*uboot)(void); /* * Init board specific nand support */ nand_chip.select_chip = NULL; nand_info.priv = &nand_chip; nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; nand_chip.dev_ready = NULL; /* preset to NULL */ nand_chip.options = 0; board_nand_init(&nand_chip);//这个很重要了,为初始化nand,逻辑上也很正确,要想加载nand 镜像 必须先驱动nand,具体函数稍后分析 if (nand_chip.select_chip) nand_chip.select_chip(&nand_info, 0); if (nand_scan(&nand_info, 1)) //CWWeng return; nand_register(0);//CWWeng /* * Load U-Boot image from NAND into RAM */ nand_load(&nand_info, CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); #ifdef CONFIG_NAND_ENV_DST nand_load(&nand_info, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, (uchar *)CONFIG_NAND_ENV_DST); #ifdef CONFIG_ENV_OFFSET_REDUND nand_load(&nand_info, CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); #endif #endif if (nand_chip.select_chip) nand_chip.select_chip(&nand_info, -1); /* * Jump to U-Boot image */ uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; (*uboot)(); }这里穿插下就u2017里面的一个函数 board_boot_order(spl_boot_list);其中 board_boot_order 是_weak标记的 其中call spl_boot_device()这个函数,板级相关,所以我放到我的板级里面,但是我不想这么麻烦,我删掉这个函数直接给于BOOT_DEVICE_NAND后期如果要增加启动的device选择 再把此函数加上。uboot 2017中的boot_from_devices很重要了,看看具体代码。
/** * boot_from_devices() - Try loading an booting U-Boot from a list of devices * * @spl_image: Place to put the image details if successful * @spl_boot_list: List of boot devices to try * @count: Number of elements in spl_boot_list * @return 0 if OK, -ve on error */ 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_image_loader /** * Holds information about a way of loading an SPL image * * @name: User-friendly name for this method (e.g. "MMC") * @boot_device: Boot device that this loader supports * @load_image: Function to call to load image */ 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); };最后存贮一个函数指针,这个很重要,也就是这里入加载了uboot的image,也就是u-boot.bin。所以看看这个函数把 给于两个参数,一个spl_image的信息,一个boot_device的信息。ok,具体是哪个的函数要看指针给谁了。
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; }这个函数 返回结构指针,具体还要再
* ll_entry_start() - Point to first entry of linker-generated array * @_type: Data type of the entry * @_list: Name of the list in which this entry is placed * * This function returns (_type *) pointer to the very first entry of a * linker-generated array placed into subsection of .u_boot_list section * specified by _list argument. * Since this macro defines an array start symbol, its leftmost index * must be 2 and its rightmost index must be 1. * * Example: * struct my_sub_cmd *msc = ll_entry_start(struct my_sub_cmd, cmd_sub); */ #define ll_entry_start(_type, _list) \ ({ \ static char start[0] __aligned(4) __attribute__((unused, \ section(".u_boot_list_2_"#_list"_1"))); \ (_type *)&start; \ })这个函数是个宏函数,具体拓展就不看了,但是只要把握主要目的也能搞清楚,l_entry_count() - Return the number of elements in linker-generated array
我首先可以说 linker-g..是个链接时候确定的东西,那么可以模糊理解,spl_image_loader结构体
* @name: User-friendly name for this method (e.g. "MMC") * @boot_device: Boot device that this loader supports * @load_image: Function to call to load imagespl_ll_find_loader 接受一个 boot_device_nand的参数且函数临时变量drv 存储链接的最初地址,n_ent存储总过有多少个spl_image_loader sturct ,for 来遍历每个struct找到,boot_device是boot_device_nand的存储地址,然后返回这个里面entry++,每次加1,有个疑问就是 加一 会是下一个 struct的地址吗。不过不纠结小细节实现
继续往下,这是初步的认识,后面一定会越来越清楚实现。
那么最后 spl_ll_find_loader返回的是上述的结构体的地址,而且该结构体的具体数值是ll_entry_start中给于的先不着急解决上面的问题,有个函数更加重要,一直要找到到底哪个函数搬移了代码到ddr这是关键。
static int spl_load_image(struct spl_image_info *spl_image, struct spl_image_loader *loader) { struct spl_boot_device bootdev; bootdev.boot_device = loader->boot_device; bootdev.boot_device_name = NULL; return loader->load_image(spl_image, &bootdev); }这个函数 太重要了, 这个里面只看return loader->load_image(spl_image, &bootdev);因为前面我直接定义boot_device_nand,这里可以这么改return loader->load_image(spl_image, boot_device_nand)这个loader->load_image调用了具体的搬移函数,但是到底是哪个函数呢,只有看哪个结构体存储的数值了。最后从目前代码来说,想要分析,struct里面第三个元素的值,也既搬移uboot代码的函数指针的值,是不可能的。后面拓展宏函数,和看链接脚本,应该能分析出来,但是这里只要找到在这里实现了把uboot从nand搬到ddr,就算可以了。之后boot_from_device成功,
switch (spl_image.os) { case IH_OS_U_BOOT: debug("Jumping to U-Boot\n"); break; #ifdef CONFIG_SPL_OS_BOOT case IH_OS_LINUX: debug("Jumping to Linux\n"); spl_board_prepare_for_linux(); jump_to_image_linux(&spl_image, (void *)CONFIG_SYS_SPL_ARGS_ADDR); #endif default: debug("Unsupported OS image.. Jumping nevertheless..\n"); }
这个我没啥好说的,做出个选择,我们uboot做出选择,查询CONFIG_SPL_OS_BOOT。Enable booting directly to an OS from SPL这里有点神奇,spl直接引导内核,是不是有点可怕,不过留个印象,以后想开发相关功能可以尝试
#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE) debug("SPL malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr, gd->malloc_ptr / 1024); #endif关于malloc不是重点
debug("loaded - jumping to U-Boot...");
spl_board_prepare_for_boot(); 该函数是空函数,nothing jump_to_image_no_args(&spl_image);最后一个函数
__weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image) { typedef void __noreturn (*image_entry_noargs_t)(void); image_entry_noargs_t image_entry = (image_entry_noargs_t)(unsigned long)spl_image->entry_point; debug("image entry point: 0x%X\n", spl_image->entry_point); image_entry(); }spl_image->entry_point ,这个地址就是最开始说的 loader address 和 entry_point的e_p image_entry();这里就直接进去执行uboot了,有个疑问,按道理这是个入口地址,这么就调用了吗?具体机制以后再说。这里算是spl分支完全粗略的分析完了,后面要思考的一点就是 uboot和uboot-spl不是有通用代码嘛,那么可能相同的代码执行了两遍,这个问题要好好想想,是怎么处理的。接下来以分析spl makefile和lds,具体然后递进代码里面回顾一些以前模糊的,未解决的问题。位于/common/spl的makefile:
ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_FRAMEWORK) += spl.o obj-$(CONFIG_SPL_LOAD_FIT) += spl_fit.o obj-$(CONFIG_SPL_NOR_SUPPORT) += spl_nor.o obj-$(CONFIG_SPL_YMODEM_SUPPORT) += spl_ymodem.o ifndef CONFIG_SPL_UBI obj-$(CONFIG_SPL_NAND_SUPPORT) += spl_nand.o obj-$(CONFIG_SPL_ONENAND_SUPPORT) += spl_onenand.o endif obj-$(CONFIG_SPL_UBI) += spl_ubi.o obj-$(CONFIG_SPL_NET_SUPPORT) += spl_net.o obj-$(CONFIG_SPL_MMC_SUPPORT) += spl_mmc.o obj-$(CONFIG_SPL_USB_SUPPORT) += spl_usb.o obj-$(CONFIG_SPL_FAT_SUPPORT) += spl_fat.o obj-$(CONFIG_SPL_EXT_SUPPORT) += spl_ext.o obj-$(CONFIG_SPL_SATA_SUPPORT) += spl_sata.o obj-$(CONFIG_SPL_DFU_SUPPORT) += spl_dfu.o obj-$(CONFIG_SPL_SPI_LOAD) += spl_spi.o endif ################################################################# ifdef variable-name The ifdef form takes the name of a variable as its argument, not a reference to a variable. If the value of that variable has a non-empty value, the text-if-true is effective; otherwise, the text-if-false, if any, is effective. Variables that have never been defined have an empty value. The text variable-name is expanded, so it could be a variable or function that expands to the name of a variable. For example: bar = true foo = bar ifdef $(foo) frobozz = yes endif The variable reference $(foo) is expanded, yielding bar, which is considered to be the name of a variable. The variable bar is not expanded, but its value is examined to determine if it is non-empty. Note that ifdef only tests whether a variable has a value. It does not expand the variable to see if that value is nonempty. Consequently, tests using ifdef return true for all definitions except those like foo =. To test for an empty value, use ifeq ($(foo),). For example, bar = foo = $(bar) ifdef foo frobozz = yes else frobozz = no endif sets ‘frobozz’ to ‘yes’, while: foo = ifdef foo frobozz = yes else frobozz = no endif sets ‘frobozz’ to ‘no’.
其实这个规则很简单,只要记着ifdef 只负责检查变量是否为empty,而不expend 而在/include/configs/xxx.h中只是定义,但是make config后会有生成的文件,里面会把定义的宏 变成xxx =y的模式,也就是autoconfg.mk文件里面。从这个makefile得知 CONFIG_SPL_BUILD CONFIG_SPL_FRAMEWORK 是需要同时配置的,但是我不使用spl_nand支持,所以我先只要这两个定义。之后从主makefile找什么地方include这个spl的makefile
spl/u-boot-spl.bin: spl/u-boot-spl @: spl/u-boot-spl: tools prepare \ $(if $(CONFIG_OF_SEPARATE)$(CONFIG_SPL_OF_PLATDATA),dts/dt.dtb) $(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all首先 看srctree的定义
ifeq ($(KBUILD_SRC),) # building in the source tree srctree := . else ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR))) # building in a subdirectory of the source tree srctree := .. else srctree := $(KBUILD_SRC) endif endif objtree := . src := $(srctree) obj := $(objtree)然后看KBUILD_SRC的具体定义
# KBUILD_SRC is set on invocation of make in OBJ directory
# KBUILD_SRC is not intended to be used by the regular user (for now)
这里我死活找不到kbuild_src的出处,看样子是make 作为cml 传进去的,那么后面的解释,我就当在说我吧。那么srctree,objtree,src,obj都等于.也就是uboot的主目录了。
spl/u-boot-spl: tools prepare \ $(if $(CONFIG_OF_SEPARATE)$(CONFIG_SPL_OF_PLATDATA),dts/dt.dtb) $(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all再回到这里, 找 makefile.spl 中的 all目标 。呜呼,实在是头大 头大,很容易被引跑偏,我们看makefile 只是想看,编译出uboot-spl.bin 用了那些文件,和用了什么lds 本着这个目的再次捋一下
all: $(ALL-y) quiet_cmd_cat = CAT $@ cmd_cat = cat $(filter-out $(PHONY), $^) > $@ quiet_cmd_copy = COPY $@ cmd_copy = cp $< $@ //而
ALL-y += $(obj)/$(SPL_BIN).bin //而
ifeq ($(CONFIG_SPL_OF_CONTROL)$(CONFIG_OF_SEPARATE)$(CONFIG_SPL_OF_PLATDATA),yy) $(obj)/$(SPL_BIN)-dtb.bin: $(obj)/$(SPL_BIN)-nodtb.bin $(obj)/$(SPL_BIN)-pad.bin \ $(obj)/$(SPL_BIN).dtb FORCE $(call if_changed,cat) $(obj)/$(SPL_BIN).bin: $(obj)/$(SPL_BIN)-dtb.bin FORCE $(call if_changed,copy) else $(obj)/$(SPL_BIN).bin: $(obj)/$(SPL_BIN)-nodtb.bin FORCE $(call if_changed,copy) endif // 这里有些地方先放下找到
$(obj)/$(SPL_BIN): $(u-boot-spl-platdata) $(u-boot-spl-init) \
$(u-boot-spl-main) $(obj)/u-boot-spl.lds FORCE $(call if_changed,u-boot-spl) //这里
$(obj)/u-boot-spl.lds: $(LDSCRIPT) FORCE $(call if_changed_dep,cpp_lds) //ldscript又在这里 # Linker Script ifdef CONFIG_SPL_LDSCRIPT # need to strip off double quotes LDSCRIPT := $(addprefix $(srctree)/,$(CONFIG_SPL_LDSCRIPT:"%"=%)) endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot-spl.lds endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot-spl.lds endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot-spl.lds endif
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { __image_copy_start = .; *(.vectors) CPUDIR/start.o (.text*) *(.text*) } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data*) } . = ALIGN(4); .u_boot_list : { KEEP(*(SORT(.u_boot_lis c07e t*))); } . = ALIGN(4); __image_copy_end = .; .rel.dyn : { __rel_dyn_start = .; *(.rel*) __rel_dyn_end = .; } .end : { *(.__end) } _image_binary_end = .; .bss __rel_dyn_start (OVERLAY) : { __bss_start = .; *(.bss*) . = ALIGN(4); __bss_end = .; } __bss_size = __bss_end - __bss_start; .dynsym _image_binary_end : { *(.dynsym) } .dynbss : { *(.dynbss) } .dynstr : { *(.dynstr*) } .dynamic : { *(.dynamic*) } .hash : { *(.hash*) } .plt : { *(.plt*) } .interp : { *(.interp*) } .gnu : { *(.gnu*) } .ARM.exidx : { *(.ARM.exidx*) } }对比 13版的 lds,13版整个放在nand_spl,对比起来还是发现有些差别的:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { start.o (.text) cpu_init.o (.text) nand_boot.o (.text) *(.text) } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); .got : { *(.got) } __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; . = ALIGN(4); .rel.dyn : { __rel_dyn_start = .; *(.rel*) __rel_dyn_end = .; } .dynsym : { __dynsym_start = .; *(.dynsym) } _end = .; .bss __rel_dyn_start (OVERLAY) : { __bss_start = .; *(.bss) . = ALIGN(4); __bss_end__ = .; } }
今天我自己分析就到这里,下面还要去查阅相关的lds的语法知识,之后去单单编译 uboot-spl.bin了
相关文章推荐
- 从零移植uboot 2017 到nuc970(第二十一天)
- 从零移植uboot 2017 到nuc970(第二十二天)
- 从零移植uboot 2017 到nuc970(第六天)
- 从零移植uboot 2017 到nuc970(第八天)
- 从零移植uboot 2017 到nuc970(第十三天)
- 从零移植uboot 2017 到nuc970(第二十天)
- 从零移植uboot 2017 到nuc970(第十四天)
- 从零移植uboot 2017 到nuc970(第二十三天)(第一阶段结束)
- 从零移植uboot 2017 到nuc970(第一天)
- 从零移植uboot 2017 到nuc970(第二天)
- 从零移植uboot 2017 到nuc970(第三天)
- 从零移植uboot 2017 到nuc970(第十一天)
- 从零移植uboot 2017 到nuc970(第十二天)
- 从零移植uboot 2017 到nuc970(第四天)
- 从零移植uboot 2017 到nuc970(第九天)
- 从零移植uboot 2017 到nuc970(第十天)
- 从零移植uboot 2017 到nuc970(第十六天)
- 从零移植uboot 2017 到nuc970(第十五天)
- 从零移植uboot 2017 到nuc970(第七天)
- 从零移植uboot 2017 到nuc970(第十七天)