MIPS Linux 最初启动流程

来自Jack's Lab
2013年1月17日 (四) 18:15Comcat (讨论 | 贡献)的版本

(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到: 导航, 搜索


1. bootloader 将ELF 格式的Kernel 加载到某个空闲地址处,然后一般有个内存移动操作,目的地址在 arch/mips/Makefile 内指定:

 load-$(CONFIG_MIPS_PB1550) += 0xFFFFFFFF80100000

则最终bootloader定会将内核移到物理地址 0x00100000处



2. 上面Makefile 里指定的的 load 地址,最后会被编译系统写入到 arch/mips/kernel/vmlinux.lds 中:

OUTPUT_ARCH(mips)
ENTRY(kernel_entry)
jiffies = jiffies_64;
SECTIONS
{
. = 0xFFFFFFFF80100000;
/* read-only */
_text = .; /* Text and read-only data */
.text : {
    *(.text)
...


这个文件最终会以参数 -Xlinker --script -Xlinker vmlinux.lds 的形式传给 gcc,并最终传给链接器 ld 来控制其行为。ld 会将 .text 节的地址链接到 0xFFFFFFFF80100000 处。

关于内核 ELF 文件的入口地址(Entry point),即 bootloader 移动完内核后,直接跳转到的地址,由ld 写入 ELF的头中,其会依次用下面的方法尝试设置入口点,当遇到成功时则停止:

a. 命令行选项 -e entry

b. 脚本中的 ENTRY(symbol)

c. 如果有定义 start 符号,则使用start符号(symbol)

d. 如果存在 .text 节,则使用第一个字节的地址。

e. 地址0


注意到上面的 ld script 中,用 ENTRY 宏设置了内核的 entry point 是 kernel_entry,因此内核取得控制权后执行的第一条指令是在 kernel_entry 处。



3. 这个 kernel_entry 定义于 arch/mips/kernel/head.S 中:

NESTED(kernel_entry, 16, sp)         # kernel entry point

    kernel_entry_setup                          # cpu specific setup,某些MIPS CPU需要额外的设置一些控制寄
                                                           存器,和具体的平台相关,一般为空宏;某些多核MIPS,启动时所
                                                           有的core的入口一起指向kernel_entry,然后在该宏里分叉,boot
                                                           core 继续往下,其它的则不停的判断循环,直到boot core 唤醒之

    setup_c0_status_pri                         # 设置cp0_status 寄存器

    ARC64_TWIDDLE_PC                        # 除非 CONFIG_ARC64,否则为空操作

#ifdef CONFIG_MIPS_MT_SMTC
    /*
     * In SMTC kernel, "CLI" is thread-specific, in TCStatus.
     * We still need to enable interrupts globally in Status,
     * and clear EXL/ERL.
     *
     * TCContext is used to track interrupt levels under
     * service in SMTC kernel. Clear for boot TC before
     * allowing any interrupts.
     */
    mtc0    zero, CP0_TCCONTEXT

    mfc0    t0, CP0_STATUS
    ori t0, t0, 0xff1f
    xori    t0, t0, 0x001e
    mtc0    t0, CP0_STATUS
#endif /* CONFIG_MIPS_MT_SMTC */

    PTR_LA      t0, __bss_start                      # clear .bss
    LONG_S      zero, (t0)
    PTR_LA      t1, __bss_stop - LONGSIZE
1:
    PTR_ADDIU   t0, LONGSIZE
    LONG_S      zero, (t0)
    bne     t0, t1, 1b

    LONG_S      a0, fw_arg0                 # firmware arguments
    LONG_S      a1, fw_arg1               # bootloader 会将要传给内核的参数
    LONG_S      a2, fw_arg2                  写在 a0 ~ a4 里。此处为将参数保存
    LONG_S      a3, fw_arg3                    在 fw_arg0 ~ fw_arg3 四个变量里

    MTC0        zero, CP0_CONTEXT        # clear context register
    PTR_LA      $28, init_thread_union     # 初始化 gp,指向一个union,THREAD_SIZE
                                                                大小,最低处是一个thread_info 结构
    PTR_LI      sp, _THREAD_SIZE - 32      # _THREAD_SIZE = THREAD_SIZE
    PTR_ADDU    sp, $28                           # sp 指向这个union结构的最高低32B处
    set_saved_sp    sp, t0, t1
    PTR_SUBU    sp, 4 * SZREG       # init stack pointer

    j       start_kernel                   # gp, sp设好,可以进入 C 语言环境了 :)
    END(kernel_entry)

[include/asm-mips/thread_info.h]:

 #define   THREAD_SIZE   (PAGE_SIZE << THREAD_SIZE_ORDER)


_THREAD_SIZE 是为预处理生成,与THREAD_SIZE 一致。


来看看 setup_c0_status_pri:

    .macro setup_c0_status_pri
#ifdef CONFIG_64BIT
    setup_c0_status ST0_KX 0
#else
    setup_c0_status 0 0               # CP0_Status[4:0] 置0,CU0 置1,其他位不变
#endif                                         
    .endm


特别注意一直到 trap_init() 中的 per_cpu_trap_init(),CP0_Status[BEV] 一直为1。因此这个阶段异常的入口一直在0xFFFFFFFFBFC000200

    .macro setup_c0_status set clr
    .set    push
#ifdef CONFIG_MIPS_MT_SMTC
    /*
     * For SMTC, we need to set privilege and disable interrupts only for
     * the current TC, using the TCStatus register.
     */
    mfc0    t0, CP0_TCSTATUS
    /* Fortunately CU 0 is in the same place in both registers */
    /* Set TCU0, TMX, TKSU (for later inversion) and IXMT */
    li t1, ST0_CU0 | 0x08001c00
    or t0, t1
    /* Clear TKSU, leave IXMT */
    xori    t0, 0x00001800
    mtc0    t0, CP0_TCSTATUS
    _ehb
    /* We need to leave the global IE bit set, but clear EXL...*/
    mfc0    t0, CP0_STATUS
    or t0, ST0_CU0 | ST0_EXL | ST0_ERL | \set | \clr
    xor t0, ST0_EXL | ST0_ERL | \clr
    mtc0    t0, CP0_STATUS
#else
    mfc0    t0, CP0_STATUS
    or t0, ST0_CU0|\set|0x1f|\clr
    xor t0, 0x1f|\clr
    mtc0    t0, CP0_STATUS
    .set    noreorder
    sll zero,3              # ehb
#endif
    .set    pop
    .endm


在完成 status, gp, fp 的设置后,直接跳转到 init/main.c 中的 start_kernel() ,开始了一个新的时代 ;)





个人工具
名字空间

变换
操作
导航
工具箱