1、uboot启动流程分析


在读者学习本章之前,最好拥有外设和架构相关理论知识

本章使用的平台为百问网IMX6ULL,通过对NXP提供uboot的分析和修改,使其支持百问网IMX6ULL

本章参考文件有:《i.MX 6 BSP Porting Guide》、《ARMv7架构参考手册》

百度网盘链接:

https://pan.baidu.com/s/1h3LjDtOmAYChti7Rr30aSA

提取码为:07yl


一、uboot准备过程

首先需要解压uboot,解压后得到的顶层文件夹有:

《i.MX 6 BSP Porting Guide》中1.2.1节所述流程如下:

1. 复制开发板目录中相关文件夹(我选择的是mx6ull_ddr3_arm2)到自定义文件夹:

$ cp -R ./board/freescale/mx6ullevk/ ./board/freescale/mx6ull_lioker

$ mv ./board/freescale/mx6ull_lioker/mx6ullevk.c ./board/freescale/mx6ull_lioker/mx6ull_lioker.c

2. 复制头文件目录中相关文件为自定义文件:

$ cp ./include/configs/mx6ullevk.h ./include/configs/mx6ull_lioker.h

3. 复制配置文件目录中相关文件为自定义文件:

$ cp ./configs/mx6ull_14x14_evk_emmc_defconfig ./configs/mx6ull_lioker_defconfig

4. 修改上述自定义文件

修改后的文件存放在百度网盘链接的./uboot-imx-rel_imx_4.1.15_2.1.0_ga_lioker/文件夹中

5. 修改相关的配置文件:arch/arm/cpu/armv7/mx6/Kconfig

  

a. 添加自定义配置选项

b. 添加自定义配置文件

在解压uboot之后,通常还需要打补丁、配置和编译。如:

1. $ patch -p1 < u-boot-2012.04.01_100ask.patch

2. $ make 100ask24x0_config

3. $ make

其中,配置和编译都会调用uboot的顶层Makefile,接下来我会简单分析此过程

本文所使用的uboot中并没有补丁文件,因此只需要配置和编译:

1. $ make ARCH=arm CROSS_COMPILE=arm-linux- mx6ull_lioker_defconfig

2. make V=1 ARCH=arm CROSS_COMPILE=arm-linux-

配置过程通过scripts/kconfig/conf生成.config,若想获取更多信息可通过修改顶层Makefile中“Q = ”实现:

通过编译过程图,可以确定编译过程所使用的链接文件和主汇编文件分别为u-boot.lds和arch/arm/cpu/armv7/start.S

二、u-boot.lds

 1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
 2 OUTPUT_ARCH(arm)
 3 ENTRY(_start)
 4 SECTIONS
 5 {
 6  . = 0x00000000;
 7  . = ALIGN(4);
 8  .text :
 9  {
10   *(.__image_copy_start)
11   *(.vectors)
12   arch/arm/cpu/armv7/start.o (.text*)
13   *(.text*)
14  }
15  . = ALIGN(4);
16  .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
17  . = ALIGN(4);
18  .data : {
19   *(.data*)
20  }
21  . = ALIGN(4);
22  . = .;
23  . = ALIGN(4);
24  .u_boot_list : {
25   KEEP(*(SORT(.u_boot_list*)));
26  }
27  . = ALIGN(4);
28  .image_copy_end :
29  {
30   *(.__image_copy_end)
31  }
32  .rel_dyn_start :
33  {
34   *(.__rel_dyn_start)
35  }
36  .rel.dyn : {
37   *(.rel*)
38  }
39  .rel_dyn_end :
40  {
41   *(.__rel_dyn_end)
42  }
43  .end :
44  {
45   *(.__end)
46  }
47  _image_binary_end = .;
48  . = ALIGN(4096);
49  .mmutable : {
50   *(.mmutable)
51  }
52  .bss_start __rel_dyn_start (OVERLAY) : {
53   KEEP(*(.__bss_start));
54   __bss_base = .;
55  }
56  .bss __bss_base (OVERLAY) : {
57   *(.bss*)
58    . = ALIGN(4);
59    __bss_limit = .;
60  }
61  .bss_end __bss_limit (OVERLAY) : {
62   KEEP(*(.__bss_end));
63  }
64  .dynsym _image_binary_end : { *(.dynsym) }
65  .dynbss : { *(.dynbss) }
66  .dynstr : { *(.dynstr*) }
67  .dynamic : { *(.dynamic*) }
68  .plt : { *(.plt*) }
69  .interp : { *(.interp*) }
70  .gnu.hash : { *(.gnu.hash) }
71  .gnu : { *(.gnu*) }
72  .ARM.exidx : { *(.ARM.exidx*) }
73  .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
74 }
View Code

其中,

变量_start定义在文件arch/arm/lib/vectors.S中,为0x87800000,此文件被用来定义中断向量表,其中还包含有变量.vectors和ax

通过链接文件可以确定vectors.S在start.S之前运行

变量__image_copy_start在System.map和u-boot.map中均有定义,此处以u-boot.map为例:

其他变量也可以在System.map和u-boot.map中找到定义

而且我们可以通过u-boot.map中函数所属的文件夹确定其粗略位置,如后面章节需要用到的_main()函数就定义在arch/arm/lib/文件夹内

接下来我将分析start.S,因其内容较多,因此以功能分节

三、进入Supervisor模式,屏蔽IRQ和FIQ

1 b reset                                vectors.S
2   -> b save_boot_params                start.S
3     -> ENTRY(save_boot_params)         start.S
4       -> b save_boot_params_ret        start.S

其中,save_boot_params_ret()函数定义如下:

1 mrs    r0, cpsr               @ 将cpsr寄存器值存储至r0寄存器中
2 and    r1, r0, #0x1f          @ 将r0寄存器值的低5位存储至r1寄存器中
3 teq    r1, #0x1a              @ 判断r1寄存器值是否等于0x1a(0b11010,Hyp模式)
4 bicne  r0, r0, #0x1f          @ 如果不相等,则将r0寄存器值的低5位置0
5 orrne  r0, r0, #0x13          @ 并且与0x13(0b10011,Supervisor模式)进行或运算
6 orr    r0, r0, #0xc0          @ 将r0寄存器值的bit[7:6]置0
7 msr    cpsr,r0

cpsr的格式在《ARMv7架构参考手册》中B1.3.3一节

其低五位为M[4:0]定义在B1.3.1一节

bit[7]为IRQ屏蔽位,bit[6]为FIQ屏蔽位

四、向量表重定位

/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
/* CP15寄存器在《ARMv7架构参考手册》的Table B3-33中有定义,如下图所示 */
/* 参考《ARMv7架构参考手册》的B4.1.130一节 */
mrc    p15, 0, r0, c1, c0, 0    @ 将CP15协处理器c1中SCTLR值存储至r0寄存器中
/* Source Insight提示arch/arm/include/asm/system.h中有其定义:#define CR_V (1 << 13)*/
bic    r0, #CR_V                @ 设置向量表地址为0,让向量表可以重定位
mcr    p15, 0, r0, c1, c0, 0    @ Write CP15 SCTLR Register

/* Set vector address in CP15 VBAR register */
ldr    r0, =_start              @ _start = 0x87800000
/* 参考《ARMv7架构参考手册》的B4.1.156一节 */
mcr    p15, 0, r0, c12, c0, 0   @ Set VBAR,设置向量表基地址

在上图中,读者可能会注意到的PL1 or higher

这个是ARM所划分的特权等级,在ARM v7-A架构中有PL0、PL1和PL2三种等级,PL0包含User,PL2包含Hyp,PL1包含其他模式,如下图所示:

五、设置CP15协处理器

bl cpu_init_cp15
  -> ENTRY(cpu_init_cp15)
 1 /* 参考《ARMv7架构参考手册》的Table B3-38 */
 2 mov    r0, #0                   @ set up for MCR
 3 mcr    p15, 0, r0, c8, c7, 0    @ invalidate TLBs,B4.2.3 PL2或Monitor Mode
 4 mcr    p15, 0, r0, c7, c5, 0    @ invalidate icache,B4.2.1 PL1 or higher
 5 mcr    p15, 0, r0, c7, c5, 6    @ invalidate branch predictors array,Table D14-3
 6 mcr    p15, 0, r0, c7, c10, 4   @ DSB
 7 mcr    p15, 0, r0, c7, c5, 4    @ ISB
 8 
 9 /*
10  * disable MMU stuff and caches
11  */
12 mrc    p15, 0, r0, c1, c0, 0
13 bic    r0, r0, #0x00002000      @ clear bits 13 (--V-)
14 bic    r0, r0, #0x00000007      @ clear bits 2:0 (-CAM)
15 orr    r0, r0, #0x00000002      @ set bit 1 (--A-) Align
16 orr    r0, r0, #0x00000800      @ set bit 11 (Z---) BTB
17 orr    r0, r0, #0x00001000      @ set bit 12 (I) I-cache
18 mcr    p15, 0, r0, c1, c0, 0
19 
20 mov    r5, lr                   @ Store my Caller
21 mrc    p15, 0, r1, c0, c0, 0    @ r1 has Read Main ID Register (MIDR)
22 mov    r3, r1, lsr #20          @ get variant field
23 and    r3, r3, #0xf             @ r3 has CPU variant
24 and    r4, r1, #0xf             @ r4 has CPU revision
25 mov    r2, r3, lsl #4           @ shift variant field for combined value
26 orr    r2, r4, r2               @ r2 has combined CPU variant + revision
27 
28 mov    pc, r5                   @ back to my caller
View Code

六、lowlevel_init.S

bl cpu_init_crit                    start.S
  -> ENTRY(cpu_init_crit)
    -> b lowlevel_init
      -> ENTRY(lowlevel_init)       arch/arm/cpu/armv7/lowlevel_init.S
        -> ldr sp, =CONFIG_SYS_INIT_SP_ADDR
        -> bl s_init

1. 使用内部RAM设置栈顶

 1 ldr    sp, =CONFIG_SYS_INIT_SP_ADDR
 2 /* 8bit对齐 */
 3 bic    sp, sp, #7

CONFIG_SYS_INIT_SP_ADDR值确定过程如下:

ldr sp, =CONFIG_SYS_INIT_SP_ADDR
  -> #define CONFIG_SYS_INIT_SP_ADDR \                        include/configs/mx6ull_lioker.h
  ->   (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)
    -> #define CONFIG_SYS_INIT_RAM_ADDR    IRAM_BASE_ADDR
    -> #define CONFIG_SYS_INIT_SP_OFFSET \
    ->   (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
    -> #define CONFIG_SYS_INIT_RAM_SIZE    IRAM_SIZE

也就是CONFIG_SYS_INIT_SP_ADDR = IRAM_BASE_ADDR + IRAM_SIZE - GENERATED_GBL_DATA_SIZE

IRAM_BASE_ADDR和IRAM_SIZE根据mx6ull_lioker.h头文件和如下的grep命令确定定义在arch/arm/include/asm/arch/imx-regs.h中

$ grep "IRAM_BASE_ADDR" * -nR

arch/arm/include/asm/arch/为arch/arm/include/asm/arch-mx6/的链接文件

GENERATED_GBL_DATA_SIZE根据如下的grep命令确定定义在include/generated/generic-asm-offsets.h中,grep命令如下:


上述变量定义如下:

 1 #define IRAM_BASE_ADDR              0x00900000
 2 
 3 /* CONFIG_MX6UL在.config中定义,因此IRAM_SIZE = 0x00040000 */
 4 #if !(defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \
 5     defined(CONFIG_MX6SLL) || defined(CONFIG_MX6SL))
 6 #define IRAM_SIZE                   0x00040000
 7 #else
 8 #define IRAM_SIZE                   0x00020000
 9 #endif
10 
11 #define GENERATED_GBL_DATA_SIZE     256

最终,CONFIG_SYS_INIT_SP_ADDR = 0x0091FF00

2. s_init()函数

 1 /* Save the old lr(passed in ip) and the current lr to stack */
 2 push   {ip, lr}
 3 /*
 4  * Call the very early init function. This should do only the
 5  * absolute bare minimum to get started. It should not:
 6  *
 7  * - set up DRAM
 8  * - use global_data
 9  * - clear BSS
10  * - try to start a console
11  */
12 bl     s_init
13 pop    {ip, pc}

s_init()函数定义在arch/arm/cpu/armv7/mx6/soc.c中:

1 void s_init(void) {
2 ...     /* 省略变量定义代码 */
3     if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL) ||
4         is_cpu_type(MXC_CPU_MX6ULL) || is_cpu_type(MXC_CPU_MX6SLL))
5         return;
6 ...
7 }

对于IMX6ULL芯片,s_init()函数为空函数

cpu_init_cp15()和cpu_init_crit()函数执行完后,开始执行指令bl _main

七、_main()函数

在第二节中,我们已经确定_main()函数定义在arch/arm/lib/文件夹内

在arch/arm/lib/文件夹内执行grep命令,从而确定_main()函数定义在arch/arm/lib/crt0.S文件中

猜你喜欢

转载自www.cnblogs.com/Lioker/p/13204417.html