本文共 3560 字,大约阅读时间需要 11 分钟。
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 entryb. 脚本中的 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 - LONGSIZE1: 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() ,开始了一个新的时代 ;)转载地址:http://risqi.baihongyu.com/