从0移植uboot启动流程分析

经过了上一篇的配置,我们已经执行make就可以编译出一个uboot.bin,但这还不够,首先,此时的uboot并不符合三星芯片对bootloader的格式要求,其次,此时的uboot.bin也没有结合我们的开发板进行配置,还无法使用。而要进行这样的个性化配置,前提条件就是对uboot开机流程和编译系统有所了解,本文主要讨论前者。uboot是一个两阶段bootloader,第一阶段主要做硬件直接相关的初始化,使用汇编编写;第二阶段主要为操作系统的运行准备环境,主要用C编写,这里以ARM平台为例分析其启动流程。下面是启动过程中主要涉及的文件

arch/arm/cpu/armv7/start.S
board/samsung/myboard/lowlevel_init.S
arch/arm/lib/crt0.S
arch/arm/lib/board.c
arch/samsung/myboard/myboard.c

第一阶段
第一阶段的主要文件和任务如下

arch/arm/cpu/armv7/start.S
1. 设置CPU为SVC模式
2. 关闭中断,MMU,Cache
board/samsung/origen/lowlevel_init.S
3. 关闭看门狗
4. 初始化内存,串口,将uboot 代码从nand 拷贝到sdram ,并跳转到sdram 运行 board_init_f
5. 设置栈
6. 代码自搬移,uboot 自举,运行 board_init_r
7. 清BSS
8. 跳转到C入口????
start.S
39 .globl _start
40 _start: b reset
41 ldr pc, _undefined_instruction
42 ldr pc, _software_interrupt
43 ldr pc, _prefetch_abort
44 ldr pc, _data_abort
45 ldr pc, _not_used
46 ldr pc, _irq
47 ldr pc, _fiq
–40–> 异常向量表设置

126 reset:
127 bl save_boot_params
131 mrs r0, cpsr
132 bic r0, r0, #0x1f
133 orr r0, r0, #0xd3
134 msr cpsr,r0
–126–>设置CPU为SVC模式

下面这三行代码非常重要,是整个uboot启动过程的交叉点

154 bl cpu_init_cp15
155 bl cpu_init_crit
158 bl _main
–154–>跳转执行cpu_init_cp15,即初始化CP15协处理器
–155–>跳转执行cpu_init_crit,
–158–>跳转执行_main,即第二阶段

287 ENTRY(cpu_init_cp15)
291 mov r0, #0 @ set up for MCR
292 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
293 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
294 mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
295 mcr p15, 0, r0, c7, c10, 4 @ DSB
296 mcr p15, 0, r0, c7, c5, 4 @ ISB
297
301 mrc p15, 0, r0, c1, c0, 0
302 bic r0, r0, #0x00002000 @ clear bits 13 (–V-)
303 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
304 orr r0, r0, #0x00000002 @ set bit 1 (–A-) Align
305 orr r0, r0, #0x00000800 @ set bit 11 (Z—) BTB
307 bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
311 mcr p15, 0, r0, c1, c0, 0
312 mov pc, lr @ back to my caller
313 ENDPROC(cpu_init_cp15)
–291–>关闭Cache
–301–>关闭MMU

324 ENTRY(cpu_init_crit)
331 b lowlevel_init @ go setup pll,mux,memory
332 ENDPROC(cpu_init_crit)
–331–>跳转到lowlevel_init,位于board/samsung/origen/lowlevel_init.S,进行板级相关的设置。

lowlevel_init.S
这是位于目录的初始化文件,主要完成特定开发板的初始化工作,包括时钟、内存和串口等。

82 bl system_clock_init
85 bl mem_ctrl_asm_init
87 1:
88 /* for UART */
89 bl uart_asm_init
90 bl tzpc_init
91 pop {pc}
114 system_clock_init:
329 uart_asm_init:
357 tzpc_init:
–82–>初始化系统时钟,即跳转到114行
–85–>初始化系统内存
–89–>初始化UART串口,即跳转到329行
–90–>初始化TrustZoneProtectorController,即跳转到357行

执行完lowlevel_init.S,依据上面那三行代码,执行流程就该回到start.S执行156行跳转到_main,开始执行第二阶段。

第二阶段
从start.S跳转到_main ,标致着uboot启动过程的第二阶段的开始。在第二阶段,核心文件是crt0.S,但我们最关心的是其中回调板级C程序的入口位置。第二阶段的流程如下:

arch/arm/lib/crt0.S
1. 初始化C运行环境,调用board_init_f()
arch/arm/lib/board.c
1. board_init_f对全局信息GD结构体进行填充
arch/arm/lib/crt0.S
1. 代码重定位
2. 代码自搬移
3. 执行超循环
arch/arm/lib/board.c
1. board_init_r()是进入定制板目录的入口

crt0.S
进入第二阶段是首要任务就是准备C语言运行的环境:

96 _main:
102 #if defined(CONFIG_NAND_SPL)
103 /* deprecated, use instead CONFIG_SPL_BUILD /
104 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
105 #elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
106 ldr sp, =(CONFIG_SPL_STACK)
107 #else
108 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
109 #endif
110 bic sp, sp, #7 /
8-byte alignment for ABI compliance /
111 sub sp, #GD_SIZE /
allocate one GD above SP /
112 bic sp, sp, #7 /
8-byte alignment for ABI compliance /
113 mov r8, sp /
GD is above SP */
114 mov r0, #0
115 bl board_init_f
_main
–104–>初始化SP,为C语言做准备
–110–>保存128B放GD结构体来存放全局信息,
–111–>GD的地址放在r8中,
–115–>跳转到board_init_f(),这个整个初始化过程中第一次执行的C代码

board.c
下面这个函数就是uboot初始化过程中执行的第一个C函数,可以看作这个文件的入口函数。函数比较长,我就不逐句分析了,这个函数主要的作用就是执行一些高等级的初始化。其中最重要的就是准备全局信息GD结构体

209 typedef int (init_fnc_t) (void);
243 init_fnc_t init_sequence[] = {
244 arch_cpu_init, /
basic arch cpu dependent setup /
245 mark_bootstage,
246 #ifdef CONFIG_OF_CONTROL
247 fdtdec_check_fdt,

277 void board_init_f(ulong bootflag)
278 {

291 gd->mon_len = _bss_end_ofs;
292 #ifdef CONFIG_OF_EMBED
293 /
Get a pointer to the FDT /
294 gd->fdt_blob = _binary_dt_dtb_start;
295 #elif defined CONFIG_OF_SEPARATE
296 /
FDT is at end of image */
297 gd->fdt_blob = (void )(_end_ofs + _TEXT_BASE);
298 #endif
299 /
Allow the early environment to override the fdt address */
300 gd->fdt_blob = (void *)getenv_ulong(“fdtcontroladdr”, 16,
301 (uintptr_t)gd->fdt_blob);
302
303 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
304 if ((*init_fnc_ptr)() != 0) {
305 hang ();
306 }
307 }

board_init_f()
–243–> 全局的函数指针数组,每个指针都是int (*ptr)(void)型的。
–291–>mon_len 通过链接脚本可以知道存放的是 uboot 代码大小;
–294–>fdt_blob 存放设备数地址;
–303–遍历函数指针数组init_sequence中的每一个成员,就是将数组中的每一个初始化函数都执行一次,这种写法可以借鉴

crt0.S
函数board_init_f()返回后,继续执行crt0.S中115行之后的部分,主要的工作是执行代码自搬移,代码重定位等工作,执行完这些之后,我们我们找到了最感兴趣的下面这几句

163 /* call board_init_r(gd_t *id, ulong dest_addr) /
164 mov r0, r8 /
gd_t /
165 ldr r1, [r8, #GD_RELOCADDR] /
dest_addr /
166 /
call board_init_r /
167 ldr pc, =board_init_r /
this is auto-relocated! */
–167–>跳转到board_init_r函数执行,这次跳出去这个文件的语句就执行完毕了,不会再回来了
board.c
这也是最后一次跳转到这个文件了,执行额函数如下

519 void board_init_r(gd_t id, ulong dest_addr)
520 {
521 ulong malloc_start;
522 #if !defined(CONFIG_SYS_NO_FLASH)
523 ulong flash_size;
524 #endif
525
526 gd->flags |= GD_FLG_RELOC; /
tell others: relocation done /
527 bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, “board_init_r”);
528
529 monitor_flash_len = _end_ofs;
530
531 /
Enable caches /
532 enable_caches();
533
534 debug(“monitor flash len: %08lX\n”, monitor_flash_len);
535 board_init(); /
Setup chipselects /

650 /
set up exceptions /
651 interrupt_init();
652 /
enable exceptions /
653 enable_interrupts();
667 eth_initialize(gd->bd);

701 /
main_loop() can return to retry autoboot, if so just run it again. */
702 for (;😉 {
703 main_loop();
704 }
705
board_init_r()
–532–>很多紧急工作都做完了,可以打开cache了
–535–>关键!!!这个就是我们苦苦寻找的板级定制文件的xxx.c的入口函数!!!
–651–>中断初始化
–653–>使能中断
–667–>网卡初始化,函数的实现在net/eth.c,会回调板级xxx.c中的board_eth_init()
–703–>执行超循环,主要功能是处理环境变量,解析命令,也就是uboot中和我们交互的命令的解析工作都在这里执行!!!

main_loop()与启动内核
main_loop()的实现在common/main.c,它的主要功能就是循环检测输入的命令并执行,其中一个环境变量bootdelay(自启动)的设置决定了是否启动内核,如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。ootloader 要想启动内核,可以直接跳到内核的第一个指令处,即内核的起始地址,这样便可以完成内核的启动工作了。但是要想启动内核还需要满足下面的一些条件,这些条件在Linux内核文档"/Documentation/kernel-parameters.txt"中有说明,
1、cpu 寄存器设置

  • R0 = 0

  • R1 = 机器类型 id

  • R2 = 启动参数在内存中的起始地址
    2、cpu 模式

  • 禁止所有中断

  • 必须为 SVC(超级用户)模式
    3、Cache、MMU

  • 关闭 MMU

  • 指令 Cache 可以开启或者关闭

  • 数据 Cache 必须关闭
    4、设备

  • DMA 设备应当停止工作
    5、PC 为内核的起始地址

关于uboot的启动分析,本文只是冰山一角的一丢丢,不过希望通过我的这一堆废话下来,能帮助你对uboot的启动流程有一个整体的认识,当然,如果文中有错误,欢迎批评指正-

在这里插入图片描述
在这里插入图片描述
整理后可得如上图的Linux内核镜像格式关系,
① vmlinux文件是Linux内核编译链接后生成的ELF文件,该文件是不能作为镜像文件烧写的
② Image文件是vmlinux经过objcopy处理之后的纯二进制文件,其关系类似于uboot和uboot.bin
③ 对Image再次进行压缩,并将解压缩文件附加在文件开头,即构成了compress目录下的vmlinux文件(注意:解压缩文件本身是不压缩的!!!)
④ 将compress目录下的vmlinux文件经过objcopy处理为二进制文件,即zImage
B. 从zImage到uImage
uImage镜像格式为uboot制定,与Linux内核本身无关,但是编译内核时使用
make uImage

uimage 只是在zimage的基础上加了一个头部;
bootm 启动:
bootm启动内核时,先找到将uimage 的头部转换为hearder 结构,然后赋值给gd
结构,然后再生成各个tag参数,然后跳转到内核启动地址,并将gd结构和tag作为参数 运行;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值