分析对象 :最新主线 U-Boot 源代码(u-boot-latest)
U-Boot 版本 :2026.07-rc2
源码获取 :git clone --depth 1 https://github.com/u-boot/u-boot.git
分析重点 :RISC-V 64-bit(RV64I)平台支持代码
许可证:GPL-2.0+
1. 概览
与此前分析的 2013.10-rc2 Sunxi 版本 相比,最新的 U-Boot(2026.07-rc2)在架构设计、驱动模型、配置系统等方面发生了革命性的演进。RISC-V 作为相对较新的处理器架构,其支持代码充分体现了现代 U-Boot 的设计思想:
- 设备模型(Device Model, DM) :所有外设和 CPU 均通过统一的
udevice/uclass框架管理 - 设备树(Device Tree)驱动 :绝大多数板级参数通过
.dts描述,而非硬编码的 C 宏 - Kconfig 配置系统 :全面替代了老旧的
boards.cfg+ 头文件宏配置方式 - 模块化与可移植性 :RISC-V 32-bit 与 64-bit 共用大量代码,通过编译时宏(
CONFIG_32BIT/CONFIG_64BIT)区分
2. RISC-V 代码在 U-Boot 中的位置
u-boot/
├── arch/riscv/ # RISC-V 架构核心(重点分析目录)
│ ├── cpu/ # CPU/SoC 级代码(启动、DRAM、Cache)
│ ├── dts/ # 设备树源文件(.dts / .dtsi)
│ ├── include/asm/ # 架构头文件(CSR、SBI、encoding)
│ ├── lib/ # 架构库函数(bootm、SMP、SBI、内存操作)
│ ├── Kconfig # 架构级 Kconfig 配置
│ ├── config.mk # 编译标志与链接器配置
│ └── Makefile # 构建规则
├── board/ # 板级支持包(BSP)
│ ├── andestech/ae350 # Andes 开发板
│ ├── andestech/voyager # Andes Voyager
│ ├── emulation/qemu-riscv # QEMU RISC-V Virt 平台
│ ├── sifive/unleashed # SiFive HiFive Unleashed
│ ├── sifive/unmatched # SiFive HiFive Unmatched
│ ├── starfive/visionfive2 # 赛昉 VisionFive 2
│ ├── thead/th1520_lpi4a # 平头哥 TH1520(荔枝派 4A)
│ ├── spacemit/bananapi-f3 # 进迭时空 K1(香蕉派 F3)
│ ├── sophgo/milkv_duo # 算能 Milk-V Duo
│ ├── sophgo/licheerv_nano # 算能 LicheeRV Nano
│ ├── canaan/k230_canmv # 嘉楠 K230 CanMV
│ ├── beagle/beaglev_fire # BeagleV-Fire (Microchip MPFS)
│ ├── microchip/mpfs_generic # Microchip PolarFire SoC
│ ├── openpiton/riscv64 # OpenPiton 多核平台
│ └── xilinx/mbv # AMD/Xilinx MicroBlaze V
├── drivers/
│ ├── cpu/riscv_cpu.c # RISC-V CPU 驱动(DM 模型)
│ ├── timer/riscv_timer.c # RISC-V 定时器驱动
│ ├── timer/riscv_aclint_timer.c # ACLINT 定时器
│ ├── serial/serial_htif.c # HTIF 串口(QEMU/SPIKE)
│ └── rng/riscv_zkr_rng.c # Zkr 扩展硬件随机数驱动
├── lib/efi_loader/
│ └── efi_riscv.c # RISC-V UEFI 支持(RISCV_EFI_BOOT_PROTOCOL)
└── configs/ # defconfig 配置文件
├── qemu-riscv64_defconfig
├── qemu-riscv64_smode_defconfig
├── sifive_unleashed_defconfig
├── visionfive2_defconfig
└── ...
3. 启动流程:从复位到 Linux(RISC-V 64-bit)
3.1 汇编入口点:arch/riscv/cpu/start.S
这是 RISC-V 核心上电后执行的第一段代码 ,同时支持 RV32 和 RV64,通过宏区分寄存器宽度:
asm
#ifdef CONFIG_32BIT
#define LREG lw
#define SREG sw
#define REGBYTES 4
#define RELOC_TYPE R_RISCV_32
#else
#define LREG ld ; 64-bit 加载
#define SREG sd ; 64-bit 存储
#define REGBYTES 8
#define RELOC_TYPE R_RISCV_64
#endif
start.S 的关键执行步骤:
- 读取 Hart ID :通过
CSR_MHARTID(M-mode)获取当前硬件线程 ID,保存到线程指针寄存器tp - 保存 DTB 指针 :固件(如 OpenSBI)传入的设备树地址保存在
s1 - 设置早期 Trap 处理程序 :将
trap_entry写入mtvec/stvec - 屏蔽中断 :向
mie/sie写入 0 - 设置栈指针 :
- 根据
CONFIG_STACK_SIZE_SHIFT(默认 14,即 16KB)为每个 CPU 分配独立栈 - SMP 场景下:
sp = BASE - (hart_id << shift)
- 根据
- Hart 彩票机制(非 XIP) :通过原子操作
amoswap选出唯一的初始化主核(Boot Hart),其余核进入secondary_hart_loop等待 IPI - 初始化全局数据(GD) :调用
board_init_f_init_reserve()设置gd结构体 - 保存启动信息 :将 DTB 地址和 Boot Hart ID 存入
gd->arch - 调用 C 代码 :
jal board_init_f
SPL 路径 (CONFIG_XPL_BUILD):
- 清除
.bss段 - 调用
spl_relocate_stack_gd()重定位栈和全局数据 - 通过
smp_call_function()同步通知从核更新栈和 GD - 调用
board_init_r
非 SPL 路径:
relocate_code():将 U-Boot 从 Flash/XIP 复制到 RAM- 处理 RELA 动态重定位(
.rela.dyn) - 刷新 I-Cache / D-Cache
- 跳转到 RAM 中的
board_init_r()
3.2 Trap 处理:arch/riscv/cpu/mtrap.S
RISC-V 的异常/中断入口点,保存全部 32 个通用寄存器到栈上,然后调用 C 函数 handle_trap:
asm
trap_entry:
addi sp, sp, -32 * REGBYTES
SREG x1, 1 * REGBYTES(sp)
...
csrr a0, MODE_PREFIX(cause) # mcause / scause
csrr a1, MODE_PREFIX(epc) # mepc / sepc
csrr a2, MODE_PREFIX(tval) # mtval / stval
mv a3, sp # 寄存器帧指针
jal handle_trap
csrw MODE_PREFIX(epc), a0 # 更新返回地址
...
LREG x2, 2 * REGBYTES(sp) # 恢复 sp (x2)
addi sp, sp, 32 * REGBYTES
MODE_PREFIX(ret) # mret / sret
32-bit 与 64-bit 差异 :仅体现在
LREG/SREG的指令选择和栈帧大小(128 bytes vs 256 bytes)。
4. 架构核心 C 代码分析
4.1 arch/riscv/cpu/cpu.c --- CPU 初始化与 ISA 扩展管理
这是 RISC-V 架构最重要的 C 文件之一,负责:
4.1.1 ISA 字符串解析与扩展检测
c
static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX);
U-Boot 能够解析设备树中的 riscv,isa 属性(如 rv64imafdc_zba_zbb),并将其映射为内部位图。支持的标准扩展包括:
| 扩展类别 | 示例 |
|---|---|
| 基础整数 | i, m, a, f, d, c |
| 位操作 | zba, zbb, zbc, zbs |
| 缓存管理 | zicbom, zicboz |
| 密码学 | zbkb, zbkc, zbkx, zknd, zkne, zknh, zkr |
| 向量扩展 | v, zve32x, zve64d, zvbb, zvkned |
| 监控/虚拟化 | h, smaia, ssaia, sstc |
4.1.2 CPU 功能配置
c
int riscv_cpu_setup(void)
{
/* 解析 ISA 字符串 */
riscv_parse_isa_string(isa);
/* 若支持 F/D 扩展,启用浮点单元 */
if (supports_extension('d') || supports_extension('f')) {
csr_set(MODE_PREFIX(status), MSTATUS_FS);
csr_write(CSR_FCSR, 0);
}
/* M-mode 下配置性能计数器、禁用分页 */
if (CONFIG_IS_ENABLED(RISCV_MMODE)) {
csr_write(CSR_MCOUNTEREN, GENMASK(2, 0));
csr_write(CSR_SATP, 0);
}
/* SMP:初始化核间中断(IPI) */
#if CONFIG_IS_ENABLED(SMP)
riscv_init_ipi();
#endif
}
64-bit 特有关系 :
PHYS_64BIT在ARCH_RV64I时被自动选中,支持 64-bit 物理地址空间。
4.2 arch/riscv/lib/sbi.c --- SBI(Supervisor Binary Interface)
在 RISC-V S-mode(监督模式)下运行的 U-Boot 必须通过 SBI 调用来访问 M-mode(机器模式)的特权功能。这是 RISC-V 架构与 ARM/x86 最大的软件架构差异之一。
核心机制 :sbi_ecall() 封装了 RISC-V 的 ecall 指令:
c
struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, ...)
{
register uintptr_t a0 asm("a0") = arg0;
register uintptr_t a7 asm("a7") = ext; // 扩展 ID
asm volatile ("ecall" : "+r"(a0), "+r"(a1)
: "r"(a2), ..., "r"(a7) : "memory");
ret.error = a0;
ret.value = a1;
return ret;
}
支持的 SBI 扩展(v0.2+):
| 扩展 | 功能 |
|---|---|
SBI_EXT_BASE |
查询 SBI 规范版本、实现 ID、支持的扩展 |
SBI_EXT_TIME |
设置下一时钟中断(sbi_set_timer) |
SBI_EXT_IPI |
发送核间中断(IPI) |
SBI_EXT_RFENCE |
远程 Fence 指令同步 |
SBI_EXT_HSM |
Hart 状态管理(启动/停止/挂起从核) |
SBI_EXT_SRST |
系统复位(关机/冷启动/热启动) |
SBI_EXT_DBCN |
调试控制台(字节级读写) |
SBI_EXT_PMU |
性能监控单元 |
SBI_EXT_SUSP |
系统休眠状态 |
32-bit 与 64-bit 在 SBI 调用中的区别:
- 32-bit 系统调用
sbi_set_timer时,64-bit 计时器值需要拆分为低 32 位和高 32 位分别传入a0和a1 - 64-bit 则直接通过
a0传入完整 64-bit 值
c
#if __riscv_xlen == 32
sbi_ecall(SBI_EXT_SET_TIMER, SBI_FID_SET_TIMER,
stime_value, stime_value >> 32, 0, 0, 0, 0);
#else
sbi_ecall(SBI_EXT_SET_TIMER, SBI_FID_SET_TIMER,
stime_value, 0, 0, 0, 0, 0);
#endif
4.3 arch/riscv/lib/bootm.c --- 引导 Linux 内核
RISC-V 平台引导 Linux 的 bootm 实现非常简洁:
c
static void boot_jump_linux(struct bootm_headers *images, int flag)
{
void (*kernel)(ulong hart, void *dtb);
kernel = (void (*)(ulong, void *))images->ep;
cleanup_before_linux();
#ifdef CONFIG_SMP
/* 向所有从核发送 IPI,使其跳转到内核入口 */
smp_call_function(images->ep, (ulong)images->ft_addr, 0, 0);
#endif
/* 主核直接跳转:a0 = boot_hart_id, a1 = dtb_addr */
kernel(gd->arch.boot_hart, images->ft_addr);
}
RISC-V Linux 启动约定:
a0= Boot Hart ID(硬件线程编号)a1= 扁平设备树(FDT)的物理地址- 内核入口地址由
images->ep指定
4.4 arch/riscv/lib/fdt_fixup.c --- 设备树修正
在将设备树传递给 Linux 之前,U-Boot 需要执行若干架构特定的修正:
- 保留内存拷贝 :将固件传递的 FDT 中的
/reserved-memory节点拷贝到最终 FDT - boot-hartid 写入 :在
/chosen节点中写入boot-hartid属性,告知 Linux 哪个核是主核 - ACPI 支持 :在
CONFIG_EFI_LOADER启用时,配合 UEFI 启动流程
c
int arch_fixup_fdt(void *blob)
{
/* 覆盖 boot-hartid,因为 U-Boot 是最后阶段的 Bootloader */
err = fdt_setprop_u32(blob, chosen_offset, "boot-hartid",
gd->arch.boot_hart);
/* 拷贝 reserved-memory 节点 */
err = riscv_fdt_copy_resv_mem_node(gd->fdt_blob, blob);
}
4.5 arch/riscv/lib/smp.c --- 对称多处理(SMP)
U-Boot 的 RISC-V SMP 支持通过 IPI(核间中断) 实现:
c
int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait)
{
struct ipi_data ipi = { .addr = addr, .arg0 = arg0, .arg1 = arg1 };
return send_ipi_many(&ipi, wait);
}
执行流程:
- 遍历设备树
/cpus节点下的所有可用 hart - 跳过 boot hart 和超出
CONFIG_NR_CPUS范围的核 - 将目标地址和参数写入
gd->arch.ipi[hart] - 通过内存屏障
__smp_store_release标记有效位 - 调用
riscv_send_ipi(hart)触发硬件 IPI - 从核在
secondary_hart_loop中被wfi唤醒,读取 IPI 数据并跳转执行
5. 设备模型(Device Model)驱动分析
现代 U-Boot 全面采用 DM(Device Model),RISC-V 平台的驱动也不例外。
5.1 CPU 驱动:drivers/cpu/riscv_cpu.c
c
U_BOOT_DRIVER(riscv_cpu) = {
.name = "riscv_cpu",
.id = UCLASS_CPU,
.of_match = riscv_cpu_ids, /* compatible = "riscv" */
.bind = riscv_cpu_bind,
.probe = riscv_cpu_probe,
.ops = &riscv_cpu_ops,
.flags = DM_FLAG_PRE_RELOC,
};
功能:
- 从设备树读取
clock-frequency、timebase-frequency、mmu-type - 统计可用 CPU 核心数
- 为 Boot Hart 自动绑定
riscv_timer驱动 - SMBIOS 支持:64-bit 时
family = 0x201,32-bit 时family = 0x200
5.2 定时器驱动
drivers/timer/riscv_timer.c
RISC-V 标准 rdtime 指令读取的定时器,通过 riscv,timer compatible 匹配。
drivers/timer/riscv_aclint_timer.c
针对 ACLINT(Advanced Core Local Interruptor) 的定时器驱动,兼容:
riscv,clint0(旧版 CLINT)riscv,aclint-mtimer(新版 ACLINT)
5.3 随机数驱动:drivers/rng/riscv_zkr_rng.c
利用 RISC-V Zkr 扩展(Entropy Source CSR)提供硬件随机数:
c
static int riscv_zkr_read(struct udevice *dev, void *data, size_t len)
{
/* 通过 SEED CSR 读取熵值 */
for (i = 0; i < len; i++) {
do {
__asm__ __volatile__ ("csrr %0, seed" : "=r"(seed));
} while (seed & SEED_OP_BIST); /* 等待 BIST 完成 */
*(u8 *)(data + i) = seed & 0xFF;
}
}
5.4 串口驱动:drivers/serial/serial_htif.c
HTIF(Host-Target Interface)是 RISC-V 仿真环境(如 QEMU、Spike)使用的虚拟串口,仅在 __riscv_xlen == 64 时支持某些功能。
6. EFI / UEFI 支持:lib/efi_loader/efi_riscv.c
U-Boot 作为 UEFI 固件时,需要向操作系统暴露 RISCV_EFI_BOOT_PROTOCOL,这是 RISC-V 特有的 UEFI 协议:
c
struct riscv_efi_boot_protocol riscv_efi_boot_prot = {
.revision = RISCV_EFI_BOOT_PROTOCOL_REVISION,
.get_boot_hartid = efi_riscv_get_boot_hartid
};
static efi_status_t EFIAPI
efi_riscv_get_boot_hartid(struct riscv_efi_boot_protocol *this,
efi_uintn_t *boot_hartid)
{
*boot_hartid = gd->arch.boot_hart;
return EFI_SUCCESS;
}
作用:让通过 UEFI 启动的操作系统(如 Linux with EFI stub)能够查询到 Boot Hart ID。
7. 构建系统与配置
7.1 arch/riscv/Kconfig --- 架构级配置
RISC-V 架构的关键 Kconfig 选项:
| 配置项 | 说明 |
|---|---|
ARCH_RV32I / ARCH_RV64I |
基础 ISA 选择(自动选择 32BIT / 64BIT / PHYS_64BIT) |
RISCV_MMODE / RISCV_SMODE |
运行特权模式(M-mode 直接硬件控制 / S-mode 依赖 SBI) |
SPL_RISCV_MMODE / SPL_RISCV_SMODE |
SPL 阶段的特权模式 |
CMODEL_MEDLOW / CMODEL_MEDANY |
代码模型(RV64 通常用 medany) |
SBI_V01 / SBI_V02 |
SBI 规范版本 |
SMP / NR_CPUS |
多核支持与最大 CPU 数(2-32) |
RISCV_ISA_C/F/D/A/ZBB |
各 ISA 扩展编译开关 |
XIP |
Execute-In-Place(就地执行,用于 NOR Flash) |
SYS_CACHE_THEAD_CMO |
平头哥 T-Head 非标准缓存操作(C906/C910) |
7.2 arch/riscv/config.mk --- 编译标志
makefile
32bit-emul := elf32$(small-endian)riscv
64bit-emul := elf64$(small-endian)riscv
ifdef CONFIG_64BIT
KBUILD_LDFLAGS += -m $(64bit-emul)
EFI_LDS := elf_riscv64_efi.lds
PLATFORM_ELFFLAGS += -B riscv -O elf64-$(large-endian)riscv
endif
PLATFORM_CPPFLAGS += -ffixed-x3 -fpic
LDFLAGS_u-boot += --gc-sections -static -pie
关键标志:
-ffixed-x3:固定gp(x3)寄存器不被 GCC 用作通用寄存器(U-Boot 用 gp 存储全局数据指针)-fpic/-pie:位置无关代码,支持重定位elf_riscv64_efi.lds:64-bit EFI 应用程序链接脚本
8. 支持的硬件平台(RISC-V 64-bit)
| 厂商/平台 | 核心/SoC | 板级目录 | 特色 |
|---|---|---|---|
| QEMU | Virt | board/emulation/qemu-riscv |
官方仿真平台,支持 ACPI |
| SiFive | FU540 | board/sifive/unleashed |
首款商用 RISC-V Linux 板 |
| SiFive | FU740 | board/sifive/unmatched |
高性能桌面级 |
| 赛昉 StarFive | JH7110 | board/starfive/visionfive2 |
四核 64-bit,集成 GPU |
| 平头哥 T-Head | TH1520 | board/thead/th1520_lpi4a |
荔枝派 4A,C910 核心 |
| 进迭时空 Spacemit | K1 | board/spacemit/bananapi-f3 |
香蕉派 F3,8 核 X60 |
| 算能 Sophgo | CV1800B / SG2002 | board/sophgo/milkv_duo |
Milk-V Duo,低成本 |
| 嘉楠 Canaan | K230 | board/canaan/k230_canmv |
集成 KPU AI 加速器 |
| Microchip | PolarFire SoC | board/beagle/beaglev_fire |
BeagleV-Fire,FPGA+RV64 |
| Andes | AX45MP | board/andestech/ae350 |
Andes 商业 IP |
| OpenPiton | RV64 | board/openpiton/riscv64 |
学术研究多核平台 |
| AMD/Xilinx | MicroBlaze V | board/xilinx/mbv |
FPGA 软核 RISC-V |
9. 与旧版 U-Boot(2013 Sunxi)的架构对比
| 特性 | 旧版 U-Boot (2013.10-rc2, ARM/Sunxi) | 新版 U-Boot (2026.07-rc2, RISC-V) |
|---|---|---|
| 配置系统 | boards.cfg + include/configs/*.h 宏 |
Kconfig + defconfig + 设备树 |
| 驱动模型 | 无统一模型,直接调用全局函数 | Device Model(udevice/uclass) |
| 板级参数 | C 语言硬编码(dram_*.c) |
设备树 .dts 描述 |
| 启动流程 | BROM → SPL → U-Boot | 固件(OpenSBI)→ U-Boot / SPL → OS |
| 多核支持 | 基本无 SMP 支持 | 完整的 SMP + IPI + Hart 管理 |
| 架构抽象 | 以 ARMv7 为主,宏区分 | 32/64-bit 共用代码,宏选择指令宽度 |
| 特权模式 | ARM 的 M-mode/EL3(隐含) | 显式支持 M-mode / S-mode |
| 固件接口 | 无(直接操作硬件) | SBI(Supervisor Binary Interface) |
| UEFI 支持 | 无 | 完整 EFI Loader + RISC-V 专用 Protocol |
| ISA 扩展 | 固定(ARMv7 NEON/VFP) | 运行时解析 riscv,isa,动态启用扩展 |
| 代码体积优化 | 手动裁剪宏 | -ffunction-sections -fdata-sections --gc-sections |
10. 总结
最新 U-Boot 中的 RISC-V 64-bit 支持代码 展现了高度的工程成熟度:
-
优雅的 32/64-bit 共存 通过
CONFIG_32BIT/CONFIG_64BIT和宏化的汇编指令(LREG/SREG),同一份start.S同时支持 RV32 和 RV64,极大减少了代码重复。 -
SBI 是 RISC-V 软件生态的核心:U-Boot 在 S-mode 下运行时完全依赖 SBI 访问特权功能,这使得 U-Boot 本身不需要直接操作大量 M-mode CSR,提升了安全性和可移植性。SPL 通常运行在 M-mode,而主 U-Boot 可以运行在 S-mode。
-
设备树驱动化 :与旧版将 DRAM 时序、GPIO 配置硬编码在 C 文件中的做法不同,现代 RISC-V U-Boot 几乎完全依赖设备树进行硬件描述,板级代码量极少(如
qemu-riscv.c仅有 58 行)。 -
SMP 原生支持 :RISC-V 多核启动机制(Hart Lottery + IPI)被优雅地实现在
start.S和smp.c中,从核通过wfi休眠等待主核调度。 -
活跃的社区生态:从 QEMU 仿真到 SiFive、StarFive、平头哥、算能等商业芯片,U-Boot 的 RISC-V 支持覆盖了从微控制器到高性能应用处理器的全谱系。