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

1.2.3 函数_main

函数_main的代码如下:

    arch/arm/lib/crt0_64.S
    1   ENTRY(_main)
    2
    3   /*
    4    * 设置初始的C语言运行环境,并且调用board_init_f(0)
    5    */
    6   #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK
    7    ldr  x0, =(CONFIG_SPL_STACK)
    8   #else
    9    ldr  x0, =(CONFIG_SYS_INIT_SP_ADDR)
    10  #endif
    11   bic  sp, x0, #0xf    /* 为了符合应用二进制接口规范,对齐到16字节*/
    12   mov  x0, sp
    13   bl   board_init_f_alloc_reserve
    14   mov  sp, x0
    15   /* 设置gd */
    16   mov  x18, x0
    17   bl   board_init_f_init_reserve
    18
    19   mov  x0, #0
    20   bl   board_init_f
    21
    22  #if ! defined(CONFIG_SPL_BUILD)
    23  /*
    24   * 设置中间环境(新的栈指针和gd),然后调用函数
    25   * relocate_code(addr_moni)
    26   *
    27   */
    28   ldr  x0, [x18, #GD_START_ADDR_SP]    /* 把寄存器x0设置为gd->start_addr_sp */
    29   bic  sp, x0, #0xf              /* 为了符合应用二进制接口规范,对齐到16字节 */
    30   ldr  x18, [x18, #GD_BD]        /* 把寄存器x18设置为gd->bd */
    31   sub  x18, x18, #GD_SIZE        /* 新的gdbd的下面 */
    32
    33   adr  lr, relocation_return
    34   ldr  x9, [x18, #GD_RELOC_OFF]    /* 把寄存器x9设置为gd->reloc_off */
    35   add  lr, lr, x9    /* 在重定位后新的返回地址 */
    36   ldr  x0, [x18, #GD_RELOCADDR]    /* 把寄存器x0设置为gd->relocaddr */
    37   b    relocate_code
    38
    39  relocation_return:
    40
    41  /*
    42   * 设置最终的完整环境
    43   */
    44   bl   c_runtime_cpu_setup      /* 仍然调用旧的例程 */
    45  #endif /* ! CONFIG_SPL_BUILD */
    46  #if defined(CONFIG_SPL_BUILD)
    47   bl   spl_relocate_stack_gd    /* 可能返回空指针 */
    48   /*
    49    * 执行“sp = (x0 ! = NULL) ? x0 : sp”,
    50    * 规避这个约束:
    51    * 带条件的mov指令不能把栈指针寄存器作为操作数
    52    */
    53   mov  x1, sp
    54   cmp  x0, #0
    55   csel x0, x0, x1, ne
    56   mov  sp, x0
    57  #endif
    58
    59  /*
    60   * 0初始化未初始化数据段
    61   */
    62   ldr  x0, =__bss_start      /* 这是自动重定位*/
    63   ldr  x1, =__bss_end        /* 这是自动重定位*/
    64  clear_loop:
    65   str  xzr, [x0], #8
    66   cmp  x0, x1
    67   b.lo clear_loop
    68
    69   /* 调用函数board_init_r(gd_t *id, ulong dest_addr) */
    70   mov  x0, x18                      /* gd_t */
    71   ldr  x1, [x18, #GD_RELOCADDR]    /* dest_addr */
    72   b    board_init_r                 /* 相对程序计数器的跳转 */
    73
    74   /* 不会运行到这里,因为函数board_init_r()不会返回*/
    75
    76  ENDPROC(_main)

第6~17行代码,设置C代码的运行环境,为调用函数board_init_f做准备。

❑ 第11行代码,设置临时的栈。

❑ 第13行代码,调用函数board_init_f_alloc_reserve,在栈的顶部为结构体global_data分配空间。

❑ 第17行代码,调用函数board_init_f_init_reserve,初始化结构体global_data。

第20行代码,调用函数board_init_f(f是front,表示前期),执行前期初始化。为了把U-Boot程序复制到内存中来执行,初始化硬件,做准备工作。文件“common/board_f.c”定义了公共的函数board_init_f,函数board_init_f依次执行数组init_sequence_f中的每个函数。

第22~45行代码,如果编译为正常的引导程序,那么调用函数relocate_code,把U-Boot程序复制到内存中,重新定位,然后调用函数c_runtime_cpu_setup,把向量基准地址寄存器设置为异常向量表的起始地址。这里是分界线,以前处理器从NOR闪存取指令,这一步执行完以后处理器从内存取指令。

第46~57行代码,如果编译为第二阶段程序加载器,那么调用函数spl_relocate_stack_gd重新定位栈。

第62~67行代码,用0初始化未初始化数据段。

第72行代码,调用函数board_init_r(r是rear,表示后期),执行后期初始化。文件“common/board_r.c”定义了函数board_init_r,依次执行数组init_sequence_r中的每个函数,最后一个函数是run_main_loop。