基础知识_06uboot,kernel和镜像编译

uboot,linux内核,dts设备树,rootfs区别和联系

1,关系表

组件作用文件类型运行阶段
U-Boot引导加载程序,初始化硬件、加载内核和设备树到内存,并启动内核二进制可执行文件(如 u-boot.bin系统上电 → 内核启动前
Linux 内核操作系统核心,管理硬件资源(CPU、内存、外设)、进程调度、驱动等压缩的二进制文件(如 ImagezImage内核启动 → 用户空间启动前
设备树(DTS)​描述硬件配置(CPU、外设、中断、寄存器地址等),供内核识别硬件文本文件(.dts) → 编译为二进制(.dtb内核启动阶段解析
rootfs根文件系统,包含用户空间程序(如 /bin/lib)、配置文件、应用程序等文件系统镜像(如 ext4squashfsinitramfs内核启动后挂载,用户空间运行

2, 协作关系(启动流程)​

1
系统上电 → U-Boot → 加载内核和设备树 → 内核启动 → 解析设备树 → 挂载 rootfs → 启动用户空间(如 systemd)

3,关键步骤详解​

  1. U-Boot 阶段
  • 初始化 CPU、内存、存储设备(如 eMMC、SD 卡)、串口等基础硬件。
  • 从存储设备(或网络)加载 ​内核镜像(Image)​ 和 ​设备树二进制(.dtb)​ 到内存。
  • 通过 bootmbootz 命令启动内核,并传递设备树的内存地址。
  1. Linux 内核阶段
  • 解析设备树(.dtb),识别 CPU 架构、外设地址、中断号等硬件信息。
  • 初始化驱动(如网卡、存储控制器),挂载 ​rootfs​(从硬盘、网络或内存加载)。
  • 启动第一个用户空间进程(如 initsystemd)。
  1. rootfs 阶段
  • 提供用户空间程序运行环境(如 bashsystemd、应用程序)。
  • 包含动态链接库(/lib)、配置文件(/etc)、设备节点(/dev)等。

rootfs生成脚本(mmdebstrap,chroot)

1
2
3
4
5
6
7
8
mmdebstrap --architectures=riscv64 \
--include="$PACKAGE_LIST" \
--skip check/empty \
sid $1 \ # $1参数为构建目标目录
"deb [trusted=yes] https://mirror.iscas.ac.cn/rockos/20250130/rockos-gles/ rockos-gles main" \
"deb [trusted=yes] https://mirror.iscas.ac.cn/rockos/20250130/rockos-kernels/ rockos-kernels main" \
"deb [trusted=yes] https://mirror.iscas.ac.cn/rockos/20250130/rockos-addons/ rockos-addons main" \
"deb [trusted=yes] https://mirror.iscas.ac.cn/rockos/20250130/rockos-base/ sid main contrib non-free non-free-firmware"

问题

1
2
3
4
5
6
7
8
9
这部分代码:
# 挂载系统目录(构建chroot环境)
mount -t proc /proc "$CHROOT_TARGET"/proc # 进程信息
mount --bind /sys "$CHROOT_TARGET"/sys # 系统信息
mount --bind /run "$CHROOT_TARGET"/run # 运行时文件
mount --bind /dev "$CHROOT_TARGET"/dev # 设备文件
mount --bind /dev/pts "$CHROOT_TARGET"/dev/pts # 伪终端

将宿主机的的sys,run,dev等挂载到"$CHROOT_TARGET"/sys (chroot的类虚拟机环境)不会有问题么?一个x86架构,一个riscv架构。,这么做为啥不报错,以及为何这么做(这么做目的时什么)

1. 为什么不报错?—— 内核接口的架构无关性

虚拟文件系统的本质

  • /proc/sysdev 等目录是 Linux 内核暴露给用户空间的 ​虚拟文件系统(Virtual File Systems)​
    • /proc: 提供进程、系统状态等运行时信息(如 cpuinfo)。
    • /sys: 暴露硬件设备、驱动、内核参数等层级化配置。
    • /dev: 设备节点文件(如 /dev/sda)。
  • 这些文件系统的内容由内核动态生成,与用户空间的 CPU 架构无关。例如:
    • cat /proc/cpuinfo 在 x86 和 RISC-V 上都会返回当前运行内核的 CPU 信息。
    • /sys/devices 展示的是内核管理的设备树,与用户空间程序架构无关。
  • 架构无感知性*
  • 挂载操作只是映射内核接口,不涉及任何二进制代码执行。
  • 即使宿主架构(x86)和目标架构(RISC-V)不同,内核仍能正确生成这些虚拟文件系统的内容,因为这些内容由宿主机的内核管理。

2. 为什么需要挂载?—— Chroot 环境的完整性

构建根文件系统的需求

chroot 环境中执行操作(如安装软件、配置服务)时,需要:

  1. 设备访问:如挂载 /dev 以便在 chroot 内创建设备节点。
  2. 系统信息查询:如通过 /proc 获取进程信息。
  3. 依赖内核功能:如通过 /sys 配置硬件参数。

跨架构构建的特殊性

  • 虽然宿主架构和目标架构不同,但 ​所有用户空间操作(如 apt install)实际由宿主机的内核通过二进制翻译(如 QEMU)执行

    1
    2
    3
    # 宿主机的内核通过 binfmt_misc 识别 RISC-V 二进制
    # 并调用 qemu-riscv64-static 解释执行
    $ sudo update-binfmts --enable qemu-riscv64
  • 挂载宿主机的 /proc/sys 到 chroot 环境后:

    • 用户空间程序(RISC-V)通过 QEMU 翻译执行
    • 内核接口(如 sysfs)仍由宿主机内核提供,但用户空间程序会按 RISC-V 的预期解析这些接口。

3. 潜在问题与规避

架构相关路径的冲突

极少数情况下,内核接口的路径可能因架构不同产生差异,例如:

  • 设备树路径:RISC-V 设备树路径 /sys/firmware/devicetree/base 可能与 x86 不同。
  • CPU 拓扑信息/sys/devices/system/cpu 的结构可能因架构而异。

解决方案

  • 动态生成配置:在 chroot 环境内通过脚本动态生成架构相关配置。
  • 避免硬编码路径:使用抽象接口(如 lscpu 替代直接读取 /proc/cpuinfo)。
  • 隔离关键目录:在构建完成后卸载宿主机的虚拟文件系统,确保目标系统首次启动时重新挂载自身的虚拟文件系统。

4. 操作的目的总结

挂载操作目的
mount -t proc /proc ...允许 chroot 内程序查询进程和系统信息(如 ps 命令)
mount --bind /sys ...提供硬件设备配置接口(如安装驱动时访问 /sys/class
mount --bind /dev ...创建设备节点(如 /dev/null, /dev/console
mount --bind /dev/pts ...支持伪终端操作(如 SSH 登录需要 /dev/pts/0
### ​5. 完整工作流程示例
1
2
3
4
5
6
宿主机 (x86_64)                          chroot 环境 (riscv64)
+-----------------------------------+ +------------------------------+
| 1. 挂载宿主机的 /proc, /sys, /dev | → | 提供内核接口,不依赖架构 |
| 2. 通过 QEMU 解释执行 RISC-V 程序 | → | apt install 安装 RISC-V 软件包 |
| 3. 卸载宿主机的虚拟文件系统 | ← | 生成完整的 riscv64 根文件系统 |
+-----------------------------------+ +------------------------------+
### ​结论
* ​安全且必要:跨架构挂载虚拟文件系统是安全的,因为这些接口由宿主机的内核统一管理,与用户空间架构无关。
* ​依赖 QEMU:实际执行 RISC-V 二进制需要 QEMU 的二进制翻译支持,但挂载操作本身不直接涉及指令集转换。
* ​最佳实践:这种方法是构建跨架构根文件系统的标准操作,广泛用于嵌入式 Linux 开发和容器化构建(如 Docker 多架构构建)。
## uboot常用命令
### 1、信息查询命令
常用的信息查询有关的命令有三个:bdinfo、printenv、version。
命令描述
bdinfo查看板子信息
printenv输出环境变量信息
version查看uboot的版本号

2、环境变量操作命令

环境变量的操作涉及到两个命令:setenv和saveenv。
setenv命令用于创建、修改环境变量的值,也可以用于删除环境变量;
saveenv命令用于保存修改后的环境变量。
一般环境变量是存放在外部flash中的,uboot启动的时候会将环境变量从flash读取到DRAM中,所以使用命令setenv修改的是DRAM中的环境变量值,修改后需使用saveenv命令将环境变量保存到flash中。
场景​​:误修改 bootcmd 导致无法启动,需恢复默认环境。

1
2
3
4
# 进入 U-Boot 命令行
=> env default -a # 恢复所有变量
=> saveenv # 保存到存储设备
=> reset # 重启(可选)

3、内存操作命令

内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 md、 nm、mm、 mw、 cp 和 cmp。

4、网络操作命令

uboot是支持网口的,在移植uboot的时候都要调通网络的功能,因为在移植linux内核的时候需要用到uboot的网络功能做调试。uboot支持的网络相关的命令有:dhcp、ping、nfs、ftfpboot。
使用网络调试前需先设置好下列几个环境变量。

环境变量描述
ipaddr开发板的IP地址,可以通过dhcp命令从路由器获取IP
ethaddr开发板的MAC地址,一定要设置
gatewayip网关地址
netmask子网掩码
serverip服务器IP地址,也就是ubuntu主机的IP地址,用于调试
1
2
3
4
5
6
setenv ipaddr 192.168.1.50
setenv ethaddr b8:ae:1d:01:00:00
setenv gatewayip 192.168.1.1
setenv netmask 255.255.255.0
setenv serverip 192.168.1.253
saveenv

5、EMMC和SD卡操作命令

uboot支持EMMC和SD卡,提供了EMMC和SD卡的操作命令。一般认为EMMC和SD卡就是同一个东西,所以没有特殊说明,统一使用MMC来代指EMMC和SD卡。uboot中常用于操作MMC设备的命令为:mmc。

mmc是一系列的命令,后面可以跟不同的参数,输入“? mmc”即可查看mmc有关的命令。如下图所示:

mmc后面跟不同的参数可以实现不同的功能。

命令描述
mmc info输出MMC设备信息
mmc read读取MMC中的数据
mmc write向MMC设备写入数据
mmc rescan扫描MMC设备
mmc part列出MMC设备的分区
mmc dev切换MMC设备
mmc list列出当前有效的所有MMC设备
mmc hwpartition设置MMC设备的分区
mmc bootbus …设置指定MMC设备的BOOT_BUS_WIDTH域的值
mmc bootpart …设置指定MMC设备的boot和RPMB分区的大小
mmc partconf …设置指定MMC设备的PARTITION_CONFG域的值
mmc rst复位MMC设备
mmc setdsr设置DSR寄存器的值

mmc rescan 命令
mmc rescan 命令用于扫描当前开发板上所有的 MMC 设备,包括 EMMC 和 SD 卡,输入“mmc rescan”即可。

mmc list 命令
mmc list 命令用于来查看当前开发板一共有几个 MMC 设备,输入“mmc list”

可以看出当前开发板有两个 MMC 设备: FSL_SDHC:0 (SD)和 FSL_SDHC:1 (eMMC),这是因为我现在用的是 EMMC 版本的核心板,加上 SD 卡一共有两个 MMC 设备, FSL_SDHC:0 是 SD卡, FSL_SDHC:1(eMMC)是 EMMC,。默认会将 EMMC 设置为当前 MMC 设备,要想查看 EMMC信息,就要使用命令“mmc dev”来将 EMMC卡设置为当前的 MMC 设备

mmc dev 命令
mmc dev 命令用于切换当前 MMC 设备,使用如下命令切换到 EMMC:

mmc info 命令
mmc info 命令用于输出当前选中的 mmc info 设备的信息,输入命令“mmc info”即可,如下图所示:

从上图可以看出,当前选中的 MMC设备是 SD卡,版本为 3.0,容量为 14.8GiB(EMMC为 4GB),速度为 50000000Hz=50MHz, 4 位宽的总线。还有一个与 mmc info 命令相同功能的命令: mmcinfo,“mmc”和“info”之间没有空格。

mmc part 命令
有时候 SD 卡或者 EMMC 会有多个分区,可以使用命令“mmc part”来查看其分区,比如查看 EMMC 的分区情况,输入如下命令,结果如下图所示:

从上图中可以看出,此时 EMMC 有两个分区,扇区 20480~ 262144 为第一个分区,扇区 282644~14987264 为第二个分区。如果 EMMC 里面烧写了 Linux 系统的话, EMMC 是有3 个分区的,第 0 个分区存放 uboot,第 1 个分区存放 Linux 镜像文件和设备树,第 2 个分区存放根文件系统。但是在图中只有两个分区,那是因为第 0 个分区没有格式化,所以识别不出来,实际上第 0 个分区是存在的。一个新的 SD 卡默认只有一个分区,那就是分区 0.

参考:
u-boot常用命令:https://blog.csdn.net/helaisun/article/details/128166820
史上最全的Uboot常用命令汇总(超全面!超详细!)收藏这一篇就够了「建议收藏」:https://cloud.tencent.cn/developer/article/2102295
U-Boot命令使用:https://blog.csdn.net/cike626/article/details/128430824
U-Boot命令之EMMC和SD卡操作命令:https://blog.csdn.net/weixin_45309916/article/details/109178989

uboot环境变量解析

样例信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
> printenv
arch=riscv
baudrate=115200
board=eic7700_d314
board_name=eic7700_d314
boot_conf_addr_r=0xc0000000
boot_conf_file=/extlinux/extlinux.conf
bootargs=root=/dev/nfs init=/linuxrc ip=dhcp rw nfsroot=10.10.192.205:/srv/nfs_server,proto=tcp,nfsvers=3,nolock console-ttyS0
bootcmd=sysboot mmc ${emmc_dev}:1 any $boot_conf_addr_r $boot_conf_file;
bootdelay=2
console=ttyS0
cpu=eic770x
emmc_dev=0
ethact=ethernet@50400000
ethaddr=8c:1f:64:13:c0:9c
fdt_addr=ed511150
fdt_addr_r=0x88000000
fdt_high=0xffffffffffffffff
fdtaddr=ed511150
fdtcontroladdr=ed511150
fdtfile=eswin/eic7700-d314.dtb
gpt_partition=gpt write mmc ${emmc_dev} $partitions
initrd_high=0xffffffffffffffff
kernel_addr_r=0x84000000
kernel_comp_addr_r=0xa0000000
kernel_comp_size=0x4000000
loadaddr=0x80200000
loadimage=dhcp 0x90000000 fitImage
nfsargs=setenv bootargs root=/dev/nfs init=/linuxrc ip=dhcp rw nfsroot=10.10.192.205:/srv/nfs_server,proto=tcp,nfsvers=3,nolock
nfsboot=echo Set nfs parameter ...;run nfsargs;run setimageload;run loadimage
partitions=name=boot,start=1MiB,size=512MiB,type=${typeid_efi},uuid=${uuid_boot};name=swap,size=4096MiB,type=${typeid_swap},uuid=${uuid_swap};name=root,size=-,type=${typeid_filesystem},uuid=${uuid_root}
preboot=setenv fdt_addr ${fdtcontroladdr};fdt addr ${fdtcontroladdr};usb start;sata init;nvme scan
pxefile_addr_r=0x88200000
ram_size=16
ramdisk_addr_r=0x88300000
scriptaddr=0x88100000
sdupdate=ext4load mmc 1:1 0x90000000 sdupdate.scr;source 0x90000000
serverip=10.10.192.205
setimageload=setenv loadimage dhcp 0x90000000 fitImage
silent=1
splashimage=0xe0000000
splashpos=1660,0
stderr=vidconsole,serial
stdin=serial,usbkbd
stdout=serial
typeid_efi=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
typeid_filesystem=0FC63DAF-8483-4772-8E79-3D69D8477DE4
typeid_swap=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F
usbupdate=ext4load usb 0 0x90000000 usbupdate.scr;source 0x90000000
uuid_boot=44b7cb94-f58c-4ba6-bfa4-7d2dce09a3a5
uuid_root=80a5a8e9-c744-491a-93c1-4f4194fd690a
uuid_swap=5ebcaaf0-e098-43b9-beef-1f8deedd135e
vendor=eswin

Environment size: 2020/524284 bytes

4大部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 硬件初始化
- 加载设备树: fdtfile=eswin/eic7700-d314.dtb
- 初始化存储: emmc_dev=0, sata/nvme

2. 启动方式选择
- 默认启动: bootcmd → 从 eMMC 的 extlinux 配置启动
- NFS 启动: nfsboot → 从服务器 10.10.192.205 加载内核和根文件系统

3. 内核加载
- 内核地址: kernel_addr_r=0x84000000
- 设备树地址: fdt_addr_r=0x88000000

4. 根文件系统挂载
- NFS 挂载: root=/dev/nfs, nfsroot=10.10.192.205:/srv/nfs_server
- 本地挂载: root=UUID=80a5a8e9... (需配置 fstab)

1. 系统基础信息

变量说明
archriscv系统架构为 ​RISC-V
boardeic7700_d314硬件板型号(与设备树文件 eic7700-d314.dtb 匹配)
vendoreswin硬件厂商标识
cpueic770xCPU 型号(可能为定制 SoC)
ram_size16系统内存容量为 ​16GB

2. 启动流程控制

变量说明
bootcmdsysboot mmc ${emmc_dev}:1 any $boot_conf_addr_r $boot_conf_file;默认启动命令
- 从 eMMC 的第 1 分区加载 extlinux 配置 (/extlinux/extlinux.conf)
- 使用 sysboot 执行配置的启动项
bootdelay2启动等待时间为 ​2 秒​(用户可按任意键中断自动启动)
boot_conf_file/extlinux/extlinux.confU-Boot 启动菜单配置文件路径
bootargsroot=/dev/nfs ... console-ttyS0内核启动参数
- 通过 NFS 挂载根文件系统 (10.10.192.205:/srv/nfs_server)
- 使用串口 ttyS0 作为控制台

3. 存储与分区

变量说明
partitionsname=boot,start=1MiB,size=512MiB...GPT 分区定义
- boot 分区:512MB,EFI 类型 (FAT32)
- swap 分区:4GB
- root 分区:剩余空间(ext4)
gpt_partitiongpt write mmc ${emmc_dev} $partitions命令:将 partitions 变量定义的分区表写入 eMMC
emmc_dev0当前操作的 eMMC 设备编号(对应硬件上的存储控制器)
uuid_boot44b7cb94-f58c-4ba6-bfa4-7d2dce09a3a5Boot 分区的 UUID(与 fstab 中的配置匹配)

4. 网络配置

变量说明
ethactethernet@50400000当前激活的以太网设备(硬件寄存器地址 0x50400000
ethaddr8c:1f:64:13:c0:9cMAC 地址(需确保与网络环境无冲突)
serverip10.10.192.205TFTP/NFS 服务器 IP(用于网络启动或文件传输)
nfsbootrun nfsargs; run loadimageNFS 启动命令
1. 设置 NFS 参数 (nfsargs)
2. 加载内核镜像 (loadimage)
5. 内核与设备树加载
变量说明
kernel_addr_r0x84000000内核镜像 (fitImage) 加载到内存的地址
fdt_addr_r0x88000000设备树文件 (eic7700-d314.dtb) 加载地址
fdtfileeswin/eic7700-d314.dtb设备树文件路径(需与硬件板型号匹配)
loadimagedhcp 0x90000000 fitImage从 TFTP 服务器下载内核镜像到内存 0x90000000

6. 高级功能

变量说明
sdupdateext4load mmc 1:1 0x90000000 sdupdate.scr; source 0x90000000SD 卡更新脚本
- 从 SD 卡加载 sdupdate.scr 脚本并执行
usbupdateext4load usb 0 0x90000000 usbupdate.scr; source 0x90000000USB 更新脚本
- 从 USB 设备加载 usbupdate.scr 并执行
prebootsata init; nvme scan启动前初始化 SATA/NVMe 设备(扩展存储支持)
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×