***********************************************
蛐蛐
http://qgjie456.blog.163.com/
MSN:qgjie@hotmail.com
本文适用于
linux-2.6.22.8
V 0.1
欢迎转载,但请保留作者信息
***********************************************
系统加电起动后,MIPS 处理器默认的程序入口是0xBFC00000,此地址在无缓存的KSEG1的地址区域内,
对应的物理地址是 0x1FC00000,即CPU从0x1FC00000开始取第一条指令,这个地址在硬件上已经确定为FLASH的位置,
Bootloader将 Linux 内核映像拷贝到 RAM 中某个空闲地址处,然后一般有个内存移动操作,
目的地址在 arch/mips/Makefile 内指定:
load-$(CONFIG_MIPS_PB1550) += 0xFFFFFFFF80100000,
则最终bootloader定会将内核移到物理地址 0x00100000 处
上面Makefile 里指定的的 load 地址,最后会被编译系统写入到 arch/mips/kernel/vmlinux.lds 中:
OUTPUT_ARCH(mips)
ENTRY(kernel_entry)
jiffies = jiffies_64;
SECTIONS
{
. = 0xFFFFFFFF80100000;
_text = .;
.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 处。
***********************************************
linux 内核启动的第一个阶段是从 /arch/mips/kernel/head.s文件开始的。
而此处正是内核入口函数kernel_entry(),该函数定义在 /arch/mips/kernel/head.s文件里。
kernel_entry()函数是体系结构相关的汇编语言,它首先初始化内核堆栈段,来为创建系统中的第一个进程进行准备,
接着用一段循环将内核映像的未初始化数据段(bss段,在_edata和_end之间)清零,
最后跳转到 /init/main.c 中的 start_kernel()初始化硬件平台相关的代码。
*********************************************
NESTED(kernel_entry, 16, sp) # kernel entry point
声明函数 kernel_entry,函数的堆栈为 16 byte,返回地址保存在 $sp 寄存器中。
-----------------------------
声明函数入口
#define NESTED(symbol, framesize, rpc) \
.globl symbol; \
.align 2; \
.type symbol,@function; \
.ent symbol,0; \
symbol: .frame sp, framesize, rpc
汇编伪指令 frame 用来声明堆栈布局。
它有三个参数:
1)第一个参数 framereg:声明用于访问局部堆栈的寄存器,一般为 $sp。
2)第二个参数 framesize:申明该函数已分配堆栈的大小,应该符合 $sp + framesize = 原来的 $sp。
3)第三个参数 returnreg:这个寄存器用来保存返回地址。
----------------------------
kernel_entry_setup # cpu specific setup
----------------------------
这个宏一般为空的,在 include/asm-mips/mach-generic/kernel-entry-init.h 文件中定义。
某些MIPS CPU需要额外的设置一些控制寄
存器,和具体的平台相关,一般为空宏;某些多核MIPS,启动时所
有的core的入口一起指向 kernel_entry,然后在该宏里分叉,
boot core 继续往下,其它的则不停的判断循环,直到boot core 唤醒之
----------------------------
setup_c0_status_pri
设置 cp0_status 寄存器
----------------------------
.macro setup_c0_status_pri
#ifdef CONFIG_64BIT
setup_c0_status ST0_KX 0
#else
setup_c0_status 0 0
#endif
.endm
----------------------------
ARC64_TWIDDLE_PC
除非 CONFIG_ARC64,否则为空操作
-----------------------------
#ifdef CONFIG_MIPS_MT_SMTC
mtc0 zero, CP0_TCCONTEXT__bss_start
mfc0 t0, CP0_STATUS
ori t0, t0, 0xff1f
xori t0, t0, 0x001e
mtc0 t0, CP0_STATUS
#endif
宏定义 CONFIG_MIPS_MT_SMTC 是使用多核的 SMTC Linux 时定义的。一般情况下不考虑。
MIPS已经开发出 SMP Linux的改进版,叫做SMTC(线程上下文对称多处理) Linux。
SMTC Linux能理解轻量级 TC 的概念,并能因此减少某些与SMP Linux相关的开销。
----------------------------
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
清除 BSS 段,清 0。
变量 __bss_start 和 __bss_stop 在连接文件arch/mips/kernel/vmlinux.lds 中定义。
--------------------------------
LONG_S a0, fw_arg0 # firmware arguments
LONG_S a1, fw_arg1
LONG_S a2, fw_arg2
LONG_S a3, fw_arg3
把 bootloader 传递给内核的启动参数保存在 fw_arg0,fw_arg1,fw_arg2,fw_arg3 变量中。
变量 fw_arg0 为内核参数的个数,其余分别为字符串指针,为 *** = XXXX 的格式。
----------------------------------
MTC0 zero, CP0_CONTEXT # clear context register
清除 CP0 的 context register,这个寄存器用来保存页表的起始地址。
----------------------------------
PTR_LA $28, init_thread_union
初始化 $gp 寄存器,这个寄存器的地址指向一个 union,
THREAD_SIZE 大小,最低处是一个thread_info 结构
---------------------------------
PTR_LI sp, _THREAD_SIZE - 32
PTR_ADDU sp, $28
设置 $sp 寄存器,堆栈指针。 $sp = (init_thread_union 的地址) + _THREAD_SIZE - 32
的得出 $sp 指向这个 union 结构的结尾地址 - 32 字节地址。
-----------------------------------
set_saved_sp sp, t0, t1
把 这个 CPU 核的堆栈地址 $sp 保存到 kernelsp[NR_CPUS] 数组。
---------------------------------
如果定义了 CONFIG_SMP 宏,即多 CPU 核。
.macro set_saved_sp stackp temp temp2
#ifdef CONFIG_MIPS_MT_SMTC
mfc0 \temp, CP0_TCBIND
#else
MFC0 \temp, CP0_CONTEXT
#endif
LONG_SRL \temp, PTEBASE_SHIFT
LONG_S \stackp, kernelsp(\temp)
.endm
如果没有定义 CONFIG_SMP 宏,单 CPU 核。
.macro set_saved_sp stackp temp temp2
LONG_S \stackp, kernelsp
.endm
变量 kernelsp 的定义,在 arch/mips/kernel/setup.c 文件中。
unsigned long kernelsp[NR_CPUS];
把 这个 CPU 核的堆栈地址 $sp 保存到 kernelsp[NR_CPUS] 数组。
---------------------------------
PTR_SUBU sp, 4 * SZREG # init stack pointer
---------------------------------
j start_kernel
END(kernel_entry)
最后跳转到 /arch/mips/kernel/main.c 中的 start_kernel()初始化硬件平台相关的代码。
----------------------------------
***************************************************
这个 init_thread_union 变量在 arch/mips/kernel/init_task.c 文件中定义。
union thread_union init_thread_union
__attribute__((__section__(".data.init_task"),
__aligned__(THREAD_SIZE))) =
{ INIT_THREAD_INFO(init_task) };
***************************************************
问题:
1)这个 init_thread_union 结构体指针是怎么初始化的?
***********************************************
系统加电起动后,MIPS 处理器默认的程序入口是0xBFC00000,此地址在无缓存的KSEG1的地址区域内,
对应的物理地址是 0x1FC00000,即CPU从0x1FC00000开始取第一条指令,这个地址在硬件上已经确定为FLASH的位置,
Bootloader将 Linux 内核映像拷贝到
目的地址在 arch/mips/Makefile 内指定:
load-$(CONFIG_MIPS_PB1550) += 0xFFFFFFFF80100000,
则最终bootloader定会将内核移到物理地址
上面Makefile 里指定的的 load 地址,最后会被编译系统写入到 arch/mips/kernel/vmlinux.lds 中:
OUTPUT_ARCH(mips)
ENTRY(kernel_entry)
jiffies = jiffies_64;
SECTIONS
{
. = 0xFFFFFFFF80100000;
_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 处。
***********************************************
linux
而此处正是内核入口函数kernel_entry(),该函数定义在 /arch/mips/kernel/head.s文件里。
kernel_entry()函数是体系结构相关的汇编语言,它首先初始化内核堆栈段,来为创建系统中的第一个进程进行准备,
接着用一段循环将内核映像的未初始化数据段(bss段,在_edata和_end之间)清零,
最后跳转到
*********************************************
NESTED(kernel_entry, 16, sp)
声明函数
-----------------------------
声明函数入口
#define NESTED(symbol, framesize, rpc)
symbol:
汇编伪指令
它有三个参数:
----------------------------
----------------------------
这个宏一般为空的,在 include/asm-mips/mach-generic/kernel-entry-init.h 文件中定义。
某些MIPS CPU需要额外的设置一些控制寄
存器,和具体的平台相关,一般为空宏;某些多核MIPS,启动时所
有的core的入口一起指向
boot core 继续往下,其它的则不停的判断循环,直到boot core 唤醒之
----------------------------
设置
----------------------------
#ifdef CONFIG_64BIT
#else
#endif
----------------------------
除非 CONFIG_ARC64,否则为空操作
-----------------------------
#ifdef CONFIG_MIPS_MT_SMTC
#endif
宏定义
MIPS已经开发出
SMTC Linux能理解轻量级
----------------------------
1:
清除
变量
--------------------------------
把
变量
----------------------------------
清除
----------------------------------
初始化
THREAD_SIZE
---------------------------------
设置
的得出
-----------------------------------
把
---------------------------------
#ifdef CONFIG_MIPS_MT_SMTC
#else
#endif
如果没有定义
变量
unsigned long kernelsp[NR_CPUS];
把
---------------------------------
---------------------------------
最后跳转到
----------------------------------
***************************************************
这个
union thread_union init_thread_union
***************************************************
问题: