Linux/MIPS启动分析

来源:互联网 发布:网络电视不清楚怎么办 编辑:程序博客网 时间:2024/06/05 07:28

Linux启动入口主要代码在 arch/mips/kernel/head.S文件中 kernel_entry函数以汇编形式出现

主要干了以下几件事:

1.  BSS段清0

2.  从boot传过来的参数赋值到全局变量

3. clear context register

4. 根据init_thread_union建立$gp寄存器  并设置  $sp 寄存器,堆栈指针    (PTR_LA      $28, init_thread_union)

5. 做好上述准备后就跳转到  /arch/mips/kernel/main.c 中的 start_kernel()初始化硬件平台相关的代码


主要涉及的数据结构 在arch/mips/kernel/init_task.c 


union thread_union init_thread_union__init_task_data
__attribute__((__aligned__(THREAD_SIZE))) =
{ INIT_THREAD_INFO(init_task) };

THREAD_SIZE在这里为8K,__attribute__((__aligned__(THREAD_SIZE)))表示这个数据结构以8K对齐  

struct task_structinit_task = INIT_TASK(init_task);

union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};

套用linux设计与实现的图表示Thread_info、stack、task_struct的关系


在mips的head.S的工作就是


这就是传说中的0号进程

1. 进程0是所有其他进程的祖先, 也称作idle进程或swapper进程。
2. 进程0是在系统初始化时由kernel自身从无到有创建。(过程集中在start_kernel函数内)
3. 进程0的数据成员大部分是静态定义的,即由预先定义好的(如上)INIT_TASK, INIT_MM等宏初始化。
 
进程0的描述符init_task定义在arch/arm/kernel/init_task.c,由INIT_TASK宏初始化。 init_mm等结构体定义在include/linux/init_task.h内,为init_task成员的初始值,分别由对应的初始化宏如INIT_MM等初始化


下面是转载网上大虾的详细分析

系统加电起动后,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;
/* 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 处。
*********************************************

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)            # kernelentry point
声明函数   kernel_entry,函数的堆栈为 16byte,返回地址保存在  $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          # cpuspecific 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 */

宏定义   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 temptemp2
#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 stackptemp 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 结构体指针是怎么初始化的?

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

原创粉丝点击