【朱兆祺带你学嵌入式】第二章第五节 U-Boot启动分析(1)
[复制链接]
在BootLoader概述中,我们已经知道BootLoader的实现依赖于处理器的体系结构,为了移植的方便,大多数BootLoader可以分为两个阶段stage1和stage2。依赖于处理器体系结构的代码,比如 CPU 初始化,一般都放在 stage1阶段,通常多用汇编语言来实现,stage1必须是位置无关码。stage2通常用C 语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。U-Boot也不例外,第一阶段主要使用汇编语言编写,程序的入口在start.s中。stage1在运行时,有可能不在其运行地址,这时不能使用静态变量,必须利用位置无关码进行编程。 U-Boot在stage1阶段经常会出现CONFIG_NAND_SPL和CONFIG_SPL_BUILD两个宏,用于控制程序的条件编译。在编译生成u-boot.bin时,它们为假。去掉一些无关紧要的过程和条件编译判定无效的代码段,我们通过分析u-boot.bin的生成过程来分析U-Boot的启动流程。 1. 程序入口.globl _start _start: b reset .globl 如果一个符号没有用.globl声明,就表示这个符号不会被链接器用到。 b是跳转指令,ARM的跳转指令可以从当前指令向前或者向后的32MB的地址空间跳转(相对跳转指令),是一种位置无关码,这类跳转指令有以下4种: 1) B 跳转指令 2) BL 带返回的跳转指令 3) BX 带状态切换的跳转指令 4) BLX 带返回和状态切换的跳转指令 _start: b reset 而这句跳转时,PC寄存器的值将不会保存到LR寄存器中。 2. 设置ARM工作模式reset: /* * set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x3f orr r0, r0, #0xd3 msr cpsr, r0 ARM微处理器支持7种运行模式,分别为: 用户模式(usr):ARM处理器正常的程序执行状态。 快速中断模式(fiq):用于高速数据传输或通道处理。 外部中断模式(irq):用于通用的中断处理。 管理模式(svc):操作系统使用的保护模式。 数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。 系统模式(sys):运行具有特权的操作系统任务。 定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。 cpsr为当前程序状态寄存器,它包含了条件标志位、中断禁止位、当前处理器模式标志以及其他的一些控制和状态位。cpsr可以在任何处理器模式下被访问。
N、Z、C、V这四位统称为条件标志位。 cpsr的第八位统称为控制位。 返回上面代码分析,mrs指令是读状态寄存器指令,如下所示: mrs r0, cpsr 这行代码的含义是将cpsr状态寄存器读取,保存到r0中。 bic指令是位清除指令,如下所示: bic r0, r0, #0x3f 这行代码的作用是将r0的低六位清零。 orr指令是或运算,如下所示: orr r0, r0, #0xd3 将r0与1101 0011进行或运算,由于之前进行了位清零,那么此时r0的低八位为:1101 0011。 msr指令是写状态寄存器指令,如下所示: msr cpsr, r0 将r0数据写入cpsr程序状态寄存器。同样cpsr的低八位即为:1101 0011。那么这八位的含义如下。 1) 第七位,即为I位,当I=1时禁止IRQ中断。 2) 第六位,即为F位,当F=1时禁止FIQ中断。 3) 第五位,即为T位,当T=1时执行ARM指令;当T=0时执行Thumb指令。 4) 低五位,即为M[4:0],当M[4:0] = 0x13,即为管理模式。 接着进入cpu_init_crit,即cpu初始化阶段。 3. caches初始化 mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ mov指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。 mov r0, #0 这行代码即表示将0这个立即数加载到r0寄存器中。 mcr指令将ARM处理器的寄存器中的数据传递到协处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。 mcr p15, 0, r0, c7, c7, 0 指令从ARM寄存器中将数据传送到协处理器p15的寄存器中,其中r0为ARM寄存器,存放源操作数;c7和c7为协处理器寄存器,为目标寄存器;p15和r0之间的0为操作码1;最后0为操作码2。 上面这行代码的作用是向c7写入0将使ICache与DCache无效。 mcr p15, 0, r0, c8, c7, 0 而这行代码的作用是向c8写入0将使TLB失效。 4. MMU初始化 mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 2 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache ······ mmu_disable: mcr p15, 0, r0, c1, c0, 0 mrc指令将协处理器寄存器中的数值传送到ARM处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。 mrc p15, 0, r0, c1, c0, 0 指令将协处理器p15寄存器中的数据传送到ARM寄存器中。其中,r0为ARM寄存器,是目标寄存器;c1和c0为协处理器寄存器,存放源操作数;p15和r0之间的0是操作码1;最后0是操作码2。 V : 表示异常向量表所在的位置,0:异常向量在0x00000000;1:异常向量在 0xFFFF0000。 I : 0 :关闭Icaches;1 :开启Icaches。 R、S : 用来与页表中的描述符一起确定内存的访问权限。 B : 0 :CPU为小字节序;1 : CPU为大字节序。 C : 0:关闭DCaches;1:开启Dcaches。 A : 0:数据访问时不进行地址对齐检查;1:数据访问时进行地址对齐检查。 M : 0:关闭MMU;1:开启MMU。 到这里,再逐句代码分析。 bic r0, r0, #0x00002300 2300即为0010 0011 0000 0000,即是将r0的第13、9、8位清零。 bic r0, r0, #0x00000087 0087即为0000 0000 1000 0111,即是将r0的第7、2、1、0位清零。 orr r0, r0, #0x00000002 5. 外设的基地址初始化#ifdef CONFIG_PERIPORT_REMAP /* Peri port setup */ ldr r0, =CONFIG_PERIPORT_BASE orr r0, r0, #CONFIG_PERIPORT_SIZE mcr p15,0,r0,c15,c2,4 #endif arm11把内存(memory)区间和外设(peripheral)区间地址分开,在CPU初始化的时候,需要通过协处理器指令CP15告诉CPU外设寄存器的地址范围。如果没有这样做,CPU默认为内存访问,也就无法访问到外设区间的寄存器。
|