机器感知 一个专注于SLAM、Linux、算法等相关技术文章分享的公众号 ![]() |
前言
以前接触到的u-boot启动方式只有Flash和Nand Flash这两种方式,最近接触是SD卡启动方式,SoC是S5P4418,启动方式也第一次接触到,根据S5P4418用户手册可以找到系统使用SD卡启动时,片内iROM内固化的代码会自动映射到0x00地址,也被称为第一阶段引导程序,该程序会检测启动引脚配置,然后从SD卡读取block1~block32内的程序到内置SRAM中执行,读取出来并执行的这段程序被称为第二阶段引导程序,由于其最大为16KB,无法存放完整的u-boot。以上两个引导程序均由芯片厂商提供,没有源码,不开源。第二阶段引导程序从SD卡中加载u-boot到DRAM中执行,至此u-boot才开始登场。
SDHCBOOT --> 2nboot --> u-boot
代码分析
程序入口
由于S5P4418采用的指令集为ARMv7,通常应该去该文件arch/arm/cpu/armv7下寻找该SoC,然而并没有,通过查看board.cfg可以看到它的CPU类型被定义为了slsiap,通过查看arch/arm/cpu/slsiap也确实存在s5p4418文件夹。
# Status, Arch, CPU:SPLCPU, SoC, Vendor
Active arm slsiap s5p4418 s5p4418
接下来就是找到第一个执行的程序在哪里文件里,即找到程序入口,查看slsiap下的链接文件
.text :
{
*(.__image_copy_start)
SDIR/start.o (.text*)
*(.text*)
}
而SDIR是什么并不知道,打开同文件夹下的config.mk
SDIR=arch/arm/cpu$(if $(CPU),/$(CPU),)$(if $(SOC),/$(SOC),)
替换变量之后
SDIR=arch/arm/cpu/slsiap/s5p4418
即第一个文件是arch/arm/cpu/slsiap/s5p4418/start.S
start.S
不同 CPU 的该文件大致功能都大同小异,设置中断向量表,将 CPU 设置为 SVC 模式,然后设置 CP15,设置 Cache,初始化内存,然后对代码进行重定位,之后开始清理 bss 段,为准备 C 语言运行环境,然后设置栈指针,执行 board_init_f 函数填充 global_data 结构体数据,最后执行 board_init_r,离开start.S不再返回。
board_init_r
该函数中调用的两个有意思的函数:serial_initialize、mmc_initialize。
void serial_initialize(void)
{
mpc8xx_serial_initialize();
...省略若干类似函数...
s5p_serial_initialize();
...省略若干类似函数...
arc_serial_initialize();
}
可以看到,该函数中不加判断的把各种种类的串口初始化都给写上,也没有使用宏去自动选择使用哪个,给人的第一感觉就是这不是浪费存储空间么,继续查看该函数所在文件其他代码可以发现
/**
* serial_initfunc() - Forward declare of driver registration routine
* @name: Name of the real driver registration routine.
*
* This macro expands onto forward declaration of a driver registration
* routine, which is then used below in serial_initialize() function.
* The declaration is made weak and aliases to serial_null() so in case
* the driver is not compiled in, the function is still declared and can
* be used, but aliases to serial_null() and thus is optimized away.
*/
#define serial_initfunc(name) \
void name(void) \
__attribute__((weak, alias("serial_null")));
serial_initfunc(mpc8xx_serial_initialize);
...省略若干类似函数...
serial_initfunc(s5p_serial_initialize);
...省略若干类似函数...
serial_initfunc(arc_serial_initialize);
英文注释说的很清楚了,这是在利用编译器特性进行自动优化的,使用 __weak 属性先声明驱动程序,因为编译程序之前会先配置程序,在编译的时候在配置阶段产生的宏在预编译阶段会决定哪些驱动程序被编译进来,比如 S5P4418 被选择时,相关的串口驱动 s5p_serial_initialize 就会编译进来,由于其声明为 __weak 属性,编译时会自动覆盖掉前项声明的函数,这样函数 serial_initialize 就不会出现把不必要的程序编译进来的情况了。
mmc_initialize 函数的分析见下回分解吧~