[转]linux-mips启动分析(1)

***********************************************


                                           蛐蛐

                    http://qgjie456.blog.163.com/

                        MSN:[email protected]

                                本文适用于
                            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 结构体指针是怎么初始化的?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章