uboot启动源码分析

Posted 一口Linux

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uboot启动源码分析相关的知识,希望对你有一定的参考价值。

  1. 初始化异常向量表

  2. 设置 SVC 模式

  3. 关中断

  4. 配置cp15协处理器

  5. 初始化 mmu、cache、tlb(cpu_init_cp15)

  6. 板级初始化(cpu_init_crit)

  7. 初始化异常向量表
    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 进入。
主要执行顺序是:

  1. 为调用 board_init_f() 设置初始化环境
    该环境提供了一个栈和存储 GD 数据结构的地方
  2. 调用 board_init_f() —— 为硬件做准备
    使用 GD 存储数据 —— relocation destination、future stack、future GD location
    (非SPL构建)
  3. 建立中间环境
    堆栈和GD为board_init_f()分配的,接下来设置BSS和SS
  4. 调用relocate_code()
    将uboot从当前位置前往board_init_f()中得到的目的地址
  5. 为调用board_init_r()设置环境
    BSS:初始化为0
    初始化 non-const 数据
    在系统中设置堆栈
    一些CPU需要调用c_runtime_cpu_setup() 为GD做一些工作
  6. 前往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启动源码分析的主要内容,如果未能解决你的问题,请参考以下文章

uboot启动源码分析

uboot启动源码分析

S5PV210-uboot源码分析-第一阶段

uboot源码分析1-启动第一阶段

uboot源码简要分析

uboot配置编译源码分析