Linux内核深度解析
上QQ阅读APP看书,第一时间看更新

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_EL3NS、IRQ、FIQEA四个位 */
    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镜像从存储设备读到内存中执行。