1.2.2 标号reset
从标号reset开始的代码如下:
arch/arm/cpu/armv8/start.S 1 reset: 2 /* 允许板卡保存重要的寄存器*/ 3 b save_boot_params 4 .globl save_boot_params_ret 5 save_boot_params_ret: 6 7 #ifdef CONFIG_SYS_RESET_SCTRL 8 bl reset_sctrl 9 #endif 10 /* 11 * 异常级别可能是3、2或者1,初始状态: 12 * 小端字节序,禁止MMU,禁止指令/数据缓存 13 */ 14 adr x0, vectors 15 switch_el x1, 3f, 2f, 1f 16 3: msr vbar_el3, x0 17 mrs x0, scr_el3 18 orr x0, x0, #0xf /* 设置寄存器SCR_EL3的NS、IRQ、FIQ和EA四个位 */ 19 msr scr_el3, x0 20 msr cptr_el3, xzr /* 启用浮点和SIMD功能*/ 21 #ifdef COUNTER_FREQUENCY 22 ldr x0, =COUNTER_FREQUENCY 23 msr cntfrq_el0, x0 /* 初始化寄存器CNTFRQ */ 24 #endif 25 b 0f 26 2: msr vbar_el2, x0 27 mov x0, #0x33ff 28 msr cptr_el2, x0 /* 启用浮点和SIMD功能 */ 29 b 0f 30 1: msr vbar_el1, x0 31 mov x0, #3 << 20 32 msr cpacr_el1, x0 /* 启用浮点和SIMD功能 */ 33 0: 34 … 35 36 /* 应用ARM处理器特定的勘误表*/ 37 bl apply_core_errata 38 39 /* 处理器特定的初始化*/ 40 bl lowlevel_init 41 42 #if defined(CONFIG_ARMV8_SPIN_TABLE) && ! defined(CONFIG_SPL_BUILD) 43 branch_if_master x0, x1, master_cpu 44 b spin_table_secondary_jump 45 /* 绝对不会返回*/ 46 #elif defined(CONFIG_ARMV8_MULTIENTRY) 47 branch_if_master x0, x1, master_cpu 48 49 /* 50 * 从处理器 51 */ 52 slave_cpu: 53 wfe 54 ldr x1, =CPU_RELEASE_ADDR 55 ldr x0, [x1] 56 cbz x0, slave_cpu 57 br x0 /* 跳转到指定地址*/ 58 #endif /* CONFIG_ARMV8_MULTIENTRY */ 59 master_cpu: 60 bl _main
第3行代码,调用各种板卡自定义的函数save_boot_params来保存重要的寄存器。
第8行代码,调用函数reset_sctrl来初始化系统控制寄存器。由配置宏CONFIG_SYS_RESET_SCTRL控制,一般不需要打开。
第15~32行代码,根据处理器当前的异常级别设置寄存器。
❑ 第16~24行代码,如果异常级别是3,那么把向量基准地址寄存器(VBAR_EL3)设置为异常向量的起始地址;设置安全配置寄存器(SCR_EL3)的NS、IRQ、FIQ和EA这4个位,也就是异常级别0和1处于非安全状态,在任何异常级别执行时都把中断、快速中断、同步外部中止和系统错误转发到异常级别3;把协处理器陷入寄存器(CPTR_EL3)设置为0,允许访问浮点和单指令多数据(Single Instruction Multiple Data, SIMD)功能;设置计数器时钟频率寄存器(CNTFRQ_EL0)。
❑ 第26~28行代码,如果异常级别是2,那么把向量基准地址寄存器(VBAR_EL2)设置为异常向量表的起始地址;设置协处理器陷入寄存器(CPTR_EL2),允许访问浮点和SIMD功能。
❑ 第30~32行代码,如果异常级别是1,那么把向量基准地址寄存器(VBAR_EL1)设置为异常向量表的起始地址;设置协处理器访问控制寄存器(CPACR_EL1),允许访问浮点和SIMD功能。
第37行代码,为处理器的缺陷打补丁。
第40行代码,调用函数lowlevel_init以执行函数board_init_f()所需要的最小初始化。当前文件定义了弱符号类型的函数lowlevel_init,处理器厂商可以自定义强符号类型的函数lowlevel_init以覆盖弱符号。
第42~58行代码,如果是多处理器系统,那么只有一个处理器是主处理器(也称为引导处理器),其他处理器是从处理器。
❑ 第42~44行代码,如果使用自旋表启动方法,并且不是编译为第二程序加载器,那么从处理器执行函数spin_table_secondary_jump。源文件“arch/arm/cpu/armv8/spin_table.c”中定义了函数spin_table_secondary_jump,执行过程为:从处理器进入低功耗状态,它被唤醒的时候,从地址spin_table_cpu_release_addr读取函数地址,如果主处理器还没有指定函数地址,继续等待;如果主处理器指定了函数地址,就跳转到指定的函数地址执行。
❑ 第46~57行代码,如果允许多个处理器进入引导程序,那么从处理器进入低功耗状态,它被唤醒的时候,从地址CPU_RELEASE_ADDR读取函数地址,如果主处理器还没有指定函数地址,继续等待;如果主处理器指定了函数地址,就跳转到指定的函数地址执行。
第60行代码,主处理器执行函数_main。
下面介绍第二阶段程序加载器。
U-Boot分为SPL和正常的U-Boot程序两个部分,如果想要编译为SPL,需要开启配置宏CONFIG_SPL_BUILD。SPL是“Secondary Program Loader”的简称,即第二阶段程序加载器,第二阶段是相对于处理器里面的只读存储器中的固化程序来说的,处理器启动时最先执行的是只读存储器中的固化程序。
固化程序通过检测启动方式来加载第二阶段程序加载器。为什么需要第二阶段程序加载器?原因是:一些处理器内部集成的静态随机访问存储器比较小,无法装载一个完整的U-Boot镜像,此时需要第二阶段程序加载器,它主要负责初始化内存和存储设备驱动,然后把正常的U-Boot镜像从存储设备读到内存中执行。