Buildroot挂载网络内核和根文件系统
通过sd卡中的u-boot挂载网络内核和根文件系统,u-boot、linux kernel、rootfs均为buildroot编译生成。
- Host: Ubuntu 22.04.
- Target: Orange-Pi One (AllWinner H3, Cortex-A7).
- Buildroot: buildroot-2025.02.4.tar.xz.
1. 设置环境
$ sudo apt update $ sudo apt install build-essential flex gawk bc bison texinfo libssl-dev libncurses-dev file tftpd-hpa nfs-kernel-server
1.1 设置TFTP服务器
创建服务器文件夹并修改权限和组(路径可自定义修改,至少保证664权限):
$ sudo mkdir -p /tftpboot $ sudo chmod -R 777 /tftpboot $ sudo chown -R nobody:nogroup /tftpboot
配置TFPT服务器:
$ sudo vim /etc/default/tftpd-hpa
修改为以下内容(tftpboot改为实际自定义路径):
TFTP_USERNAME="tftp" TFTP_DIRECTORY="/tftpboot" # 共享目录路径,改为实际的 TFTP_ADDRESS=":69" TFTP_OPTIONS="--secure --create" # 允许上传和创建文件
重启服务:
$ sudo systemctl restart tftpd-hpa
测试有效性:
$ echo "Hello TFTP" > /tftpboot/test.txt # 在/tftpboot放置测试文件 $ cd $HOME $ tftp localhost # 本地下载测试 > get test.txt > quit $ cat test.txt # 应显示"Hello TFTP"
1.2 设置NFS服务器
创建文件夹并修改权限(路径可自定义修改,至少保证664权限):
$ sudo mkdir -p /nfsroot $ sudo chmod 777 /nfsroot
配置nfs:
$ sudo vim /etc/exports
文尾添加如下内容(nfsroot改为实际自定义路径):
/nfsroot *(rw,sync,no_root_squash,no_subtree_check)
重启服务:
$ sudo systemctl restart nfs-kernel-server
验证有效性:
$ sudo mount -t nfs 127.0.0.1:/nfsroot /mnt # 在本地临时挂载测试 $ df -h | grep nfs # 查看挂载结果, 存在就是正常的 $ sudo umount /mnt # 卸载
1.3 防火墙设置
设置允许防火墙(改为实际IP,并建议设置成静态IP,否则uboot可能会经常需要改成实际动态获取的IP):
$ sudo ufw allow 69/udp # 允许TFTP/UDP $ sudo ufw allow from 192.168.72.21/24 to any port nfs # 允许NFS(需开放多个端口)
1.4 设置静态IP
Ubuntu上文件名通常为 00-installer-config.yaml 或 50-cloud-init.yaml (哪个存在改哪个,这里是后者):
$ sudo cp /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.bak $ sudo vim /etc/netplan/50-cloud-init.yaml
改为如下内容:
network: version: 2 renderer: networkd ethernets: enp0s3: dhcp4: no addresses: - 192.168.72.21/24 gateway4: 192.168.72.1 nameservers: addresses: - 180.76.76.76 - 8.8.8.8
更新配置:
$ sudo netplan apply
2. 编译镜像
先编一个基本可用的镜像,以验证软硬件均可正常使用,下载代码(建议用这种有明确日期的LTS版本,否则后边不好维护):
$ wget https://buildroot.org/downloads/buildroot-2025.02.4.tar.xz $ tar -xf buildroot-2025.02.4.tar.xz $ mv buildroot-2025.02.4 buildroot-opi1 $ cd buildroot-opi1
进行配置:
$ make orangepi_one_defconfig $ make source # 先把要用到源码都下载下来,再编译,减少网络风险 $ make -j8
编译完成后,插入sd卡进行烧录测试(/dev/sdb改为实际设备节点):
$ cd buildroot-opi1/output/images $ sudo dd if=sdcard.img of=/dev/sdb status=progress; sync
烧录完成后,通过比如串口进行简单测试,如果都正常,就开始定制网络挂载版本。
3. 定制网络挂载
这里修改了u-boot和文件系统配置。u-boot的默认配置无法使用"saveenv"命令,默认的根文件系统也没有打包,需要一份打包的根文件系统。
3.1 u-boot部分
修改镜像打包文件"buildroot/board/orangepi/common/genimage.cfg",做如下修改:
image sdcard.img { partition u-boot { in-partition-table = false image = "u-boot-sunxi-with-spl.bin" offset = 8K - size = 1000K + size = 880K # 原1000K → 880K,预留128KB空间 } hdimage { partition-table-type = "gpt" gpt-location = 1008K # GPT位置不变(880K + 128K = 1008K) gpt-no-backup = true } partition rootfs { offset = 1M # rootfs仍从1MB开始 image = "rootfs.ext4" partition-uuid = %PARTUUID% } }
然后,配置u-boot:
$ make uboot-menuconfig
做如下修改(对,第一项默认是勾选的,去掉勾选):
Environment ---> [ ] Environment is in a FAT filesystem [*] Environment in an MMC device (0xDE000) Environment offset (0x1F000) Environment Size (0) mmc device number
也可以在对应的"defconfig"中或".config"直接添加配置也行:
# 禁用FAT存储 CONFIG_ENV_IS_IN_FAT=n # 启用原始扇区存储 CONFIG_ENV_IS_IN_MMC=y # MMC设备号(0表示eMMC/SD卡) CONFIG_SYS_MMC_ENV_DEV=0 # 888K的十六进制偏移(888*1024=0xDE000) CONFIG_ENV_OFFSET=0xDE000 # 环境变量大小(120KB=0x1F000) CONFIG_ENV_SIZE=0x1F000
编译:
# 如果使用menuconfig修改配置 $ make uboot -j4 # 或者,无视修改方式通用的: $ make uboot-rebuild
这时,编译出的u-boot就可以正常使用"saveenv"命令了。
3.2 rootfs部分
做如下配置:
$ cd buildroot-opi1 $ make menuconfig
添加如下配置:
Filesystem images ---> [*] tar the root filesystem
然后,再打包一次,把u-boot和rootfs的修改都放进去,并重新刷机:
$ make -j8 $ sudo dd if=./output/images/sdcard.img of=/dev/sdb status=progress; sync
然后,正常开机测试是否正常,如果一切顺利,就可以配置u-boot加载网络内核和网络根文件系统了。
4. 加载网络内核和根文件系统
开发板上电开机后,按下回车使u-boot进入u-boot命令行,执行以下配置(改为实际IP和nfs路径后,直接整个复制过去即可,无需一条一条粘贴):
setenv ipaddr 192.168.72.22 setenv serverip 192.168.72.21 setenv gatewayip 192.168.72.1 setenv netmask 255.255.255.0 setenv kernel_addr 0x40008000 setenv fdt_addr 0x41000000 setenv bootfile zImage setenv fdtfile sun8i-h3-orangepi-one.dtb setenv nfs_root /nfsroot setenv root_path /dev/nfs setenv bootargs root=${root_path} rw nfsroot=${serverip}:${nfs_root} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}::eth0:off console=ttyS0,115200n8 earlyprintk setenv bootcmd tftp ${kernel_addr} ${bootfile}\; tftp ${fdt_addr} ${fdtfile}\; bootz ${kernel_addr} - ${fdt_addr}
然后保存环境变量,后续就不需要输入上面的命令了:
saveenv
执行下面命令加载网络内核和网络根文件系统:
run bootcmd
后续,上电开机会自动加载网络内核和根文件系统。
下面是成功使用的日志:
U-Boot SPL 2024.10 (Aug 03 2025 - 08:07:22 +0000) DRAM: 512 MiB Trying to boot from MMC1 U-Boot 2024.10 (Aug 03 2025 - 08:07:22 +0000) Allwinner Technology CPU: Allwinner H3 (SUN8I 1680) Model: Xunlong Orange Pi One DRAM: 512 MiB Core: 67 devices, 20 uclasses, devicetree: separate WDT: Not starting watchdog@1c20ca0 MMC: mmc@1c0f000: 0 Loading Environment from MMC... Reading from MMC(0)... OK In: serial,usbkbd Out: serial,vidconsole Err: serial,vidconsole Net: eth0: ethernet@1c30000 starting USB... Bus usb@1c1a000: USB EHCI 1.00 Bus usb@1c1a400: USB OHCI 1.0 Bus usb@1c1b000: USB EHCI 1.00 Bus usb@1c1b400: USB OHCI 1.0 scanning bus usb@1c1a000 for devices... 1 USB Device(s) found scanning bus usb@1c1a400 for devices... 1 USB Device(s) found scanning bus usb@1c1b000 for devices... 1 USB Device(s) found scanning bus usb@1c1b400 for devices... 1 USB Device(s) found scanning usb for storage devices... 0 Storage Device(s) found Hit any key to stop autoboot: 0 ethernet@1c30000 Waiting for PHY auto negotiation to complete. done Using ethernet@1c30000 device TFTP from server 192.168.72.21; our IP address is 192.168.72.22 Filename 'zImage'. Load address: 0x40008000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ######################################################## 299.8 KiB/s done Bytes transferred = 5580320 (552620 hex) Using ethernet@1c30000 device TFTP from server 192.168.72.21; our IP address is 192.168.72.22 Filename 'sun8i-h3-orangepi-one.dtb'. Load address: 0x41000000 Loading: ### 329.1 KiB/s done Bytes transferred = 30037 (7555 hex) Kernel image @ 0x40008000 [ 0x000000 - 0x552620 ] ## Flattened Device Tree blob at 41000000 Booting using the fdt blob at 0x41000000 Working FDT set to 41000000 Loading Device Tree to 49ff5000, end 49fff554 ... OK Working FDT set to 49ff5000 Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0 ... ... Starting crond: OK Welcome to Buildroot buildroot login: root # pwd /root
一些需要注意的点,这里buildroot编译出的zImage尺寸是5.4MB,加载地址是0x40008000,文件末尾地址没有达到0x41000000,所以没有和设备树地址冲突,实际中需要考虑这一点,防止地址冲突。
另外,这里加载的是zImage,u-boot使用的是bootz,如果你加载uImage,则应使用bootm。而这在arm64上则不存在,应为arm64默认不压缩内核镜像。