這是一系列文章,說明Linux內核如何在ARM體系結構上啟動。這是第一部分。
ARM Linux引導過程:
我們將解釋圍繞ARM920T ARM Thumb處理器構建的AT91RM9200片上系統的啟動過程。 Kwickbyte基於AT91RM9200構建了一個名為kb9202的嵌入式板。我們將以該板為例,了解Linux如何在該板上啟動。
在開始閱讀本文檔之前,您需要閱讀AT91RM9200數據手冊(規格)。
您還需要閱讀《 ARM體系結構參考手冊》,以更好地理解引導過程。
Linux引導過程中的組件:
Linux引導過程涉及以下組件。
引導程序
內核映像
根文件系統
在我們了解上述組件如何工作之前,以下是用於arm體系結構的Linux Kernel引導過程的調用流程。這給了整個Linux啟動過程的全景圖。我們使用U-boot引導程序。
ARM Linux引導過程:大圖
U型靴:
_start(cpu / arm920t / start.S)
start_code(cpu / arm920t / start.S)
start_armboot(lib_arm / board.c)
board_init(board / kb9202 / kb9202.c)
timer_init(cpu / arm920t / at91 / timer.c)
serial_init(驅動程序/串列/at91rm9200_usart.c)
main_loop(lib_arm / board.c)
現在,u-boot已啟動並正在運行,並且處於u-boot提示符下並準備接受命令。假定內核映像已載入到RAM中並發出了bootm命令。
do_bootm(common / cmd_bootm.c)
bootm_start(common / cmd_bootm.c)
bootm_load_os(common / cmd_bootm.c)
do_bootm_linux(lib_arm / bootm.c)
stext(linux / arch / arm / kernel / head.S)
控制權交給了Linux。
Linux內核:
stext(arch / arm / kernel / head.S:78)
__lookup_processor_type(arch / arm / kernel / head-common.S:160)
__lookup_machine_type(arch / arm / kernel / head-common.S:211)
__create_page_tables(arch / arm / kernel / head.S:219)
__arm920_setup(arch / arm / mm / proc-arm920.S:389)
__enable_mmu(arch / arm / kernel / head.S:160)
__turn_mmu_on(足弓/手臂/內核/頭部.S:205)
__switch_data(arch / arm / kernel / head-common.S:20)
start_kernel(init / main.c:529)
start_kernel(init / main.c:529)
tick_init(內核/時間/tick-common.c:413)
setup_arch(arch / arm / kernel / setup.c:666)
setup_machine(arch / arm / kernel / setup.c:369)
lookup_machine_type()
setup_command_line(init / main.c:408)
build_all_zonelists(mm / page_alloc.c:3031)
parse_args(內核/參數.c:129)
mm_init(init / main.c:516)
mem_init(arch / arm / mm / init.c:528)
kmem_cache_init(mm / slab.c,mm / slob.c,mm / slub.c)
sched_init(內核/sched.c)
init_IRQ(arch / arm / kernel / irq.c)
init_timers(內核/timer.c:1713)
hrtimers_init(內核/hrtimer.c:1741)
softirq_init(內核/softirq.c:674)
console_init(驅動程序/char/tty_io.c:3084)
vfs_caches_init(fs / dcache.c:2352)
mnt_init(fs / namespace.c:2308)
init_rootfs()
init_mount_tree(fs / namespace.c:2285)
do_kern_mount(fs / namespace.c:1053)
set_fs_pwd(fs / fs_struct.c:29)
set_fs_root(fs / fs_struct.c:12)
bdev_cache_init(fs / block_dev.c:465)
chrdev_init(fs / char_dev.c:566)
signals_init(內核/signal.c:2737)
rest_init(init / main.c:425)
kernel_thread(431,arch / arm / kernel / process.c:388)
kernel_thread()創建一個內核線程,並將控制權交給kernel_init()。
kernel_init(431,init / main.c:856)
do_basic_setup(888,init / main.c:787)
init_workqueues(789,kernel / workqueue.c:1204)
driver_init(793,drivers / base / init.c:20)
do_initcalls(796,init / main.c:769)/ *調用所有子系統的初始化函數* /
prepare_namespace(906,init / do_mounts.c:366)
initrd_load(399,init / do_mounts_initrd.c:107)
rd_load_image(117,init / do_mounts_rd.c:158)/ *如果指定了initrd * /
identity_ramdisk_image(179,init / do_mounts_rd.c:53)
handle_initrd(119,init / do_mounts_initrd.c:37)/ *如果rd_load_image成功* /
mount_block_root(45,init / do_mounts.c:233)
do_mount_root(247,初始/do_mounts.:218)
mount_root(417,init / do_mounts.c:334)/ *如果未指定initrd * /
mount_block_root(359,init / do_mounts.c:233)
do_mount_root(247,init / do_mounts.c:218)
init_post(915,init / main.c:816)
run_init_process(847,init / main.c:807)
kernel_execve(810,arch / arm / kernel / sys_arm.c:81)
用戶空間
init()/ *用戶空間/ sbin / init * /
引導程序:
引導載入程序是一個小程序,它將內核映像載入到RAM中並引導內核映像。這也稱為引導程序,因為它通過載入操作系統來拉起(拉起)系統。 Bootloader在任何其他軟體啟動之前啟動並初始化處理器,並使CPU準備執行諸如操作系統之類的程序。大多數處理器都有一個默認地址,在接通電源或複位板後,將從該地址獲取代碼的第一個位元組。硬體設計人員使用此信息將引導程序代碼存儲在該地址的ROM或快閃記憶體中。由於它應該初始化cpu並應該運行位於特定於體系結構的程序,因此地址引導載入程序高度依賴於處理器,並且特定於電路板。每個嵌入式主板都帶有一個引導程序,用於將內核映像或獨立應用程序下載到板上並開始執行內核映像或應用程序。當處理器板上電時,將執行Bootloader。基本上,它將具有一些最小的功能來載入映像並啟動它。
也可以使用硬體調試介面(例如JTAG)來控制系統。通過指示處理器核執行對非易失性存儲器進行編程的必要動作,該介面可用於將引導載入程序寫入可引導的非易失性存儲器(例如快閃記憶體)。通常是第一次下載基本的引導程序並進行一些恢復過程。 JTAG是許多主板供應商提供的標準且流行的介面。一些微控制器提供了特殊的硬體介面,這些介面不能用於對系統進行任意控制或直接運行代碼,而是通過簡單的協議將引導代碼插入可引導的非易失性存儲器(例如快閃記憶體)中。然後在製造階段,使用此類介面將啟動代碼(可能還有其他代碼)注入非易失性存儲器中。系統複位後,微控制器開始執行編程到其非易失性存儲器中的代碼,就像普通處理器使用ROM進行引導一樣。在許多情況下,此類介面是通過硬連線邏輯實現的。在其他情況下,可以通過從GPIO引腳在集成片內啟動ROM中運行的軟體來創建此類介面。
還有其他一些第三方引導載入程序可用,它們提供了豐富的功能集和簡單的用戶界面。您可以將這些第三方自舉程序下載到板上,並使它們成為您板的默認自舉程序。通常,主板供應商提供的引導程序會被這些第三方引導程序替換。可用的第三方boolader數量很少,其中一些是開源的(或免費的引導程序),有些是商業的。其中一些是Das U-Boot,Red boot,GRUB(用於台式機),LILO,Loadlin,bootsect-loader,SYSLINUX,EtherBoot和ELILO。
我們將U-boot引導載入程序作為我們的引導載入程序。 U-boot是嵌入式系統中廣泛使用的引導載入程序。我們將解釋u-boot-2010.03源代碼。您可以從以下站點下載U-boot。 http://www.denx.de/wiki/U-Boot
U-boot的構建方式:
根據U-boot的配置,使用針對特定體系結構構建的交叉編譯器編譯所有彙編文件(.S)和C文件(.c),並將生成目標文件(.o)。所有這些目標文件都由鏈接器鏈接,並且將創建一個可執行文件。目標文件或可執行文件是節如.text,.data,.bss等的集合。目標文件和可執行文件的文件格式如elf。目標文件的所有部分都將基於稱為鏈接程序腳本的腳本排列在可執行文件中。該腳本告訴運行時所有節將在內存中載入的位置。了解此腳本對於了解引導載入程序和內核的組成方式以及如何將引導載入程序或內核的不同部分載入到內存中非常重要。
通常,運行(執行)程序時,載入程序將讀取可執行文件,並將可執行文件的不同部分載入到指定的內存位置,然後開始執行鏈接描述文件中指定的啟動函數(入口點)。但是,如果您要運行(載入)引導載入程序,則不會有任何載入程序(基本上是為了了解文件格式)將可執行文件的不同部分載入到內存中。然後,您需要使用一個名為objcopy的工具,它將從可執行文件中獲取所有部分,並創建一個沒有任何文件格式的二進位文件。該二進位文件可以載入到內存中並執行,或者可以在特定地址(特定於體系結構)寫入ROM中,該特定地址在板上供電時將由cpu執行。
假設基於U-boot配置,將編譯所有文件並創建目標文件。 U-boot生成文件使用以下鏈接程序腳本(特定於體系結構)來構建可執行文件。
檔案:cpu / arm920t / u-boot.lds
32 OUTPUT_FORMAT(「 elf32-littlearm」,「 elf32-littlearm」,「 elf32-littlearm」)
33 OUTPUT_ARCH(手臂)
34 ENTRY(_start)
35節
36 {
37. = 0x00000000;
38
39. = ALIGN(4);
40.文字:
41 {
42 cpu / arm920t / start.o(.text)
43 *(。text)
44}
4546. = ALIGN(4);
47.rodata:{*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata *)))}
48
49. = ALIGN(4);
50.data:{*(。data)}
51
52. = ALIGN(4);
53.got:{*(。got)}
54
55. = .;
第56章(許我傾城)
57.u_boot_cmd:{*(。u_boot_cmd)}
第58章(許我傾城)
59
60. = ALIGN(4);
第61章
62.bss(NOLOAD):{*(。bss)。 = ALIGN(4); }
63 _end = .;
64}
第32行中的OUTPUT_FORMAT指定可執行文件的文件格式。這裡的可執行文件格式為elf32,位元組序為little endian。第33行的OUTPUT_ARCH指定運行此代碼的體系結構。第34行中的ENTRY指定u-boot程序的啟動功能(入口點)。此處的入口點是_start。
第35行中的SECTIONS定義了如何在可執行文件中映射不同的節。載入程序使用本節中指定的地址將程序的不同部分載入到內存中。
‘。’第37行中的起始地址指定了以下部分應載入的起始地址。在這種情況下,起始地址為0x00000000。在#39行之後,內存按4個位元組對齊,.text節在#40行之後。
40.文字:
41 {
42 cpu / arm920t / start.o(.text)
43 *(。text)
44}
在「。」位置(0x00000000),將映射cpu / arm920t / start.o中的代碼,並遵循所有其他目標(.o)文件的.text部分中的代碼。 cpu / arm920t / start.o包含_start()函數(以彙編語言表示),該函數是該程序的入口點。
現在 ‘。’將為0x00000000 + sizeof(.text)。同樣,內存按4個位元組對齊,.rodata節位於第47行。
。 = ALIGN(4);
47.rodata:{*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata *)))}
所有目標文件中的.rodata節都映射到該地址。遵循.data和.git部分。
49. = ALIGN(4);
50.data:{*(。data)}
51
52. = ALIGN(4);
53.got:{*(。got)}
每個U-boot命令都是「 cmd_tbl_t」類型的對象,其中包含命令名稱,幫助字元串和在運行此命令時要執行的功能指針。所有這些命令對象都順序放置在內存中。每個命令對象都內置在目標文件中名為U.boot_cmd的U-boot定義的部分中。這些all.u_boot_cmd節放在上述節(.data和.git)之後。
。 = .;
第56章(許我傾城)
57.u_boot_cmd:{*(。u_boot_cmd)}
第58章(許我傾城)
__u_boot_cmd_start包含命令對象的開頭,而__u_boot_cmd_end包含命令對象的結尾。
接下來是.bss(未初始化的全局變數)部分。
60. = ALIGN(4);
第61章
62.bss(NOLOAD):{*(。bss)。 = ALIGN(4); }
63 _end = .;
__bss_start指向.bss的起始地址,而_end包含所有節的結尾。
使用此鏈接器腳本鏈接器將生成一個名為u-boot的可執行文件。 Objcopy工具用於從u-boot可執行文件生成二進位文件。
u-boot.bin:u-boot
$(OBJCOPY)$ {OBJCFLAGS} -O二進位$