一个方便的 U-Boot 技巧
从事内核或裸机程序开发的嵌入式开发人员经常经历多个开发周期。每次开发人员修改代码时,都必须编译代码,将 ELF(可执行和可链接格式)/内核镜像复制到 SD 卡上,然后将卡从 PC 转移到开发板并重新启动。根据我作为开发人员的经验,我发现最后两个步骤是主要的瓶颈。即使将文件复制到最快的 SD 卡也比在硬盘驱动器之间复制文件,有时甚至比在网络上的计算机之间复制文件要慢。
此外,频繁地将 SD 卡插入和拔出插槽会增加损坏开发板上脆弱连接器的风险。相信我!我因为在拿着板子拔出 SD 卡时不小心用力过猛而损失了一块 BeagleBoard。压力导致 I2C 总线故障。由于电源管理芯片由 I2C 控制,因此之后除了串行终端之外,什么都无法工作。撇开板子的成本不谈,如果在项目的关键时刻板子发生故障,如果您没有备用板子,那将是灾难性的。
在丢失 BeagleBoard 之后,我突然想到通过 bootp 和 TFTP 在 LAN 上加载我的裸机代码,并保持开发板不动。这不仅降低了机械损坏我的板子的风险,而且还提高了我的周转时间。我不再需要将文件复制到 SD 卡并到处移动它。
在本文中,我将简要介绍 U-Boot,然后描述使用 DHCP 和 TFTP 设置开发环境所需的配置。我在此处介绍的设置将使您能够快速部署和测试新构建,而无需重新启动开发板。我在本文的示例中使用 BeagleBone Black 作为目标平台,Ubuntu 作为开发平台。但是,您可以使用此处介绍的方法来处理任何使用 U-Boot 或 Barebox 作为其 stage-2 引导加载程序的开发板。
U-BootU-Boot 是一种流行的引导加载程序,被许多开发平台使用。它支持多种架构,包括 ARM、MIPS、AVR32、Nios、Microblaze、68K 和 x86。U-Boot 还内置支持多种文件系统,包括 FAT32、ext2、ext3、ext4 和 Cramfs。它还具有一个 shell,可以在其中交互式地接受来自用户的输入,并且它支持脚本编写。它在 GPLv2 许可下分发。U-Boot 是一个 stage-2 引导加载程序。
U-Boot 项目还包括 x-loader。x-loader 是一个用于 ARM 的小型 stage-1 引导加载程序。大多数现代芯片都具有内置于 ROM 中的读取 FAT32 文件系统的能力。x-loader 将 U-Boot 加载到内存中并将控制权转移给它。U-Boot 是一个非常先进的引导加载程序,能够从 NAND、SD 卡、USB 驱动器甚至通过 bootp、DHCP 和 TFTP 从以太网加载内核和 ramdisk 镜像。
图 1 显示了 BeagleBone Black 的默认启动顺序。此顺序或多或少适用于大多数嵌入式系统。x-loader 和 U-Boot 可执行文件分别存储在名为 MLO 和 uboot.img 的文件中。这些文件存储在 FAT32 分区中。BeagleBone 的串行端口输出如列表 1-3 所示。x-loader 负责列表 1 中所示的输出。一旦执行权移交给 U-Boot,它会为您提供几秒钟的时间来中断启动顺序,如列表 2 所示。如果您选择不中断,U-Boot 将执行名为 bootcmd
的环境变量。bootcmd
保存了名为 uImage
的文件的搜索顺序。这是内核镜像。内核镜像被加载到内存中,执行权最终转移到内核,如列表 3 所示。

图 1. 启动顺序
列表 1. 来自 Stage-1 引导加载程序的串行控制台输出
U-Boot SPL 2013.04-rc1-14237-g90639fe-dirty (Apr 13 2013 - 13:57:11)
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx,
↪HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Peripheral mode controller at 47401000 using PIO, IRQ 0
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx,
↪HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Host mode controller at 47401800 using PIO, IRQ 0
OMAP SD/MMC: 0
mmc_send_cmd : timeout: No status update
reading u-boot.img
reading u-boot.img
列表 2. 来自 Stage-2 引导加载程序的串行控制台输出
U-Boot 2013.04-rc1-14237-g90639fe-dirty (Apr 13 2013 - 13:57:11)
I2C: ready
DRAM: 512 MiB
WARNING: Caches not enabled
NAND: No NAND device found!!!
0 MiB
MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
*** Warning - readenv() failed, using default environment
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx,
↪HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Peripheral mode controller at 47401000 using PIO, IRQ 0
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx,
↪HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Host mode controller at 47401800 using PIO, IRQ 0
Net: <ethaddr> not set. Validating first E-fuse MAC
cpsw, usb_ether
Hit any key to stop autoboot: 0
列表 3. 来自 Stage-2 引导加载程序和内核的串行控制台输出
gpio: pin 53 (gpio 53) value is 1
Card did not respond to voltage select!
.
.
.
gpio: pin 54 (gpio 54) value is 1
SD/MMC found on device 1
reading uEnv.txt
58 bytes read in 4 ms (13.7 KiB/s)
Loaded environment from uEnv.txt
Importing environment from mmc ...
Running uenvcmd ...
Booting the bone from emmc...
gpio: pin 55 (gpio 55) value is 1
4215264 bytes read in 778 ms (5.2 MiB/s)
gpio: pin 56 (gpio 56) value is 1
22780 bytes read in 40 ms (555.7 KiB/s)
Booting from mmc ...
## Booting kernel from Legacy Image at 80007fc0 ...
Image Name: Angstrom/3.8.6/beaglebone
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4215200 Bytes = 4 MiB
Load Address: 80008000
Entry Point: 80008000
Verifying Checksum ... OK
## Flattened Device Tree blob at 80f80000
Booting using the fdt blob at 0x80f80000
XIP Kernel Image ... OK
OK
Using Device Tree in place at 80f80000, end 80f888fb
Starting kernel ...
Uncompressing Linux... done, booting the kernel.
[ 0.106033] pinctrl-single 44e10800.pinmux: prop pinctrl-0
↪index 0 invalid phandle
.
.
.
[ 9.638448] net eth0: phy 4a101000.mdio:01 not found on slave 1
.---O---.
| | .-. o o
| | |-----.-----.-----.| | .----..-----.-----.
| | | __ | ---'| '--.| .-'| | |
| | | | | |--- || --'| | | ' | | | |
'---'---'--'--'--. |-----''----''--' '-----'-'-'-'
-' |
'---'
The Angstrom Distribution beaglebone ttyO0
Angstrom v2012.12 - Kernel 3.8.6
beaglebone login:
在 bootcmd
变量中定义的搜索顺序和文件名 (uImage) 硬编码在 U-Boot 源代码中(参见列表 9)。列表 4 显示了环境变量 bootcmd
的格式化内容。bootcmd
的有趣部分是第 19-28 行。脚本的这部分检查名为 uEnv.txt 的文件是否存在。如果找到该文件,则将该文件加载到内存中(第 19 行)。然后,将其导入到环境以供读取或执行(第 22 行)。之后,脚本检查变量 uenvcmd
是否已定义(第 24 行)。如果已定义,则执行变量中的脚本。uEnv.txt 文件是用户将脚本插入环境的一种方法。在这里,我们将使用它来覆盖默认搜索顺序并从 TFTP 服务器加载内核镜像或 ELF 文件。
01 gpio set 53;
02 i2c mw 0x24 1 0x3e;
03 run findfdt;
04 mmc dev 0;
05 if mmc rescan ;
06 then
07 echo micro SD card found;
08 setenv mmcdev 0;
09 else
10 echo No micro SD card found, setting mmcdev to 1;
11 setenv mmcdev 1;
12 fi;
13 setenv bootpart ${mmcdev}:2;
14 mmc dev ${mmcdev};
15 if mmc rescan;
16 then
17 gpio set 54;
18 echo SD/MMC found on device ${mmcdev};
19 if run loadbootenv;
20 then
21 echo Loaded environment from ${bootenv};
22 run importbootenv;
23 fi;
24 if test -n $uenvcmd;
25 then
26 echo Running uenvcmd ...;
27 run uenvcmd;
28 fi;
29 gpio set 55;
30 if run loaduimage;
31 then
32 gpio set 56;
33 run loadfdt;
34 run mmcboot;
35 fi;
36 fi;
为了更好地了解 U-Boot 的工作原理,我建议中断执行并进入 U-Boot shell。在 shell 中,您可以通过键入 help
或 ?
来查看支持的命令列表。您可以使用 env print
命令列出所有已定义的环境变量。这些环境变量是脚本编写的强大工具。要恢复启动顺序,您可以发出 boot
命令或 run bootcmd
。了解 bootcmd
在做什么的好方法是从 U-Boot shell 一次执行一个命令,并查看其效果。您可以通过执行不带 if
部分的条件语句并键入 echo $?
来检查其输出来替换 if...then...else...fi
块。
DHCP(动态主机配置协议)是一种为主机提供访问网络所需信息的协议。这包括主机的 IP 地址、DNS 服务器、网关服务器、时间服务器、TFTP 服务器等等。DHCP 服务器还可以提供包含内核镜像的文件名,主机必须从 TFTP 服务器获取该文件才能继续启动。DHCP 服务器可以设置为为整个网络或按主机提供配置。为整个网络配置文件名(列表 5)不是一个好主意,因为一个内核镜像或 ELF 文件将仅在为其构建的架构上执行。例如,为 x86_64 构建的 vmlinuz 镜像将无法在基于 ARM 处理器的系统上工作。
列表 5. DHCP 服务器的主机配置部分
subnet 192.168.0.0 netmask 255.255.0.0 {
next-server 192.168.146.1;
option domain-name-servers 192.168.146.1;
option routers 192.168.146.1;
range 192.168.145.1 192.168.145.254;
# The BeagleBone Black 1
host BBB-1 {
next-server 192.168.146.1;
filename "/BI/uImage";
hardware ethernet C8:A0:30:B0:88:EB;
fixed-address 192.168.146.4;
}
}
重要提示
在使用 DHCP 服务器时要格外小心。一个网络不得有多个 DHCP 服务器。第二个 DHCP 服务器将在网络上引起严重问题。其他用户将失去网络访问权限。如果您在公司或大学网络中,您将生成一个高优先级事件,邀请 IT 部门来找您。
Ubuntu apt 存储库提供两个 DHCP 服务器:isc-dhcp-server
和 dhcpcd
。我更喜欢使用 isc-dhcp-server
。
来自 Ubuntu 存储库的 isc-dhcpd-server
非常先进,并实现了所有必要的功能。我建议使用 Webmin 来配置它。Webmin 是一种基于 Web 的配置工具,支持配置多个基于 Linux 的服务和守护程序。我建议从 apt 存储库安装 Webmin。有关将 Webmin apt 存储库添加到 Ubuntu 的说明,请参阅 Webmin 文档。
安装 DHCP 服务器后,您需要配置一个子网,并选择一个 IP 地址池,以便根据请求分配给网络上的主机。之后,将列表 5 中与主机对应的行添加到您的 /etc/dhcp/dhcpcd.conf 文件中,或者从 Webmin 的直观界面执行等效操作。在列表 5 中,C8:A0:30:B0:88:EB
对应于 BeagleBone 的以太网地址。next-server
是 TFTP 服务器的地址,从中获取内核镜像或 ELF。/BI/uImage
文件名是内核镜像的名称。将镜像重命名为您使用的任何名称。
TFTP(简单文件传输协议)是一种轻量级的文件传输协议。它不支持身份验证方法。任何人都可以连接并按名称从服务器下载任何文件,或将任何文件上传到服务器。但是,您可以通过设置防火墙规则来拒绝特定范围之外的 IP 地址,从而在一定程度上保护您的服务器。您还可以使 TFTP 主目录对世界只读。这应该可以防止任何恶意上传到服务器。Ubuntu apt 存储库有两个不同的 TFTP 服务器:atftp
和 tftp-hpa
。我推荐 tftp-hpa
,因为 atftp
的开发自 2004 年以来已经停止。
tftpd-hpa 在安装后或多或少就可以运行了。默认文件存储通常是 /var/lib/tftpboot/,tftp 的配置文件可以在 /etc/default/tftpd-hpa 中找到。您可以通过更改 TFTP_DIRECTORY
选项将默认文件存储的位置更改为您选择的任何其他位置。TFTP 安装创建一个用户和一个名为 tftp 的组。tftp 服务器以该用户身份运行。我建议将您自己添加到 tftp 组并将 tftp 数据目录的权限更改为 775。这将使您无需每次都切换到 root 用户即可读取和写入 tftp 数据目录。此外,如果 tftp 数据目录中的文件归 root 用户所有,则 tftp 服务器将无法读取并通过网络提供它们。您可以通过将文件放在那里并尝试使用 tftp 客户端获取它来测试您的服务器
$ tftp 192.168.146.1 -c get uImage[COMMAND]
您可能遇到的一些常见问题包括权限错误。确保文件可由 tftp 用户或 tftpd 运行的任何用户读取。此外,目录必须具有执行权限,否则 tftp 将无法下降并读取该目录的内容,并且当您尝试获取文件时,您将看到“Permission denied”错误。
U-Boot 脚本现在您已经有了 DHCP 和 TFTP 服务器,让我们编写一个 U-Boot 脚本,该脚本将获取内核镜像并启动它。我将介绍两种方法:使用 DHCP 和仅使用 TFTP。正如我之前提到的,运行配置不当的 DHCP 服务器将导致全网络范围的服务中断。但是,如果您知道自己在做什么并且具有设置网络服务的先前经验,那么这是启动开发板的最简单方法。
只需添加或修改 uEnv.txt 文件中的 uenvcmd
变量即可启动 DHCP 启动,如列表 6 所示。uEnv.txt 位于 BeagleBone Black 的 FAT32 分区中。当 BeagleBone Black 通过 USB 电缆连接到您的计算机时,可以挂载此分区。
echo Booting the BeagleBone Black from LAN (DHCP)...
dhcp ${kloadaddr}
tftpboot ${fdtaddr} /BI/${fdtfile}
setenv bootargs console=${console} ${optargs} root=${mmcroot}
↪rootfstype=${mmcrootfstype} optargs=quiet
bootm ${kloadaddr} - ${fdtaddr}
对于仅 TFTP 启动,您可以手动为开发板和 TFTP 服务器指定 IP 地址。这是一个更安全的过程,并且您几乎不会干扰网络上的其他用户。与配置为使用 DHCP 启动的情况一样,您必须修改 uEnv.txt 文件中的 uenvcmd
变量。列表 7 中显示的脚本是如何设置 BeagleBone Black 以从 TFTP 服务器获取内核镜像并将其执行权传递给它的示例。
echo Booting the BeagleBone Black from LAN (TFTP)...
env set ipaddr 192.168.146.10
env set serverip 192.168.146.1
tftpboot ${kloadaddr} /BI/${bootfile}
tftpboot ${fdtaddr} /BI/${fdtfile}
setenv bootargs console=${console} ${optargs} root=${mmcroot}
↪rootfstype=${mmcrootfstype} optargs=quiet
bootm ${kloadaddr} - ${fdtaddr}
列表 6 和 7 都经过格式化,以便清楚地了解该过程。实际的 uEnv.txt 文件应类似于列表 8 中显示的脚本。有关 U-Boot 脚本编写的更多信息,请参阅 U-Boot FAQ 和 U-Boot 手册。uenvcmd
变量中的各种命令必须位于同一行,并用分号分隔。您可能会注意到我将脚本放在 uenvcmdx
而不是 uenvcmd
中。这是因为 test -n
会根据它正在测试的变量的内容向控制台抛出错误。某些变量内容,尤其是长而复杂的脚本,会导致 test -n
失败,并在控制台中显示错误消息。因此,我在 uenvcmd
中放置了一个简单的命令 run uenvcmdx
。如果您发现 uEnv.txt 中的脚本未被执行,请在串行控制台上查找如下错误
test - minimal test like /bin/sh
Usage:
test [args..]
列表 8. TFTP 启动的 uEnv.txt 示例
optargs=quiet
uenvcmdx=echo Booting the bone from emmc...; env set ipaddr
↪192.168.146.10; env set serverip 192.168.146.1; tftpboot
↪${kloadaddr} /BI/${bootfile}; tftpboot ${fdtaddr}
↪/BI/${fdtfile}; setenv bootargs console=${console}
↪${optargs} root=${mmcroot} rootfstype=${mmcrootfstype}
↪optargs=quiet; bootm ${kloadaddr} - ${fdtaddr}
uenvcmd=run uenvcmdx
在某些开发板(如 BeagleBoard xM)上,以太网端口在 USB 总线上实现。因此,在尝试任何基于网络的启动之前,必须启动 USB 子系统。如果您的开发板上没有板载闪存,则它也可能没有 MAC 地址。在这种情况下,您必须设置 MAC 地址,然后才能发出任何网络请求。您可以通过设置环境变量 ethaddr
以及其余的 uEnv.txt 脚本来做到这一点。
更改默认启动顺序的另一种方法(但比较麻烦)是修改 U-Boot 源代码。修改源代码使您可以更灵活地启动开发板。当您中断 U-Boot 启动顺序时,进入 U-Boot shell 并发出 env print
命令,您将看到许多默认定义的环境变量。这些环境变量在源代码中定义为宏。修改源代码的目的是修改这些变量。如图 1 所示,U-Boot 通过执行 bootcmd
中的脚本开始加载内核。因此,这是必须修改的变量。
首先,您需要从 git 存储库获取 U-Boot 的源代码
$ git clone git://git.denx.de/u-boot.git
在进行任何修改之前,我建议编译未修改的源代码作为健全性检查
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- am335x_evm_config
$ make -j 8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
这很可能不会出现问题。现在您可以修改 u-Boot/include/configs/am335x_evm.h 文件。在此文件中,您将找到类似于列表 9 的代码。根据您的意愿修改此代码并重新编译。根据您的目标板,您必须修改不同的文件。一些常见目标平台的文件是
-
Panda Board: u-Boot/include/configs/omap4_common.h
-
BeagleBoard: u-Boot/include/configs/omap3_beagle.h
#define CONFIG_BOOTCOMMAND \
"mmc dev ${mmcdev}; if mmc rescan; then " \
"echo SD/MMC found on device ${mmcdev};" \
"if run loadbootenv; then " \
"echo Loaded environment from ${bootenv};" \
"run importbootenv;" \
"fi;" \
"if test -n $uenvcmd; then " \
"echo Running uenvcmd ...;" \
"run uenvcmd;" \
"fi;" \
"if run loaduimage; then " \
"run mmcboot;" \
"fi;" \
"fi;" \
结论
我希望此处提供的说明可以帮助您创建一个系统,以快速开发和部署裸机程序和内核镜像。您可能还想了解 u-boot-v2,也称为 Barebox。我在此处建议的最有用的代码修改是编译 U-Boot,其中包含详细的启动顺序,您可以根据自己的需要进行调整,而无需进行最少的修改。您可以尝试一些花哨的脚本来检查和更新 LAN 上的固件——我认为这真的很酷。请写信给我:bharath(你知道是什么)lohray(你知道是什么)com。