[轉]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 結構體指針是怎麼初始化的?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章