uboot命令

  • 2022 年 7 月 12 日
  • 筆記

目錄

概念

機器的啟動過程

  1. 計算機系統就是有 CPU 來做核心進行運行的系統。典型的計算機系統有:PC 機(台式機+筆記本)、嵌入式設備(手機、 平板電腦、 遊戲機) 、 單片機(家用電器像電飯鍋、空調)。計算機系統的組成部件非常多,不同的計算機系統組成部件也不同。 但是所有的計算機系統運行時需要的主要核心部件都是 3 個東西: CPU + 外部存儲器( Flash/硬盤) + 內部存儲器( DDR SDRAM/SDRAM/SRAM) 。

  2. 典型的 PC 機的部署: BIOS 程序部署在 PC 機主板上(隨主板出廠時已經預製了) , 操作系統部署在硬盤上, 內存在掉電時無作用, CPU在掉電時不工作。

    啟動過程: PC 上電後先執行 BIOS 程序(實際上 PC 的 BIOS 就是 NorFlash) , BIOS 程序負責初始化 DDR 內存, 負責初始化硬盤, 然後從硬盤上將 OS 鏡像讀取到 DDR 中, 然後跳轉到 DDR中去執行 OS 直到啟動(OS 啟動後 BIOS 就無用了)。

  3. 嵌入式系統的部署和啟動都是參考 PC 機的。 只是設備上有一些差別。 典型嵌入式系統的部署: uboot 程序部署在 Flash(能作為啟動設備的 Flash) 上、 OS 部署在 Flash(嵌入式系統中用 Flash代替了硬盤) 上、 內存在掉電時無作用, CPU 在掉電時不工作。

    啟動過程:嵌入式系統上電後先執行 uboot、 然後 uboot 負責初始化 DDR, 初始化 Flash, 然後將 OS 從 Flash 中讀取到 DDR 中, 然後啟動 OS(OS 啟動後 uboot 就無用了)

    總結: 嵌入式系統和 PC 機的啟動過程幾乎沒有兩樣, 只是 BIOS 成了 uboot, 硬盤成了 Flash。

  4. android 系統的啟動和 linux 系統(前面講的典型的嵌入式系統啟動) 幾乎一樣, 只是在內核啟動後加載根文件系統後不同了。 可以認為啟動分為 2 個階段:第一個階段是 uboot 到 OS 啟動; 第二個階段是 OS 啟動後到 rootfs 加載到命令行執行。

什麼是 uboot

  1. uboot 就是 universal bootloader(通用的啟動代碼) ,通用的意思就是在各種地方都可以用。所以說 uboot 具有可移植性。 uboot 具有可移植性並不是說 uboot 在哪個開發板都可以隨便用, 而是說 uboot 具有在源代碼級別的移植能力,可以針對多個開發板進行移植,移植後就可以在這個開發板上使用了。

  2. uboot 的本質是一個裸機程序,和我們裸機全集中寫的那些裸機程序 xx.bin 並沒有本質區別。如果非說要有區別,那就是:我們寫的大部分小於 16KB,而 uboot 一般 uboot 在 180k-400k 之間。uboot 本身是一個開源項目,由若干個.c 文件和.h 文件組成,配置編譯之後會生成一個 u-boot.bin,這就是 uboot 這個裸機程序的鏡像文件。然後這個鏡像文件被合理的燒錄到啟動介質中拿給 SoC 去啟動。也就是說 uboot 在沒有運行時表現為 uboot.bin,躺在啟動介質中。運行時會被加載到內存中然後一條指令一條指令的拿給 CPU 去運行。

  3. uboot 主要作用是用來啟動操作系統內核、負責部署整個計算機系統、操作 Flash 等板子上硬盤的驅動、提供一個命令行界面供人來操作。

  4. uboot 的入口就是開機自動啟動, uboot 的唯一出口就是啟動內核。 uboot 還可以執行很多別的任務(譬如燒錄系統),但是其他任務執行完後都可以回到 uboot 的命令行繼續執行 uboot 命令,而啟動內核命令一旦執行就回不來了。

uboot 必須解決哪些問題

  1. 自身可開機直接啟動: 一般的 SoC 都支持多種啟動方式,譬如 SD 卡啟動、NorFlash 啟動、NandFlash 啟動等,uboot 要能夠開機啟動,必須根據具體的 SoC 的啟動設計來設計 uboot。uboot 必須進行和硬件相對應的代碼級別的更改和移植,才能夠保證可以從相應的啟動介質啟動。uboot 中第一階段的 start.S 文件中具體處理了這一塊。

  2. 能夠引導操作系統內核啟動並給內核傳參: uboot 的終極目標就是啟動內核。

  3. 能提供系統部署功能: uboot 必須能夠被人藉助而完成整個系統(包括 uboot、 kernel、 rootfs 等的鏡像) 在 Flash 上的燒錄下載工作。 例如利用 uboot 中的 fastboot 功能將各種鏡像燒錄到 iNand中, 然後從 iNand 啟動。

  4. 能進行 soc 級和板級硬件管理:uboot 中實現了一部分硬件的控制能力(uboot 中初始化了一部分硬件) ,因為 uboot 為了完成一些任務必須讓這些硬件工作。譬如 uboot 要實現刷機必須能驅動 iNand,譬如 uboot 要在刷機時 LCD 上顯示進度條就必須能驅動 LCD,譬如 uboot 能夠通過串口提供操作界面就必須驅動串口。譬如 uboot 要實現網絡功能就必須驅動網卡芯片。SoC 級(譬如串口)就是 SoC 內部外設,板級就是 SoC 外面開發板上面的硬件(譬如網卡、 iNand)

uboot 命令和環境變量

  1. uboot 啟動後大部分時間和工作都是在 shell 下完成的(譬如 uboot 要部署系統要在 shell 下輸命令、 要設置環境變量也得在命令行下,要啟動內核也要在命令行底下敲命令)

  2. 命令就是 uboot 的 shell 中可以識別的各種命令。 uboot 中有幾十個命令。不同 uboot 的命令不一定相同。我們還可以自己給 uboot 添加命令。

  3. uboot 的環境變量和操作系統的環境變量工作原理和方式幾乎完全相同。uboot 在設計時藉助了操作系統的設計理念。環境變量可以被認為是系統的全局變量,環境變量名都是系統內置的(認識就認識, 不認識就不認識,這部分是系統自帶的默認的環境變量,譬如 PATH;但是也有一部分環境變量是自己添加的,自己添加的系統就不認識但是我們自己認識)。系統或者我們自己的程序在運行時可以通過讀取環境變量來指導程序的運行。這樣設計的好處就是靈活,譬如我們要讓一個程序更改運行方法,不用去重新修改程序代碼再重新編譯運行,而只要修改相應的環境變量就可以了。環境變量就是運行時的配置屬性。

  4. 環境變量就好像程序的全局變量一樣。程序中任何地方都可以根據需要去調用或者更改環境變量(一般都是調用 ,環境變量和全局變量不同之處在於:全局變量的生命周期是在程序的一次運行當中,開始運行時誕生程序結束時死亡,下次運行程序時從頭開始;但是環境變量被存儲在Flash 的另一塊專門區域(Flash 上有一個環境變量分區),一旦我們在程序中保存了該環境變量,那麼下次開機時該環境變量的值將維持上一次更改保存後的值。

Uboot 重定位

  1. Uboot 源碼中一個比較重要的概念是「重定向”(relocate),簡單來說就是將 u-boot 的運行環境轉移到 SDRAM 中去。

  2. 通常來說,當 Uboot 運行於 DDR 時,無需重定向就可以運行起來。當 U-Boot 在 NorFlash、MMC、SPI Flash 等作為啟動設備的存儲介質中時,SoC 內部的 RAM 空間通常無法滿足U-Boot的需求,此時,需要 Uboot 自身通過重定向功能實現程序的搬移。

uboot工作流程

①尋找程序入口
②第一階段程序(BL1)分析
③第二階段程序(BL2)分析

以2440為例子作為說明。

  1. 尋找程序入口:可通過查看鏈接文件得知。對於三星的 smdk2440 ,從 u-boot.lds 文件中可以看到,start.o 位於整個代碼的最前端,並且 ENTRY(_start) 說明程序入口為 _start,而 start.o 對應的程序為 start.S,因此 start.S 中的 _start 為 uboot 程序的入口

  2. BL1 代碼分析(只分析從 nand flash 啟動時代碼做的工作)
    (1)設置中斷向量表
    (2)設置處理器為 svc 模式
    (3)刷新 I/D cache
    (4)關閉 mmu 和 cache
    (5)關閉看門狗
    (6)關閉所有中斷
    (7)初始化系統時鐘
    (8)初始化串口
    (9)簡單初始化 nand flash
    (10)進行內存初始化
    (11)複製 nand flash 中的 bl 到內存
    (12)設置堆棧
    (13)清除 bss 段
    (14)跳轉到 start_armboot(start_armboot 為 BL2 的程序入口)

  3. BL2
    (1)初始化串口
    (2)LCD 初始化
    (3)初始化網卡
    (4)初始化 LED
    (5)執行用戶輸入的命令(死循環)

Uboot 命令

信息查詢命令

bdinfo

功能:直接輸入 bdinfo 查看板子信息:

arch_number = 0x00000000 //架構編號
boot_params = 0x80000100 //啟動參數保存起始地址
DRAM bank = 0x00000000
-> start = 0x80000000 //DRAM bank 的起始地址-> size = 0x20000000 //DRAM bank 的長度
eth0name = FEC1 //網卡名字
ethaddr = 00:04:9f:04:d2:35
current eth = FEC1 //當前使用的網卡
ip_addr = 192.168.1.20
baudrate = 115200 bps //串口波特率
......

printenv

功能:輸出環境變量信息
說明: uboot 按下 TAB 鍵會自動補全命令, 也可只輸入 pri 或者 print。

version

功能:查看 uboot 的版本號:

U-Boot 2016.03-gd9420c3 (Nov 01 2019 - 12:03:59 +0800) 	   # uboot 版本號為 2016.03
arm-poky-linux-gnueabi-gcc (GCC) 5.3.0 			   # 編譯器為 arm-linux-gnueabi-gcc
GNU ld (GNU Binutils) 2.26.0.20160214

環境變量操作命令

setenv /set

功能:命令 setenv 用於設置或者修改環境變量的值。
說明:有時候我們修改的環境變量值可能會有空格,比如 bootcmd、 bootargs 等,這個時候環境變量值就得用單引號括起來,比如下面修改環境變量 bootcmd 的值:

setenv bootcmd 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv author zuozhongkai 					# 新建環境變量
setenv author 								# 刪除環境變量:參數為空即可

saveenv /save

功能:保存環境變量的更改。
說明:命令不帶參數,直接執行,作用是將內存中的環境變量的值同步保存到 Flash 中環境變量的分區。 如果只 set 不 save, 那 set 後就只是當前本次運行的 uboot 有效。

內存操作命令

內存操作命令就是用於直接對 DRAM 進行讀寫操作。

md

功能: md 就是 memory display, 用於顯示內存值。使用方法如下:

格式:md[.b, .w, .l] address [# of objects]
示例:md.b 80000000 14 				# 查看以 0X80000000 開始的 20 個位元組的內存值
  • [.b .w .l] 對應 byte、word 和 long; [# of objects] 表示要查看的數據長度。
  • uboot 都是 16 進制, 所以可以不用寫「0x」 前綴。

nm 和 mm

功能: 都是用於修改指定地址的內存值

格式:nm [.b, .w, .l] address

輸入命令後,在彈出的命令行中輸入新值,可重複修改,按 q 退出。 nm 只能修改指定一個地址的值,而 mm(即 memory modify) 修改後會自動加地址。

mw

功能: mw 就是 memory write, 將內容填充到內存中

格式:mw [.b, .w, .l] address value [count]
示例:mw.l 80000000 0A0A0A0A 10					#給指定個每個地址都填充一樣的數。

cp

功能: 用於將 DRAM 中的數據從一段內存拷貝到另一段內存中.

格式:cp [.b, .w, .l] source target count
示例:cp.l 80000000 80000100 10

source 為源地址, target 為目的地址, count 為拷貝的長度。

cmp

功能: 比較命令, 用於比較兩段內存的數據是否相等。

格式:cmp [.b, .w, .l] addr1 addr2 count
示例:cmp.l 80000000 80000100 10

addr1 為第一段內存首地址, addr2 為第二段內存首地址, count 為要比較的長度。

網絡操作命令

相關環境變量

  1. ipaddr 開發板的本地 IP 地址
  2. ethaddr 是開發板的本地網卡的 MAC 地址。
  3. serverip 是開發板通過 tftp 指令去 tftp 服務器下載東西時, tftp 服務器的 IP 地址。
  4. netmask 子網掩碼
  5. gatewayip 網關地址

dhcp

功能: 用於從路由器獲取 IP 地址, 前提得開發板連接到路由器上的, 如果開發板是和電腦直連的, 那麼 dhcp 命令就會失效。

nfs

功能: 在計算機之間通過網絡來分享資源。

格式:nfs [loadAddress] [[hostIPaddr:]bootfilename]
示例:nfs 80800000 192.168.1.141:/home/gec/linux/nfs/zImage

loadAddress 是要保存的 DRAM 地址, [[hostIPaddr:]bootfilename]是要下載的文件地址。

tftp

功能: 同nfs 命令一樣, 都是用於通過網絡下載東西到 DRAM 中

示例:tftp 80800000 zImage

Ubuntu 主機作為 TFTP 服務器。 需要搭建 TFTP 服務器和安裝 tftp-hpa 和 tftpd-hpa 服務。

Flash 操作命令

uboot 支持 EMMC 和 SD 卡, 因此也要提供 EMMC 和 SD 卡的操作命令。 一般認為 EMMC 和 SD 卡是同一個東西, uboot 中常用於操作 EMMC 設備的命令為「mmc」。

開發板如果用 SD 卡/EMMC/iNand 等作為 Flash,則在 uboot 中操作 flash 的指令為 movi(或mmc)。

movi

movi 指令是一個命令集,有很多子命令,具體用法可以 help movi 查看。

movi read {u-boot | kernel} {addr} 		# 讀取 iNand 到 DDR 上
movi write								# 將 DDR 中的內容寫入 iNand 中

示例:

movi read u-boot 0x30000000				

把iNand中的 u-boot 分區讀出到 DDR 的 0x30000000 起始的位置處。uboot 的命令行中所有數字都被默認當作十六進制處理,這裡的 0x30000000 也可以直接寫作30000000。

mmc info / mmcinfo

功能: 用於輸出當前選中的 mmc info 設備的信息:

Device: FSL_SDHC
Manufacturer ID: 15
OEM: 100
Name: 8GTF4
Tran Speed: 52000000
Rd Block Len: 512
MMC version 4.0 				  # MMC 版本為 4.0
High Capacity: Yes
Capacity: 7.3 GiB 				  # 容量為 7.3Gib
Bus Width: 8-bit 				  # 8 位寬的總線
Erase Group Size: 512 KiB

mmc list

功能: 用於來查看當前開發板一共有幾個 MMC 設備

FSL_SDHC: 0 						# FSL_SDHC:0 是 SD 卡
FSL_SDHC: 1 (eMMC)

mmc dev

功能: 切換當前 MMC 設備

格式:mmc dev [dev] [part]
示例:mmc dev 0 					   # 切換到 SD 卡, 0 為 SD 卡, 1 為 eMMC
	 mmc dev 1 2 					 # 切換到 EMMC 的分區 2

mmc part

功能: 查看當前 MMC 設備的分區:

$ mmc part 
Partition Map for MMC device 1 -- Partition Type: DOS
Part Start Sector Num Sectors UUID 			Type
1 		2048 		65536 	  e5788bc5-01 	0c Boot
2 		67584 		15202304  e5788bc5-02 	83

說明: 可以看出, 此時 EMMC 有兩個分區, 扇區 2048~65536 為第一個分區, 扇區67584~15202304 為第二個分區。 第 0 個分區沒有格式化所以不顯示。

mmc read

功能: 用於讀取 mmc 設備的數據。

格式:mmc read addr blk cnt
示例:mmc read 80800000 600 10 		# 讀取數據
  • addr 是數據要讀取到 DRAM 中的地址,blk 是要讀取的塊起始地址(十六進制),一個塊是512 位元組,這裡的塊和扇區是一個意思,在 MMC 設備中我們通常說扇區, cnt 是要讀取的塊數量(十六進制)。
  • 示例中從 EMMC 的第 1536(0x600)個塊開始,讀取 16(0x10)個塊的數據到 DRAM 的 0X80800000 地址處。

mmc write

功能: 將數據寫到 MMC 設備裏面。

格式:mmc write addr blk cnt

addr 是數據在 DRAM 中的起始地址, blk 是要寫入 MMC 的塊起始地址, cnt 是要寫入的塊大小, 一個塊為 512 位元組。 我們可以使用命令「mmc write」來升級 uboot。 注意千萬不要寫 SD卡或者 EMMC 的前兩個塊(扇區), 裏面保存着分區表。

BOOT 操作指令

bootz

功能: bootz 命令用於啟動 zImage 鏡像文件和設備樹文件。

格式:bootz [addr [initrd[:size]] [fdt]]
示例:tftp 80800000 zImage 						# Linux 鏡像
	 tftp 83000000 imx6ull-alientek-emmc.dtb 	  # 設備樹
	 bootz 80800000 – 83000000 				  	  # 啟動 LInux 系統

addr 是 Linux 鏡像文件在 DRAM 中的位置, initrd 是 initrd 文件在 DRAM 中的地址,如果不使用 initrd 的話使用 「-」 代替即可, fdt 就是設備樹文件在 DRAM 中的地址。

bootm

功能: bootz 功能類似, 用於啟動 uImage 鏡像文件和設備樹文件。

格式:bootm addr 							# 啟動 uImage 鏡像文件,不使用設備樹
	 bootm [addr [initrd[:size]] [fdt]]   # 使用設備樹

addr 是 uImage 鏡像在 DRAM 中的首地址。 initrd 是 initrd 的地址, fdt 是設備樹(.dtb)文件。 在 DRAM 中的首地址, 如果 initrd 為空的話, 同樣是用「-」 來替代。

bootdelay 和 bootcmd 和 bootargs

  1. bootdelay: uboot 開啟後啟動內核的倒計時。無打斷則執行 bootcmd 所對應的命令集。
  2. bootargs : 保存着 uboot 傳遞給 Linux 內核的參數。
  3. bootcmd : 保存着 uboot 默認命令,用來啟動 Linux 內核。
  4. linux 內核啟動時可以接收 uboot 給他傳遞的啟動參數,這些啟動參數是 uboot 和內核約定好的形式、內容,linux 內核在這些啟動參數的指導下完成啟動過程。這樣的設計是為了靈活,為了內核在不重新編譯的情況下可以用不同的方式啟動。在 uboot 的環境變量中設置 bootargs,然後 bootm 命令啟動內核時會自動將 bootargs 傳給內核。
bootargs=console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

(1)console=ttySAC2,115200 : 控制台使用串口 2, 波特率 115200.
(2)root=/dev/mmcblk0p2 rw : 根文件系統在 SD 卡端口 0 設備(iNand) 第 2 分區, 可讀寫
(3)init=/linuxrc : linux 的進程 1(init 進程) 的路徑
(4)rootfstype=ext3 : 根文件系統的類型是 ext3

其他命令

reset

複位, 輸入「reset」即可複位重啟。

go

用於跳到指定的地址處執行應用, 命令格式: go addr [arg …]

run

用於運行環境變量中定義的命令, 如 run bootcmd

mtest

內存讀寫測試命令, 如 mtest 80000000 80001000: 測試 0X80000000~0X80001000 這段內存