Chinaunix首页 | 论坛 | 博客
  • 博客访问: 362642
  • 博文数量: 87
  • 博客积分: 1322
  • 博客等级: 少尉
  • 技术积分: 915
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-25 18:04
文章分类

全部博文(87)

文章存档

2013年(10)

2012年(9)

2011年(68)

分类: LINUX

2011-11-29 10:21:13

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
阅读(3812) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~