***********************************************
蛐蛐
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 結構體指針是怎麼初始化的?
蛐蛐
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 結構體指針是怎麼初始化的?