7.1 start.S 修改
在上一节中的流程中,发现初始化的过程并没由设置看门狗,也未进行中断屏蔽
如果看门狗不禁用,会导致系统反复重启,因此需要在初始化的时候禁用看门狗;中断屏蔽保证启动过程中不出现异常现象
时钟不需要初始化,直接由外部晶振提供初始化,在第二阶段 C 部分再进行初始化。
代码主要在 start.S 中进行修改。修改后的代码如下:
1 /* 头文件包含 */ 2 #include <asm-offsets.h> 3 #include <common.h> 4 #include <config.h> 5 6 /* 7 ************************************************************************* 8 * 9 * Startup Code (called from the ARM reset exception vector) 10 * 11 * do important init only if we don't start from memory! 12 * relocate armboot to ram 13 * setup stack 14 * jump to second stage 15 * 16 ************************************************************************* 17 */ 18 19 .globl reset 20 21 reset: 22 /* 23 * set the cpu to SVC32 mode 24 * 将 CPU 设置为管理员(SVC)模式 25 */ 26 mrs r0, cpsr /* 将状态寄存器的内容传送至通用寄存器,将CPSR中的内容传送至R0 */ 27 bic r0, r0, #0x1f /* 位清除指令 将R0最低5位清零,其余位不变 工作模式位清零 */ 28 orr r0, r0, #0xd3 /* 工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1 "1101 0011" 指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中 */ 29 msr cpsr, r0 /* 将通用寄存器的内容传送至状态寄存器,将中的内容R0传送至CPSR */ 30 31 #if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) 32 /* 33 * relocate exception table 34 */ 35 ldr r0, =_start 36 ldr r1, =0x0 37 mov r2, #16 38 copyex: 39 subs r2, r2, #1 40 ldr r3, [r0], #4 41 str r3, [r1], #4 42 bne copyex 43 #endif 44 45 #ifdef CONFIG_S3C24X0 /* turn off the watchdog */ 46 /* JZ2440中未定义 */ 47 #if defined(CONFIG_S3C2400) 48 #define pWTCON 0x15300000 /* 看门狗寄存器 */ 49 #define INTMSK 0x14400008 /* Interrupt-Controller base addresses */ 50 #define CLKDIVN 0x14800014 /* clock divisor register */ 51 #else /* 定义控制寄存器地址 */ 52 #define pWTCON 0x53000000 /* 看门狗地寄存器地址 */ 53 #define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */ 54 #define INTSUBMSK 0x4A00001C /* 中断掩码 */ 55 #define CLKDIVN 0x4C000014 /* clock divisor register */ 56 #define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01)) 57 #endif /* end CONFIG_S3C2440 */ 58 59 /* 关闭看门狗 */ 60 ldr r0, =pWTCON 61 /* r0中存放看门狗寄存器地址 */ 62 mov r1, #0x0 /* 将立即数0存放到r1中,r1 = 0x0 */ 63 str r1, [r0] /* 将r1中的值存放到以r0中的值为地址的存储单元中,即 pwTCON = 0 */ 64 65 /* 66 * mask all IRQs by setting all bits in the INTMR - default 67 * 屏蔽中断 */ 68 mov r1, #0xffffffff /* 将立即数 0xffffffff 存放到r1中*/ 69 ldr r0, =INTMSK /* r0中存放中断控制寄存器基地址 */ 70 str r1, [r0] /* 将r1中的值存放到以r0中的值为地址的存储单元中,即 INTMSK = 0xffffffff */ 71 #if defined(CONFIG_S3C2440) 72 ldr r1, =0x7fff 73 ldr r0, =INTSUBMSK 74 str r1, [r0] 75 #endif /* end CONFIG_S3C2440 */ 76 #endif /* end CONFIG_S3C24X0 */ 77 78 79 /* 80 * we do sys-critical inits only at reboot, 81 * not when booting from ram! 82 * 未从 ram 启动的时候,所做的CPU 关键初始化工作 83 */ 84 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 85 bl cpu_init_crit /* CPU 关键初始化 */ 86 #endif 87 88 bl _main /* 跳转到 _main 开始执行,进行初始化C环境 和进行第二阶段 */ 89 90 /*------------------------------------------------------------------------------*/ 91 92 .globl c_runtime_cpu_setup 93 c_runtime_cpu_setup: 94 95 mov pc, lr 96 97 /* 98 ************************************************************************* 99 * 100 * CPU_init_critical registers 101 * 102 * setup important registers 103 * setup memory timing 104 * 105 ************************************************************************* 106 */ 107 108 109 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 110 cpu_init_crit: 111 /* 112 * flush v4 I/D caches 113 * 刷新L1 cache的icache和dcache 114 */ 115 mov r0, #0 /* 置零r0通用寄存器 */ 116 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache 向c7写入0将使ICache与DCache无效 "0"表示省略opcode_2 MCR{<cond>} p15, 0, <Rd>, <CRn>, <CRm>{,<opcode_2>}*/ 117 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2 */ 118 119 /* 120 * disable MMU stuff and caches 121 * 关闭 MMU 及其 caches 122 */ 123 mrc p15, 0, r0, c1, c0, 0 124 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) 125 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) 126 orr r0, r0, #0x00000002 @ set bit 1 (A) Align 127 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache 128 mcr p15, 0, r0, c1, c0, 0 129 130 #ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY 131 /* 132 * before relocating, we have to setup RAM timing 133 * because memory timing is board-dependend, you will 134 * find a lowlevel_init.S in your board directory. 135 */ 136 mov ip, lr /* 保存当前程序地址到 ip 寄存器 */ 137 138 bl lowlevel_init /* 执行 SDRAM 初始化 */ 139 mov lr, ip 140 #endif 141 mov pc, lr /* 返回 */ 142 #endif /* CONFIG_SKIP_LOWLEVEL_INIT */
7.2 _main --- C环境
执行完一系列初始化后,开始跳转到 _main 中执行进行C环境初始化和第二阶段的代码
crt0.S (arch\arm\lib)
_main 执行过程如下:
- 建立调用 board_init_f() 的环境,此环境只是提供一个用来保存全局变量 GD 结构体的栈和空间,栈和空间都位于 SRAM 中。在调用board_init_f()前,GD 需要被清 0。
- 调用 board_init_f(),该函数从系统RAM(DRAM,DDR ...)准备将要执行的硬件。由于系统RAM可能还不可用,因此board_init_f()必须使用当前的GD来存储必须传递到后面阶段的任何数据。 这些数据包括重定位目标,未来堆栈和未来的GD位置。
- 设置中间环境,其中堆栈和GD是由系统RAM中的board_init_f()分配的,但BSS和初始化的 non-const 数据仍然不可用。
- 对于U-Boot本身(不是SPL),调用relocate_code()。 该函数将U-Boot从其当前位置重定位到由board_init_f()计算的重定位地址。
- 设置调用board_init_r()的最终环境。 这个环境具有BSS(初始化为0),初始化的 non-const 数据(初始化为其预期值),和系统RAM中的堆栈(用于SPL将堆栈和GD移入RAM是可选的 - 参见CONFIG_SPL_STACK_R)。 GD保留由board_init_f()设置的值。
- 对于U-Boot(不是SPL),有些CPU在内存方面还有一些工作要做,所以调用c_runtime_cpu_setup。
- 最后 跳到 board_init_r() 去执行
7.2.1 设置栈并初始化GD 和全局数据
1 /* 2 * Set up initial C runtime environment and call board_init_f(0). 3 */ 4 /* CONFIG_SYS_INIT_SP_ADDR = 0x30000000 + 0x1000 - 0xE0 = 0x3000 0F20 */ 5 ldr r0, =(CONFIG_SYS_INIT_SP_ADDR) 6 bic r0, r0, #7 /* 8-byte alignment for ABI compliance */ 7 mov sp, r0 /* 栈指向 0x3000 0F20 */ 8 bl board_init_f_alloc_reserve /* 分配global_data的空间*/ 9 mov sp, r0 /* 栈指向 GD 地址的起始位置 */ 10 /* set up gd here, outside any C code */ 11 mov r9, r0 /* GD 的起始位置放入 R9 中,并作为入参传入给 board_init_f_init_reserve */ 12 bl board_init_f_init_reserve /* 初始化global_data区域的代码 */ 13 14 mov r0, #0 /* r0 = 0, 作为board_init_f的入参 */ 15 bl board_init_f /* 板子初次初始化 */
7.2.2 board_init_f_alloc_reserve
1 /* 从'顶部'地址分配保留空间作为'全局'顶地址,并返回分配空间的'底部'地址 2 分配global data所需的空间 3 如果定义了CONFIG_SYS_MALLOC_F_LEN,则会先预留出early malloc所需的空间。 4 CONFIG_SYS_MALLOC_F_LEN = 0x400 */ 5 ulong board_init_f_alloc_reserve(ulong top) 6 { 7 /* Reserve early malloc arena */ 8 #if CONFIG_VAL(SYS_MALLOC_F_LEN) 9 /* 从顶部向下分配一块CONFIG_SYS_MALLOC_F_LEN大小的空间给early malloc使用 */ 10 /* 这块内存是用于在relocation前用于给malloc函数提供内存池。 */ 11 top -= CONFIG_VAL(SYS_MALLOC_F_LEN); 12 #endif 13 /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */ 14 /* 分配sizeof(struct global_data)大小的内存给global_data使用,向下16byte对齐 */ 15 top = rounddown(top-sizeof(struct global_data), 16); 16 17 return top; /* 将top,也就是global_data的地址返回 */ 18 }
7.2.4 board_init_f_init_reserve
1 /* 这个函数用于对global_data区域进行初始化,也就是清空global_data区域 */ 2 /* 传入的参数就是global_data的基地址 */ 3 void board_init_f_init_reserve(ulong base) 4 { 5 struct global_data *gd_ptr; 6 7 /* 8 * clear GD entirely and set it up. 9 * Use gd_ptr, as gd may not be properly set yet. 10 */ 11 12 gd_ptr = (struct global_data *)base; 13 /* zero the area,贤ü齧emset函数对global_data数据结构进行清零 */ 14 memset(gd_ptr, '\0', sizeof(*gd)); 15 /* set GD unless architecture did it already */ 16 #if !defined(CONFIG_ARM) 17 arch_setup_gd(gd_ptr); 18 #endif 19 /* next alloc will be higher by one GD plus 16-byte alignment */ 20 /* global_data区域是16Byte对齐的,对齐后,后面的地址就是early malloc的内存池的地址 */ 21 /* 这里就获取了early malloc的内存池的地址 */ 22 base += roundup(sizeof(struct global_data), 16); 23 24 /* 25 * record early malloc arena start. 26 * Use gd as it is now properly set for all architectures. 27 */ 28 29 #if CONFIG_VAL(SYS_MALLOC_F_LEN) 30 /* go down one 'early malloc arena', 内存池的地址写入到gd->malloc_base中 */ 31 gd->malloc_base = base; 32 /* next alloc will be higher by one 'early malloc arena' size */ 33 /* 加上CONFIG_SYS_MALLOC_F_LEN,获取early malloc的内存池的末尾地址,这里并没有什么作用, 34 是为了以后在early malloc的内存池后面多加一个区域时的修改方便。 */ 35 base += CONFIG_VAL(SYS_MALLOC_F_LEN); 36 #endif 37 }
7.2.5 global_data内存分布
7.3 global data 结构体
global_data的地址存放在r9中,当我们需要global_data的时候,直接从r9寄存器中获取其地址即可。
uboot中定义了一个宏DECLARE_GLOBAL_DATA_PTR,使我们可以更加简单地获取global_data。
定义如下: arch/arm/include/asm/global_data.h
1 #ifdef CONFIG_ARM64 2 #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("x18") 3 #else 4 #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9") 5 #endif 6 #endif
DECLARE_GLOBAL_DATA_PTR定义了gd_t *gd,并且其地址是r9中的值。 一旦使用了DECLARE_GLOBAL_DATA_PTR声明之后,后续就可以直接使用gd变量,也就是global_data了。
Global_data.h (include\asm-generic)中定义了 global_data 的结构体
1 typedef struct global_data { 2 bd_t *bd; /* board info数据结构定义,位于文件 include/asm-arm/u-boot.h定义,主要是保存开发板的相关参数。 */ 3 unsigned long flags; 4 unsigned int baudrate; 5 unsigned long cpu_clk; /* CPU clock in Hz! */ 6 unsigned long bus_clk; 7 /* We cannot bracket this with CONFIG_PCI due to mpc5xxx */ 8 unsigned long pci_clk; 9 unsigned long mem_clk; 10 #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO) 11 unsigned long fb_base; /* Base address of framebuffer mem */ 12 #endif 13 #if defined(CONFIG_POST) 14 unsigned long post_log_word; /* Record POST activities */ 15 unsigned long post_log_res; /* success of POST test */ 16 unsigned long post_init_f_time; /* When post_init_f started */ 17 #endif 18 #ifdef CONFIG_BOARD_TYPES 19 unsigned long board_type; 20 #endif 21 unsigned long have_console; /* serial_init() was called */ 22 #if CONFIG_IS_ENABLED(PRE_CONSOLE_BUFFER) 23 unsigned long precon_buf_idx; /* Pre-Console buffer index */ 24 #endif 25 unsigned long env_addr; /* Address of Environment struct,环境变量的地址*/ 26 unsigned long env_valid; /* Environment valid? enum env_valid */ 27 unsigned long env_has_init; /* Bitmask of boolean of struct env_location offsets */ 28 int env_load_location; 29 30 unsigned long ram_top; /* Top address of RAM used by U-Boot,RAM空间的顶端地址 */ 31 unsigned long relocaddr; /* Start address of U-Boot in RAM,UBOOT重定向后地址 */ 32 phys_size_t ram_size; /* RAM size, 物理ram的size */ 33 unsigned long mon_len; /* monitor len */ 34 unsigned long irq_sp; /* irq stack pointer,中断的堆栈地址 */ 35 unsigned long start_addr_sp; /* start_addr_stackpointer,堆栈地址 */ 36 unsigned long reloc_off; /* uboot的relocation的偏移 */ 37 struct global_data *new_gd; /* relocated global data, 重定向后的struct global_data结构体 */ 38 39 #ifdef CONFIG_DM 40 struct udevice *dm_root; /* Root instance for Driver Model */ 41 struct udevice *dm_root_f; /* Pre-relocation root instance */ 42 struct list_head uclass_root; /* Head of core tree */ 43 #endif 44 #ifdef CONFIG_TIMER 45 struct udevice *timer; /* Timer instance for Driver Model */ 46 #endif 47 48 const void *fdt_blob; /* Our device tree, NULL if none,我们设备的dtb地址 */ 49 void *new_fdt; /* Relocated FDT, relocation之后的dtb地址 */ 50 unsigned long fdt_size; /* Space reserved for relocated FDT, dtb的长度 */ 51 #ifdef CONFIG_OF_LIVE 52 struct device_node *of_root; 53 #endif 54 struct jt_funcs *jt; /* jump table */ 55 char env_buf[32]; /* buffer for env_get() before reloc. */ 56 #ifdef CONFIG_TRACE 57 void *trace_buff; /* The trace buffer */ 58 #endif 59 #if defined(CONFIG_SYS_I2C) 60 int cur_i2c_bus; /* current used i2c bus */ 61 #endif 62 #ifdef CONFIG_SYS_I2C_MXC 63 void *srdata[10]; 64 #endif 65 unsigned int timebase_h; 66 unsigned int timebase_l; 67 #if CONFIG_VAL(SYS_MALLOC_F_LEN) 68 unsigned long malloc_base; /* base address of early malloc() */ 69 unsigned long malloc_limit; /* limit address */ 70 unsigned long malloc_ptr; /* current address */ 71 #endif 72 #ifdef CONFIG_PCI 73 struct pci_controller *hose; /* PCI hose for early use */ 74 phys_addr_t pci_ram_top; /* top of region accessible to PCI */ 75 #endif 76 #ifdef CONFIG_PCI_BOOTDELAY 77 int pcidelay_done; 78 #endif 79 struct udevice *cur_serial_dev; /* current serial device, 当前使用的串口设备 */ 80 struct arch_global_data arch; /* architecture-specific data */ 81 #ifdef CONFIG_CONSOLE_RECORD 82 struct membuff console_out; /* console output */ 83 struct membuff console_in; /* console input */ 84 #endif 85 #ifdef CONFIG_DM_VIDEO 86 ulong video_top; /* Top of video frame buffer area */ 87 ulong video_bottom; /* Bottom of video frame buffer area */ 88 #endif 89 #ifdef CONFIG_BOOTSTAGE 90 struct bootstage_data *bootstage; /* Bootstage information */ 91 struct bootstage_data *new_bootstage; /* Relocated bootstage info */ 92 #endif 93 #ifdef CONFIG_LOG 94 int log_drop_count; /* Number of dropped log messages */ 95 int default_log_level; /* For devices with no filters */ 96 struct list_head log_head; /* List of struct log_device */ 97 int log_fmt; /* Mask containing log format info */ 98 #endif 99 } gd_t;