Linux核心啟動分析(一)
序:關于Linux的啟動已經是一個老生常談的話題了,但這裡還是對Linux核心的啟動進行一下總結。現在的Linux核心代碼已經複雜到讓人無法剖根究底,但是分析其主幹還是很有必要的。就此,本人按自己的觀點對Linux啟動代碼進行剖析。如果有不對之處,還望各位看官予以糾正。
對于Linux核心的啟動,我們無須花太多時間在解壓縮部分,可直接分析arch/arm/kernel/head.S(分析的核心版本為Linux2.6.30,硬體平台為GT2440,其他軟硬體平台原理一緻)。
觀連結腳本arch/arm/kernel/vmlinux.lds,可知連結的入口點為stext。該該入口定義在arch/arm/kernel/head.S中。
首先對Linux啟動的第一階段的功能進行總結再來分析代碼,該階段的主要功能:
·設定處理器為SVC模式
·檢查核心是否支援該處理器
·檢查核心是否支援該處理器平台
·檢查U-BOOT傳遞給核心參數的位址是否合法
·為啟動階段運作建立初始化頁表
·進行CPU級的初始化(CACHE及協處理器設定)
·使能MMU
·清除BSS
以上代碼實作了多半功能,但涉及一些子函數分支。上面的功能代碼已經注釋的非常詳細,下面對一些子函數及疑問進行分析。
注:Linux核心的連結位址在連結腳本中指定為:PAGE_OFFSET + TEXT_OFFSET = 0xC0008000而核心解壓後開始運作時會位于實體記憶體0x30008000(S3C2440)處,是以必定會有一段代碼的運作位址與該段代碼的連結位址不一緻。是以這段代碼必須是位置無關的,否則無法運作。
是以在上面這段代碼,我們需關注與位址相關的指令。下圖為上述彙編代碼的反彙編代碼:
其中:
sl = r10
b/bl為相對跳轉指令,跳轉的目的位址為PC值加個偏移值
而我們所不解的指令: ldr r13, __switch_data竟然翻譯成了:
ldr sp, [pc,#240] ;即使sp = [30008120] 記憶體位址為30008120處的值0xC0008148
主要的話說明CPU并不是直接取出0xC0008120處的值指派給SP,而是通過計算處0xC0008120處的偏移位址後再取值,這也就說的過去了。是以,當我們看到這種無法解釋的指令的時候,通過反彙編即可迎刃而解。
而原來的指令: adr lr, __enable_mmu的反彙編為:add lr, pc, #0,而pc此時的值為3000802c+8。是以lr = 300080034,該位址處的代碼如下:
是以執行完CPU初始化代碼後的傳回位址為0x300080034。
現在開始分析這幾個子函數:
__lookup_processor_type:檢視核心是否支援該處理器
對于處理器,Linux核心使用struct proc_info_list結構來描述。而在arch/arm/mm/proc-xxx.S中針對不同的處理器有該結構的定義(如ARM920T,有proc-arm920.S),以下為該結構的定義:
對于ARM920T,在arch/arm/mm/proc-arm920.S中有如下定義:
以下為__lookup_processor_type的核心代碼:
标号3處定義的變量:
__lookup_machine_type:檢視核心是否支援該處理器平台
對于處理器的硬體平台,Linux核心使用struct machine_desc結構來描述,并構造宏MACHINE_START(_type,_name)來定義該結構。對于SMDK2440開發闆,在arch/arm/mach-s3c2440/mach-smdk2440.c中有如下定義:
硬體平台描述結構如下:
具體的代碼分析如下:
__vet_atags:檢查參數位址
__create_page_tables:建立初始化頁表
前面所有的操作都是運作在MMU關閉的狀态,為了是核心運動位址與連結位址一緻應盡早的開啟MMU,而開啟MMU之前需要建立頁表。是以,在此處先建立初始化頁表,這階段建立的頁表會在後期銷毀,隻是起臨時作用。
第一步:清空頁表項所占的記憶體空間
第二步:建立頁表映射
如果支援調試,還得映射好序列槽:
建立頁表後設定好sp和lr後便去初始化CPU了:
是以将調用proc-arm920.S中__arm920_proc_info結構中的跳轉指令:
對應的代碼:
__enable_mmu:使能MMU
開啟MMU後,将R13指派給PC。是以,核心跳轉到__switch_data處執行。
是以,CPU将跳轉到__mmap_switched處執行:
總結:
1.Linux啟動階段的映射圖
2.為何需要将虛拟位址範圍0x30000000 ~ 0x30100000映射到實體位址範圍0x30000000 ~ 0x30100000?
當CPU執行mcr p15, 0, r0, c1, c0, 0這條指令時,PC應該指向了第一條mov r3, r3指令,且此時的PC值必然為0x3xxxxxxx。由于已經開啟了MMU,是以如果沒有虛拟-實體位址相同的映射才mov r3, r3開始的三條指令都無法運作。證明方法:将__create_page_tables子過程的以下幾條指令注釋:
核心将無法啟動。這裡涉及幾個知識點:ARM支援流水線操作,當執行某條指令時預取指令的位址PC不等于目前正在執行的指令的位址(PC+X)。取指的過程為從PC指向的記憶體位址取出指令到CPU内部。是以,取指的過程可以看作是先讀出PC值然後從PC值對應的記憶體位址中取出4位元組的指令碼。