u-boot.bin 从 start.S 中的 reset 开始执行,执行一些 CPU 底层初始化,然后跳转到 crt0.S 中的_main函数。
这里仅为执行 board_init_f 函数设置栈,以及为全局变量 gd 预留一块存储空间。 在 u-boot-spl.bin 中
调用了 copy_bl2_to_ram,将 u-boot.bin 拷贝到 DDR 的基地址 0x20000000,然后给 PC 指针赋值为 DDR
的基地址直接跳转到 0x20000000 地址取指执行。 gd 保存了一些重要的全局的变量,其定义在
u-boot-2014.04/arch/arm/include/asm/global_data.h 头文件中。
从这得知全局变量 gd 是一个寄存器变量, 保存在 r9 寄存器中, gd_t 结构体定义在
u-boot-2014.04/include/asm-generic/global_data.h。
CONFIG_SYS_INIT_SP_ADDR 在单板配置文件 smdkv210.h 中定义。这个可以随便设置,但要保证能够
供 board_init_f 使用,以及存储 GD ('global data'),我们可以直接设置为 DDR 的最高地址 0x60000000
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + PHYS_SDRAM_1_SIZE) /* modied by Louis */
其中 PHYS_SDRAM_1_SIZE 定义为 DDR 的大小
#define PHYS_SDRAM_1 CONFIG_SYS_SDRAM_BASE /* SDRAM Bank #1 */
#define PHYS_SDRAM_1_SIZE (1024 << 20) /* 0x40000000, 1024 MB Bank #1 (modied by Louis) */
GD_SIZE 在 u-boot-2014.04/include/generated/generic-asm-offsets.h 中定义,其值为 160
设置后的内存布局如下图:
接着调用 board.c 中的 board_init_f 函数,给它传了一个参数 r0=0,事实上没有用到。这个函数使用 到了一些全局变量,因此我们需要让 u-boot.bin 位于其链接地址,通过修改 smdkv210.h 中的宏CONFIG_SYS_TEXT_BASE 指定其链接地址。 我们将其指定为 u-boot.bin 在 DDR 中的起始地址
首先定义了几个重要的变量: addr 最终为重定位地址, addr_sp 为最终的用户栈指针地址。
初始化全局变量 gd,同时计算出 u-boot.bin 的大小,保存到 gd->mon_len
依次调用数组 init_sequence 中的每个函数,进行一系列初始化操作。
arch_cpu_init 在 u-boot-2014.04/arch/arm/cpu/armv7/s5p-common/cpu_info.c 中定义,它调用了s5p_set_cpu_id 读取 CPU 版本和 ID,保存到 s5p_cpu_rev 和 s5p_cpu_id 中。接着调用 u-boot-2014.04/arch/arm/cpu/armv7/s5p-common/timer.c 中的 timer_init 初始化 PWM 定时器接着调用 serial_init 初始化串口,通过跟踪代码发现在单板配置文件 smdkv210.h 中通过宏CONFIG_SERIAL0 指定使用哪个串口
默认使用的是串口 0, 我们还需要为串口 0 配置 GPIO 端口,在u-boot-2014.04/board/samsung/smdkv210/lowlevel_init.S 中添加代码
接着调用 (\u-boot-2014.04\arch\arm\lib\board.c)display_banner,显示 u-boot 版本信息,我们可以在这个函数中添加显示自己的 log,例如:
接着调用 u-boot-2014.04/arch/arm/cpu/armv7/s5p-common/cpu_info.c 中的 print_cpuinfo,打印 CPU名称和时钟
s5p_get_cpu_name 得到 CPU 的名称, 在 u-boot-2014.04/arch/arm/include/asm/arch-s5pc1xx/cpu.h 中定义为:
其中 s5p_cpu_id 为一个整数,对于 S5PC100 和 S5PC110 可以通过处理得到 0xc100 和 0xc110, 打印出来就是 S5PC100 和 S5PC110,但对于 S5PV210 就没法将那个 v 用整数表示了。 我们直接修改为打印S5PV210。
get_arm_clk 根据 CPU 的 ID 决定调用那个函数来获取时钟,其定义在u-boot-2014.04/arch/arm/cpu/armv7/s5pc1xx/clock.c 中
我们仿照 s5pc110_get_arm_clk 为 S5PV210 实现一个函数 s5pv210_get_arm_clk, S5PC110 和 S5PV210比较相近,然后将 get_arm_clk 修改为
在 s5pv210_get_arm_clk 中调用了 get_pll_clk(APLL)来获得 APLL 输出时钟,其定义为:
同样仿照 s5pc110_get_pll_clk 为 S5PV210 实现一个函数 s5pv210_get_pll_clk
这里用到一个宏 CONFIG_SYS_CLK_FREQ_V210,在单板配置文件 smdkv210.h 中定义
其实为 S5PV210 添的这 2 个函数和 S5PC110 的基本一样。
继续回到 board.c 中的 init_sequence,接着调用
u-boot-2014.04/board/samsung/smdkv210/smdkv210.c 中的 dram_init,其定义为
这里用到的宏 PHYS_SDRAM_1 和 PHYS_SDRAM_1_SIZE 在 smdkv210.h 中定义,分别为 SDRAM 的基地址和大小。 这里得到内存大小,赋值给全局变量 gd->ram_size。
继续往下执行
这句执行后得到: addr=0x60000000,即内存的最高地址。
这里为重定位的 u-boot 预留一块内存空间, gd->mon_len 为 u-boot 的大小
这里开始计算栈指针地址, 为 malloc 预留一块内存空间,作为堆内存
这里为 bd 预留一块内存空间,同时使 gd->bd 指向现在的 addr_sp 所在地址, bd 变量保存了单板的一些信息,比如机器码
这里将配置的机器码保存到 gd->bd->bi_arch_number, 这个机器码必须和内核的机器码相同,否则启动不了内核,我们可以在 smdkv210.h 中定义这个机器码。
这里为 gd 预留一块内存空间,同时让临时变量 id 指向现在 addr_sp 所在地址
将一些关键变量的值保持到全局变量 gd 中, 这里的_start 的地址为 0x20000000,因为在前面分析中,我们通过宏 CONFIG_SYS_TEXT_BASE 将代码段的基地址设置为 0x20000000, addr - (ulong)&_start 刚好得到重定位地址相对 u-boot 当前所处的地址(0x20000000) 的偏移地址。 然后将全局变量 gd 的内容复制到 id 所指向的那块内存,后面的代码会将 gd 指向这块内存。 现在的内存布局如下图所示:
r9 即 gd,其中 GD_START_ADDR_SP、 GD_BD、 GD_SIZE、 GD_RELOC_OFF 和 GD_RELOCADDR 均在
u-boot-2014.04/include/generated/generic-asm-offsets.h 中定义
只要知道这两点,再结合 u-boot 的注释,这段代码就很清楚了。 执行这段代码后的内存布局为:
同时将标号here的相对地址赋值给lr,然后将lr减去将要重定位的地址相对u-boot当前地址的偏移,结果是 lr 保存了 u-boot 重定位后的地址, 调用 relocate_code 重定位完成后,返回跳转到 lr 地址执行(现在已经位于重定位后的区域)。