uboot启动源码分析
Posted 一口Linux
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uboot启动源码分析相关的知识,希望对你有一定的参考价值。
-
初始化异常向量表
-
设置 SVC 模式
-
关中断
-
配置cp15协处理器
-
初始化 mmu、cache、tlb(cpu_init_cp15)
-
板级初始化(cpu_init_crit)
-
初始化异常向量表
arch/arm/cpu/armv7/start.S
32 #include <asm-offsets.h>
33 #include <config.h>
34 #include <version.h>
35 #include <asm/system.h>
36 #include <linux/linkage.h>
37
38 .globl _start
39 _start: b reset
40 ldr pc, _undefined_instruction
41 ldr pc, _software_interrupt
42 ldr pc, _prefetch_abort
43 ldr pc, _data_abort
44 ldr pc, _not_used
45 ldr pc, _irq
46 ldr pc, _fiq
38 声明 _start 为全局符号,_start 会被链接器链接,链接脚本的入口地址
39 复位异常:复位电平有效时,程序跳转到复位处理程序处执行
40 未定义指令异常:遇到不能处理的指令时,产生未定义指令异常
41 软件中断异常:执行SWI指令产生的异常
42 预存指令异常:处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取终止异常
43 数据操作异常:处理器数据访问指令的地址不存在时,或该地址不允许当前指令访问时,产生数据中止异常
44 未使用
45 外部中断请求有效,且 CPSR 中的 I 位为 0 时,产生 IRQ 异常
46 快速中断请求引脚有效,且 CPSR 中的 F 位为 0 时,产生 FIQ 异常
122 /*
123 * the actual reset code
124 */
125
126 reset:
127 bl save_boot_params
128 /*
129 * set the cpu to SVC32 mode
130 */
131 mrs r0, cpsr
132 bic r0, r0, #0x1f
133 orr r0, r0, #0xd3
134 msr cpsr,r0
135
136 #if 1
137 ldr r0, =0x11000c40 @GPK2_7 led2
138 ldr r1, [r0]
139 bic r1, r1, #0xf0000000
140 orr r1, r1, #0x10000000
141 str r1, [r0]
142
143 ldr r0, =0x11000c44
144 mov r1,#0xff
145 str r1, [r0]
146 #endif
147
148 /*
149 * Setup vector:
150 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
151 * Continue to use ROM code vector only in OMAP4 spl)
152 */
153 #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
154 /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
155 mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register
156 bic r0, #CR_V @ V = 0
157 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register
158
159 /* Set vector address in CP15 VBAR register */
160 ldr r0, =_start
161 mcr p15, 0, r0, c12, c0, 0 @Set VBAR
162 #endif
163
164 /* the mask ROM code should have PLL and others stable */
165 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
166 bl cpu_init_cp15
167 bl cpu_init_crit
168 #endif
169
170 bl _main
……
287 ENTRY(save_boot_params)
288 bx lr @ back to my caller // 什么也没做,返回,栈没有初始化,最好不要在这里有操作
289 ENDPROC(save_boot_params)
290 .weak save_boot_params// 如果该函数在其它地方没有定义,则为空函数,有则定义该函数
126 cpu 上电或者重启后执行reset
127 跳转到save_boot_params, 实际什么也没做,直接返回,毕竟栈没有初始化
131-134 修改cpu模式为SVC模式
136-146 增加的点灯程序,用来确认程序是否走到该位置
155-157 关闭数据预取指令
161-162 设置异常向量表地址为_start
166 跳转至函数cpu_init_cp15
167 跳转至函数cpu_init_crit
170 跳转至函数_main
cpu_init_cp15
该函数仍然位于文件arch/arm/cpu/armv7/start.S
299 ENTRY(cpu_init_cp15)
300 /*
301 * Invalidate L1 I/D
302 */
303 mov r0, #0 @ set up for MCR
304 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
305 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
306 mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
307 mcr p15, 0, r0, c7, c10, 4 @ DSB
308 mcr p15, 0, r0, c7, c5, 4 @ ISB
309
310 /*
311 * disable MMU stuff and caches
312 */
313 mrc p15, 0, r0, c1, c0, 0
314 bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
315 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
316 orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
317 orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
318 #ifdef CONFIG_SYS_ICACHE_OFF
319 bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
320 #else
321 orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
322 #endif
323 mcr p15, 0, r0, c1, c0, 0
324 mov pc, lr @ back to my caller
325 ENDPROC(cpu_init_cp15)
303-306 关闭数据预取功能
307-308 DSB:多核CPU对数据处理指令
313-321 关闭MMU,使能I-caches
324 返回167行,
167 跳转至函数cpu_init_crit
cpu_init_crit
函数cpu_init_crit位于arch/arm/cpu/armv7/start.S
336 ENTRY(cpu_init_crit)
337 /*
338 * Jump to board specific initialization...
339 * The Mask ROM will have already initialized
340 * basic memory. Go here to bump up clock rate and handle
341 * wake up conditions.
342 */
343 b lowlevel_init @ go setup pll,mux,memory
344 ENDPROC(cpu_init_crit)
345 #endif
343 跳转至函数lowlevel_init,该函数位于board/samsung/origen/lowlevel_init.S
40 .globl lowlevel_init
41 lowlevel_init:
42 ldr sp,=0x02060000 @use iRom stack in bl2
43 push {lr}
44
45 /* r5 has always zero */
46 mov r5, #0
47 ldr r7, =EXYNOS4_GPIO_PART1_BASE
48 ldr r6, =EXYNOS4_GPIO_PART2_BASE
49
50 /* check reset status */
51 ldr r0, =(EXYNOS4_POWER_BASE + INFORM1_OFFSET)
52 ldr r1, [r0]
53
54 /* AFTR wakeup reset */
55 ldr r2, =S5P_CHECK_DIDLE
56 cmp r1, r2
57 beq exit_wakeup
58
59 /* LPA wakeup reset */
60 ldr r2, =S5P_CHECK_LPA
61 cmp r1, r2
62 beq exit_wakeup
63
64 /* Sleep wakeup reset */
65 ldr r2, =S5P_CHECK_SLEEP
66 cmp r1, r2
67 beq wakeup_reset
68
69 #if 1 /*for close watchdog */
70 /* PS-Hold high */
71 ldr r0, =0x1002330c
72 ldr r1, [r0]
73 orr r1, r1, #0x300
74 str r1, [r0]
75 ldr r0, =0x11000c08
76 ldr r1, =0x0
77 str r1, [r0]
78 /* Clear MASK_WDT_RESET_REQUEST */
79 ldr r0, =0x1002040c
80 ldr r1, =0x00
81 str r1, [r0]
82 #endif
83 /*
84 * If U-boot is already running in ram, no need to relocate U-Boot.
85 * Memory controller must be configured before relocating U-Boot
86 * in ram.
87 */
88 ldr r0, =0x0ffffff /* r0 <- Mask Bits*/
89 bic r1, pc, r0 /* pc <- current addr of code */
90 /* r1 <- unmasked bits of pc */
91 ldr r2, _TEXT_BASE /* r2 <- original base addr in ram */
92 bic r2, r2, r0 /* r2 <- unmasked bits of r2*/
93 cmp r1, r2 /* compare r1, r2 */
94 beq 1f /* r0 == r1 then skip sdram init */
95
96 /* init system clock */
97 bl system_clock_init
98
99 /* Memory initialize */
100 bl mem_ctrl_asm_init
101
102 1:
103 /* for UART */
104 bl uart_asm_init
105 #if 0
106 bl tzpc_init
107 #endif
108 pop {pc}
109
110 wakeup_reset:
111 bl system_clock_init
112 bl mem_ctrl_asm_init
113 bl tzpc_init
114
115 exit_wakeup:
116 /* Load return address and jump to kernel */
117 ldr r0, =(EXYNOS4_POWER_BASE + INFORM0_OFFSET)
118
119 /* r1 = physical address of exynos4210_cpu_resume function */
120 ldr r1, [r0]
121
122 /* Jump to kernel*/
123 mov pc, r1
42 初始化栈,为进入C阶段做准备
47-48 将EXYNOS的GPIO part1、part2基地址填充进r7、r6
51-57 读取PMU的寄存器信息,从而判断上次重启之前状态,如果是唤醒之后的reset或者LPA唤醒则进入exit_wakeup,执行123行代码跳转到内核
60-62 如果是设备休眠后被唤醒进入wakeup_reset,
如果设备此次被唤醒之前场景是电量比较低或者休眠,那么内核其实已经存在于ram中,所以此次进入reset异常之后就不需要执行ubot后续代码
79-81 关闭看门狗
88-94 通过将pc的值与0x0ffffff进行位清除操作,以保留当前pc值的高字节bit[31:24],根据该值判断当前程序是否运行于ram中,因为ram的基地址分别是0x4000_0000、0xA000_0000,如果位于sram中则pc的高地址必不为0,如果位于sram中则跳转到局部标号1,否则顺序执行97、100行代码
97-100 初始化系统时钟和uart
108 返回arch/arm/cpu/armv7/start.S的171行
_main
函数_main位于文件arch/arm/lib/crt0.S下。
96 _main:
97
98 /*
99 * Set up initial C runtime environment and call board_init_f(0).
100 */
101
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)// 加载栈指针到sp中
109 #endif
110 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */// 8 字节对齐
111 sub sp, #GD_SIZE /* allocate one GD above SP */// 减去GD_SIZE的大小
112 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
113 mov r8, sp /* GD is above SP */// r8寄存器保存gd结构体的首地址
114 mov r0, #0
115 bl board_init_f
102-109 判断宏CONFIG_NAND_SPL是否定义,如果定义了设置栈地址为CONFIG_SYS_INIT_SP_ADDR,如果定义了CONFIG_SPL_BUILD、CONFIG_SPL_STACK则设置栈地址为CONFIG_SPL_STACK,否则设置栈地址为CONFIG_SYS_INIT_SP_ADDR,目的是为了执行C语言做准备
110 栈地址8位对齐
111-113 在栈顶预留GD_SIZE大小的内存,并把新的栈地址给r8
寄存器r8在文件include\\asm\\global_data.h做了重新声明:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r8”)
也就是全局指针变量register volatile gd_t *gd对应到寄存器r8,
变量gd,后面初始化还会用到
114 将r0设置为0,该寄存器值是传递给后面调用的函数board_init_f的参数
115 跳转到函数board_init_f
board_init_f
函数board_init_f定义位于文件arch\\arm\\lib\\board.c中。
/*这个文件处理uboot启动中与生成目标无关的阶段,即准备C环境。
该文件由 _start.S 进入。
主要执行顺序是:
- 为调用 board_init_f() 设置初始化环境
该环境提供了一个栈和存储 GD 数据结构的地方 - 调用 board_init_f() —— 为硬件做准备
使用 GD 存储数据 —— relocation destination、future stack、future GD location
(非SPL构建) - 建立中间环境
堆栈和GD为board_init_f()分配的,接下来设置BSS和SS - 调用relocate_code()
将uboot从当前位置前往board_init_f()中得到的目的地址 - 为调用board_init_r()设置环境
BSS:初始化为0
初始化 non-const 数据
在系统中设置堆栈
一些CPU需要调用c_runtime_cpu_setup() 为GD做一些工作 - 前往board_init_r()*/
277 void board_init_f(ulong bootflag)
278 {
……
289 memset((void *)gd, 0, sizeof(gd_t));
290
291 gd->mon_len = _bss_end_ofs; //初始化mon_len,代表uboot code的大小
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 }
308
……
// 一部分内存空间隐藏
333 gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
334 #endif
335
336 addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;// 可用sdram的顶端
// 预留出tlb空间
356 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
357 /* reserve TLB table */
358 gd->tlb_size = 4096 * 4;//如果打开了 icache 以及 dcache,则预留出PATABLE_SIZE大小的tlb空间
359 addr -= gd->tlb_size;
360
361 /* round down to next 64 kB limit */
362 addr &= ~(0x10000 - 1);
363
364 gd->tlb_addr = addr;// tlb空间存放首地址
365 debug("TLB table from %08lx to %08lx\\n", addr, addr + gd->tlb_size);
366 #endif
367
368 /* round down to next 4 kB limit */
369 addr &= ~(4096 - 1);
370 debug("Top of RAM usable for U-Boot at: %08lx\\n", addr);
371 // 确定 frambuffer
372 #ifdef CONFIG_LCD
373 #ifdef CONFIG_FB_ADDR
374 gd->fb_base = CONFIG_FB_ADDR;// 获取frambuffer基地址
375 #else
376 /* reserve memory for LCD display (always full pages) */
377 addr = lcd_setmem(addr);
378 gd->fb_base = addr;// 获取frambuffer基地址
379 #endif /* CONFIG_FB_ADDR */
380 #endif /* CONFIG_LCD */
381
382 /*
383 * reserve memory for U-Boot code, data & bss
384 * round down to next 4 kB limit
385 */
386 addr -= gd->mon_len;
387 addr &= ~(4096 - 1);
388
389 debug("Reserving %ldk for U-Boot at: %08lx\\n", gd->mon_len >> 10, addr);
390
391 #ifndef CONFIG_SPL_BUILD
392 /*
393 * reserve memory for malloc() arena
394 */
395 addr_sp = addr - TOTAL_MALLOC_LEN;
396 debug("Reserving %dk for malloc() at: %08lx\\n",
397 TOTAL_MALLOC_LEN >> 10, addr_sp);
398 /*
399 * (permanently) allocate a Board Info struct
400 * and a permanent copy of the "global" data
401 */
402 addr_sp -= sizeof (bd_t);
403 bd = (bd_t *) addr_sp;
404 gd->bd = bd;
405 debug("Reserving %zu Bytes for Board Info at: %08lx\\n",
406 sizeof (bd_t), addr_sp);
407
408 #ifdef CONFIG_MACH_TYPE
409 gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
410 #endif
411
412 addr_sp -= sizeof (gd_t);
413 id = (gd_t *) addr_sp;
414 debug("Reserving %zu Bytes for Global Data at: %08lx\\n",
415 sizeof (gd_t), addr_sp);
416
417 #if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL)
418 /*
419 * If the device tree is sitting immediate above our image then we
420 * must relocate it. If it is embedded in the data section, then it
421 * will be relocated with other data.
422 */
423 if (gd->fdt_blob) {
424 fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
425
426 addr_sp -= fdt_size;
427 new_fdt = (void *)addr_sp;
428 debug("Reserving %zu Bytes for FDT at: %08lx\\n",
429 fdt_size, addr_sp);
430 }
431 #endif
432
433 /* setup stackpointer for exeptions */
434 gd->irq_sp = addr_sp;
435 #ifdef CONFIG_USE_IRQ
436 addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
437 debug("Reserving %zu Bytes for IRQ stack at: %08lx\\n",
438 CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
439 #endif
440 /* leave 3 words for abort-stack */
441 addr_sp -= 12;
442
443 /* 8-byte alignment for ABI compliance */
444 addr_sp &= ~0x07;
445 #else
446 addr_sp += 128; /* leave 32 words for abort-stack */
447 gd->irq_sp = addr_sp;
448 #endif
449
450 debug("New Stack Pointer is: %08lx\\n", addr_sp);
451
452 #ifdef CONFIG_POST
453 post_bootmode_init();
454 post_run(NULL, POST_ROM | post_bootmode_get(0));
455 #endif
456
457 gd->bd->bi_baudrate = gd->baudrate;
458 /* Ram ist board specific, so move it to board code ... */
459 dram_init_banksize();
460 display_dram_config(); /* and display it */
461
462 gd->relocaddr = addr;
463 gd->start_addr_sp = addr_sp;
464 gd->reloc_off = addr - _TEXT_BASE;
465 debug("relocation Offset is: %08lx\\n", gd->reloc_off);
466 if (new_fdt) {
467 memcpy(new_fdt, gd->fdt_blob, fdt_size);
468 gd->fdt_blob = new_fdt;
469 }
470 memcpy(id, (void *)gd, sizeof(gd_t));
471 }
机构体变量gd_t *gd存储了uboot初始化所需要的各种内存地址,是最重要的一个结构体变量。
291 初始化mon_len,_bss_end_ofs是bss段的结束为止,代表uboot code的大小
292-301 初始化设备树地址信息
303-307 遍历调用 init_sequence 所有函数该函数定义位于函数board_init_f上方,如下:
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,
248 #endif
249 #if defined(CONFIG_BOARD_EARLY_INIT_F)
250 board_early_init_f,
251 #endif
252 timer_init, /* initialize timer */
253 #ifdef CONFIG_BOARD_POSTCLK_INIT
254 board_postclk_init,
255 #endif
256 #ifdef CONFIG_FSL_ESDHC
257 get_clocks,
258 #endif
259 env_init, /* initialize environment */
260 init_baudrate, /* initialze baudrate settings */
261 serial_init, /* serial communications setup */
262 console_init_f, /* stage 1 init of console */
263 display_banner, /* say that we are here */
264 #if defined(CONFIG_DISPLAY_CPUINFO)
265 print_cpuinfo, /* display cpu info (and speed) */
266 #endif
267 #if defined(CONFIG_DISPLAY_BOARDINFO)
268 checkboard, /* display board info */
269 #endif
270 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
271 init_func_i2c,
272 #endif
273 dram_init, /* configure available RAM banks */
274 NULL,
275 };
333 将一部分内存空间隐藏
336 设置可用sdram的顶端
356 -365 预留出tlb空间
372-380 初始化 frambuffer地址信息
386-387 为uboot的code留出空间
395-396 为malloc操作预留内存空间
402-404 全局信息 bd_t 结构体空间的首地址存在 gd->bd
409 存储board的id,为Linux内核使用
417-431 如果设备树位于image空间上,必须将设备树重定位
434 中断异常的栈指针
457 初始化波特率
462-464 中定位的地址
_main
函数board_init_f结束,程序回到_main中继续执行
119 /*
120 * Set up intermediate environment (new sp and gd) and call
121 * relocate_code(addr_sp, gd, addr_moni). Trick here is that
122 * we'll return 'here' but relocated.
123 */
124
125 ldr sp, [r8, #GD_START_ADDR_SP] /* r8 = gd->start_addr_sp */
126 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
127 ldr r8, [r8, #GD_BD] /* r8 = gd->bd */
128 sub r8, r8, #GD_SIZE /* new GD is below bd */
129
130 adr lr, here
131 ldr r0, [r8, #GD_RELOC_OFF] /* lr = gd->start_addr_sp */
132 add lr, lr, r0
133 ldr r0, [r8, #GD_START_ADDR_SP] /* r0 = gd->start_addr_sp */
134 mov r1, r8 /* r1 = gd */
135 ldr r2, [r8, #GD_RELOCADDR] /* r2 = gd->relocaddr */
136 b relocate_code
125 因为r8存储的是结构体指针变量gd_t *gd的中的地址,通过ldr指令偏移#GD_START_ADDR_SP实现访问成员 gd->start_addr_sp
130 设置程序返回地址为137行的here标签,下面程序跳转后,直接返回here位置继续执行
131 读取重定位地址的偏移量到r0
132 修正寄存器lr中的返回地址,因为后面程序可能会从flash中转移到ram中执行,所以返回地址必须修正
133 读取gd->start_addr_sp到r0
134 将gd的值存储到r1
135 读取重定位地址 gd->relocaddr到r2
136 跳转至函数relocate_code
relocate_code
函数relocate_code主要实现的功能是代码的自搬移,将flash中的uboot代码拷贝到ram中,然后跳转到ram中执行uboot剩余的代码。
代码位于文件arch/arm/cpu/armv7/start.S。
174 #ifndef CONFIG_SPL_BUILD
175 /*
176 * void relocate_code (addr_sp, gd, addr_moni)
177 *
178 * This "function" does not return, instead it continues in RAM
179 * after relocating the monitor code.
180 *
181 */
182 ENTRY(relocate_code)
183 mov r4, r0 /* save addr_sp */
184 mov r5, r1 /* save addr of gd */
185 mov r6, r2 /* save addr of destination */
186
187 adr r0, _start
以上是关于uboot启动源码分析的主要内容,如果未能解决你的问题,请参考以下文章