您的位置:首页 > 其它

从零移植uboot 2017 到nuc970(第五天)

2017-03-02 18:39 537 查看
今天接着看b_i_r,首先看到结构体
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 image
     spl_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了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  u-boot 移植