uboot启动流程分析

陶鹏

uboot版本为NXP维护的2016.03版本
下载地址为http://git.freescale.com/git/...
分析uboot的启动流程,需要编译一下uboot,然后打开链接脚本
u-boot.lds
u-boot.lds

1     OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
2     OUTPUT_ARCH(arm)
3     ENTRY(_start)

_startarch/arm/lib/vectors.S

48     _start:
49 
50     #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
51         .word    CONFIG_SYS_DV_NOR_BOOT_CFG
52     #endif
53 
54         b    reset
55         ldr    pc, _undefined_instruction
56         ldr    pc, _software_interrupt
57         ldr    pc, _prefetch_abort
58         ldr    pc, _data_abort
59         ldr    pc, _not_used
60         ldr    pc, _irq
61         ldr    pc, _fiq

第54行跳转到reset函数里面,resetarch/arm/cpu/armv7/start.S

32         .globl    reset
33         .globl    save_boot_params_ret
34 
35     reset:
36         /* Allow the board to save important registers */
37         b    save_boot_params
38     save_boot_params_ret:
39         /*
40          * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
41          * except if in HYP mode already
42          */
43         mrs    r0, cpsr
44         and    r1, r0, #0x1f        @ mask mode bits
45         teq    r1, #0x1a        @ test for HYP mode
46         bicne    r0, r0, #0x1f        @ clear all mode bits
47         orrne    r0, r0, #0x13        @ set SVC mode
48         orr    r0, r0, #0xc0        @ disable FIQ and IRQ
49         msr    cpsr,r0
......
57         /* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
58         mrc    p15, 0, r0, c1, c0, 0    @ Read CP15 SCTLR Register
59         bic    r0, #CR_V        @ V = 0
60         mcr    p15, 0, r0, c1, c0, 0    @ Write CP15 SCTLR Register
61 
62         /* Set vector address in CP15 VBAR register */
63         ldr    r0, =_start
64         mcr    p15, 0, r0, c12, c0, 0    @Set VBAR
65     #endif
66 
67         /* the mask ROM code should have PLL and others stable */
68     #ifndef CONFIG_SKIP_LOWLEVEL_INIT
69         bl    cpu_init_cp15
70         bl    cpu_init_crit
71     #endif
72 
73         bl    _main

第38行save_boot_params_ret函数实现以下功能:
43-49行将处理器设置SVC模式,并且关闭FIQ和IRQ
57-64行设置向量表重定位
第69行cpu_init_cp15函数初始化cp15,关闭mmu及tlb
第70行cpu_init_crit函数, 调用lowlevel_init函数,lowlevel_init在ocram中初始化sp然后调用s_init函数,如果CPU 为 MX6SX、MX6UL、MX6ULL 或MX6SLL中的任意一种,那么s_init为空函数。(为什么初始化sp?因为存在函数嵌套,只有一个lr寄存器)
接下来,分析_main函数。_main arch/arm/lib/crt0.S

67 ENTRY(_main)
68 
69 /*
70  * Set up initial C runtime environment and call board_init_f(0).
71  */
72 
73 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
74     ldr    sp, =(CONFIG_SPL_STACK)
75 #else
76     ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)
77 #endif
78 #if defined(CONFIG_CPU_V7M)    /* v7M forbids using SP as BIC destination */
79     mov    r3, sp
80     bic    r3, r3, #7
81     mov    sp, r3
82 #else
83     bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
84 #endif
85     mov    r0, sp
86     bl    board_init_f_alloc_reserve
87     mov    sp, r0
88     /* set up gd here, outside any C code */
89     mov    r9, r0
90     bl    board_init_f_init_reserve
91 
92     mov    r0, #0
93     bl    board_init_f
94 
95 #if ! defined(CONFIG_SPL_BUILD)
96 
97 /*
98  * Set up intermediate environment (new sp and gd) and call
99  * relocate_code(addr_moni). Trick here is that we'll return
100 * 'here' but relocated.
101 */
102
103    ldr    sp, [r9, #GD_START_ADDR_SP]    /* sp = gd->start_addr_sp */
104#if defined(CONFIG_CPU_V7M)    /* v7M forbids using SP as BIC destination */
105    mov    r3, sp
106    bic    r3, r3, #7
107    mov    sp, r3
108#else
109    bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
110#endif
111    ldr    r9, [r9, #GD_BD]        /* r9 = gd->bd */
112    sub    r9, r9, #GD_SIZE        /* new GD is below bd */
113
114    adr    lr, here
115    ldr    r0, [r9, #GD_RELOC_OFF]        /* r0 = gd->reloc_off */
116    add    lr, lr, r0
117#if defined(CONFIG_CPU_V7M)
118    orr    lr, #1                /* As required by Thumb-only */
119#endif
120    ldr    r0, [r9, #GD_RELOCADDR]        /* r0 = gd->relocaddr */
121    b    relocate_code
122here:
123/*
124 * now relocate vectors
125 */
126
127    bl    relocate_vectors
128
129/* Set up final (full) environment */
130
131    bl    c_runtime_cpu_setup    /* we still call old routine here */
132#endif
133#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
134# ifdef CONFIG_SPL_BUILD
135    /* Use a DRAM stack for the rest of SPL, if requested */
136    bl    spl_relocate_stack_gd
137    cmp    r0, #0
138    movne    sp, r0
139    movne    r9, r0
140# endif
141    ldr    r0, =__bss_start    /* this is auto-relocated! */
142
143#ifdef CONFIG_USE_ARCH_MEMSET
144    ldr    r3, =__bss_end        /* this is auto-relocated! */
145    mov    r1, #0x00000000        /* prepare zero to clear BSS */
146
147    subs    r2, r3, r0        /* r2 = memset len */
148    bl    memset
149#else
150    ldr    r1, =__bss_end        /* this is auto-relocated! */
151    mov    r2, #0x00000000        /* prepare zero to clear BSS */
152
153clbss_l:cmp    r0, r1            /* while not at end of BSS */
154#if defined(CONFIG_CPU_V7M)
155    itt    lo
156#endif
157    strlo    r2, [r0]        /* clear 32-bit BSS word */
158    addlo    r0, r0, #4        /* move to next */
159    blo    clbss_l
160#endif
161
162#if ! defined(CONFIG_SPL_BUILD)
163    bl coloured_LED_init
164    bl red_led_on
165#endif
166    /* call board_init_r(gd_t *id, ulong dest_addr) */
167    mov     r0, r9                  /* gd_t */
168    ldr    r1, [r9, #GD_RELOCADDR]    /* dest_addr */
169    /* call board_init_r */
170#if defined(CONFIG_SYS_THUMB_BUILD)
171    ldr    lr, =board_init_r    /* this is auto-relocated! */
172    bx    lr
173#else
174    ldr    pc, =board_init_r    /* this is auto-relocated! */
175#endif
176    /* we should not return here. */
177#endif
178
179ENDPROC(_main)

第76行,设置sp指向0X0091FF00
第85行,读取sp到寄存器r0里面,此时r0=0X0091FF00
第86行,调用函数 board_init_f_alloc_reserve,此函数参数为r0中的值,也就是0X0091FF00,此函数定义在common/init/board_init.c

56 ulong board_init_f_alloc_reserve(ulong top)
57 {
58 /* Reserve early malloc arena */
59 #if defined(CONFIG_SYS_MALLOC_F)
60 top -= CONFIG_SYS_MALLOC_F_LEN;
61 #endif
62 /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
63 top = rounddown(top-sizeof(struct global_data), 16);
64
65 return top;
66 }

函数 board_init_f_alloc_reserve主要是留出早期的malloc内存区域和gd内存区域,ocram分布图如下:
image.png
接着分析_main函数
第87行,r0保存着函数 board_init_f_alloc_reserve的返回值,所以sp=0X0091FA00
第89行,r9寄存器存放着全局变量gd的地址,gd为gd_t结构体变量 include/asm-generic/global_data.h定义gd_t结构体

27 typedef struct global_data {
28 bd_t *bd;
29 unsigned long flags;
30 unsigned int baudrate;
31 unsigned long cpu_clk; /* CPU clock in Hz! */
32 unsigned long bus_clk;
33 /* We cannot bracket this with CONFIG_PCI due to mpc5xxx */
34 unsigned long pci_clk;
35 unsigned long mem_clk;
36 #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
37 unsigned long fb_base; /* Base address of framebuffer mem */
38 #endif
......
121 #ifdef CONFIG_DM_VIDEO
122 ulong video_top; /* Top of video frame buffer area */
123 ulong video_bottom; /* Bottom of video frame buffer area */
124 #endif
125 } gd_t;

gd保存着系统初始化参数
所以,第89行设置gd指向0X0091FA00
接着分析_main函数
第90行,调用函数board_init_f_init_reserve,此函数定义在common/init/board_init.c

110 void board_init_f_init_reserve(ulong base)
111 {
112 struct global_data *gd_ptr;
113 #ifndef _USE_MEMCPY
114 int *ptr;
115 #endif
116
117 /*
118 * clear GD entirely and set it up.
119 * Use gd_ptr, as gd may not be properly set yet.
120 */
121
122 gd_ptr = (struct global_data *)base;
123 /* zero the area */
124 #ifdef _USE_MEMCPY
125 memset(gd_ptr, '\0', sizeof(*gd));
126 #else
127 for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
128 *ptr++ = 0;
129 #endif
130 /* set GD unless architecture did it already */
131 #if !defined(CONFIG_ARM)
132 arch_setup_gd(gd_ptr);
133 #endif
134 /* next alloc will be higher by one GD plus 16-byte alignment */
135 base += roundup(sizeof(struct global_data), 16);
136
137 /*
138 * record early malloc arena start.
139 * Use gd as it is now properly set for all architectures.
140 */
141
142 #if defined(CONFIG_SYS_MALLOC_F)
143 /* go down one 'early malloc arena' */
144 gd->malloc_base = base;
145 /* next alloc will be higher by one 'early malloc arena' size */
146 base += CONFIG_SYS_MALLOC_F_LEN;
147 #endif
148 }

此函数将gd清零,gd->malloc_base=0X0091FB00
接着分析_main函数
第93行,调用board_init_f函数,此函数定义在文件 common/board_f.c,主要用来初始化DDR,定时器,完成代码拷贝

1035 void board_init_f(ulong boot_flags)
1036 {
1037 #ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
1038 /*
1039 * For some archtectures, global data is initialized and used
1040 * before calling this function. The data should be preserved.
1041 * For others, CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined
1042 * and use the stack here to host global data until relocation.
1043 */
1044 gd_t data;
1045
1046 gd = &data;
1047
1048 /*
1049 * Clear global data before it is accessed at debug print
1050 * in initcall_run_list. Otherwise the debug print probably
1051 * get the wrong vaule of gd->have_console.
1052 */
1053 zero_global_data();
1054 #endif
1055
1056 gd->flags = boot_flags;
1057 gd->have_console = 0;
1058
1059 if (initcall_run_list(init_sequence_f))
1060 hang();
1061
1062 #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
1063 !defined(CONFIG_EFI_APP)
1064 /* NOTREACHED - jump_to_copy() does not return */
1065 hang();
1066 #endif
1067 }

第1059行,通过函数initcall_run_list来运行初始化序列 init_sequence_f里面的一系列函数,init_sequence_f里面包含了一系列的初始化函数,最终,内存分配如图:
image.png
接着分析_main函数
第103行,sp=gd->start_addr_sp=0X9EF44E90
第 121 行,调用函数relocate_code,此函数定义在文件arch/arm/lib/relocate.S
拷贝uboot到DDR

79 ENTRY(relocate_code)
80 ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
81 subs r4, r0, r1 /* r4 <- relocation offset */
82 beq relocate_done /* skip relocation */
83 ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */
84
85 copy_loop:
86 ldmia r1!, {r10-r11} /* copy from source address [r1] */
87 stmia r0!, {r10-r11} /* copy to target address [r0] */
88 cmp r1, r2 /* until source end address [r2] */
89 blo copy_loop
90
91 /*
92 * fix .rel.dyn relocations
93 */
94 ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
95 ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
96 fixloop:
97 ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
98 and r1, r1, #0xff
99 cmp r1, #23 /* relative fixup? */
100 bne fixnext
101
102 /* relative fix: increase location by offset */
103 add r0, r0, r4
104 ldr r1, [r0]
105 add r1, r1, r4
106 str r1, [r0]
107 fixnext:
108 cmp r2, r3
109 blo fixloop
110
111 relocate_done:
112
113 #ifdef __XSCALE__
114 /*
115 * On xscale, icache must be invalidated and write buffers
116 * drained, even with cache disabled - 4.2.7 of xscale core
117 developer's manual */
118 mcr p15, 0, r0, c7, c7, 0 /* invalidate icache */
119 mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */
120 #endif
121
122 /* ARMv4- don't know bx lr but the assembler fails to see that */
123
124 #ifdef __ARM_ARCH_4__
125 mov pc, lr
126 #else
127 bx lr
128 #endif
129
130 ENDPROC(relocate_code)

接着分析_main函数
第 127 行,调用函数relocate_vectors,对中断向量表做重定位,此函数定义在文件arch/arm/lib/relocate.S
重定位向量表

27 ENTRY(relocate_vectors)
28
29 #ifdef CONFIG_CPU_V7M
30 /*
31 * On ARMv7-M we only have to write the new vector address
32 * to VTOR register.
33 */
34 ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
35 ldr r1, =V7M_SCB_BASE
36 str r0, [r1, V7M_SCB_VTOR]
37 #else
38 #ifdef CONFIG_HAS_VBAR
39 /*
40 * If the ARM processor has the security extensions,
41 * use VBAR to relocate the exception vectors.
42 */
43 ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
44 mcr p15, 0, r0, c12, c0, 0 /* Set VBAR */
45 #else
46 /*
47 * Copy the relocated exception vectors to the
48 * correct address
49 * CP15 c1 V bit gives us the location of the vectors:
50 * 0x00000000 or 0xFFFF0000.
51 */
52 ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
53 mrc p15, 0, r2, c1, c0, 0 /* V bit (bit[13]) in CP15 c1 */
54 ands r2, r2, #(1 << 13)
55 ldreq r1, =0x00000000 /* If V=0 */
56 ldrne r1, =0xFFFF0000 /* If V=1 */
57 ldmia r0!, {r2-r8,r10}
58 stmia r1!, {r2-r8,r10}
59 ldmia r0!, {r2-r8,r10}
60 stmia r1!, {r2-r8,r10}
61 #endif
62 #endif
63 bx lr
64
65 ENDPROC(relocate_vectors)

接着分析_main函数
第 131 行,调用函数c_runtime_cpu_setup,此函数定义在文件 arch/arm/cpu/armv7/start.S

77 ENTRY(c_runtime_cpu_setup)
78 /*
79 * If I-cache is enabled invalidate it
80 */
81 #ifndef CONFIG_SYS_ICACHE_OFF
82 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
83 mcr p15, 0, r0, c7, c10, 4 @ DSB
84 mcr p15, 0, r0, c7, c5, 4 @ ISB
85 #endif
86
87 bx lr
88
89 ENDPROC(c_runtime_cpu_setup)

接着分析_main函数
第 141~159 行,清除 BSS 段
第 174 行、调用函数board_init_r,此函数定义在文件 common/board_r.c
board_init_f函数调用一系列的函数来初始化一些外
设和 gd 的成员变量,但是 board_init_f 并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数board_init_r来完成的。

991 void board_init_r(gd_t *new_gd, ulong dest_addr)
992 {
993 #ifdef CONFIG_NEEDS_MANUAL_RELOC
994 int i;
995 #endif
996
997 #ifdef CONFIG_AVR32
998 mmu_init_r(dest_addr);
999 #endif
1000
1001 #if !defined(CONFIG_X86) && !defined(CONFIG_ARM)
&& !defined(CONFIG_ARM64)
1002 gd = new_gd;
1003 #endif
1004
1005 #ifdef CONFIG_NEEDS_MANUAL_RELOC
1006 for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
1007 init_sequence_r[i] += gd->reloc_off;
1008 #endif
1009
1010 if (initcall_run_list(init_sequence_r))
1011 hang();
1012
1013 /* NOTREACHED - run_main_loop() does not return */
1014 hang();
1015 }

第1010行调用initcall_run_list函数来执行初始化序列 init_sequence_rinit_sequence_r是一个函数集合,定义如下:

1 init_fnc_t init_sequence_r[] = {
2 initr_trace,
3 initr_reloc,
4 initr_caches,
5 initr_reloc_global_data,
6 initr_barrier,
7 initr_malloc,
8 initr_console_record,
9 bootstage_relocate,
10 initr_bootstage,
11 board_init, /* Setup chipselects */
12 stdio_init_tables,
13 initr_serial,
14 initr_announce,
15 INIT_FUNC_WATCHDOG_RESET
16 INIT_FUNC_WATCHDOG_RESET
17 INIT_FUNC_WATCHDOG_RESET
18 power_init_board,
19 initr_flash,
20 INIT_FUNC_WATCHDOG_RESET
21 initr_nand,
22 initr_mmc,
23 initr_env,
24 INIT_FUNC_WATCHDOG_RESET
25 initr_secondary_cpu,
26 INIT_FUNC_WATCHDOG_RESET
27 stdio_add_devices,
28 initr_jumptable,
29 console_init_r, /* fully init console as a device */
30 INIT_FUNC_WATCHDOG_RESET
31 interrupt_init,
32 initr_enable_interrupts,
33 initr_ethaddr,
34 board_late_init,
35 INIT_FUNC_WATCHDOG_RESET
36 INIT_FUNC_WATCHDOG_RESET
37 INIT_FUNC_WATCHDOG_RESET
38 initr_net,
39 INIT_FUNC_WATCHDOG_RESET
40 run_main_loop,
41 };

第40行,run_main_loop函数,定义在common/board_r.c中:

753 static int run_main_loop(void)
754 {
755 #ifdef CONFIG_SANDBOX
756 sandbox_main_loop_init();
757 #endif
758 /* main_loop() can return to retry autoboot, if so just run it
again */
759 for (;;)
760 main_loop();
761 return 0;
762 }

第760行,main_loop函数,定义在common/main.c

43 /* We come here after U-Boot is initialised and ready to process
commands */
44 void main_loop(void)
45 {
46 const char *s;
47
48 bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
49
50 #ifndef CONFIG_SYS_GENERIC_BOARD
51 puts("Warning: Your board does not use generic board. Please
read\n");
52 puts("doc/README.generic-board and take action. Boards not\n");
53 puts("upgraded by the late 2014 may break or be removed.\n");
54 #endif
55
56 #ifdef CONFIG_VERSION_VARIABLE
57 setenv("ver", version_string); /* set version variable */
58 #endif /* CONFIG_VERSION_VARIABLE */
59
60 cli_init();
61
62 run_preboot_environment_command();
63
64 #if defined(CONFIG_UPDATE_TFTP)
65 update_tftp(0UL, NULL, NULL);
66 #endif /* CONFIG_UPDATE_TFTP */
67
68 s = bootdelay_process();
69 if (cli_process_fdt(&s))
70 cli_secure_boot_cmd(s);
71
72 autoboot_command(s);
73
74 cli_loop();
75 }

第48行,调用`bootstage_mark_name函数,打印出启动进度
第72行,autoboot_command函数,定义在common/autoboot.c,检查倒计时是否结束?倒计时结束之前有没有被打断?

380 void autoboot_command(const char *s)
381 {
382 debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
383
384 if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay))
{
385 #if defined(CONFIG_AUTOBOOT_KEYED)
&& !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
386 int prev = disable_ctrlc(1); /* disable Control C checking
*/
387 #endif
388
389 run_command_list(s, -1, 0);
390
391 #if defined(CONFIG_AUTOBOOT_KEYED)
&& !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
392 disable_ctrlc(prev); /* restore Control C checking */
393 #endif
394 }
395
396 #ifdef CONFIG_MENUKEY
397 if (menukey == CONFIG_MENUKEY) {
398 s = getenv("menucmd");
399 if (s)
400 run_command_list(s, -1, 0);
401 }
402 #endif /* CONFIG_MENUKEY */
403 }

第400行,如果倒计时自然结束那么久执行函数run_command_list,此函数为执行环境变量bootcmd的命令。
如果倒计时结束之前按下按键,那么就执行main_loop函数中第74行的cli_loop函数,此函数为命令处理函数。
综上,整理下uboot启动流程
image.png

阅读 1.3k

搞过plc,玩过dji,立志从事驱动开发。

1 声望
0 粉丝
0 条评论
你知道吗?

搞过plc,玩过dji,立志从事驱动开发。

1 声望
0 粉丝
宣传栏