天天看點

Linux核心啟動分析(一)

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核心啟動分析(一)

以上代碼實作了多半功能,但涉及一些子函數分支。上面的功能代碼已經注釋的非常詳細,下面對一些子函數及疑問進行分析。

注:Linux核心的連結位址在連結腳本中指定為:PAGE_OFFSET + TEXT_OFFSET = 0xC0008000而核心解壓後開始運作時會位于實體記憶體0x30008000(S3C2440)處,是以必定會有一段代碼的運作位址與該段代碼的連結位址不一緻。是以這段代碼必須是位置無關的,否則無法運作。

是以在上面這段代碼,我們需關注與位址相關的指令。下圖為上述彙編代碼的反彙編代碼:

Linux核心啟動分析(一)

其中:

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,該位址處的代碼如下:

Linux核心啟動分析(一)

是以執行完CPU初始化代碼後的傳回位址為0x300080034。

現在開始分析這幾個子函數:

__lookup_processor_type:檢視核心是否支援該處理器

對于處理器,Linux核心使用struct proc_info_list結構來描述。而在arch/arm/mm/proc-xxx.S中針對不同的處理器有該結構的定義(如ARM920T,有proc-arm920.S),以下為該結構的定義:

Linux核心啟動分析(一)

對于ARM920T,在arch/arm/mm/proc-arm920.S中有如下定義:

Linux核心啟動分析(一)

以下為__lookup_processor_type的核心代碼:

Linux核心啟動分析(一)

标号3處定義的變量:

Linux核心啟動分析(一)

__lookup_machine_type:檢視核心是否支援該處理器平台

對于處理器的硬體平台,Linux核心使用struct machine_desc結構來描述,并構造宏MACHINE_START(_type,_name)來定義該結構。對于SMDK2440開發闆,在arch/arm/mach-s3c2440/mach-smdk2440.c中有如下定義:

Linux核心啟動分析(一)

硬體平台描述結構如下:

Linux核心啟動分析(一)

具體的代碼分析如下:

Linux核心啟動分析(一)

__vet_atags:檢查參數位址

Linux核心啟動分析(一)

__create_page_tables:建立初始化頁表

前面所有的操作都是運作在MMU關閉的狀态,為了是核心運動位址與連結位址一緻應盡早的開啟MMU,而開啟MMU之前需要建立頁表。是以,在此處先建立初始化頁表,這階段建立的頁表會在後期銷毀,隻是起臨時作用。

第一步:清空頁表項所占的記憶體空間

Linux核心啟動分析(一)

第二步:建立頁表映射

Linux核心啟動分析(一)
Linux核心啟動分析(一)

如果支援調試,還得映射好序列槽:

Linux核心啟動分析(一)

建立頁表後設定好sp和lr後便去初始化CPU了:

Linux核心啟動分析(一)

是以将調用proc-arm920.S中__arm920_proc_info結構中的跳轉指令:

Linux核心啟動分析(一)

對應的代碼:

Linux核心啟動分析(一)

__enable_mmu:使能MMU

Linux核心啟動分析(一)

開啟MMU後,将R13指派給PC。是以,核心跳轉到__switch_data處執行。

Linux核心啟動分析(一)

是以,CPU将跳轉到__mmap_switched處執行:

Linux核心啟動分析(一)

總結:

1.Linux啟動階段的映射圖

Linux核心啟動分析(一)

2.為何需要将虛拟位址範圍0x30000000 ~ 0x30100000映射到實體位址範圍0x30000000 ~ 0x30100000?

Linux核心啟動分析(一)

當CPU執行mcr p15, 0, r0, c1, c0, 0這條指令時,PC應該指向了第一條mov r3, r3指令,且此時的PC值必然為0x3xxxxxxx。由于已經開啟了MMU,是以如果沒有虛拟-實體位址相同的映射才mov r3, r3開始的三條指令都無法運作。證明方法:将__create_page_tables子過程的以下幾條指令注釋:

Linux核心啟動分析(一)

核心将無法啟動。這裡涉及幾個知識點:ARM支援流水線操作,當執行某條指令時預取指令的位址PC不等于目前正在執行的指令的位址(PC+X)。取指的過程為從PC指向的記憶體位址取出指令到CPU内部。是以,取指的過程可以看作是先讀出PC值然後從PC值對應的記憶體位址中取出4位元組的指令碼。