Linux kernel啟動流程第一階段
head.S:kernel第一階段入口
來自:http://blog.csdn.net/ooonebook/article/details/52710290
1、safe_svcmode_maskallr9;關閉普通中斷、快速中斷,使能SVC模式
實現程式碼:arch/arm/include/asm/assembler.h
註解:來自http://blog.csdn.net/crosskernel/article/details/21091819
從Hyper態返回SVC態
//reg—暫存暫存器
.macro safe_svcmode_maskall reg:req
#if __LINUX_ARM_ARCH__ >= 6
//讀取cpsr到暫存暫存器reg
mrs \reg , cpsr
/*以下兩條指令區分當前cpsr是否處在HYP_MODE,
*若處在HYP_MODE模式,標誌位清零
*/
eor \reg, \reg, #HYP_MODE
tst \reg, #MODE_MASK
bic \reg , \reg , #MODE_MASK
//在暫存暫存器裡存放SVC控制位
orr \reg , \reg , #PSR_I_BIT | PSR_F_BIT | SVC_MODE
THUMB( orr \reg , \reg , #PSR_T_BIT )
/*若當前暫存器處於hyper模式,返回SVC模式走一個杜撰的HVC異常返回。*/
bne 1f
orr \reg, \reg, #PSR_A_BIT
/*設定返回到SVC態的地址是標號2,即該巨集呼叫的後一條指令。*/
adr lr, BSYM(2f)
//退出hyper後的cpsr暫存器
msr spsr_cxsf, \reg
//放到ELR_hyp中
__MSR_ELR_HYP(14)
//返回到SVC態
__ERET
//當前處理器不在hyper狀態,強制切換到SVC態
1: msr cpsr_c, \reg
2:
2、proccessor info的獲取
cpu id和procinfo是一一對應的關係,所以可以通過cpu id來獲取到對應的procinfo結構體。
procinfo使用proc_info_list結構體,用來說明一個cpu的資訊,包括這個cpu的ID號,對應的核心資料對映區的MMU標識等等。
注意: - proc_info_list結構體中存在MMU標識,也就是我們需要在開啟MMU之前需要先獲取procinfo的原因,因為開啟MMU之前需要配置臨時核心頁表,而配置臨時核心頁表需要這裡的MMU標識來進行設定。
對應程式碼:
1)mrc p15, 0, r9, c0, c0 @ get processor id
解釋:arm體系將CPUID(處理器識別符號,主識別符號)存放在協處理器cp15的c0暫存器中。
2)bl__lookup_processor_type @r5 = procinfor9 = cupid
arch/arm/kernel/head-common.S 通過比較各個CPU的procinfo中的cpu id的值來查詢對應的procinfo結構體。若不支援當前CPU,則r5=0
3)判斷r5,若為0,則列印錯誤。
3、地址的一次轉換
@ 在呼叫__enable_mmu前使用的都是實體地址,而核心卻是以虛擬地址連線的,這裡進行一次轉換
#ifndef CONFIG_XIP_KERNEL
adr r3,2f @ r3= 第124行程式碼的實體地址
ldmia r3, {r4,r8} @r4= 第124行程式碼的虛似地址,r8=PAGE_OFFSET
sub r4, r3,r4 @ (PHYS_OFFSET - PAGE_OFFSET)即實體地址與虛似地址差值
add r8, r8,r4 @ PHYS_OFFSET r8=PAGE_OFFSET對應的實體地址
#else
ldr r8,=PLAT_PHYS_OFFSET @ RAM的起始實體地址,值為0x30000000
L124 2: .long . @ "."號表示當前這行程式碼編譯連線後的虛似地址
L125 .long PAGE_OFFSET
4、驗證dtb bl __vet_atags
實現程式碼:arch/arm/kernel/head-common.S
TIPS:
1、dtb裡面存放了各種硬體資訊,如果dtb有問題,會導致後續開機過程中讀取的裝置資訊有問題而導致無法開機。
2、在生成dtb的時候會在頭部上新增一個幻數magic,而驗證dtb是否合法主要也就是看這個dtb的magic是否和預期的值一致。其中magic是固定值:0xd00dfeed(大端)或者0xedfe0dd0(小端)。我們只要提取待驗證dtb的地址(Uboot傳入的r2)上的資料的前四個位元組,與0xd00dfeed(大端)或者0xedfe0dd0(小端)進行比較,如果匹配的話,就說明對應待驗證dtb就是一個合法的dtb。
程式碼註解:
Ldr r5, [r2, #0] @獲取前4個位元組放在r5中
Ldrr6, =OF_DT_MAGIC @獲取magic放在r6
5、bl__create_page_tables(建立臨時核心頁表====>開啟MMU)
實現程式碼:arch/arm/kernel/head.S
TIPS:
1、MMU(Memory ManageUnion):
將線性地址(虛擬地址)對映為實體地址(RAM地址);
提供硬體機制的記憶體訪問授權;
根據頁表找到對應關係以及許可權;
2、為了開啟MMU,核心需要建立一個臨時核心頁表,用於kenrel啟動過程中的開啟MMU的過渡階段。並且,使用的是段式管理的方法。
程式碼註解:
1)獲取核心頁表的起始地址
pgtbl r4, r8 @ page table address
pgtbl 巨集用於通過DRAM實體地址來獲取頁表的實體地址。前面我們已經知道r8用於存放DRAM的起始實體地址,r4則是要存放計算得到的頁表實體地址。
pgtbl 巨集如下:
arch/arm/kernel/head.S
.macro pgtbl, rd, phys
add \rd, \phys,
#TEXT_OFFSET
sub \rd, \rd,
#PG_DIR_SIZE
.endm
kernel在放在DRAM上偏移TEXT_OFFSET的位置上。而linux規定將TEXT_OFFSET之前的PG_DIR_SIZE大小的空間用作臨時頁表。所以計算方式如下:
kernel起始地址=DRAM起始實體地址+TEXT_OFFSET=0x20008000核心頁表地址=kernel起始地址-PG_DIR_SIZE=0x20004000=>PG_DIR_SIZE=0x4000(16K)所以程式碼換算成如下計算:
\rd(r4) = phys(r8) +TEXT_OFFSET
\rd(r4) = \rd(r4) -PG_DIR_SIZE
2)清零頁表
str r3, [r0], #4
@ 從r0(臨時核心頁表實體地址)指向的暫存器上開始寫入0值,每16個位元組一個迴圈
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
每16個位元組一迴圈可以減少teq的頻率,同時不會出現清零越界(因:頁表大小為16K)
3)設定MMU的標識並存放到r7暫存器
ldr r7,[r10, #PROCINFO_MM_MMUFLAGS]@ mm_mmuflags
PROCINFO_MM_MMUFLAGS對應如下
DEFINE(PROCINFO_MM_MMUFLAGS, offsetof(struct proc_info_list, __cpu_mm_mmu_flags));
4)建立對映表
未懂
6、b __enable_mmu(呼叫平臺特定的__CPU_flush函式(在結構體proc_info_list中),使能MMU)
程式碼:
ldr r13, =__mmap_switched @ address to jump to after
@ mmu has been enabled
@ 把__mmap_switched函式的地址放在暫存器
@ __mmap_switched實現了開啟MMU之後跳轉到start_kernel。
adr lr, BSYM(1f) @ return (PIC) address
@簡單說,就是儲存了呼叫子程式後返回的指令地址
movr8, r4 @ set TTBR1 to swapper_pg_dir
@ 把臨時核心頁表的地址放在r8暫存器中
ldrr12, [r10, #PROCINFO_INITFUNC]
@ 把cpu對應procinfo中的__cpu_flush存放到r12暫存器中
@ __cpu_flush成員存放的是cpu對應架構的setup函式的地址,在結構體proc_info_list中
@ 對於s5pv210來說,這個值就是__v7_setup的連線地址。
addr12, r12, r10
retr12
@ 這裡實現為跳轉到__v7_setup的實體地址上,也就是呼叫__v7_setup
1: b __enable_mmu
@ 跳轉到__enable_mmu
__enable_mmu:
· 需要先將頁表實體地址寫入到cp15的c2暫存器中
· 需要在cp15的c3暫存器中寫入位域相應的許可權
· 配置cp15的c1暫存器,用來控制MMU的相應功能
· 跳轉到__turn_mmu_on
__turn_mmu_on:真正的開啟MMU的函式,開啟MMU後CPU會把所有地址都當做虛擬地址處理。
注:__turn_mmu_on中會執行命令:
mov r3, r13
ret r3
之前在head.S中有指令“ldr r13, =__mmap_switched”,因此會直接跳轉去執行__mmap_switched,該函式負責跳轉至核心。
7、跳轉至核心
準備階段:
· 資料段的準備
· 堆疊段的準備
· 一些後續會訪問到的變數的設定(CPU ID、machine id、&dtb、當前程序堆疊指標)
· 當前程序堆疊指標的設定
程式碼:
__mmap_switched_data結構體:
__mmap_switched_data:
.long __data_loc @ r4
.long _sdata @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
.long __machine_arch_type @ r5
.long __atags_pointer @ r6
#ifdefCONFIG_CPU_CP15
.long cr_alignment @ r7
#else
.long0 @ r7
#endif
.long init_thread_union +THREAD_START_SP @ sp
.size __mmap_switched_data, . - __mmap_switched_data
__mmap_switched:
adrr3, __mmap_switched_data
@ 將__mmap_switched_data的地址載入到r3中
ldmia r3!, {r4, r5, r6, r7}
@ 將__mmap_switched_data(r3)上的值分別載入到r4、r5、r6、r7暫存器中,__mmap_switched_data前面說明了
@ 經過上述動作,r4、r5、r6、r7暫存器分別存放了如下值
@ r4 -> __data_loc:資料段儲存地址
@ r5 -> _sdata:資料段起始地址
@ r6 -> __bss_start:堆疊段起始地址
@ r7 -> _end:堆疊段結束地址
cmpr4, r5 @ Copy data segment if needed
1: cmpne r5, r6
ldrne fp, [r4], #4
strne fp, [r5], #4
bne 1b
@ 判斷資料段儲存地址(r4)和資料段起始地址(r5)
@ 如果不一樣的話需要搬移到資料段起始地址(r5)上。
movfp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
@ 清空堆疊段。
@ 從堆疊段起始地址(r6)開始寫入0,一直寫到地址為堆疊段結束地址(r7)
ARM( ldmia r3, {r4, r5, r6, r7, sp})
THUMB(ldmia r3, {r4, r5, r6, r7} )
THUMB(ldr sp, [r3, #16] )
@ 繼續將__mmap_switched_data(r3)上的值分別載入到r4、r5、r6、r7、sp暫存器中,注意是前面r3已經載入過一部分了,地址和__mmap_switched_data已經不一樣了。
@ 經過上述動作,r4、r5、r6、r7暫存器分別存放了如下值
@ r4 -> processor_id變數地址:其內容是cpu處理器ID
@ r5 -> __machine_arch_type變數地址:其內容是machine id
@ r6 -> __atags_pointer變數地址:其內容是dtb的地址
@ r7 -> cr_alignment變數地址:其內容是cp15的c1的暫存器的值
@ sp-> init_thread_union + THREAD_START_SP,設定了當前程序的堆疊
strr9, [r4] @ Save processor ID
@ 把cpu處理器id(r9)放到processor_id變數中([r4])
strr1, [r5] @ Save machine type
@ 把mechine id(r1)存放到__machine_arch_type變數中([r5])
strr2, [r6] @ Save atags pointer
@ 把dtb的地址指標(r2)存放到__atags_pointer變數中([r6])
cmpr7, #0
strne r0, [r7] @ Save control register values
@ 把cp15的c1的暫存器的值(r0)存放到cr_alignment變數中([r7])
b start_kernel
@ 跳轉到start_kernel中,也就是啟動流程的第二階段。
ENDPROC(__mmap_switched)
Kernel啟動流程中的Tips:
1、 Kernel一般會存在於儲存裝置上,比如FLASH\EMMC\SDCARD. 因此,需要先將kernel映象載入到RAM的位置上,CPU才可以去訪問到kernel。
但是注意,載入的位置是有要求的,一般是載入到物理RAM偏移0x8000的位置,也就是要在前面預留出32K的RAM。kernel會從載入的位置上開始解壓,而kernel前面的32K空閒RAM中,16K作為boot params,16K作為臨時頁表
2、 Arch/arm/kernel/head.S(kernel的入口函式)
3、 bootloader需要通過設定PC指標到kernel的入口程式碼處(也就是kernel的載入位置)來實現kernel的跳轉。
Kernel的硬體要求如下(解釋了bootloader為何去那樣初始化硬體):
* MMU =off MMU用來處理實體地址到虛擬記憶體地址的對映,因此需要軟體上需要先配置其對映表(也就是後續文章會說明的頁表)。MMU關閉的情況下,CPU定址的地址都是實體地址,也就是不需要經過轉化直接訪問相應的硬體。一旦開啟之後,CPU定址的所有地址都是虛擬地址,都會經過MMU對映到真正的實體地址上,即使你在程式碼中訪問的是一個實體地址,也會被當作虛擬記憶體地址使用。而對映表是由kernel自己建立的,因此,在建立對映表之前kernel訪問的地址都是實體地址,所以必須保證MMU是關閉狀態。
D-cache= off
CACHE是CPU和記憶體之間的高速緩衝儲存器,又分成資料緩衝器D-cache和指令緩衝器I-cache。資料Cache一定要關閉,否則可能kernel剛啟動的過程中,去取資料的時候,從Cache裡面取,而這時候RAM中資料還沒有Cache過來,導致資料預取異常。博主的理解是,假設開啟MMU之前,cache上存了一個項“地址0x20000000、資料0xffff0000”,開啟MMU之後,讀取0x20000000地址上(虛擬地址)資料,但是此時會直接從cache中讀到項“地址0x20000000、資料0xffff0000”,但實際上對應實體地址上的資料並不是這個,所以會導致讀取的資料錯誤。
巨集 |
位置 |
預設值 |
說明 |
KERNEL_RAM_ADDR |
arch/arm/kernel/head.S +26 |
0xc0008000 |
kernel在RAM中的虛擬地址 |
PAGE_OFFSET |
include/asm-arm/memeory.h +50 |
0xc0000000 |
核心空間的起始虛擬地址 |
TEXT_OFFSET |
arch/arm/Makefile +131 |
0x00008000 |
核心在RAM中起始位置相對於 RAM起始地址的偏移 |
TEXTADDR |
arch/arm/kernel/head.S +49 |
0xc0008000 |
kernel的起始虛擬地址 |
PHYS_OFFSET |
include/asm-arm/arch- *** /memory.h |
平臺相關 |
RAM的起始實體地址,對於s3c2410來說在include/asm-arm/arch-s3c2410/memory.h下定義,值為0x30000000(ram接在片選6上) |
相關推薦
Linux kernel啟動流程第一階段
head.S:kernel第一階段入口 來自:http://blog.csdn.net/ooonebook/article/details/52710290 1、safe_svcmode_maskallr9;關閉普通中斷、快速中斷,使能SVC模式 實現程式碼:arch/a
ARM linux kernel啟動流程 head.S(一)
1. kernel執行的史前時期和記憶體佈局 在arm平臺下,zImage.bin壓縮映象是由bootloader載入到實體記憶體,然後跳到zImage.bin裡一段程式,它專門於將被壓縮的kernel解壓縮到KERNEL_RAM_PADDR開始的一段記憶體中,接著跳進真
kernel啟動流程第二階段
整個流程基本為從網上大牛的分享結合自己的理解所述,部分引用可能未貼上連結。 Kernel啟動流程中的Tips: 1、 Kernel一般會存在於儲存裝置上,比如FLASH\EMMC\SDCARD. 因此,需要先將kernel映象載入到RAM的位置上,CPU才可以去訪問到k
[kernel 啟動流程] (第二章)第一階段之——設定SVC、關閉中斷
本文是基於arm平臺。例子都是以tiny210(s5pv210 armv7)為基礎的。 [kernel 啟動流程]系列: 建議參考文件: ================================================ 零、說
[kernel 啟動流程] (第五章)第一階段之——臨時核心頁表的建立
本文是基於arm平臺。例子都是以tiny210(s5pv210 armv7)為基礎的。 [kernel 啟動流程]系列: 建議參考文件: ================================================ 零、說明
linux下kernel啟動流程
&nb
Linux系統啟動流程
linux系統啟動流程Linux系統啟動流程Linux系統啟動流程圖1、加電自檢 上電自檢POST,主要負責檢測系統外圍關鍵設備(如:CPU、內存、顯卡、I/O、鍵盤鼠標等)是否正常, 自檢完成後從BIOS中設置的系統啟動順序來搜索用於啟動系統的驅動器2、grub引導(1)grub引導第一階段(stag
linux系統啟動流程(CentOS5/6/7為例)
centos 系統一個操作系統要完整啟動起來需要哪些步驟呢?今天以CentOS為例探索一下linux的啟動流程;一、Linux系統的組成--kernel+rootfs(根文件系統)kernel: 內核,進行進程管理、內存管理、網絡管理、驅動程序、文件系統、安全功能等等rootfs: 根文件系統1.內核設計流派
LINUX之啟動流程
啟動 分享 src 引導 一個 技術分享 bio 化工 swap分區 (上圖片轉自一位高手所做) 啟動第一步--加載BIOS當你打開計算機電源,計算機會首先加載BIOS信息,BIOS信息是如此的重要,以至於計算機必須在最開始就找到它。這是因為BIOS中包含了CPU
Linux的啟動流程
啟動流程前言:當我們了解了系統的啟動過程,那麽系統啟動出問題時,我們就能知道什麽地方出錯了,就能哪裏出錯修哪裏,不需要重裝系統。CentOS6和CentOS7啟動流程類似,不過CentOS6和7在啟動後期不一樣,CentOS6後期是init,CentOS7是systemd,那麽細節之處呢,跟著我一塊看看吧!l
Linux 的啟動流程
情況 啟動腳本 問題 .com tin 系統環境 通用 bubuko ant 本文轉載自:http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html 半年前,我寫了《計算機是如何啟動的?》,探討BIO
Linux系統啟動流程之chkconfig
Linux系統啟動流程chkconfig根據用戶的要求,需要在系統正常啟動後自動運行某些腳本。chkconfig xxx on 這個命令就自動在對應的rc2 rc3 rc4的目錄下創建腳本先拿rc2.d來看看這個是rc2.d目錄裏一個文件的內容,chkconfig 2345 57 432345指明了運行級別,
第七篇:Linux系統啟動流程
.com 標誌位 linu http 操作系統 流程 mbr 我們 png 1.bios:是在主板上的一段程序,決定計算機從哪一塊啟動介質中讀操作系統。2.硬盤最小單位是扇區,一個扇區512byte,計算機啟動第一個讀的扇區叫“主引導記錄”(MBR),446B:引導信息 6
Linux 系統啟動流程
認證 don grand 導致 procedure 圖形界面 大小 發行版 內核 內核簡介: kernel功能:進程管理、內存管理、網絡管理、驅動程序、文件系統、安全功能 庫:函數的集合,同時提供調用接口;不能作為程序的執行入口單獨執行,只能被程序調用 過程調用:pr
Linux 伺服器啟動流程詳解
啟動第一步--載入 BIOS 當你開啟計算機電源,計算機會首先載入 BIOS 資訊,BIOS 資訊是如此的重要,以至於計算機必須在最開始就找到它。這是因為 BIOS 中包含了 CPU 的相關資訊、裝置啟動順序信息、硬碟資訊、記憶體資訊、時鐘資訊、PnP 特性等等。在此之後,計算機心裡就有譜了,知道應該去讀取
linux作業系統啟動流程和光碟映象製作
1、簡述linux作業系統啟動流程 POST (加電自檢):自檢主要硬體裝置如:CPU、記憶體、硬碟是否正常,以及輸入輸出裝置是否存在問題等。 BIOS(Boot Sequence):BIOS(基本的輸入輸出系統)裝載在硬體晶片CMOS之上,自檢時會啟動這個程式,並根據CMOS上的配置資訊去讀取其他的硬體資
第七週 簡述linux作業系統啟動流程
目錄 1、簡述linux作業系統啟動流程 centos6啟動流程: centos7啟動流程: 2、簡述grub啟動載入程式配置及命令列介面詳解 grub的版本: grub的三個階段 與
Linux核心啟動流程分析(一)
1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB; 命令:arm-linux-gnu-ld -o vmlinux -T a
Linux系統啟動流程及系統裁剪
一、核心管理簡要理論 1、核心的功能 (1)程序管理 (2)記憶體管理(核心管理程式碼中程式碼量最大的部分) (3)I/O管理:中斷及中斷處理 (4)檔案系統:ext3,ext4,reiserfs,xfs等。。 (5)驅動程式 (6)安全相關:SELinux
國嵌視訊學習---linux核心啟動流程
一、核心檔案uImage的構成 uImage:Uboot header和zImage zImage:解壓程式碼和壓縮後的vmlinux映象 二、zImage核心的構成 其中解壓程式碼由Head.s和misc.s組成。 三、vmlinux核心構成 1.啟動程式碼部分: