高通linux系統啟動
本平臺採用的是高通apq8009 arm平臺,linux核心版本3.18,採用裝置樹方式。
linux系統啟動過程從軟體方面看可分為:bootloader,linux核心,檔案系統和應用程式。
裝置是以emmc方式啟動的, 燒寫檔案都燒寫到emmc中。上電後讀取emmc,emmc被分了很多區,這裡會有一個分割槽資訊描述,就像x86 windows的mbr, 啟動時候可確定bootloader在emmc中位置,開始啟動bootloader。
bootloader是引導載入程式, uboot是常用的bootloader,它有很多功能,初始化處理器,除錯口,有些還會初始化usb,乙太網,便於資料傳輸;uboot還得支援裝置樹方式;uboot可以通過幫助命令,檢視支援哪些功能,本平臺就可以支援fastboot功能,bootcmd中是對核心影響的引數;
進入linux核心階段,解析裝置樹, 根據裝置樹資訊的的描述,核心匹配了apq8009 arm平臺, 初始化,載入驅動,最後核心會啟動一個init程序。它是Linux系統中的1號程序(Linux系統沒有0號程序)。到此,完成了核心啟動階段的工作,交接給init來管理。
init程序執行一系列的指令碼(startup scripts),可到etc/下檢視,這些指令碼功能可檢測檔案系統,掛載硬碟,設定網路,執行各種應用程式, 等等。
啟動完以後,命令列方式會顯示login, 把許可權交給使用者,然後linux系統旅程就開始了。
核心編譯後會產生映像檔案,映像檔案型別分為壓縮和非壓縮方式。這主要是嵌入式系統儲存器容量小,對容量有嚴格要求的,一般採用壓縮式映像檔案,核心啟動用時間換空間。這兩種方式,核心啟動時是不同的,壓縮式啟動的時候,需要解壓映像檔案。
壓縮式映像檔案:uImage和zImage。
非壓縮方式映像檔案:Image, 由elf格式vmlinux轉換而來。
vmlinux屬於未壓縮,帶除錯資訊、符號表
Image是由objcopy工具去除vmlinux除錯資訊、註釋、符號表等內容。
uImage可由工具mkimage 把zImage和64位元組的頭資訊生成uImage。頭資訊描述映像檔案的型別、載入位置、生成時間、大小等資訊。
熟悉了zImage生成過程,才能更容易明白核心是怎麼啟動起來的。linux核心映像檔案生成如下圖:
生成帶裝置樹的核心,命令格式: cat zImage xxx.dtb > zImage-dtb
-
- ARM GCC 內嵌(inline)彙編手冊
可直接看網址:http://www.ethernut.de/en/documents/arm-inline-asm.html
摘錄網址中內容,gcc 中ARM暫存器使用:
Register |
Alt. Name |
Usage |
r0 |
a1 |
First function argument Integer function result Scratch register |
r1 |
a2 |
Second function argument Scratch register |
r2 |
a3 |
Third function argument Scratch register |
r3 |
a4 |
Fourth function argument Scratch register |
r4 |
v1 |
Register variable |
r5 |
v2 |
Register variable |
r6 |
v3 |
Register variable |
r7 |
v4 |
Register variable |
r8 |
v5 |
Register variable |
r9 |
v6 rfp |
Register variable Real frame pointer |
r10 |
sl |
Stack limit |
r11 |
fp |
Argument pointer |
r12 |
ip |
Temporary workspace |
r13 |
sp |
Stack pointer |
r14 |
lr |
Link register Workspace |
r15 |
pc |
Program counter |
理解了這些後,看核心中彙編程式碼就容易多了。
該平臺是arm系列處理器,入口程式路徑為arch/arm/;對於非arm平臺, 比如x86, 入口程式路徑為arch/x86/。
用的是壓縮式核心zImage,zImage的入口程式即為 arch/arm/boot/compressed/head.S。
解壓準備階段將執行中斷禁用、分配動態記憶體、初始化BBS區域、初始化頁目錄、開啟快取等任務。
對軟硬體進行初始化完成後,開是執行任務start任務,見下方程式碼:
- start:
- .type start,#function
- .rept 7
- mov r0, r0
- .endr
- ARM( mov r0, r0 )
- ARM( b 1f )
- THUMB( adr r12, BSYM(1f) )
- THUMB( bx r12 )
- .word _magic_sig @ Magic numbers to help the loader
- .word _magic_start @ absolute load/run zImage address
- .word _magic_end @ zImage end address
- .word 0x04030201 @ endianness flag
- THUMB( .thumb )
- 1:
- ARM_BE8( setend be ) @ go BE8 if compiled for BE8
- mrs r9, cpsr
- #ifdef CONFIG_ARM_VIRT_EXT
- bl __hyp_stub_install @ get into SVC mode, reversibly
- #endif
- mov r7, r1 @ save architecture ID
- mov r8, r2 @ save atags pointer
- /*
- * Booting from Angel - need to enter SVC mode and disable
- * FIQs/IRQs (numeric definitions from angel arm.h source).
- * We only do this if we were in user mode on entry.
- */
- mrs r2, cpsr @ get current mode
- tst r2, #3 @ not user?
- bne not_angel
- mov r0, #0x17 @ angel_SWIreason_EnterSVC
- ARM( swi 0x123456 ) @ angel_SWI_ARM
- THUMB( svc 0xab ) @ angel_SWI_THUMB
- not_angel:
- safe_svcmode_maskall r0
- msr spsr_cxsf, r9 @ Save the CPU boot mode in
- @ SPSR
- /*
- * Note that some cache flushing and other stuff may
- * be needed here - is there an Angel SWI call for this?
- */
- /*
- * some architecture specific code can be inserted
- * by the linker here, but it should preserve r7, r8, and r9.
- */
- .text
- #ifdef CONFIG_AUTO_ZRELADDR
- @ determine final kernel image address
- mov r4, pc
- and r4, r4, #0xf8000000
- add r4, r4, #TEXT_OFFSET
- #else
- ldr r4, =zreladdr
- #endif
- /*
- * Set up a page table only if it won't overwrite ourself.
- * That means r4 < pc && r4 - 16k page directory > &_end.
- * Given that r4 > &_end is most unfrequent, we add a rough
- * additional 1MB of room for a possible appended DTB.
- */
- mov r0, pc
- cmp r0, r4
- ldrcc r0, LC0+32
- addcc r0, r0, pc
- cmpcc r4, r0
- orrcc r4, r4, #1 @ remember we skipped cache_on
- blcs cache_on
行23是儲存體系結構ID,行24是儲存atags指標。34行開始輸入SVC模式,禁用FIQs/IRQs。
行68開始,給DTB留空間,記住跳開cache_on 。
- cache_on: mov r3, #8 @ cache_on function
- b call_cache_fn
- call_cache_fn: adr r12, proc_types
- #ifdef CONFIG_CPU_CP15
- mrc p15, 0, r9, c0, c0 @ get processor ID
- #else
- ldr r9, =CONFIG_PROCESSOR_ID
- #endif
- 1: ldr r1, [r12, #0] @ get value
- ldr r2, [r12, #4] @ get mask
- eor r1, r1, r9 @ (real ^ match)
- tst r1, r2 @ & mask
- ARM( addeq pc, r12, r3 ) @ call cache function
- THUMB( addeq r12, r3 )
- THUMB( moveq pc, r12 ) @ call cache function
- add r12, r12, #PROC_ENTRY_SIZE
- b 1b
- /*
- * Table for cache operations. This is basically:
- * - CPU ID match
- * - CPU ID mask
- * - 'cache on' method instruction
- * - 'cache off' method instruction
- * - 'cache flush' method instruction
- *
- * We match an entry using: ((real_id ^ match) & mask) == 0
- *
- * Writethrough caches generally only need 'on' and 'off'
- * methods. Writeback caches _must_ have the flush method
- * defined.
- */
- .align 2
- .type proc_types,#object
- proc_types:
- .word 0x41000000 @ old ARM ID
- .word 0xff00f000
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- .word 0x41007000 @ ARM7/710
- .word 0xfff8fe00
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- .word 0x41807200 @ ARM720T (writethrough)
- .word 0xffffff00
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- mov pc, lr
- THUMB( nop )
- .word 0x41007400 @ ARM74x
- .word 0xff00ff00
- W(b) __armv3_mpu_cache_on
- W(b) __armv3_mpu_cache_off
- W(b) __armv3_mpu_cache_flush
- .word 0x41009400 @ ARM94x
- .word 0xff00ff00
- W(b) __armv4_mpu_cache_on
- W(b) __armv4_mpu_cache_off
- W(b) __armv4_mpu_cache_flush
- .word 0x41069260 @ ARM926EJ-S (v5TEJ)
- .word 0xff0ffff0
- W(b) __arm926ejs_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv5tej_mmu_cache_flush
- .word 0x00007000 @ ARM7 IDs
- .word 0x0000f000
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- @ Everything from here on will be the new ID system.
- .word 0x4401a100 @ sa110 / sa1100
- .word 0xffffffe0
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv4_mmu_cache_flush
- .word 0x6901b110 @ sa1110
- .word 0xfffffff0
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv4_mmu_cache_flush
- .word 0x56056900
- .word 0xffffff00 @ PXA9xx
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv4_mmu_cache_flush
- .word 0x56158000 @ PXA168
- .word 0xfffff000
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv5tej_mmu_cache_flush
- .word 0x56050000 @ Feroceon
- .word 0xff0f0000
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv5tej_mmu_cache_flush
- #ifdef CONFIG_CPU_FEROCEON_OLD_ID
- /* this conflicts with the standard ARMv5TE entry */
- .long 0x41009260 @ Old Feroceon
- .long 0xff00fff0
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv5tej_mmu_cache_flush
- #endif
- .word 0x66015261 @ FA526
- .word 0xff01fff1
- W(b) __fa526_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __fa526_cache_flush
- @ These match on the architecture ID
- .word 0x00020000 @ ARMv4T
- .word 0x000f0000
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv4_mmu_cache_flush
- .word 0x00050000 @ ARMv5TE
- .word 0x000f0000
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv4_mmu_cache_flush
- .word 0x00060000 @ ARMv5TEJ
- .word 0x000f0000
- W(b) __armv4_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv5tej_mmu_cache_flush
- .word 0x0007b000 @ ARMv6
- .word 0x000ff000
- W(b) __armv6_mmu_cache_on
- W(b) __armv4_mmu_cache_off
- W(b) __armv6_mmu_cache_flush
- .word 0x000f0000 @ new CPU Id
- .word 0x000f0000
- W(b) __armv7_mmu_cache_on
- W(b) __armv7_mmu_cache_off
- W(b) __armv7_mmu_cache_flush
- .word 0 @ unrecognised type
- .word 0
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- mov pc, lr
- THUMB( nop )
- .size proc_types, . - proc_types
- /*
- * If you get a "non-constant expression in ".if" statement"
- * error from the assembler on this line, check that you have
- * not accidentally written a "b" instruction where you should
- * have written W(b).
- */
- .if (. - proc_types) % PROC_ENTRY_SIZE != 0
- .error "The size of one or more proc_types entries is wrong."
- .endif
- /*
- * Turn off the Cache and MMU. ARMv3 does not support
- * reading the control register, but ARMv4 does.
- *
- * On exit,
- * r0, r1, r2, r3, r9, r12 corrupted
- * This routine must preserve:
- * r4, r7, r8
- */
- .align 5
行2,把常數8寫入暫存器r3中並跳轉到call_cache_fn;
開啟彙編程式arch/arm/kernel/head.S,head.S依次完成:開啟 MMU 和 cache,呼叫 decompress_kernel()解壓核心,最後通過呼叫 call_kernel()進入非壓縮核心 Image 的啟動。下面將具體分析在此之後 Linux 核心的啟動過程。