詳解Linux內(nèi)核在arm上的啟動(dòng)過(guò)程
Linux內(nèi)核加載過(guò)程
通常,Linux內(nèi)核都是經(jīng)過(guò)gzip加載過(guò)之后的映像文件。
- bootloader復(fù)制壓縮內(nèi)核到內(nèi)存空間。
- 內(nèi)核自解壓。
- 運(yùn)行內(nèi)核。
編譯完成的Linux內(nèi)核存放在哪里?
- ./vmlinux elf格式未壓縮內(nèi)核。
- arch/arm/boot/compressed/vmlinux 壓縮以后的elf格式內(nèi)核。
- arch/arm/boot/zImage 壓縮內(nèi)核。
壓縮內(nèi)核(zImage)的入口
- /arch/arm/boot/compressed/vmlinux.lds 該文件為編譯器指定link順序。
- ENTRY(_start) 壓縮內(nèi)核從.start段開始執(zhí)行。
- 在/arch/arm/boot/compressed/head.S中執(zhí)行以下愛(ài)操作:
(1)檢測(cè)系統(tǒng)空間。
(2)初始化C代碼空間。
(3)跳轉(zhuǎn)到C代碼decompress_kernel,
arch/arm/boot/compressed/misc.c中。
解壓之前的串口輸出
- include/asm-arm/arch-s3c2410/uncompress.h 中定義了puts作為串口輸出函數(shù)。
- 解壓結(jié)束之后,程序跳轉(zhuǎn)到r5:解壓之后內(nèi)核的起始地址。
開始真正的Linux內(nèi)核
1、入口在arch/arm/kernel/head-armv.S
2、查找處理器類型
- __lookup_processor_type
- __lookup_architecture_type
3、初始化頁(yè)表:__creat_page_tables
4、初始化C代碼空間
5、跳轉(zhuǎn)到C代碼中,start_kernel
ARM的MMU單元
MMU:內(nèi)存管理單元
作用:
- 虛擬地址到物理地址的映射
- 存儲(chǔ)器訪問(wèn)權(quán)限
- 控制Cache
通過(guò)MMU的訪存
- MMU會(huì)先查找TLB中的虛擬地址表
- 如果TLB中沒(méi)有虛擬地址的入口,硬件從主存儲(chǔ)器中的轉(zhuǎn)換表中獲取轉(zhuǎn)換與訪問(wèn)權(quán)限。
ARM的MMU訪存原理
ARM的MMU頁(yè)表格式
MMU支持基于節(jié)或者頁(yè)的存儲(chǔ)器訪問(wèn)。
- 節(jié):1MB的存儲(chǔ)器塊
- 大頁(yè):64KB的存儲(chǔ)器塊
- 小頁(yè):4KB的存儲(chǔ)器塊
- 微頁(yè):1KB的存儲(chǔ)器塊
頁(yè)表的級(jí)別
存在主存儲(chǔ)器內(nèi)的轉(zhuǎn)換頁(yè)表有兩個(gè)級(jí)別:
- 第一級(jí)表:存儲(chǔ)節(jié)轉(zhuǎn)換表與指向第二級(jí)表的指針
- 第二級(jí)表:
(1)存儲(chǔ)大頁(yè)和小頁(yè)的轉(zhuǎn)換表。
(2)存儲(chǔ)微頁(yè)的轉(zhuǎn)換表。
一級(jí)頁(yè)表的地址
第一級(jí)表占用空間16KB,必須16KB對(duì)齊
第一級(jí)描述符
一級(jí)表每個(gè)入口描述了它所關(guān)聯(lián)的1MB虛擬地址是如何映射的。
節(jié)描述符
- Bits[1:0] 描述符類型(10b 表示節(jié)描述符)
- Bits[3:2] 高速緩存(cache)和緩沖位(buffer)
- Bits[4] 由具體實(shí)現(xiàn)定義
- Bits[8:5] 控制的節(jié)的16 種域之一
- Bits[9] 現(xiàn)在沒(méi)有使用,應(yīng)該為零
- Bits[11:10] 訪問(wèn)控制(AP)
- Bits[19:12] 現(xiàn)在沒(méi)有使用,應(yīng)該為零
- Bits[31:20] 節(jié)基址,形成物理地址的高12 位
節(jié)的轉(zhuǎn)換過(guò)程
臨時(shí)內(nèi)核頁(yè)表的創(chuàng)建 __create_page_tables
- __create_page_tables:
- pgtbl r4 @ page table address 0x30008000-0x4000
- mov r0, r4 @r0=0x30004000
- mov r3, #0
- add r2, r0, #0x4000
- 1: str r3, [r0], #4
- str r3, [r0], #4
- str r3, [r0], #4
- str r3, [r0], #4
- teq r0, r2
- bne 1b
把一級(jí)頁(yè)表0x30004000-0xa0080000清空
- krnladr r2, r4 @ start of kernel
r4=0xa0004000,r2 = 內(nèi)核起始地址所在1MB對(duì)齊空間,0x30000000
- add r3, r8, r2 @ flags + kernel base
r8 為從處理器信息中得到的MMU 頁(yè)表標(biāo)志,r8=0xc0e, r3=0x30000c0e
- str r3, [r4, r2, lsr #18]@ identity mapping
地址:0x300068000, value:0x30000c0e
- add r0, r4, #(TEXTADDR & 0xff000000) >> 18
- @ start of kernel
- bic r2, r3, #0x00f00000
- str r2, [r0] @ PAGE_OFFSET + 0MB
- add r0, r0, #(TEXTADDR & 0x00f00000) >> 18
- str r3, [r0], #4 @ KERNEL + 0MB
- ......
映射表內(nèi)容
映射結(jié)果
進(jìn)入C代碼
init/main.c中的start_kernel函數(shù),進(jìn)入到了Linux內(nèi)核代碼中。
- printk函數(shù)
- 重新初始化頁(yè)表
- 初始化中斷,trap_init
- 設(shè)置系統(tǒng)定時(shí)器、控制臺(tái)…
- 創(chuàng)建內(nèi)核進(jìn)程init