LinuxKernelStartARM
ARM Linux內核啟動過程.
Updated Feb 24, 2011 by swordhui...@gmail.com
注: 本文轉自ChinaUnix 作者為XPL.
本文針對arm linux, 從kernel的第一條指令開始分析,一直分析到進入 start_kernel()函數. 我們當前以linux-2.6.19內核版本作為范例來分析,本文中所有的代碼,前面都會加上行號以便于和源碼進行對照, 例: 在文件init/main.c中: 00478: asmlinkage void init start_kernel(void) 前面的'00478:' 表示478行,冒號后面的內容就是源碼了.
在分析代碼的過程中,我們使用縮進來表示各個代碼的調用層次.
由于啟動部分有一些代碼是平臺特定的,雖然大部分的平臺所實現的功能都比較類似,但是為了更好的對code進行說明,對于平臺相關的代碼,我們選擇 at91(ARM926EJS)平臺進行分析.
另外,本文是以uncompressed kernel開始講解的.對于內核解壓縮部分的code,在 arch/arm/boot/compressed中,本文不做討論.
一. 啟動條件
通常從系統上電到執行到linux kenel這部分的任務是由boot loader來完成. 關于boot loader的內容,本文就不做過多介紹. 這里只討論進入到linux kernel的時候的一些限制條件,這一般是boot loader在最后跳轉到kernel之前要完成的:
1. CPU必須處于SVC(supervisor)模式,并且IRQ和FIQ中斷都是禁止的;
2. MMU(內存管理單元)必須是關閉的, 此時虛擬地址對物理地址;
3. 數據cache(Data cache)必須是關閉的
4. 指令cache(Instruction cache)可以是打開的,也可以是關閉的,這個沒有強制要求;
5. CPU 通用寄存器0 (r0)必須是 0;
6. CPU 通用寄存器1 (r1)必須是 ARM Linux machine type (關于machine type, 我們后面會有講解)
7. CPU 通用寄存器2 (r2) 必須是 kernel parameter list 的物理地址(parameter list 是由boot loader傳遞給kernel,用來描述設備信息屬性的列表,詳細內容可參考'Booting ARM Linux'文檔).
二. starting kernel
首先,我們先對幾個重要的宏進行說明(我們針對有MMU的情況):
宏 | 位置 | 默認值 | 說明 |
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 +137 | 0x00008000 | 內核相對于存儲空間的偏移 |
TEXTADDR | arch/arm/kernel/head.S +49 | 0xc0008000 | kernel的起始虛擬地址 |
PHYS_OFFSET | include/asm-arm/arch-xxx/memory.h | 平臺相關 | RAM的起始物理地址 |
內核的入口是stext,這是在arch/arm/kernel/vmlinux.lds.S中定義的:
00011: ENTRY(stext)
對于vmlinux.lds.S,這是ld script文件,此文件的格式和匯編及C程序都不同,本文不對ld script作過多的介紹,只對內核中用到的內容進行講解,關于ld的詳細內容可以參考ld.info 這里的ENTRY(stext) 表示程序的入口是在符號stext. 而符號stext是在arch/arm/kernel/head.S中定義的: 下面我們將arm linux boot的主要代碼列出來進行一個概括的介紹,然后,我們會逐個的進行詳細的講解.
在arch/arm/kernel/head.S中 72 - 94 行,是arm linux boot的主代碼: 00072: ENTRY(stext)
00073: msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
00074: @ and irqs disabled
00075: mrc p15, 0, r9, c0, c0 @ get processor id
00076: bl __lookup_processor_type @ r5=procinfo r9=cpuid
00077: movs r10, r5 @ invalid processor (r5=0)?
00078: beq __error_p @ yes, error 'p'
00079: bl __lookup_machine_type @ r5=machinfo
00080: movs r8, r5 @ invalid machine (r5=0)?
00081: beq __error_a @ yes, error 'a'
00082: bl __create_page_tables
00083:
00084: /*
00085: * The following calls CPU specific code in a position independent
00086: * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
00087: * xxx_proc_info structure selected by __lookup_machine_type
00088: * above. On return, the CPU will be ready for the MMU to be
00089: * turned on, and r0 will hold the CPU control register value.
00090: */
00091: ldr r13, __switch_data @ address to jump to after
00092: @ mmu has been enabled
00093: adr lr, __enable_mmu @ return (PIC) address
00094: add pc, r10, #PROCINFO_INITFUNC
其中,73行是確保kernel運行在SVC模式下,并且IRQ和FIRQ中斷已經關閉,這樣做是很謹慎的.
arm linux boot的主線可以概括為以下幾個步驟:
1. 確定 processor type (75 - 78行)
2. 確定 machine type (79 - 81行)
3. 創建頁表 (82行)
4. 調用平臺特定的cpu_flush函數 (在struct proc_info_list中) (94 行)
5. 開啟mmu (93行)
6. 切換數據 (91行)
最終跳轉到start_kernel (在switch_data的結束的時候,調用了 b start_kernel)
下面,我們按照這個主線,逐步的分析Code.
1. 確定 processor type
arch/arm/kernel/head.S中:
00075: mrc p15, 0, r9, c0, c0 @ get processor id
00076: bl __lookup_processor_type @ r5=procinfo r9=cpuid
00077: movs r10, r5 @ invalid processor (r5=0)?
00078: beq __error_p @ yes, error 'p'
75行: 通過cp15協處理器的c0寄存器來獲得processor id的指令. 關于cp15的詳細內容可參考相關的arm手冊
76行: 跳轉到lookup_processor_type.在lookup_processor_type中,會把processor type 存儲在r5中
77,78行: 判斷r5中的processor type是否是0,如果是0,說明是無效的processor type,跳轉到error_p(出錯)
lookup_processor_type 函數主要是根據從cpu中獲得的processor id和系統中的proc_info進行匹配,將匹配到的proc_info_list的基地址存到r5中, 0表示沒有找到對應的processor type.
下面我們分析lookup_processor_type函數
arch/arm/kernel/head-common.S中:
00145: .type __lookup_processor_type, %function
00146: __lookup_processor_type:
00147: adr r3, 3f
00148: ldmda r3, {r5 - r7}
00149: sub r3, r3, r7 @ get offset between virt&phys
00150: add r5, r5, r3 @ convert virt addresses to
00151: add r6, r6, r3 @ physical address space
00152: 1: ldmia r5, {r3, r4} @ value, mask
00153: and r4, r4, r9 @ mask wanted bits
00154: teq r3, r4
00155: beq 2f
00156: add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
00157: cmp r5, r6
00158: blo 1b
00159: mov r5, #0 @ unknown processor
00160: 2: mov pc, lr
00161:
00162: /*
00163: * This provides a C-API version of the above function.
00164: */
00165: ENTRY(lookup_processor_type)
00166: stmfd sp!, {r4 - r7, r9, lr}
00167: mov r9, r0
00168: bl __lookup_processor_type
00169: mov r0, r5
00170: ldmfd sp!, {r4 - r7, r9, pc}
00171:
00172: /*
00173: * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
00174: * more information about the __proc_info and __arch_info structures.
00175: */
00176: .long __proc_info_begin
00177: .long __proc_info_end
00178: 3: .long .
00179: .long __arch_info_begin
00180: .long __arch_info_end
上一篇:玩轉S3C6410之一 交叉工具鏈制作(gcc4.5.1-armv6-vfp)
下一篇:s3c6410學習筆記-燒寫uboot+構建文件系統
推薦閱讀最新更新時間:2025-04-05 23:34





設計資源 培訓 開發板 精華推薦
- STM32一文通(6) 外部中斷
- ESP32玩轉MicroPython(一) 燒入固件, 初識
- ESP32玩轉MicroPython(二) 連接WIFI網絡 webperl文件傳輸
- ESP32玩轉MicroPython(三) 延時、計時 和GPIO操作
- 玩轉 ESP32 + Arduino (一) 開發環境搭建
- 玩轉 ESP32 + Arduino (四) 電容按鍵 霍爾傳感器 外部中斷 延時 脈沖檢測
- 玩轉 ESP32 + Arduino (五) platformIO引入類庫 自開發庫 串口打印字符串
- 玩轉 ESP32 + Arduino (六) 硬件定時器, IIC, SPI
- 玩轉 ESP32 + Arduino (八) U8G2驅動OLED
- 使用 ROHM Semiconductor 的 BD49E49G-TR 的參考設計
- 51+sl811讀寫U盤的源程序+原理圖
- MC78M08BTG 8V 可調輸出穩壓器的典型應用
- 18.1W, 3.4V, 5.1V, 12V, 16V AC 轉 DC 多輸出 DVD 播放器電源
- LTC2939HMS ±5V 電源監視器的典型應用電路,看門狗已禁用且未使用的輸入為高電平
- EVAL2293Q,直流電機 MOSFET 功率驅動器評估板
- 使用 Analog Devices 的 LT1634ACS8-1.25 的參考設計
- LTC2470 的典型應用 - 具有 10ppm/攝氏度最大精度基準的可選 208sps/833sps、16 位 ADC
- FL7760LED1GEVK:用于歐司朗脈沖 LED 的 LED 驅動器
- AM2F-2412SH30Z 12V 2W DC/DC 轉換器的典型應用