掌握嵌入式Linux編程3引導程式

来源:https://www.cnblogs.com/testing-/archive/2023/09/26/17525233.html
-Advertisement-
Play Games

一、vi編譯器介紹 Vi編輯器是所有Unix及Linux系統下標準的編輯器,類似於windows系統下的notepad(記事本)編輯器,由於在Unix及Linux系統的任何版本,Vi編輯器是完全相同的,因 此可以在其他任何介紹vi的地方都能進一步瞭解它,Vi也是Linux中最基本的文本編輯器,學會它 ...


3引導程式

引導程式是嵌入式Linux的第二個要素。它是啟動系統和載入操作系統內核的部分。在這一章中,我們將研究引導程式的作用,特別是它如何使用一種叫做設備樹的數據結構將控制權從自身傳遞給內核,設備樹也被稱為扁平化設備樹或FDT(flattened device tree)。我將介紹設備樹的基本知識,因為這將幫助你遵循設備樹中描述的連接,並將其與真實的硬體聯繫起來。

我將研究流行的開源引導程式U-Boot的,並向你展示如何使用它來引導目標設備,以及如何通過使用BeagleBone Black作為例子來定製它,使它能夠運行在新設備上。
在本章中,我們將介紹以下內容:

  • Bootloader是做什麼的?
  • 啟動順序
  • 從引導程式轉移到內核
  • 設備樹
  • U-Boot

引導程式是做什麼的?

在嵌入式Linux系統中,引導程式有兩項主要工作:將系統初始化到基本level和載入內核。事實上,第一項工作在某種程度上是附屬於第二項工作的,因為它只需要讓系統工作到載入內核所需的程度。

當啟動程式代碼的第一行被執行時,在開機或複位之後,系統處於非常小的狀態。DRAM控制器沒有被設置,所以主存儲器不能被訪問。同樣,其他介面也沒有被配置,所以通過NAND快閃記憶體控制器、MMC控制器等訪問的存儲是不可用的。通常情況下,開始時唯一可以運行的資源是單個CPU核心、一些片上靜態存儲器和啟動ROM。
系統引導由幾個階段的代碼組成,每個階段都會使更多的系統進入運行狀態。引導程式的最終行為是將內核載入到RAM中併為其創造執行環境。引導程式和內核之間的介面細節是針對特定的架構的,但在每一種情況下,它都必須做兩件事。首先,bootloader必須傳遞指針到包含硬體配置信息的結構。第二,它必須傳遞指向內核命令行的指針。

內核命令行是控制Linux行為的文本字元串。一旦內核開始執行,就不再需要bootloader了,它所使用的所有記憶體都可以被回收。
Bootloader 的一個附屬工作是提供維護模式,用於更新 Boot 配置,將新的 Boot Image 載入到記憶體中,也可能用於運行診斷程式。這通常是由簡單的命令行用戶界面控制的,通常是通過串列控制台。

啟動順序

幾年前,只需要在處理器的複位矢量處把引導程式放在非易失性存儲器中。當時 NOR 快閃記憶體很普遍,由於它可以直接映射到地址空間,所以是理想的存儲方法。下圖顯示了這樣的配置,複位向量位於快閃記憶體區域的上端0xfffffffc處。Bootloader被連接起來,所以在這個位置有一條跳轉指令,指向Bootloader代碼的開始:

從那時起,在NOR快閃記憶體中運行的引導程式代碼可以初始化DRAM控制器,使主存儲器--DRAM--變得可用,然後將自己複製到DRAM中。一旦完全運行,引導程式可以將內核從快閃記憶體載入到DRAM中,並將控制權轉移給它。
然而,一旦你離開了簡單的可線性定址的存儲介質,如NOR快閃記憶體,啟動序列就會變成複雜的多階段程式。每個SoC的細節都是非常具體的,但它們一般都遵循以下各個階段。

第1階段--ROM代碼

在沒有可靠的外部存儲器的情況下,複位或開機後立即運行的代碼必須被存儲起來。

複位或上電的過程必須存儲在SoC的片上;這被稱為ROM代碼。它在製造時被載入到晶元中,因此,ROM代碼是專有的,不能被開放源碼的等同物所取代。通常,它不包括初始化存儲器控制器的代碼,因為DRAM的配置是高度特定於設備的,所以它只能使用靜態隨機存取存儲器(SRAM Static Random Access Memory ),它不需要存儲器控制器。
大多數嵌入式SoC設計都有少量的片上SRAM,大小從4KB到幾百KB不等:

ROM代碼能夠從幾個預編程的位置之一載入一小塊代碼到SRAM中。舉例來說,TI OMAP和Sitara晶元試圖從NAND快閃記憶體的前幾頁載入代碼,或從通過串列外設介面(SPI Serial Peripheral Interface)連接的快閃記憶體,或從MMC設備(可能是eMMC晶元或SD卡)的第一個扇區,或從MMC設備的第一個分區上名為MLO的文件。如果它從所有這些存儲器設備中讀取失敗,那麼它就會嘗試從乙太網、USB或UART中讀取位元組流;後者主要是作為生產過程中向快閃記憶體載入代碼的手段,而不是用於正常操作。大多數嵌入式SoC都有以類似方式工作的ROM代碼。在SoC中,如果SRAM不夠大,無法載入完整的引導程式,如U-Boot,就必須有一個中間的載入器,稱為二級程式載入器(SPL)。
在ROM代碼階段結束時,SPL(secondary program loader)存在於SRAM中,ROM代碼會跳到該代碼的開頭。

第2階段--二級程式載入器

SPL必須設置記憶體控制器和系統的其他基本部分,以準備將三級程式載入器(TPL Tertiary Program Loader)載入到DRAM中。SPL的功能受限於SRAM的大小。它可以從存儲設備的列表中讀取程式,就像ROM代碼一樣,再次使用從快閃記憶體設備開始的預編程偏移量。如果SPL內置了文件系統驅動程式,它可以從磁碟分區中讀取知名的文件名,如u-boot.img。SPL通常不允許任何用戶互動,但它可以列印版本信息和進度信息,你可以在控制台看到。下圖解釋了第二階段的結構:

前面的圖顯示了從ROM代碼跳到SPL的過程。當SPL在SRAM內執行時,它將TPL載入到DRAM中。在第二階段結束時,TPL存在於DRAM中,而SPL可以跳轉到該區域。
SPL可能是開源的,如TI x-loader和Atmel AT91Bootstrap的情況,但它包含由製造商提供的二進位blob的專有代碼是很常見的。

第三階段--TPL

在這一點上,我們正在運行一個完整的引導程式,例如U-Boot,我們將在本章的後面瞭解一下它。通常,有簡單的命令行用戶界面,可以讓你執行維護任務,比如將新的引導和內核映像載入到快閃記憶體中,以及載入和啟動內核,還有一種方法是自動載入內核而不需要用戶干預。
下圖解釋了第三階段的架構:

前面的圖顯示了從SRAM中的SPL到DRAM中的TPL的跳躍過程。隨著TPL的執行,它將內核載入到DRAM中。如果我們願意,我們還可以選擇在DRAM中的映像上附加一個FDT和/或初始RAM磁碟。無論哪種方式,在第三階段結束時,記憶體中都有一個內核,等待被啟動。
一旦內核開始運行,嵌入式引導程式通常會從記憶體中消失,不再參與系統的運行。在這之前,TPL需要把啟動過程的控制權交給內核。

從 Bootloader 轉移到 kernel

當引導程式將控制權交給內核時,它必須傳遞一些基本信息,其中包括以下內容:

  • 機器號,在不支持設備樹的PowerPC和Arm平臺上使用,用來識別SoC的類型。
  • 到目前為止已經檢測到的硬體的基本細節,包括(最起碼)物理RAM的大小和位置以及CPU的時鐘速度。
  • 內核命令行。
  • 可選的,設備樹二進位的位置和大小。
  • 可選,初始RAM磁碟的位置和大小,稱為初始RAM文件系統(initramfs)。

內核命令行是一個普通的ASCII字元串,通過給出例如包含根文件系統的設備名稱來控制Linux的行為。我們將在下一章討論這個問題的細節。將根文件系統作為RAM盤提供是很常見的,在這種情況下,引導程式有責任將RAM盤鏡像載入到記憶體中。我們將在第五章 "構建根文件系統 "中介紹如何創建初始RAM磁碟。
這種信息的傳遞方式取決於體繫結構,並且在最近幾年有所改變。例如,對於PowerPC,引導程式只是簡單地傳遞一個指向電路板信息結構的指針,而對於Arm,它傳遞一個指向A標簽列表的指針。在文檔/arm/Booting的內核源中,對A標簽的格式有一個很好的描述。
在這兩種情況下,所傳遞的信息量都非常有限,大部分信息都是在運行時發現的,或者作為平臺數據硬編碼到內核中。平臺數據的廣泛使用意味著每個板子都必須有一個為該平臺配置和修改的內核。我們需要一個更好的方法,這個方法就是設備樹。在Arm世界中,隨著Linux 3.8的發佈,從A標簽的轉變開始認真進行。今天,幾乎所有的Arm系統都使用設備樹來收集硬體平臺的具體信息,允許單一的內核二進位文件在廣泛的這些平臺上運行。
現在我們已經瞭解了引導程式的作用,引導序列的階段是什麼,以及它如何將控制權傳遞給內核,讓我們學習如何配置引導程式,使其在流行的嵌入式SoC上運行。

介紹設備樹

如果你正在使用Arm或PowerPC SoC,你幾乎肯定會在某個時候遇到設備樹。本節旨在讓你快速瞭解它們是什麼以及它們是如何工作的。在本書過程中,我們將反覆討論設備樹的話題。
設備樹是定義電腦系統硬體組件的一種靈活方式。請記住,設備樹只是靜態數據,不是可執行代碼。通常,設備樹由引導程式載入並傳遞給內核,儘管有可能將設備樹與內核鏡像捆綁在一起,以滿足不能單獨載入的引導程式。
這個格式來自於Sun Microsystems公司的OpenBoot的引導程式,它被正式確定為Open Firmware規範,也就是IEEE標準IEEE1275-1994。它被用於基於PowerPC的Macintosh電腦中,因此是PowerPC Linux埠的合理選擇。從那時起,它已經被許多Arm Linux實現大規模採用,在較小的程度上,被MIPS、MicroBlaze、ARC和其他架構採用。
我建議訪問https://www.devicetree.org,瞭解更多信息。

設備樹基礎知識

Linux內核在arch/$ARCH/boot/dts中包含了大量的設備樹源文件,這是學習設備樹的好的起點。在arch/$ARCH/dts的U-boot源代碼中也有少量的源文件。如果你的硬體是從第三方購買的,dts文件是板卡支持包的一部分,所以你應該期望和其他源文件一起收到。
設備樹將電腦系統表示為以層次結構連接在一起的組件的集合,如一棵樹。設備樹以根節點開始,根節點用正斜杠/表示,它包含代表系統硬體的後續節點。每個節點都有一個名稱,並包含一些名稱="值 "形式的屬性。下麵是簡單的例子:

/dts-v1/;
/{
model = "TI AM335x BeagleBone";
compatible = "ti,am33xx";
#address-cells = <1>;
#size-cells = <1>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a8";
device_type = "cpu";
reg = <0>;
};
};
memory@0x80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
};
};

這裡,我們有一個根節點,它包含一個cpus節點和一個記憶體節點。cpus節點包含一個名為cpu@0的CPU節點。這些節點的名字通常包括一個@,後面是一個地址,用來區分該節點和其他同類型的節點。如果節點有reg屬性,@是必須的。

根節點和CPU節點都有一個相容屬性。Linux內核使用這個屬性,通過與每個設備驅動程式在of_device_id結構中輸出的字元串進行比較,找到一個匹配的設備驅動程式(更多信息請參見第11章,與設備驅動程式的介面)。

按照慣例,相容屬性的值由製造商名稱和組件名稱組成,以減少不同製造商製造的類似設備之間的混淆;因此,ti,am33xx和arm,cortex-a8。如果有一個以上的驅動程式可以處理這個設備,那麼相容屬性有一個以上的值也是很常見的。它們被列在第一位,最合適的被提到。
CPU節點和記憶體節點有一個device_type屬性,它描述了設備的類別。節點名稱通常從device_type派生。

reg屬性

前面顯示的記憶體和CPU節點有一個reg屬性,它指的是寄存器空間中的單位範圍。reg屬性由兩個值組成,分別代表實際物理地址和範圍的大小(長度)。這兩個值都被寫成零或多個32位整數,稱為單元。因此,前面的記憶體節點指的是從0x80000000開始、長度為0x20000000位元組的單組記憶體。
當地址或大小值不能用32位表示時,理解寄存器的屬性就變得更加複雜。例如,在具有64位定址的設備上,你需要為每個單元格設置兩個:

/{
#address-cells = <2>;
#size-cells = <2>;
memory@80000000 {
device_type = "memory";
reg = <0x00000000 0x80000000 0 0x80000000>;
};
};

關於所需單元格數量的信息被保存在祖先節點的#address-cells和#size_cells屬性中。換句話說,要理解一個reg屬性,你必須在節點層次結構中向後看,直到找到#address-cells和#size_cells。如果沒有的話,每一個的預設值都是1--但是對於設備樹編寫者來說,依賴預設值是不好的做法。
現在,讓我們回到cpu和cpus的節點上。CPU也有地址;在一個四核設備中,它們可能被編為0、1、2和3。這可以被認為是沒有任何深度的一維數組,所以大小為零。因此,你可以看到我們在cpus節點中有#address-cell = <1>和#size-cells = <0>,而在子節點cpu@0中,我們為reg屬性分配了一個值,reg = <0>。

標簽和中斷

到目前為止,我們所描述的設備樹的結構假定有單一的組件層次,而事實上,有幾個。除了組件與系統其他部分之間明顯的數據連接外,它還可能與中斷控制器、時鐘源和電壓調節器相連接。為了表達這些連接,我們可以給節點添加一個標簽,並從其他節點引用該標簽。這些標簽有時被稱為phandles,因為當設備樹被編譯時,從另一個節點引用的節點被分配了一個獨特的數值,這個屬性稱為phandle。如果你反編譯設備樹的二進位文件,你可以看到它們。
以一個包含可以產生中斷的LCD控制器和一個中斷控制器的系統為例:

/dts-v1/;
{
intc: interrupt-controller@48200000 {
compatible = "ti,am33xx-intc";
interrupt-controller;
#interrupt-cells = <1>;
reg = <0x48200000 0x1000>;
};
lcdc: lcdc@4830e000 {
compatible = "ti,am33xx-tilcdc";
reg = <0x4830e000 0x1000>;
interrupt-parent = <&intc>;
interrupts = <36>;
ti,hwmods = "lcdc";
status = "disabled";
};
};

在這裡,我們有一個interrupt-controller@48200000節點,標簽為intc。interrupt-controller屬性將其標識為中斷控制器。像所有的中斷控制器一樣,它有一個#interrupt-cell屬性,它告訴我們需要多少個單元來表示中斷源。在這種情況下,只有一個表示中斷請求(IRQ)號碼。其他中斷控制器可能會使用額外的單元來描述中斷的特征;例如,指示它是邊緣觸發還是電平觸發。中斷單元的數量和它們的含義在每個中斷控制器的綁定中都有描述。設備樹的綁定可以在Linux內核源文件/devicetree/bindings/目錄下找到。

看一下lcdc@4830e000節點,它有interrupt-parent屬性,使用標簽引用它所連接的中斷控制器。它還有一個interrupts屬性,在這種情況下是36。註意,這個節點有它自己的標簽,lcdc,它被用在其他地方:任何節點都可以有一個標簽。

設備樹包含文件

很多硬體在同一家族的SoC之間和使用同一SoC的板子之間是通用的。這反映在設備樹中,就是將共同的部分分割成包含文件,通常以.dtsi為擴展名。開放固件標准將/include/定義為要使用的機制,如vexpress-v2p-ca9.dts中的這個片段:
/include/ "vexpress-v2m.dtsi"
不過,翻看內核中的.dts文件,你會發現一個從C語言中借用的替代性包含語句;例如,在am335x-boneblack.dts中:

#include "am33xx.dtsi"
#include "am335x-bon-common.dtsi"

下麵是am33xx.dtsi的另一個例子:

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/am33xx.h>
#include <dt-bindings/clock/am3.h>.

最後,include/dt-bindings/pinctrl/am33xx.h包含正常的C語言巨集:

#define PULL_DISABLE (1 << 3)
#define INPUT_EN (1 << 5)
#define SLEWCTRL_SLOW (1 << 6)
#define SLEWCTRL_FAST 0

如果使用Kbuild系統構建設備樹源碼,所有這些都可以解決,該系統通過C預處理器CPP運行,其中的#include和#define語句被處理成適合設備樹編譯器的文本。這個動機在前面的例子中已經說明瞭;它意味著設備樹源可以使用與內核代碼相同的常量定義。
當我們包含文件時,使用任何一種語法,節點都會疊加在一起,形成複合樹,外層擴展或修改內層。例如,am33xx.dtsi是所有am33xx SoC的通用文件,它定義了第一個MMC控制器介面,像這樣:

mmc1: mmc@48060000 {
compatible = "ti,omap4-hsmmc";
ti,hwmods = "mmc1";
ti,dual-volt;
ti,needs-special-reset;
ti,needs-special-hs-handling;
dmas = <&edma_xbar 24 0 0
&edma_xbar 25 0 0>;
dma-names = "tx", "rx";
interrupts = <64>;
reg = <0x48060000 0x1000>;
status = "disabled";
};

請註意,status是禁用的,這意味著沒有設備驅動程式應該被綁定到它,而且它的標簽是mmc1。
BeagleBone和BeagleBone Black都有連接到mmc1的microSD卡介面。這就是為什麼在am335x-bon-common.dtsi中,同一個節點被其標簽所引用;也就是&mmc1:

&mmc1 {
status = "okay";
bus-width = <0x4>;
pinctrl-names = "default";
pinctrl-0 = <&mmc1_pins>;
cd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
};

狀態屬性被設置為OK,這使得MMC設備驅動在BeagleBone的兩個變體上運行時與這個介面綁定。同時,在引腳控制配置mmc1_pins中添加了標簽的引用。唉,這裡沒有足夠的空間來描述引腳控制和引腳復用。你可以在Linux內核源碼中的Documentation/devicetree/bindings/pinctrl目錄下找到一些信息。
然而,mmc1介面在BeagleBone Black上連接到一個不同的電壓調節器。這在am335x-boneblack.dts中表示,你會看到另一個對mmc1的引用,它通過vmmcsd_fixed標簽將其與電壓調節器聯繫起來:

&mmc1 {
vmmc-supply = <&vmmcsd_fixed>;
};

所以,像這樣將設備樹的源文件分層,給了我們靈活性,並減少了對重覆代碼的需求。

編譯設備樹

Bootloader和內核需要設備樹的二進位表示,所以必須使用設備樹編譯器來編譯,也就是DTC。結果是以.dtb結尾的文件,它被稱為設備樹二進位或設備樹blob。
在Linux源代碼中的scripts/dtc/dtc中有dtc的副本,它也可以作為軟體包在許多Linux發行版中使用。你可以用它來編譯簡單的設備樹(不使用#include的設備樹),像這樣:

$ dtc simpledts-1.dts -o simpledts-1.dtb
DTC: dts->dts on file "simpledts-1.dts"

要註意的是,DTC不會給出有用的錯誤信息,而且除了語言的基本語法外,不會做任何檢查,這意味著調試源文件中的輸入錯誤可能是一個漫長的過程。
要建立更複雜的例子,你必須使用Kbuild內核,如第4章 "配置和建立內核 "中所示。
像內核一樣,引導程式可以使用設備樹來初始化嵌入式SoC及其外圍設備。當你從大容量存儲設備(如QSPI快閃記憶體)載入內核時,這個設備樹是至關重要的。雖然嵌入式Linux提供了多種引導程式的選擇,但我們將只介紹一種。接下來我們將深入探討這個引導程式。

U-Boot

我們將專門討論U-Boot,因為它支持大量的處理器架構和大量的單板和設備。它已經存在了很長時間,並且有很好的社區來支持。
U-Boot,或者說它的全名是Das U-Boot,開始時是用於嵌入式PowerPC板的開放源碼引導程式。然後,它被移植到基於Arm的板子上,後來又移植到其他架構上,包括MIPS和SH。它是由Denx軟體工程公司主持和維護的。有很多關於它的信息,好的開始是https://www.denx.de/wiki/U-Boot。在[email protected],也有一個郵件列表,你可以通過填寫和提交https://lists.denx.de/listinfo/u-boot 提供的表格來訂閱。

構建U-Boot

從獲取源代碼開始。和大多數項目一樣,推薦的方法是克隆.git檔案,並查看你打算使用的標簽--在這種情況下,它是編寫時的版本:

$ git clone git://git.denx.de/u-boot.git
$ cd u-boot
$ git checkout v2021.01

另外,你也可以從ftp://ftp.denx.de/pub/u-boot,得到一個tarball。
在configs/目錄下有超過1000個常見開發板和設備的配置文件。在大多數情況下,你可以根據文件名來猜測使用哪個文件,但你可以通過查看board/目錄下每個板子的README文件來獲得更詳細的信息,或者你可以在適當的網路教程或論壇中找到信息。
以BeagleBone Black為例,我們會發現可能有一個名為configs/am335x_evm_defconfig的配置文件,以及文本 本板產生的二進位文件支持 ... Beaglebone Black在板子的README文件中的am335x晶元,board/ti/am335x/README。有了這些知識,為BeagleBone Black構建U-Boot就很簡單了。你需要通過設置CROSS_COMPILE make變數來通知U-Boot你的交叉編譯器的首碼,然後用make [board]_defconfig類型的命令來選擇配置文件。因此,要使用我們在第2章 "學習工具鏈 "中創建的Crosstool-NG編譯器來構建U-Boot,你應該輸入以下內容:

$ source ../MELP/Chapter02/set-path-arm-cortex_a8-linux-gnueabihf
$ make am335x_evm_defconfig
$ make
``
編譯的結果如下:
- u-boot: ELF對象格式的U-Boot,適合與調試器一起使用
- u-boot.map: 符號表
- u-boot.bin: 原始二進位格式的U-Boot,適合在你的設備上運行
- u-boot.img: 這是添加了U-Boot頭的u-boot.bin,適合上傳到正在運行的U-Boot副本中。
- u-boot.srec: 摩托羅拉S-記錄(SRECORD或SRE)格式的U-Boot,適合通過串列連接傳輸。

BeagleBone Black還需要一個二級程式載入器(SPL secondary program loader),如前所述。這也是同時建立的,被命名為MLO:
```sh
$ ls -l MLO u-boot*
-rw-rw-r- 1 frank frank 108260 Feb 8 15:24 MLO
-rwxrwxr-x 1 frank frank 6028304 Feb 8 15:24 u-boot
-rw-rw-r- 1 frank frank 594076 Feb 8 15:24 u-boot.bin
-rw-rw-r- 1 frank frank 20189 Feb 8 15:23 u-boot.cfg
-rw-rw-r- 1 frank frank 10949 Feb 8 15:24 u-boot.cfg.configs
-rw-rw-r- 1 frank frank 54860 Feb 8 15:24 u-boot.dtb
-rw-rw-r- 1 frank frank 594076 Feb 8 15:24 u-boot-dtb.bin
-rw-rw-r- 1 frank frank 892064 Feb 8 15:24 u-boot-dtb.img
-rw-rw-r- 1 frank frank 892064 Feb 8 15:24 u-boot.img
-rw-rw-r- 1 frank frank 1722 Feb 8 15:24 u-boot.lds
-rw-rw-r- 1 frank frank 802250 Feb 8 15:24 u-boot.map
-rwxrwxr-x 1 frank frank 539216 Feb 8 15:24 u-boot-nodtb.bin
-rwxrwxr-x 1 frank frank 1617810 Feb 8 15:24 u-boot.srec
-rw-rw-r- 1 frank frank 211574 Feb 8 15:24 u-boot.sym

其他目標的程式也類似。

安裝U-boot

第一次在板子上安裝引導程式需要一些外部協助。如果板子有硬體調試介面,比如JTAG(聯合測試行動組 Joint Test Action Group),通常可以把U-Boot的拷貝直接載入到RAM中並使其運行。在這一點上,你可以使用U-Boot命令,使它自己複製到快閃記憶體中。這方面的細節與電路板有關,不在本書的討論範圍之內。
許多SoC設計都有一個內置的引導ROM,可以用來從各種外部來源讀取引導代碼,如SD卡、串列介面或USB大容量存儲。BeagleBone Black中的am335x晶元就是這種情況,這使得嘗試新軟體變得很容易。
你將需要SD卡讀卡器來把image寫入卡中。有兩種類型:插入USB埠的外部讀卡器,以及許多筆記本電腦上都有的內部SD讀卡器。當卡被插入讀卡器時,Linux會分配一個設備名稱。lsblk命令是一個有用的工具,可以找出哪個設備已經被分配。例如,當我把一張標稱8GB的microSD卡插入讀卡器時,我看到的就是這個:

$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 1 7.4G 0 disk
└─sda1 8:1 1 7.4G 0 part /media/frank/6662-6262
nvme0n1 259:0 0 465.8G 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot/efi
├─nvme0n1p2 259:2 0 16M 0 part
├─nvme0n1p3 259:3 0 232.9G 0 part
└─nvme0n1p4 259:4 0 232.4G 0 part /

在這種情況下,nvme0n1是我的512GB硬碟,sda是microSD卡。它有單一的分區,sda1,它被掛載為/media/frank/6662-6262目錄。

在這種情況下,microSD卡顯示為mmcblk0,分區為mmcblk0p1。註意,你使用的microSD卡的格式化方式可能與此不同,所以你可能會看到不同數量的分區和不同的掛載點。在格式化SD卡時,確定其設備名稱是非常重要的。你真的不想把你的硬碟誤認為是SD卡,而去格式化它。這種情況已經不止一次發生在我身上了。因此,我在本書的代碼檔案中提供了名為MELP/format-sdcard.sh的shell腳本,它有合理數量的檢查以防止你(和我)使用錯誤的設備名稱。參數是microSD卡的設備名,在第一個例子中是sdb,在第二個例子中是mmcblk0。下麵是它的一個使用例子:

$ MELP/format-sdcard.sh mmcblk0

該腳本創建了兩個分區:第一個是64 MiB,格式為FAT32,將包含引導程式;第二個是1 GiB,格式為ext4,你將在第5章建立根文件系統中使用它。當腳本應用於任何大於32GiB的驅動器時,它就會中止,所以如果你使用更大的microSD卡,要準備好修改它。

一旦你格式化了microSD卡,就把它從讀卡器中取出來,然後重新插入,這樣分區就會自動掛載。在當前版本的Ubuntu上,這兩個分區應該被安裝為/media/[user]/boot和/media/[user]/rootfs。現在,你可以把SPL和U-Boot複製到它,像這樣:

$ cp MLO u-boot.img /media/frank/boot

最後,卸載它:

$ sudo umount /media/frank/boot

現在,在BeagleBone板上沒有電源的情況下,將microSD卡插入讀卡器。插上串列電纜。在你的電腦上應該出現一個串口,即/dev/ttyUSB0。啟動一個合適的終端程式,如gtkterm、minicom或picocom,並以每秒115200比特(bps)的速度連接到該埠,沒有流量控制。gtkterm可能是最容易設置和使用的:

$ gtkterm -p /dev/ttyUSB0 -s 115200

如果你得到許可權錯誤,那麼你可能需要把自己加入撥出組,並重新啟動以使用這個埠。
按住BeagleBone Black上的啟動開關按鈕(離microSD插槽最近),使用外部5V電源連接器給電路板供電,大約5秒鐘後釋放按鈕。你應該在串列控制台看到一些輸出,然後是U-Boot提示:

U-Boot SPL 2021.01 (Feb 08 2021 - 15:23:22 -0800)
Trying to boot from MMC1
U-Boot 2021.01 (Feb 08 2021 - 15:23:22 -0800)
CPU : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM: 512 MiB
WDT: Started with servicing (60s timeout)
NAND: 0 MiB
MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... *** Warning - bad CRC, using default environment
<ethaddr> not set. Validating first E-fuse MAC
Net: eth2: ethernet@4a100000, eth3: usb_ether
Hit any key to stop autoboot: 0
=>

按鍵盤上的任意鍵,停止U-Boot在預設環境下的自動啟動。現在我們面前有一個U-Boot的提示,讓我們把U-Boot放在它的位置上。

使用U-Boot

在本節中,我將介紹一些常見的任務,你可以使用U-Boot來執行。
通常,U-Boot通過串口提供一個命令行介面。它提供為每個板子定製的命令提示。在這些例子中,我將使用=>。輸入help會列印出所有在這個版本的U-Boot中配置的命令;輸入help 會列印出關於某個特定命令的更多信息。
BeagleBone Black的預設命令解釋器是非常簡單的。按左鍵或右鍵不能進行命令行編輯;按Tab鍵沒有命令完成;按up鍵沒有命令歷史。按這些鍵中的任何一個都會打亂你目前正在嘗試輸入的命令,你將不得不輸入Ctrl + C,然後重新開始。你唯一可以安全使用的行編輯鍵是退格鍵。作為一個選項,你可以配置一個不同的命令外殼,叫做Hush,它有更複雜的互動式支持,包括命令行編輯。
預設的數字格式是十六進位。考慮以下命令作為例子:

=> nand read 82000000 400000 200000

這將從NAND快閃記憶體開始的偏移量0x400000讀取0x200000位元組到RAM地址0x82000000。

U-Boot廣泛地使用環境變數來存儲和傳遞函數之間的信息,甚至用來創建腳本。環境變數是簡單的name=value對,被存儲在記憶體的一個區域。變數的初始數量可以在電路板配置頭文件中編碼,像這樣:

#define CONFIG_EXTRA_ENV_SETTINGS
"myvar1=value1"
"myvar2=value2"
[…]

你可以使用setenv從U-Boot命令行創建和修改變數。例如,setenv foo bar用bar值創建foo變數。註意,在變數名稱和數值之間沒有=符號。你可以通過將一個變數設置為空字元串來刪除它,setenv foo。你可以用printenv把所有的變數列印到控制台,或者用printenv foo把單個變數列印出來。
如果U-Boot已經配置了存儲環境的空間,你可以使用saveenv命令來保存它。如果有原始的NAND或NOR快閃記憶體,那麼可以為這個目的保留擦除塊,通常另被用作冗餘拷貝以防止損壞。如果有eMMC或SD卡存儲,它可以被存儲在保留的扇區陣列中,或存儲在磁碟分區中名為uboot.env的文件中。其他選擇包括將其存儲在通過I2C或SPI介面連接的串列EEPROM或非易失性RAM中。

U-Boot沒有文件系統。相反,它用64位元組的頭來標記信息塊,這樣它就可以跟蹤內容。我們使用mkimage命令行工具為U-Boot準備文件,它與Ubuntu的u-boot-tools軟體包捆綁在一起。你也可以通過在U-Boot源代碼樹中運行make tools來獲得mkimage,然後以tools/mkimage的形式調用它。
下麵是對該命令用法的簡要總結:

$ mkimage
Error: Missing output filename
Usage: mkimage -l image
-l ==> list image header information
mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type'
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place)
mkimage [-D dtc_options] [-f fit-image.its|-f auto|-F] [-b <dtb> [-b <dtb>]] [-i <ramdisk.cpio.gz>] fit-image
<dtb> file is used with -f auto, it may occur multiple times.
-D => set all options for device tree compiler
-f => input filename for FIT source
-i => input filename for ramdisk file
Signing / verified boot options: [-E] [-B size] [-k keydir] [-K dtb] [ -c <comment>] [-p addr] [-r] [-N engine]
-E => place data outside of the FIT structure
-B => align size in hex for FIT structure and header
-k => set directory containing private keys
-K => write public keys to this .dtb file
-c => add comment in signature node
-F => re-sign existing FIT image
-p => place external data at a static position
-r => mark keys used as 'required' in dtb
-N => openssl engine to use for signing
mkimage -V ==> print version information and exit
Use '-T list' to see a list of available image types

例如,要為Arm處理器準備一個內核鏡像,你可以使用
下麵的命令:

$ mkimage -A arm -O linux -T kernel -C gzip -a 0x80008000 \
-e 0x80008000
-n 'Linux' -d zImage uImage

在這個例子中,架構是arm,操作系統是linux,圖像類型是kernel。此外,壓縮方案是gzip,載入地址是0x80008000,入口點與載入地址相同。最後,圖像
名稱是Linux,圖像數據文件被命名為zImage,正在生成的圖像被命名為uImage。

通常情況下,你會從可移動的存儲器中載入鏡像,比如SD卡或網路。SD卡在U-Boot中是由MMC驅動處理的。典型的順序是用來載入圖像到記憶體中的,如下:

=> mmc rescan
=> fatload mmc 0:1 82000000 uimage
reading uimage
4605000 bytes read in 254 ms (17.3 MiB/s)
=> iminfo 82000000
## Checking Image at 82000000 ...
Legacy image found
Image Name: Linux-3.18.0
Created: 2014-12-23 21:08:07 UTC
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4604936 Bytes = 4.4 MiB
Load Address: 80008000
Entry Point: 80008000
Verifying Checksum ... OK

mmc rescan命令重新初始化了MMC驅動,也許是為了檢測最近是否有SD卡被插入。接下來,fatload被用來從SD卡上FAT格式的分區中讀取文件。其格式如下:

fatload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]

如果是mmc,就像我們的例子一樣,dev:part是MMC介面的設備號,從0開始計算,分區號從1開始計算。因此,<0:1>是第一個設備上的第一個分區,也就是microSD卡的mmc 0(板載eMMC是mmc 1)。記憶體位置,0x82000000,被選擇在RAM的一個區域,這個區域目前還沒有被使用。如果我們打算啟動這個內核,我們必須確保當內核映像被解壓縮並位於運行時位置0x80008000時,RAM的這個區域不會被覆蓋。
要通過網路載入映像文件,你必須使用瑣碎的文件傳輸協議(TFTP)。這需要你在你的開發系統上安裝一個TFTP守護程式,tftpd,並開始運行它。你還必須配置你的PC和目標板之間的任何防火牆,以允許UDP 69埠的TFTP協議通過。TFTP的預設配置只允許訪問/var/lib/tftpboot目錄。下一步是將你想傳輸到目標板的文件複製到該目錄。然後,假設你使用的是一對靜態IP地址,這樣就不需要進一步的網路管理,載入一組內核鏡像文件的命令序列應該是這樣的:

=> setenv ipaddr 192.168.159.42
=> setenv serverip 192.168.159.99
=> tftp 82000000 uImage
link up on port 0, speed 100, full duplex
Using cpsw device
TFTP from server 192.168.159.99; our IP address is 192.168.159.42
Filename 'uImage'.
Load address: 0x82000000
Loading:
########################################################################################################################################################################################################################################################################################################################
3 MiB/s
done
Bytes transferred = 4605000 (464448 hex)

最後,讓我們看看如何將圖像編程到NAND快閃記憶體中並讀回,這由nand命令來處理。這個例子通過TFTP載入一個內核鏡像,並將其編程到快閃記憶體中:

=> tftpboot 82000000 uimage
=> nandecc hw
=> nand erase 280000 400000
NAND erase: device 0 offset 0x280000, size 0x400000
Erasing at 0x660000 -- 100% complete.
OK
=> nand write 82000000 280000 400000
NAND write: device 0 offset 0x280000, size 0x400000
4194304 bytes written: OK

現在,你可以使用nand read命令從快閃記憶體中載入內核:

=> nand read 82000000 280000 400000

一旦內核被載入到RAM中,我們就可以啟動它。

引導Linux

bootm命令啟動一個正在運行的內核鏡像。其語法如下:

bootm [address of kernel] [address of ramdisk] [address of dtb].

內核映像的地址是必須的,但是如果內核配置不需要它們,ramdisk和dtb的地址可以省略。如果有dtb但沒有initramfs,第二個地址可以用破折號(-)代替。這看起來就像這樣:

=> bootm 82000000 – 83000000

很明顯,每次開機時輸入一長串命令來啟動你的板子是不能接受的。讓我們來看看如何使啟動過程自動化。

U-Boot將一連串的命令儲存在環境變數中。如果名為bootcmd的特殊變數包含一個腳本,它就會在開機時經過bootdelay秒的延遲而運行。如果你在串列控制台觀察,你會看到延遲倒計時為零。你可以在這期間按任何一個鍵來終止倒計時,併進入與U-Boot的互動式會話。
創建腳本的方法很簡單,雖然不容易讀懂。你只需將命令用分號隔開,分號前面必須有一個/轉義字元。因此,舉例來說,要從快閃記憶體的偏移量中載入一個內核鏡像並啟動它,你可以使用下麵的命令:

setenv bootcmd nand read 82000000 400000 200000\;bootm 82000000

我們現在知道如何使用U-Boot在BeagleBone Black上啟動內核了。但是我們如何將U-Boot移植到一個沒有BSP的新板子上呢?我們將在本章的其餘部分介紹這個問題。

移植U-Boot到新板

讓我們假設你的硬體部門已經創建了一個新的板子,叫做Nova,是基於BeagleBone Black的,你需要把U-Boot移植到它上面。你將需要瞭解U-Boot代碼的佈局以及板子的配置機制是如何工作的。在本節中,我將向你展示如何創建現有板子的變體--BeagleBone Black--你可以把它作為進一步定製的基礎。有相當多的文件需要被修改。我把它們放在代碼檔案中的MELP/Chapter03/0001-BSP-for-Nova.patch中。你可以簡單地把這個補丁應用到2021.01版的U-Boot的一個乾凈的拷貝上,像這樣:

$ cd u-boot
$ patch -p1 < MELP/Chapter03/0001-BSP-for-Nova.patch

如果你想使用不同版本的U-Boot,你必須對補丁做一些修改,才能幹凈地應用。
本節的其餘部分將描述如何創建該補丁。如果你想按部就班,你將需要一個沒有Nova BSP補丁的U-Boot 2021.01的乾凈拷貝。我們要處理的主要目錄如下:

  • arch

包含arm、mips和powerpc目錄中每個支持的架構的特定代碼。在每個架構中,每個家族成員都有一個子目錄;例如,在arch/arm/cpu/中,有架構變體的目錄,包括amt926ejs、armv7和armv8。

  • 板卡

包含特定於某個板卡的代碼。如果有幾個來自同一供應商的板子,它們可以被收集到一個子目錄里。因此,對BeagleBone所基於的am335x EVM板的支持就在board/ti/am335x中。

  • common

包含核心功能,包括命令外殼和可以從中調用的命令,每個都在一個名為cmd_[命令名稱].c的文件中。

  • doc

包含了幾個README文件,描述了U-Boot的各個方面。如果你想知道如何進行你的U-Boot移植,這是一個好的開始。

  • include

除了許多共用的頭文件之外,這裡還包含了非常重要的include/configs/子目錄,在這裡你可以找到大部分的板子配置設置。
Kconfig從Kconfig文件中提取配置信息並將全部系統配置存儲在一個名為.config的文件中的方式將在第四章 "配置和構建內核 "中詳細描述。每塊板都有一個預設的配置,存儲在configs/[板名]_defconfig中。對於Nova板,我們可以先為EVM製作一個配置的副本:

$ cp configs/am335x_evm_defconfig configs/nova_defconfig

現在,編輯configs/nova_defconfig併在CONFIG_AM33XX=y之後插入CONFIG_TARGET_NOVA=y,如圖所示:


CONFIG_ARM=y
CONFIG_ARCH_CPU_INIT=y
CONFIG_ARCH_OMAP2PLUS=y
CONFIG_TI_COMMON_CMD_OPTIONS=y
CONFIG_AM33XX=y
CONFIG_TARGET_NOVA=y
CONFIG_SPL=y
[…]

註意 CONFIG_ARM=y 會導致 arch/arm/Kconfig 的內容被包含在內,而 CONFIG_AM33XX=y 會導致 arch/arm/mach-omap2/am33xx/Kconfig 被包含在內。
接下來,將CONFIG_SYS_CUSTOM_LDSCRIPT=y和CONFIG_SYS_LDSCRIPT=="board/ti/nova/u-boot.lds "插入到CONFIG_DISTRO_DEFAULTS=y之後的同一個文件,如圖所示:

[...]
CONFIG_SPL=y
CONFIG_DEFAULT_DEVICE_TREE="am335x-evm"
CONFIG_DISTRO_DEFAULTS=y
CONFIG_SYS_CUSTOM_LDSCRIPT=y
CONFIG_SYS_LDSCRIPT="board/ti/nova/u-boot.lds"
CONFIG_SPL_LOAD_FIT=y
[...]

現在我們已經完成了對configs/nova_defconfig的修改。

每個板子都有一個名為board/[板名]或board/[供應商]/[板名]的子目錄,它應該包含以下內容:

  • Kconfig: 包含電路板的配置選項。
  • MAINTAINERS: 包含板子目前是否被維護的記錄,如果是的話,由誰維護。
  • Makefile: 用來構建板子的特定代碼。
  • README: 包含任何關於U-Boot這個埠的有用信息;例如,包括哪些硬體變體。
    此外,還可能有針對板卡功能的源文件。
    我們的Nova板是基於BeagleBone的,而BeagleBone又是基於TI am335x EVM的。因此,我們應該複製am335x板的文件:
$ mkdir board/ti/nova
$ cp -a board/ti/am335x/* board/ti/nova

接下來,編輯board/ti/nova/Kconfig,將SYS_BOARD設置為 "nova",這樣它就會在board/ti/nova中建立文件。然後,將SYS_CONFIG_NAME也設為 "nova",這樣它就會使用include/configs/nova.h作為配置文件:

if TARGET_NOVA
config SYS_BOARD
        default "nova"
config SYS_VENDOR
        default "ti"
config SYS_SOC
        default "am33xx"
config SYS_CONFIG_NAME
        default "nova"
[…]

這裡還有一個文件,我們需要修改。鏈接器腳本被放置在board/ti/nova/u-boot.lds,包含一個硬編碼的引用,指board/ti/am335x/built-in.o:

{
*(.__image_copy_start)
*(.vectors)
CPUDIR/start.o (.text*)
board/ti/nova/built-in.o (.text*)
}

現在,我們需要將Nova的Kconfig文件鏈接到Kconfig文件鏈中。首先,編輯arch/arm/Kconfig,在source "board/tcl/sl50/Kconfig "之後插入source "board/ti/nova/Kconfig",如下圖所示:

{
    *(.__image_copy_start)
    *(.vectors)
    CPUDIR/start.o (.text*)
    board/ti/nova/built-in.o (.text*)
}

然後,編輯arch/arm/mach-omap2/am33xx/Kconfig,在TARGET_AM335X_EVM後面緊接著添加一個TARGET_NOVA的配置選項,如圖所示:

[…]
source "board/st/stv0991/Kconfig"
source "board/tcl/sl50/Kconfig"
source "board/ti/nova/Kconfig"
source "board/toradex/colibri_pxa270/Kconfig"
source "board/variscite/dart_6ul/Kconfig"
[…]

然後,編輯 arch/arm/mach-omap2/am33xx/Kconfig,在 TARGET_AM335X_EVM 之後添加 TARGET_NOVA 的配置選項,如圖所示:

[…]
config TARGET_NOVA
        bool "Support the Nova! board"
        select DM
        select DM_GPIO
        select DM_SERIAL
        select TI_I2C_BOARD_DETECT
        imply CMD_DM
        imply SPL_DM
        imply SPL_DM_SEQ_ALIAS
        imply SPL_ENV_SUPPORT
        imply SPL_FS_EXT4
        imply SPL_FS_FAT
        imply SPL_GPIO_SUPPORT
        imply SPL_I2C_SUPPORT
        imply SPL_LIBCOMMON_SUPPORT
        imply SPL_LIBDISK_SUPPORT
        imply SPL_LIBGENERIC_SUPPORT
        imply SPL_MMC_SUPPORT
        imply SPL_NAND_SUPPORT
        imply SPL_OF_LIBFDT
        imply SPL_POWER_SUPPORT
        imply SPL_SEPARATE_BSS
        imply SPL_SERIAL_SUPPORT
        imply SPL_SYS_MALLOC_SIMPLE
        imply SPL_WATCHDOG_SUPPORT
        imply SPL_YMODEM_SUPPORT
        help
          The Nova target board
[…]

所有 imply SPL_ 行都是必要的,這樣 U-Boot 才能順利編譯,不會出錯。
現在,我們已經複製並修改了 Nova 板的特定文件,接下來就看頭文件了。

每塊板都有一個頭文件,位於 include/configs/ 中,其中包含大部分配置信息。該文件以電路板 Kconfig 文件中的 SYS_CONFIG_NAME 標識命名。該文件的格式在 U-Boot 源代碼樹頂層的 README 文件中有詳細描述。對於我們的 Nova 板,只需將 include/configs/am335x_evm.h 複製到 include/configs/nova.h,然後做一些修改,如圖所示:

[…]
#ifndef __CONFIG_NOVA_H
#define __CONFIG_NOVA_H
include <configs/ti_am335x_common.h>
#include <linux/sizes.h>
#undef CONFIG_SYS_PROMPT
#define CONFIG_SYS_PROMPT "nova!> "
#ifndef CONFIG_SPL_BUILD
# define CONFIG_TIMESTAMP
#endif
[…]
#endif  /* ! __CONFIG_NOVA_H */

除了將 __CONFIG_AM335X_EVM_H 替換為 __CONFIG_NOVA_H,唯一需要做的改動是設置一個新的命令提示符,以便在運行時識別這個引導載入器。
這樣我們就可以在運行時識別這個引導載入器。
完全修改了源代碼樹之後,我們現在就可以為定製板構建 U-Boot 了。

參考資料

構建和測試

要為 Nova 板構建 U-Boot,請選擇剛剛創建的配置:

$ source ../MELP/Chapter02/set-path-arm-cortex_a8-linux-gnueabihf
$ make distclean
$ make nova_defconfig
$ make

將 MLO 和 u-boot.img 複製到之前創建的 microSD 卡啟動分區,然後啟動主板。你應該會看到這樣的輸出(註意命令提示符):

U-Boot SPL 2021.01-dirty (Feb 08 2021 - 21:30:41 -0800)
Trying to boot from MMC1
U-Boot 2021.01-dirty (Feb 08 2021 - 21:30:41 -0800)
CPU  : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM:  512 MiB
WDT:   Started with servicing (60s timeout)
NAND:  0 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... *** Warning - bad CRC, using default environment
<ethaddr> not set. Validating first E-fuse MAC
Net:   eth2: ethernet@4a100000, eth3: usb_ether
Hit any key to stop autoboot:  0
nova!>

您可以使用 git format-patch 命令為所有這些更改創建補丁:

$ git add .
$ git commit -m "BSP for Nova"
[detached HEAD 093ec472f6] BSP for Nova
12 files changed, 2379 insertions(+)
create mode 100644 board/ti/nova/Kconfig
create mode 100644 board/ti/nova/MAINTAINERS
create mode 100644 board/ti/nova/Makefile
create mode 100644 board/ti/nova/README
create mode 100644 board/ti/nova/board.c
create mode 100644 board/ti/nova/board.h
create mode 100644 board/ti/nova/mux.c
create mode 100644 board/ti/nova/u-boot.lds
create mode 100644 configs/nova_defconfig
create mode 100644 include/configs/nova.h
$ git format-patch -1
0001-BSP-for-Nova.patch

生成此補丁後,U-Boot 作為 TPL 的覆蓋範圍就結束了。U-Boot 也可以配置為完全繞過啟動過程中的 TPL 階段。接下來,讓我們來看看啟動 Linux 的另一種方法。

Falcon模式

我們習慣於認為,啟動現代嵌入式處理器需要 CPU 引導 ROM 載入 SPL,SPL 載入 u-boot.bin,u-boot.bin 再載入 Linux 內核。你可能想知道是否有辦法減少這些步驟,從而簡化並加快啟動過程。答案就是 U-Boot Falcon 模式。這個想法很簡單:讓 SPL 直接載入內核映像,省去 u-boot.bin。沒有用戶交互,也沒有腳本。它只是將內核從快閃記憶體或 eMMC 中的已知位置載入到記憶體中,然後將預先準備好的參數塊傳遞給它,並啟動它運行。配置獵鷹模式的細節超出了本書的範圍。如果你想瞭解更多信息,請查看 doc/README.falcon。

重要提示
獵鷹模式是以游隼命名的,游隼是所有鳥類中速度最快的一種,在俯衝中能達到每小時 200 英里以上的速度。

小結

每個系統都需要一個引導載入器來啟動硬體和載入內核。U-Boot 支持各種有用的硬體,而且很容易移植到新設備上,因此受到許多開發者的青睞。在本章中,我們學習瞭如何通過串列控制台的命令行互動式檢查和驅動 U-Boot。這些命令行練習包括使用 TFTP 通過網路載入內核,以實現快速迭代。最後,我們學習瞭如何通過為 Nova 板生成補丁,將 U-Boot 移植到新設備上。
在過去幾年中,由於嵌入式硬體的複雜性和多樣性不斷增加,引入了設備樹作為描述硬體的方法。設備樹只是系統的文字表述,它被編譯成設備樹二進位文件(DTB),併在載入時傳遞給內核。內核負責解釋設備樹,併為其找到的設備載入和初始化驅動程式。
在使用中,U-Boot 非常靈活,可以從大容量存儲器、快閃記憶體或網路載入映像,然後啟動。在介紹了啟動 Linux 的一些複雜之處後,我們將在下一章介紹該過程的下一階段,即嵌入式項目的第三個元素--內核--的作用。

釘釘或微信號: pythontesting 微信公眾號:pythontesting
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 如果分析的數據與地域相關,那麼,把分析結果結合地圖一起展示的話,會讓可視化的效果得到極大的提升。 比如,分析各省GDP數據,人口數據,用柱狀圖,餅圖之類的雖然都可以展示分析結果,不過,如果能在全國的地圖上展示各省的分析結果的話,會讓人留下更加深刻的印象。 將數據的分析結果展示在地圖上,難點在於: 如 ...
  • AbstractQueuedSynchronizer抽象同步隊列簡稱AQS,它是實現同步器的基礎組件,併發包中鎖的底層就是使用AQS實現的。大多數開發者可能永遠不會直接使用AQS,但是知道其原理對於架構設計還是很有幫助的。 ...
  • 在開發Go應用程式時,處理配置是一個常見的需求。配置可能來自於配置文件、環境變數、命令行參數等等。Viper是一個強大的庫,可以幫助我們處理這些配置。 什麼是Viper? Viper是一個應用程式配置解決方案,用於Go應用程式。它支持JSON、TOML、YAML、HCL、envfile和Java p ...
  • 記憶體進程讀寫可以讓我們訪問其他進程的記憶體空間並讀取或修改其中的數據。這種技術通常用於各種調試工具、進程監控工具和反作弊系統等場景。在`Windows`系統中,記憶體進程讀寫可以通過一些`API`函數來實現,如`OpenProcess`、`ReadProcessMemory`和`WriteProcess... ...
  • 歡迎訪問我的GitHub 這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos 本篇概覽 本篇是《strimzi實戰》系列的第二篇,前文完成了介紹和準備工作,是時候體驗strimzi的核心功能了:發送和接受kafka消息,為了完成這個 ...
  • 文章目錄 生命周期 PreConfigureServices 添加依賴註入或者其它配置之前 ConfigureServices 添加依賴註入或者其它配置 PostConfigureServices 添加依賴註入或者其它配置之後 OnPreApplicationInitialization 初始化所有 ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式有記憶體泄露,跟著我的錯題集也沒找出是什麼原因,剛好手頭上有一個 7G+ 的 dump,讓我幫忙看下是怎麼回事,既然找到我了那就給他看看吧,不過他的微信頭像有點像 二道販子,不管到我這裡是不是 三道,該分析的還得要分析呀。😄😄😄 二:Wi ...
  • 引言 在C#的併發編程中,Channel是一種非常強大的數據結構,用於在生產者和消費者之間進行通信。本文將首先通過一個實際的使用案例,介紹如何在C#中使用Channel,然後深入到Channel的源碼中,解析其內部的實現機制。 使用案例一:文件遍歷和過濾 在我們的使用案例中,我們需要遍歷一個文件夾及 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...