U-Boot 最新版 RISC-V 64-bit 平台代码结构分析
分析对象:最新主线 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,通过宏区分寄存器宽度:
#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 #endifstart.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:
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 / sret32-bit 与 64-bit 差异:仅体现在
LREG/SREG的指令选择和栈帧大小(128 bytes vs 256 bytes)。
4. 架构核心 C 代码分析
4.1arch/riscv/cpu/cpu.c— CPU 初始化与 ISA 扩展管理
这是 RISC-V 架构最重要的 C 文件之一,负责:
4.1.1 ISA 字符串解析与扩展检测
staticDECLARE_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 功能配置
intriscv_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) */#ifCONFIG_IS_ENABLED(SMP)riscv_init_ipi();#endif}64-bit 特有关系:
PHYS_64BIT在ARCH_RV64I时被自动选中,支持 64-bit 物理地址空间。
4.2arch/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指令:
structsbiretsbi_ecall(intext,intfid,unsignedlongarg0,...){registeruintptr_ta0asm("a0")=arg0;registeruintptr_ta7asm("a7")=ext;// 扩展 IDasmvolatile("ecall":"+r"(a0),"+r"(a1):"r"(a2),...,"r"(a7):"memory");ret.error=a0;ret.value=a1;returnret;}支持的 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 值
#if__riscv_xlen==32sbi_ecall(SBI_EXT_SET_TIMER,SBI_FID_SET_TIMER,stime_value,stime_value>>32,0,0,0,0);#elsesbi_ecall(SBI_EXT_SET_TIMER,SBI_FID_SET_TIMER,stime_value,0,0,0,0,0);#endif4.3arch/riscv/lib/bootm.c— 引导 Linux 内核
RISC-V 平台引导 Linux 的bootm实现非常简洁:
staticvoidboot_jump_linux(structbootm_headers*images,intflag){void(*kernel)(ulong hart,void*dtb);kernel=(void(*)(ulong,void*))images->ep;cleanup_before_linux();#ifdefCONFIG_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.4arch/riscv/lib/fdt_fixup.c— 设备树修正
在将设备树传递给 Linux 之前,U-Boot 需要执行若干架构特定的修正:
- 保留内存拷贝:将固件传递的 FDT 中的
/reserved-memory节点拷贝到最终 FDT - boot-hartid 写入:在
/chosen节点中写入boot-hartid属性,告知 Linux 哪个核是主核 - ACPI 支持:在
CONFIG_EFI_LOADER启用时,配合 UEFI 启动流程
intarch_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.5arch/riscv/lib/smp.c— 对称多处理(SMP)
U-Boot 的 RISC-V SMP 支持通过IPI(核间中断)实现:
intsmp_call_function(ulong addr,ulong arg0,ulong arg1,intwait){structipi_dataipi={.addr=addr,.arg0=arg0,.arg1=arg1};returnsend_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
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,timercompatible 匹配。
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-VZkr 扩展(Entropy Source CSR)提供硬件随机数:
staticintriscv_zkr_read(structudevice*dev,void*data,size_tlen){/* 通过 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 协议:
structriscv_efi_boot_protocolriscv_efi_boot_prot={.revision=RISCV_EFI_BOOT_PROTOCOL_REVISION,.get_boot_hartid=efi_riscv_get_boot_hartid};staticefi_status_tEFIAPIefi_riscv_get_boot_hartid(structriscv_efi_boot_protocol*this,efi_uintn_t*boot_hartid){*boot_hartid=gd->arch.boot_hart;returnEFI_SUCCESS;}作用:让通过 UEFI 启动的操作系统(如 Linux with EFI stub)能够查询到 Boot Hart ID。
7. 构建系统与配置
7.1arch/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.2arch/riscv/config.mk— 编译标志
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 支持覆盖了从微控制器到高性能应用处理器的全谱系。
