uboot中会根据情况将变量或函数放到指定的段section中,例如uboot cmd功能
这样做可以使添加的代码与uboot其他模块相对独立,例如增加一个cmd,只需要将具体操作实现,然后调用一个接口将cmd.ops放到相应段里即可。
以君正x1000e uboot为源码说明:
具体使用如下:
drivers/mtd/devices/nand_device/mxic_nand.c
1、定义flash.info,例如芯片id、容量等信息
2、实现旺宏(mxic)nand-flash芯片的读、写等接口
3、实现mxic_nand_init接口,将ops注册进nand队列中,用于启动时读取flash id后,获取对应的读写接口
4、调用SPINAND_MOUDLE_INIT(mxic_nand_init)
SPINAND_MOUDLE_INIT定义如下:
typedef int32_t (*spinand_regcall_t)(void);
#define ingenic_entry_declare(_type, _name, _list) \
static _type ingenic_##_list##_2_##_name __aligned(4) \
__attribute__((used, \
section(".u_boot_list_2_"#_list"_2_"#_name)))
#define SPINAND_MOUDLE_INIT(fn) \
ingenic_entry_declare(spinand_regcall_t, _1##fn, flash) = fn
##:以字符串连接前后两个成员,例如上面即ingenic_##list##2 ==> ingenic_flash_2
__aligned:在include/linux/compiler-gcc.h中定义,类似aligned,指定以4字节对齐
attribute:为定义的成员指定属性,即为static spinand_regcall_t ingenic_flash_2__1mxic_nand_init __aligned(4)指定属性
section:指定放到哪个段中,这里是.u_boot_list_2_flash_2__1mxic_nand_init段
综上,SPINAND_MOUDLE_INIT(mxic_nand_init)展开为:
static spinand_regcall_t ingenic_flash_2__1mxic_nand_init __aligned(4) \
__attribute__((used, \
section(".u_boot_list_2_flash_2__1mxic_nand_init))) = mxic_nand_init
它定义了一个函数static spinand_regcall_t ingenic_flash_2__1mxic_nand_init,它指向mxic_nand_init
如何执行呢?
driver/mtd/devices/jz_sfc_nand.c::jz_sfc_nand_init()
jz_sfc_nand_init:
spinand_moudle_init::
static inline int32_t spinand_moudle_init(void)
{
spinand_regcall_t *call = ll_entry_start(spinand_regcall_t, flash);
int ret = 0 , i, count;
for (i = 0, count = ll_entry_count(spinand_regcall_t, flash);
i < count;
call++, i++) {
ret = (*call)();
if (ret) {
printf("jz spi nand ops init func error\n");
break;
}
}
return ret;
}
其中ll_entry_start在include/linker_lists.h中定义:
#define ll_entry_start(_type, _list) \
({ \
static char start[0] __aligned(4) __attribute__((unused, \
section(".u_boot_list_2_"#_list"_1"))); \
(_type *)&start; \
})
将ll_entry_start(spinand_regcall_t, flash);展开即
static char start[0] __aligned(4) __attribute__((unused, \
section(".u_boot_list_2_flash_1"))); \
(_type *)&start;
这里定义了一个位于.u_boot_list_2_flash_1段的start变量,将它的地址返回
即spinand_regcall_t *call = (spinand_regcall_t *)&start;
之前将mxic_nand_init放入的是.u_boot_list_2_flash_2__1mxic_nand_init段中
为什么可以通过.u_boot_list_2_flash_1段找到mxic_nand_init呢?
这是因为编译uboot时,指定了lds,其中,不同架构有其对应的lds文件,这里用到:
./arch/mips/cpu/u-boot.lds
SECTIONS
{
....
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
....
}
lds规定了,编译时将段以什么规则去存放,这里就将段中的u_boot_list*按照大小顺序进行排列
即定义的.u_boot_list_2_flash_1,一定位于定义的.u_boot_list_2_flash_2_xxxxxx的头部
至此,流程全部结束。