一篇很好的講解嵌入式linux啟動(dòng)的文章
嵌入式Linux啟動(dòng)分為兩個(gè)部分,系統引導與Linux啟動(dòng)。系統引導將完成Linux裝入內存前,初始化CPU和相關(guān)IO設備,并將Linux調入內存的工作。系統引導主要由BootLoader實(shí)現。在BootLoader將Linux內核調入內存之后,將權力交給LinuxKernel,進(jìn)入Linux的啟動(dòng)部分。以下詳細分析啟動(dòng)的過(guò)程與使用的文件。
一、系統引導與BootLoader
BootLoader因嵌入式系統的不同與PC機有很大不同,這里將以Hyper250(Inter Xscale GDPXA250)的啟動(dòng)為例來(lái)分析。由于沒(méi)有BIOS驅動(dòng)主板,EnbeddedOS必須由bootloader驅動(dòng)所有的硬件,并完成硬件的初始化工作。
所有的初始化文件在hyper250/Bootloader目錄下。
首先分析開(kāi)機運行的分件:
hyper250/Bootloader/X-Hyper250R1.1-Boot/src/start_xscale.S
文件包含兩個(gè)庫文件:
hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/config.h
hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/start_xscale.h
文件config.h主要完成系統各硬件的宏定義與設定,xscale.h主要完成對系統芯片的及系統操作的設定。
以下分析config.h文件:
(1)存儲總線(xiàn)設備的宏定義:定義Flash的大小、字長(cháng)等信息,定義SRAM的基址、大小和塊大小。
(2)動(dòng)態(tài)內存設定:定義DRAM的大小、基址。
(3)軟件包信息:包名稱(chēng)、版本號。
(4)設定BOOT LOADER的位置:在DRAM和SRAM的最大值、DRAM裝入位置、棧的基址。
(5)設定kernel的位置:在DRAM和SRAM的基址、KERNEL的最大值、KERNEL中塊的數量。
(6)設定文件系統的位置:根目錄在DRAM和SRAM的基址、文件系統的最大值、文件系統中塊的數量。
(7)設定LOADER程序:LOADER程序的靜態(tài)內存基址、LOADER程序的最大值、塊的數量。
(8)網(wǎng)絡(luò )設定
以下分析start_xcalse.h文件:
(1)定義內存基址(A0000000)
(2)定義中斷基址(40D00000)和中斷保護棧的偏移量
(3)定義時(shí)鐘管理基址(41300000)和寄存器偏移及其初始值
(4)定義GPIO接口寄存器基址(40E00000)及各寄存器的偏移
(5)定義GPIO接口各寄存器的初始值
(6)定義內存控制寄存器基址(48000000)和各寄存器的偏移
(7)定義內存控制寄存器的初始值
(8)定義電源管理寄存器的參數
(9)定義FFUART寄存器的基址(40100000)和各寄存器的偏移
(10)定義FFUART各寄存器的初始值
以下分析start_xcalse.S文件:
(1)設定中斷基址(40D00000),完成中斷保護棧的初始化
(2)初始化GPIO接口
(3)初始化內存SDRAM
(4)將Bootloader從Flash拷貝到SDRAM中
(5)裝入Linux內核鏡像,將內核從Flash(000C 0000)裝入SDRAM(A0008000)中.
(6)設定保護棧
(7)調用main.c的主函數c_main()
以上start_xcalse.S通過(guò)APCS的編程標準書(shū)寫(xiě)的匯編文件初始化了系統相關(guān)的硬件,并且完成了BootLoader的裝入內存和Linux內核的裝入,最后將權力轉交給main.c。
以下將分析main.c文件:
hyper250/Bootloader/X-Hyper250R1.1-Boot/src/main.c
以及兩個(gè)庫文件
hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/main.h
hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/scc.h
#2
二、Linux啟動(dòng)過(guò)程分析
1.Makefile分析:
在分析arch/arm/boot/compressed目錄下的文件的時(shí)候,對于Makefile的分析是很重要的,因為內核將在這個(gè)目錄相產(chǎn)生。這里主要工作是對內核的壓縮和解壓工作。本目錄在編譯完成后將產(chǎn)生vmlinux、head.o、misc.o、head-xscale.o、piggy.o這幾個(gè)文件。其中vmlinux是沒(méi)有壓縮過(guò)的內核。head.o是內核的頭部文件,負責初始設置。misc.o將主要負責內核的解壓工作,它在head.o之后。head-xscale.o文件主要針對Xscale的初始化,將在鏈接時(shí)與head.o合并。piggy.o是一個(gè)中間文件,其實(shí)是一個(gè)壓縮的內核,只不過(guò)沒(méi)有和初始化文件及解壓文件鏈接而已。
2.Decompress分析:
在BootLoader完成系統的引導以后并將Linux內核調入內存之后,調用bootLinux(),這個(gè)函數將跳轉到kernel的起始位置。如果kernel沒(méi)有壓縮,就可以啟動(dòng)了。如果kernel壓縮過(guò),則要進(jìn)行解壓,在壓縮過(guò)的kernel頭部有解壓程序。壓縮過(guò)得kernel入口第一個(gè)文件源碼位置在arch/arm/boot/compressed/head.S。它將調用函數decompress_kernel(),這個(gè)函數在文件arch/arm/boot/compressed/misc.c中,decompress_kernel()又調用proc_decomp_setup(),arch_decomp_setup()進(jìn)行設置,然后使用在打印出信息“Uncompressing Linux...”后,調用gunzip()。將內核放于指定的位置。
啟動(dòng)首先運行的文件有:
arch/arm/boot/compressed/head.S
arch/arm/boot/compressed/head-xscale.S
arch/arm/boot/compressed/misc.c
這些文件主要用于解壓內核和以及啟動(dòng)內核映象。一旦內核啟動(dòng),則這些文件所占內存空間將被釋放。而且,一旦系統通過(guò)reset重起,當BootLoader將壓縮過(guò)的內核放入內存中,首先執行的必然是這些代碼。
以下分析head.S文件:
(1)對于各種Arm CPU的DEBUG輸出設定,通過(guò)定義宏來(lái)統一操作。
(2)設置kernel開(kāi)始和結束地址,保存architecture ID。
(3)如果在ARM2以上的CPU中,用的是普通用戶(hù)模式,則升到超級用戶(hù)模式,然后關(guān)中斷。
(4)分析LC0結構delta offset,判斷是否需要重載內核地址(r0存入偏移量,判斷r0是否為零)。
這里是否需要重載內核地址,我以為主要分析arch/arm/boot/Makefile、arch/arm/boot/compressed/Makefile和arch/arm/boot/compressed/vmlinux.lds.in三個(gè)文件,主要看vmlinux.lds.in鏈接文件的主要段的位置,LOAD_ADDR(_load_addr)=0xA0008000,而對于TEXT_START(_text、_start)的位置只設為0,BSS_START(__bss_start)=ALIGN(4)。對于這樣的結果依賴(lài)于,對內核解壓的運行方式,也就是說(shuō),內核解壓前是在內存(RAM)中還是在FLASH上,因為這里,我們的BOOTLOADER將壓縮內核(zImage)移到了RAM的0xA0008000位置,我們的壓縮內核是在內存(RAM)從0xA0008000地址開(kāi)始順序排列,因此我們的r0獲得的偏移量是載入地址(0xA0008000)。接下來(lái)的工作是要把內核鏡像的相對地址轉化為內存的物理地址,即重載內核地址。
(5)需要重載內核地址,將r0的偏移量加到BSS region和GOT table中。
(6)清空bss堆??臻gr2-r3。
(7)建立C程序運行需要的緩存,并賦于64K的??臻g。
(8)這時(shí)r2是緩存的結束地址,r4是kernel的最后執行地址,r5是kernel境象文件的開(kāi)始地址。檢查是否地址有沖突。
將r5等于r2,使decompress后的kernel地址就在64K的棧之后。
(9)調用文件misc.c的函數decompress_kernel(),解壓內核于緩存結束的地方(r2地址之后)。此時(shí)各寄存器值有如下變化:
r0為解壓后kernel的大小
r4為kernel執行時(shí)的地址
r5為解壓后kernel的起始地址
r6為CPU類(lèi)型值(processor ID)
r7為系統類(lèi)型值(architecture ID)
(10)將reloc_start代碼拷貝之kernel之后(r5+r0之后),首先清除緩存,而后執行reloc_start。
(11)reloc_start將r5開(kāi)始的kernel重載于r4地址處。
(12)清除cache內容,關(guān)閉cache,將r7中architecture ID賦于r1,執行r4開(kāi)始的kernel代碼。
關(guān)于head-xscale.S文件,它定義了xcale處理器的64k的cache緩存的實(shí)現代碼和關(guān)閉MMU及緩存的代碼,這些代碼將在鏈接過(guò)程中與head.S的合并。
關(guān)于misc.c文件,它引入了以下幾個(gè)文件:
include/linux/kernel.h
include/asm-arm/arch-pxa/uncompress.h
include/asm-arm/proc-armv/uncompress.h
include/asm-arm/uaccess.h
lib/inflate.c
以下分析misc.c文件的decompress_kernel()函數:
(1)首先傳入參數:解壓后內核地址,緩存開(kāi)始地址,緩存結束地址,arch id。這些參數通過(guò)寄存器r0(r5),r1,r2,r3(r7)傳入。
(2)接著(zhù)執行proc_decomp_setup(),它在include/asm-arm/proc-armv/uncompress.h文件中。主要刷新并起用i cache,鎖住交換緩存,這是一段嵌入的arm匯編代碼。
(3)接著(zhù)執行arch_decomp_setup(),它在include/asm-arm/arch-pxa/uncompress.h文件中,是一個(gè)空函數,用于擴展。
(4)然后執行makecrc(),它在lib/inflate.c中,主要將產(chǎn)生CRC-32 table,進(jìn)行循環(huán)冗余校驗。
(5)調用gunzip()解壓kernel,它也在lib/inflate.c中。
(6)返回head.S,解壓后kernel的長(cháng)度傳給r0,解壓后的內核地址預先在r5中定義了。
聯(lián)系客服