linux内核head.S文件分析
1:內(nèi)核運(yùn)行的物理地址與虛擬地址
????(1)KERNEL_RAM_VADDR(VADDR就是virtual address),這個(gè)宏定義了內(nèi)核運(yùn)行時(shí)的虛擬地址。值為0xC0008000
????(2)KERNEL_RAM_PADDR(PADDR就是physical address),這個(gè)宏定義內(nèi)核運(yùn)行時(shí)的物?? 理地址。值為0x30008000
????(3)總結(jié):內(nèi)核運(yùn)行的物理地址是0x30008000,對應(yīng)的虛擬地址是0xC0008000。而物理地址為什么是30008000是因?yàn)樵谝浦瞮boot的時(shí)候?yàn)榱说玫竭B續(xù)的512M的內(nèi)存,將內(nèi)存地址的開頭設(shè)置為30000000
?
2:內(nèi)核的真正入口
????(1)內(nèi)核的真正入口就是ENTRY(stext)處
????(2)前面的__HEAD定義了后面的代碼屬于段名為.head.text的段
?
3:內(nèi)核運(yùn)行的硬件條件
????(1) 內(nèi)核的起始部分代碼是被解壓代碼調(diào)用的。回憶之前講zImage的時(shí)候,uboot啟動(dòng)內(nèi)核后實(shí)際調(diào)用運(yùn)行的是zImage前面的那段未經(jīng)壓縮的解壓代碼,解壓代碼運(yùn)行時(shí)先將zImage后段的內(nèi)核解壓開,然后再去調(diào)用運(yùn)行真正的內(nèi)核入口。
????(2) 內(nèi)核啟動(dòng)不是無條件的,而是有一定的先決條件,這個(gè)條件由啟動(dòng)內(nèi)核的bootloader(我們這里就是uboot)來構(gòu)建保證。
????(3) ARM體系中,函數(shù)調(diào)用時(shí)實(shí)際是通過寄存器傳參的(函數(shù)調(diào)用時(shí)傳參有兩種設(shè)計(jì):一種是寄存器傳參,另一種是棧內(nèi)存?zhèn)鲄?#xff09;。所以uboot中最后theKernel (0, machid, bd->bi_boot_params);執(zhí)行內(nèi)核時(shí),運(yùn)行時(shí)實(shí)際把0放入r0中,machid放入到了r1中,bd->bi_boot_params放入到了r2中。ARM的這種處理技巧剛好滿足了kernel啟動(dòng)的條件和要求。
????(4) kernel啟動(dòng)時(shí)MMU是關(guān)閉的,因此硬件上需要的是物理地址。但是內(nèi)核是一個(gè)整體(zImage)只能被連接到一個(gè)地址(不能分散加載),這個(gè)連接地址肯定是虛擬地址。因此內(nèi)核運(yùn)行時(shí)前段head.S中尚未開啟MMU之前的這段代碼就很難受。所以這段代碼必須是位置無關(guān)碼,而且其中涉及到操作硬件寄存器等時(shí)必須使用物理地址。
?
4:內(nèi)核啟動(dòng)的匯編階段
__lookup_processor_type
????(1) 我們從cp15協(xié)處理器的c0寄存器中讀取出硬件的CPU ID號(hào),然后調(diào)用這個(gè)函數(shù)來進(jìn)行合法性檢驗(yàn)。如果合法則繼續(xù)啟動(dòng),如果不合法則停止啟動(dòng),轉(zhuǎn)向__error_p啟動(dòng)失敗。
????(2) 該函數(shù)檢驗(yàn)cpu id的合法性方法是:內(nèi)核會(huì)維護(hù)一個(gè)本內(nèi)核支持的CPU ID號(hào)碼的數(shù)組,然后該函數(shù)所做的就是將從硬件中讀取的cpu id號(hào)碼和數(shù)組中存儲(chǔ)的各個(gè)id號(hào)碼依次對比,如果沒有一個(gè)相等則不合法,如果有一個(gè)相等的則合法。
????(3) 內(nèi)核啟動(dòng)時(shí)設(shè)計(jì)這個(gè)校驗(yàn),也是為了內(nèi)核啟動(dòng)的安全性著想。?????????????????????????????????????
?__lookup_machine_type
????該函數(shù)的設(shè)計(jì)理念和思路和上面校驗(yàn)cpu id的函數(shù)一樣的。不同之處是本函數(shù)校驗(yàn)的是機(jī)器碼。
__vet_atags
????(1) 該函數(shù)的設(shè)計(jì)理念和思路和上面2個(gè)一樣,不同之處是用來校驗(yàn)uboot給內(nèi)核的傳參ATAGS格式是否正確。這里說的傳參指的是uboot通過tag給內(nèi)核傳的參數(shù)(主要是板子的內(nèi)存分布memtag、uboot的bootargs)
????(2) 內(nèi)核認(rèn)為如果uboot給我的傳參格式不正確,那么我就不啟動(dòng)。
????(3) uboot給內(nèi)核傳參的部分如果不對,是會(huì)導(dǎo)致內(nèi)核不啟動(dòng)的。譬如uboot的bootargs設(shè)置不正確內(nèi)核可能就會(huì)不啟動(dòng)。
__create_page_tables
????(1) 顧名思義,這個(gè)函數(shù)用來建立頁表。
????(2) linux內(nèi)核本身被連接在虛擬地址處,因此kernel希望盡快建立頁表并且啟動(dòng)MMU進(jìn)入虛擬地址工作狀態(tài)。但是kernel本身工作起來后頁表體系是非常復(fù)雜的,建立起來也不是那么容易的。kernel想了一個(gè)好辦法
????(3) kernel建立頁表其實(shí)分為2步。第一步,kernel先建立了一個(gè)段式頁表(和uboot中之前建立的頁表一樣,頁表以1MB為單位來區(qū)分的),這里的函數(shù)就是建立段式頁表的。段式頁表本身比較好建立(段式頁表1MB一個(gè)映射,4GB空間需要4096個(gè)頁表項(xiàng),每個(gè)頁表項(xiàng)4字節(jié),因此一共需要16KB內(nèi)存來做頁表),壞處是比較粗不能精細(xì)管理內(nèi)存;第二步,再去建立一個(gè)細(xì)頁表(4kb為單位的細(xì)頁表),然后啟用新的細(xì)頁表廢除第一步建立的段式映射頁表。
????(4) 內(nèi)核啟動(dòng)的早期建立段式頁表,并在內(nèi)核啟動(dòng)前期使用;內(nèi)核啟動(dòng)后期就會(huì)再次建立細(xì)頁表并啟用。等內(nèi)核工作起來之后就只有細(xì)頁表了。
__switch_data
????(1) 建立了段式頁表后進(jìn)入了__switch_data部分,這東西是個(gè)函數(shù)指針數(shù)組。
????(2) 分析得知下一步要執(zhí)行__mmap_switched函數(shù)
????(3) 復(fù)制數(shù)據(jù)段、清除bss段(目的是構(gòu)建C語言運(yùn)行環(huán)境)
????(4) 保存起來cpu id號(hào)、機(jī)器碼、tag傳參的首地址。
????(5) b start_kernel跳轉(zhuǎn)到C語言運(yùn)行階段。
?
總結(jié):匯編階段其實(shí)也沒干啥,主要原因是uboot干了大部分活。匯編階段主要就是校驗(yàn)啟動(dòng)合法性、建立段式映射的頁表并開啟MMU以方便使用內(nèi)存、跳入C階段。
總結(jié)
以上是生活随笔為你收集整理的linux内核head.S文件分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cortex-M3存储器系统
- 下一篇: linux修改文件没有备份,归档模式,恢