本文基于 armv8 架構(gòu)來對 u-boot 進行啟動流程分析,u-boot版本為2022-01。
1 概述
首先引用wiki上的簡介:u-boot 是一個主要用于嵌入式系統(tǒng)的引導(dǎo)加載程序,可以支持多種不同的計算機系統(tǒng)結(jié)構(gòu)。
u-boot最先是由德國DENX軟件中心團隊開發(fā),后續(xù)眾多有志于開放源碼bootloader移植工作的嵌入式開發(fā)人員將各個不同系列嵌入式處理器的移植工作不斷展開和深入,以支持了更多的嵌入式操作系統(tǒng)的裝載與引導(dǎo)。
選擇u-boot的理由:
開放源碼;支持多種嵌入式操作系統(tǒng)內(nèi)核的引導(dǎo),如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android;支持多個處理器系列,如PowerPC、ARM、x86、MIPS;
較高的可靠性和穩(wěn)定性;高度靈活的功能設(shè)置,適合U-Boot調(diào)試、操作系統(tǒng)不同引導(dǎo)要求、產(chǎn)品發(fā)布等;
豐富的設(shè)備驅(qū)動源碼,如串口、以太網(wǎng)、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、鍵盤等;
較為豐富的開發(fā)調(diào)試文檔與強大的網(wǎng)絡(luò)技術(shù)支持;
基于以上理由本篇文章對現(xiàn)在主流的armv8架構(gòu)的u-boot啟動流程進行詳細分析,以便所有人快速學習和理解u-boot的工作流程。
2 armv8 u-boot的啟動
先看arm官網(wǎng)提供的一張圖:
上圖詳細概括了arm官方推薦的armv8的啟動層次結(jié)構(gòu):
官方將啟動分為了BL1,BL2,BL31,BL32,BL33階段,根據(jù)順序,芯片啟動后首先執(zhí)行BL1階段代碼,接著驗簽啟動BL2,BL2根據(jù)具體設(shè)計啟動BL31或者BL33,BL32只有在有BL31時才可能會存在并被驗簽加載啟動。
armv8分為Secure World和Non-Secure World(Normal World),四種異常級別從高到低分別為EL3,EL2,EL1,EL0。
Secure World就是可以執(zhí)行可信的firmware和app,比如密碼支付,指紋識別等一系列依賴安全保證的服務(wù)。Non-Secure World就是我們常見的u-boot,linux,qnx等裸機程序或者操作系統(tǒng)。
EL3具有最高管理權(quán)限,負責安全監(jiān)測和安全模式切換。EL2主要提供了對虛擬化的支持。EL1是一個特權(quán)模式,能夠執(zhí)行一些特權(quán)指令,用于運行各類操作系統(tǒng),在安全模式則是可信任OS。EL0是無特權(quán)模式,所有APP應(yīng)用都在EL0。
上圖中的BL1,BL2,BL31,BL32,BL33分別對應(yīng)如下功能:
BL1:是一切信任的根,一般就是固化在ROM中的一段啟動加載代碼,用于引導(dǎo)bl2,并對bl2進行驗簽保證可信任執(zhí)行;
BL2:一般是在flash中的一段可信安全啟動代碼,它的可信建立在bl1對它的驗證,主要完成一些平臺相關(guān)的初始化,比如對ddr的初始化等,并在完成初始化后尋找BL31或者BL33進行執(zhí)行;如果找到了BL31則不會繼續(xù)調(diào)用BL33,如果沒有BL31則BL33必須有;
BL31:BL31不像BL1和BL2是一次性運行的,它作為最后一道可信任固件存在,在系統(tǒng)運行時通過smc指令陷入EL3調(diào)用系統(tǒng)安全服務(wù)或者在Secure World和Non-Secure World之間進行切換;在完成BL31初始化后會去尋找BL32或者BL33進行驗簽后加載執(zhí)行;
BL32:OPTee OS + 安全app,它是一個可信安全的OS運行在EL1并在EL0啟動可信任APP(上述的指紋驗證等app),并在Trust OS運行完成后通過smc指令返回BL31,BL31切換到Non-Seucre World繼續(xù)執(zhí)行BL33;
BL33:非安全固件,也就是我們常見的UEFI firmware或者u-boot也可能是直接啟動Linux kernel;
啟動BL1,BL2,BL31,BL32則是一個完整的ATF信任鏈建立流程(ARM Trusted Firmware),像常見的PSCI(Power State Coordination Interface)功能則是在ATF的BL31上實現(xiàn);
最后一張圖完整展示整個調(diào)用流程:
BL2根據(jù)是否存在BL31和BL32可選擇性的加載不同firmware;
綜上所述,可知u-boot是一個運行在非安全世界的bootloader,負責加載各類操作系統(tǒng),并提供豐富的驅(qū)動接口;并根據(jù)是否存在安全固件還可以進行不同的boot流程,如下。
u-boot,u-boot-spl,u-boot-tpl的關(guān)系:對于一般嵌入式而言只需要一個u-boot作為bootloader即可,但是在小內(nèi)存,或者有atf的情況下還可以有spl,tpl;
spl:Secondary Program Loader,二級加載器?
tpl:Tertiary Program Loader,三級加載器
出現(xiàn)spl和tpl的原因最開始是因為系統(tǒng)sram太小,rom無法在ddr未初始化的情況下一次性把所有代碼從flash,emmc,usb等搬運到sram中執(zhí)行,也或者是flash太小,無法完整放下整個u-boot來進行片上執(zhí)行。
所以u-boot又定義了spl和tpl,spl和tpl走u-boot完全相同的boot流程,不過在spl和tpl中大多數(shù)驅(qū)動和功能被去除了,根據(jù)需要只保留一部分spl和tpl需要的功能,通過CONFIG_SPL_BUILD和CONFIG_TPL_BUILD控制;
一般只用spl就足夠了,spl完成ddr初始化,并完成一些外設(shè)驅(qū)動初始化,比如usb,emmc,以此從其他外圍設(shè)備加載u-boot,但是如果對于小系統(tǒng)spl還是太大了,則可以繼續(xù)加入tpl,tpl只做ddr等的特定初始化保證代碼體積極小,以此再次從指定位置加載spl,spl再去加載u-boot。
從目前來看,spl可以取代上圖中bl2的位置,或者bl1,根據(jù)具體廠商實現(xiàn)來決定,有一些芯片廠商會將spl固化在rom中,使其具有從emmc,usb等設(shè)備加載u-boot或者其他固件的能力。
當然在有atf的情況下可以由atf加載u-boot,或者由spl加載atf,atf再去加載u-boot。甚至在快速啟動的系統(tǒng)中可以直接由spl啟動加載linux等操作系統(tǒng)而跳過啟動u-boot;在上圖中arm官方只是給出了一個建議的啟動信任鏈,具體實現(xiàn)都需要芯片廠商來決定;
后續(xù)分析啟動流程中會在具有SPL和TPL的地方拓展它們的分叉執(zhí)行路徑盡量把SPL和TPL的功能也一并分析;
3 u-boot源碼整體結(jié)構(gòu)和一些編譯配置方式
3.1 編譯配置方式
u-boot使用了同Linux一樣的編譯配置方式,即使用kbuild系統(tǒng)來管理整體代碼的配置和編譯,通過defconfig來定制各種不同廠商的芯片bootloader二進制程序。編譯只需要注意通過環(huán)境變量或者命令行參數(shù)的方式引入一個交叉編譯工具即可:
CROSS_COMPILE:定義交叉編譯工具鏈,可以是aarch64-linux-gnu-,arm-none-eabi-或者ppc-linux-gnu-等等;u-boot有幾個配置是需要由對應(yīng)board配置的。
SYS_ARCH,SYS_CPU,SYS_SOC,SYS_BOARD,SYS_VENDOR,SYS_CONFIG_NAME;一般在board/vendor/board/Kconfig中可全部定義,部分SYS_CPU,SYS_SOC也可以在arch/xxx/Kconfig中定義,根據(jù)這幾個配置即可確定使用的cpu架構(gòu),廠商,板級信息,soc信息。
Makefile會自動根據(jù)上述信息進入對應(yīng)目錄組織編譯規(guī)則,一般如果沒有自己對應(yīng)的這些board信息,需要自己在對應(yīng)目錄建立這些Kconfig和在configs中建立defconfig。
在configs目錄中保存了uboot中所有支持的board配置,比如要使用rk3399的evb板的配置信息使用如下方式即可編譯出來:
?
make?CROSS_COMPILE=aarch64-linux-gnu-?evb-rk3399_defconfig make
?
如果沒有對應(yīng)的defconfig可以找一個與自己板級信息類似的defconfig生成一個.config,再通過menuconfig來完成自己board的配置,并最后通過savedefconfig保存為自己board的defconfig:
?
make?CROSS_COMPILE=aarch64-linux-gnu-?evb-rk3399_defconfig make?menuconfig make?savedefconfig cp?defconfig?configs/my_defconfig
?
下面是evb rk3399的定義:
?
CONFIG_SYS_ARCH="arm" CONFIG_SYS_CPU="armv8" CONFIG_SYS_SOC="rk3399" CONFIG_SYS_VENDOR="rockchip" CONFIG_SYS_BOARD="evb_rk3399" CONFIG_SYS_CONFIG_NAME="evb_rk3399"
?
根據(jù)CONFIG_SYS_BOARD的定義還會為每個源文件自動包含include/configs/xxxx.h頭文件,evb rk3399則是include/configs/evb_rk3399.h頭文件。這個頭文件可在其中定義board的一些關(guān)鍵配置,系統(tǒng)的ram大小,環(huán)境變量的起始地址和大小,GIC基地址,時鐘頻率,是否開啟看門狗等定義,可根據(jù)具體需求來定義。
u-boot使用Kconfig和include/configs/xxx.h來靈活的確定u-boot編譯流程及最終生成的文件。比如當定義CONFIG_SYS_CPU為"armv8",CONFIG_SYS_ARCH為"arm"時,即確定了目標架構(gòu)為armv8會自動根據(jù)Makefile進入對應(yīng)目錄進行編譯鏈接。
3.2 u-boot源碼結(jié)構(gòu)
這里只對一些常用的目錄進行說明:
arch:各種架構(gòu)的啟動初始化流程代碼,鏈接腳本等均在此目錄對應(yīng)的架構(gòu)中存放;
board:包含了大部分廠商的board初始化代碼,基本平臺化相關(guān)的代碼都在對應(yīng)的board目錄中,早期的一些board代碼在arch/xxx/xxx-mach中,現(xiàn)在基本不會放在arch目錄下面了;
cmd:包含了大量實用的u-boot命令的實現(xiàn),比如md,cp,cmp,tftp,fastboot,ext4load等命令的實現(xiàn),我們也可以在此處添加自己實現(xiàn)的命令;
common:包含了u-boot的核心初始化代碼,包括board_f,board_r,spl等一系列代碼;
configs:包含了所有board的配置文件,可直接使用;
drivers:大量驅(qū)動代碼的存放處;
dts:編譯生成dtb,內(nèi)嵌dtb到u-boot的編譯規(guī)則定義目錄;
env:環(huán)境變量功能實現(xiàn)代碼;
fs:文件系統(tǒng)讀寫功能的實現(xiàn),里面包含了各類文件系統(tǒng)的實現(xiàn);
include:所有公用頭文件的存放路徑;
lib:大量通用功能實現(xiàn),提供給各個模塊使用;
net:網(wǎng)絡(luò)相關(guān)功能的實現(xiàn);
scripts:編譯,配置文件的腳本文件存放處;
tools:測試和實用工具的實現(xiàn),比如mkimage的實現(xiàn)代碼在此處;
4 u-boot armv8鏈接腳本
在進行源碼分析之前,首先看看u-boot的鏈接腳本,通過鏈接腳本可以從整體了解一個u-boot的組成,并且可以在啟動分析中知道某些邏輯是在完成什么工作。
在armv8中,u-boot使用arch/arm/cpu/armv8/u-boot.lds進行鏈接。
u-boot-spl和u-boot-tpl使用arch/arm/cpu/armv8/u-boot-spl.lds進行鏈接,因為每個board的情況可能不同,所以u-boot可以通過Kconfig來自定義u-boot-spl.lds和u-boot-tpl.lds。
4.1 u-boot.lds
?
/*?SPDX-License-Identifier:?GPL-2.0+?*/ /* ?*?(C)?Copyright?2013 ?*?David?Feng??* ?*?(C)?Copyright?2002 ?*?Gary?Jennejohn,?DENX?Software?Engineering,? ?*/ #include? #include? OUTPUT_FORMAT("elf64-littleaarch64",?"elf64-littleaarch64",?"elf64-littleaarch64") OUTPUT_ARCH(aarch64) ENTRY(_start)?--------------------------------------------------------------------?(1) /* ?*(1)首先定義了二進制程序的輸出格式為"elf64-littleaarch64", ?*????架構(gòu)是"aarch64",程序入口為"_start"符號; ?*/ SECTIONS { #ifdef?CONFIG_ARMV8_SECURE_BASE?--------------------------------------------------?(2) /* ?*(2)ARMV8_SECURE_BASE是u-boot對PSCI的支持,在定義時可以將PSCI的文本段, ?*????數(shù)據(jù)段,堆棧段重定向到指定的內(nèi)存,而不是內(nèi)嵌到u-boot中。 ?*????不過一般廠商實現(xiàn)會使用atf方式使其與bootloader分離,這個功能不常用; ?*/ ?/DISCARD/?:?{?*(.rela._secure*)?} #endif ?.?=?0x00000000;?--------------------------------------------------------------?(3) /* ?*(3)定義了程序鏈接的基地址,默認是0,通過配置CONFIG_SYS_TEXT_BASE可修改 ?*????這個默認值。 ?*/ ?.?=?ALIGN(8); ?.text?: ?{ ??*(.__image_copy_start)?---------------------------------------------------?(4) /* ?*(4)__image_copy_start和__image_copy_end用于定義需要重定向的段, ?*????u-boot是一個分為重定向前初始化和重定向后初始化的bootloader, ?*????所以此處會定義在完成重定向前初始化后需要搬運到ddr中數(shù)據(jù)的起始地址和結(jié)束地址; ?* ?*????大多數(shù)時候u-boot是運行在受限的sram或者只讀的flash上, ?*????u-boot為了啟動流程統(tǒng)一會在ddr未初始化和重定位之前不去訪問全局變量, ?*????但是又為了保證u-boot能夠正常讀寫全局變量,內(nèi)存,調(diào)用各類驅(qū)動能力, ?*????所以u-boot將啟動初始化分為了兩個部分,重定向前初始化board_f和 ?*????重定向后初始化??board_r,在重定向之前完成一些必要初始化, ?*????包括可能的ddr初始化,然后通過__image_copy_start和__image_copy_end ?*????將u-boot搬運到ddr中,并在ddr中進行重定向后初始化,這個時候的u-boot就可以 ?*????正常訪問全局變量等信息了。 ?*? ?*????如果想要在board_f過程中讀寫一些全局變量信息該怎么辦呢? ?*????u-boot通過定義global_data(gd)來完成此功能, ?*????后續(xù)在分析到時會詳細講解實現(xiàn)方式。 ?*/ ??CPUDIR/start.o?(.text*)?--------------------------------------------------?(5) /* ?*(5)定義了鏈接程序的頭部文本段,armv8就是 ?*????arch/arm/cpu/armv8/start.S,? ?*??? start.S中所有文本段將會鏈接到此段中并且段入口符號就是_start; ?*/ ?} ?/*?This?needs?to?come?before?*(.text*)?*/ ?.efi_runtime?:?{?------------------------------------------------------------?(6) /* ?*(6)在定義了efi運行時相關(guān)支持時才會出現(xiàn)使用的段,一般不用關(guān)心; ?*/ ????????__efi_runtime_start?=?.; ??*(.text.efi_runtime*) ??*(.rodata.efi_runtime*) ??*(.data.efi_runtime*) ????????__efi_runtime_stop?=?.; ?} ?.text_rest?:?----------------------------------------------------------------?(7) /* ?*(7)除了start.o,其他的所有文本段將會鏈接到此段中; ?*/ ?{ ??*(.text*) ?} #ifdef?CONFIG_ARMV8_PSCI?--------------------------------------------------------?(8) /* ?*(8)同(2),是PSCI相關(guān)功能的支持,一般不會使用; ?*/ ?.__secure_start?: #ifndef?CONFIG_ARMV8_SECURE_BASE ??ALIGN(CONSTANT(COMMONPAGESIZE)) #endif ?{ ??KEEP(*(.__secure_start)) ?} #ifndef?CONFIG_ARMV8_SECURE_BASE #define?CONFIG_ARMV8_SECURE_BASE #define?__ARMV8_PSCI_STACK_IN_RAM #endif ?.secure_text?CONFIG_ARMV8_SECURE_BASE?: ??AT(ADDR(.__secure_start)?+?SIZEOF(.__secure_start)) ?{ ??*(._secure.text) ??.?=?ALIGN(8); ??__secure_svc_tbl_start?=?.; ??KEEP(*(._secure_svc_tbl_entries)) ??__secure_svc_tbl_end?=?.; ?} ?.secure_data?:?AT(LOADADDR(.secure_text)?+?SIZEOF(.secure_text)) ?{ ??*(._secure.data) ?} ?.secure_stack?ALIGN(ADDR(.secure_data)?+?SIZEOF(.secure_data), ???????CONSTANT(COMMONPAGESIZE))?(NOLOAD)?: #ifdef?__ARMV8_PSCI_STACK_IN_RAM ??AT(ADDR(.secure_stack)) #else ??AT(LOADADDR(.secure_data)?+?SIZEOF(.secure_data)) #endif ?{ ??KEEP(*(.__secure_stack_start)) ??.?=?.?+?CONFIG_ARMV8_PSCI_NR_CPUS?*?ARM_PSCI_STACK_SIZE; ??.?=?ALIGN(CONSTANT(COMMONPAGESIZE)); ??KEEP(*(.__secure_stack_end)) ?} #ifndef?__ARMV8_PSCI_STACK_IN_RAM ?.?=?LOADADDR(.secure_stack); #endif ?.__secure_end?:?AT(ADDR(.__secure_end))?{ ??KEEP(*(.__secure_end)) ??LONG(0x1d1071c);?/*?Must?output?something?to?reset?LMA?*/ ?} #endif ?.?=?ALIGN(8); ?.rodata?:?{?*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))?}?-------------------?(9) /* ?*(9)所有僅讀數(shù)據(jù)將會在這個段中對齊排序存放好; ?*/ ?.?=?ALIGN(8); ?.data?:?{?--------------------------------------------------------------------?(10) /* ?*(10)所有數(shù)據(jù)段將會鏈接到此段中; ?*/ ??*(.data*) ?} ?.?=?ALIGN(8); ?.?=?.; ?.?=?ALIGN(8); ?.u_boot_list?:?{?-------------------------------------------------------------?(11) /* ?*(11)u_boot_list段定義了系統(tǒng)中當前支持的所有命令和設(shè)備驅(qū)動,此段把散落在各個文件中 ?*?????通過U_BOOT_CMD的一系列拓展宏定義的命令和U_BOOT_DRIVER的拓展宏定義的設(shè)備驅(qū)動收集到一起, ?*?????并按照名字排序存放,以便后續(xù)在命令行快速檢索到命令并執(zhí)行和檢測注冊的設(shè)備和設(shè)備樹匹配 ?*???? probe設(shè)備驅(qū)動初始化;(設(shè)備驅(qū)動的probe只在定義了dm模塊化驅(qū)動時有效) ?*/ ??KEEP(*(SORT(.u_boot_list*))); ?} ?.?=?ALIGN(8); ?.efi_runtime_rel?:?{ ????????????????__efi_runtime_rel_start?=?.; ??*(.rel*.efi_runtime) ??*(.rel*.efi_runtime.*) ????????????????__efi_runtime_rel_stop?=?.; ?} ?.?=?ALIGN(8); ?.image_copy_end?: ?{ ??*(.__image_copy_end) ?} ?.?=?ALIGN(8); ?.rel_dyn_start?:?--------------------------------------------------------?(12) /* ?*(12)一般u-boot運行時是根據(jù)定義的基地址開始執(zhí)行,如果加載地址和鏈接地址 ?*?????不一致則會出現(xiàn)不能執(zhí)行u-boot的問題。通過一個 ?*?????配置CONFIG_POSITION_INDEPENDENT即可打開地址無關(guān)功能, ?*?????此選項會在鏈接u-boot時添加-PIE參數(shù)。此參數(shù)會在u-boot ELF文件中 ?*?????生成rela*段,u-boot通過讀取此段中表的相對地址值與實際運行時地址值 ?*?????依次遍歷進行修復(fù)當前所有需要重定向地址,使其可以實現(xiàn)地址無關(guān)運行; ?*?????即無論鏈接基地址如何定義,u-boot也可以在任意ram地址 ?*?????運行(一般需要滿足最低4K或者64K地址對齊); ?*? ?*?????注意此功能只能在sram上實現(xiàn),因為此功能會在運行時修改文本段數(shù)據(jù)段中的地址, ?*?????如果此時運行在片上flash,則不能寫flash,導(dǎo)致功能失效無法實現(xiàn)地址無關(guān); ?*/ ?{ ??*(.__rel_dyn_start) ?} ?.rela.dyn?:?{ ??*(.rela*) ?} ?.rel_dyn_end?: ?{ ??*(.__rel_dyn_end) ?} ?_end?=?.; ?.?=?ALIGN(8); ?.bss_start?:?{?--------------------------------------------------------?(13) /* ?*(13)眾所周知的bbs段; ?*/ ??KEEP(*(.__bss_start)); ?} ?.bss?:?{ ??*(.bss*) ???.?=?ALIGN(8); ?} ?.bss_end?:?{ ??KEEP(*(.__bss_end)); ?} ?/DISCARD/?:?{?*(.dynsym)?}?--------------------------------------------?(14) /* ?*(14)一些在鏈接時無用需要丟棄的段; ?*/ ?/DISCARD/?:?{?*(.dynstr*)?} ?/DISCARD/?:?{?*(.dynamic*)?} ?/DISCARD/?:?{?*(.plt*)?} ?/DISCARD/?:?{?*(.interp*)?} ?/DISCARD/?:?{?*(.gnu*)?} #ifdef?CONFIG_LINUX_KERNEL_IMAGE_HEADER?-----------------------------------?(15) /* ?*(15)在efi加載時會很有用,主要在u-boot的二進制頭部添加了一些頭部信息, ?*?????包括大小端,數(shù)據(jù)段文本段大小等,以便于efi相關(guān)的加載器讀取信息, ?*?????此頭部信息來自于Linux arm64的Image的頭部信息;該頭部也不屬于u-boot的 ?*?????一部分只是被附加上去的; ?*/ #include?"linux-kernel-image-header-vars.h" #endif }
?
4.2 u-boot-spl.lds
此鏈接腳本是標準的spl鏈接腳本,還包含了u_boot_list段,如果對應(yīng)自己board不需要命令行或者模塊化驅(qū)動設(shè)備,只作為一個加載器則可以自定義更簡略的鏈接腳本。
?
/*?SPDX-License-Identifier:?GPL-2.0+?*/ /* ?*?(C)?Copyright?2013 ?*?David?Feng??* ?*?(C)?Copyright?2002 ?*?Gary?Jennejohn,?DENX?Software?Engineering,? ?* ?*?(C)?Copyright?2010 ?*?Texas?Instruments,? ?*?Aneesh?V? ?*/ MEMORY?{?.sram?:?ORIGIN?=?IMAGE_TEXT_BASE,?----------------------------------------?(1) /* ?*(1)>XXX 的形式可以將指定段放入XXX規(guī)定的內(nèi)存中;一般u-boot-spl只有 ?*????很小的可運行內(nèi)存塊,所以spl中會舍去大量不需要用的段只保留關(guān)鍵的 ?*????文本段數(shù)據(jù)段等,并且通過>.sram的形式將不在ddr初始化前用到的段定義到sdram中, ?*????后續(xù)只需在完成ddr初始化后將這些段搬運到ddr中即可,而不需要額外的 ?*????地址修復(fù)邏輯,如下:有一個sram 0x18000-0x19000, ?*????一個sdram?0x80000000?-?0x90000000, ?*????那么通過>.sram方式則map文件可能如下: ?*???????0x18000?stext ?*???????... ?*???????0x18100?sdata ?*???????... ?*???????0x80000000?sbss ?*???????... ?*/ ??LENGTH?=?IMAGE_MAX_SIZE?} MEMORY?{?.sdram?:?ORIGIN?=?CONFIG_SPL_BSS_START_ADDR, ??LENGTH?=?CONFIG_SPL_BSS_MAX_SIZE?} OUTPUT_FORMAT("elf64-littleaarch64",?"elf64-littleaarch64",?"elf64-littleaarch64") OUTPUT_ARCH(aarch64) ENTRY(_start)?--------------------------------------------------------------------?(2) /* ?*(2)同u-boot.lds一致,共用一套邏輯入口_start; ?*/ SECTIONS { ?.text?:?{ ??.?=?ALIGN(8); ??*(.__image_copy_start)?--------------------------------------------------?(3) /* ?*(3)同樣的,如果spl需要重定向則會使用此段定義,大多數(shù)情況下spl中會用上重定向; ?*/ ??CPUDIR/start.o?(.text*) ??*(.text*) ?}?>.sram ?.rodata?:?{ ??.?=?ALIGN(8); ??*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) ?}?>.sram ?.data?:?{ ??.?=?ALIGN(8); ??*(.data*) ?}?>.sram #ifdef?CONFIG_SPL_RECOVER_DATA_SECTION?----------------------------------------?(4) /* ?*(4)SPL_RECOVER_DATA_SECTION段用于保存數(shù)據(jù)段數(shù)據(jù), ?*????一些board在初始化時修改data段數(shù)據(jù),并在后續(xù)某個階段 ?*????從此段中恢復(fù)data的原始數(shù)據(jù); ?*/ ?.data_save?:?{ ??*(.__data_save_start) ??.?=?SIZEOF(.data); ??*(.__data_save_end) ?}?>.sram #endif ?.u_boot_list?:?{ ??.?=?ALIGN(8); ??KEEP(*(SORT(.u_boot_list*))); ?}?>.sram ?.image_copy_end?:?{ ??.?=?ALIGN(8); ??*(.__image_copy_end) ?}?>.sram ?.end?:?{ ??.?=?ALIGN(8); ??*(.__end) ?}?>.sram ?_image_binary_end?=?.; ?.bss_start?(NOLOAD)?:?{ ??.?=?ALIGN(8); ??KEEP(*(.__bss_start)); ?}?>.sdram?--------------------------------------------------------------?(5) /* ?*(5)將bss段數(shù)據(jù)定義到>.sdram中,即可在初始化ddr后直接對此段地址清零 ?*????即可使用全局未初始化變量,并且不會帶來副作用。 ?*/ ?.bss?(NOLOAD)?:?{ ??*(.bss*) ???.?=?ALIGN(8); ?}?>.sdram ?.bss_end?(NOLOAD)?:?{ ??KEEP(*(.__bss_end)); ?}?>.sdram ?/DISCARD/?:?{?*(.rela*)?} ?/DISCARD/?:?{?*(.dynsym)?} ?/DISCARD/?:?{?*(.dynstr*)?} ?/DISCARD/?:?{?*(.dynamic*)?} ?/DISCARD/?:?{?*(.plt*)?} ?/DISCARD/?:?{?*(.interp*)?} ?/DISCARD/?:?{?*(.gnu*)?} }
?
從上述的鏈接腳本可以看出,armv8的u-boot的啟動是從arch/arm/cpu/armv8/start.S中的_start開始的,并在后續(xù)初始化中調(diào)用了很多鏈接腳本中定義的地址符號表。
原文:https://blog.csdn.net/maybeYoc/article/details/122937844
審核編輯:劉清
評論
查看更多