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 核心上电后执行的第一段代码 ,同时支持 RV32RV64,通过宏区分寄存器宽度:

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 的关键执行步骤

  1. 读取 Hart ID :通过 CSR_MHARTID(M-mode)获取当前硬件线程 ID,保存到线程指针寄存器 tp
  2. 保存 DTB 指针 :固件(如 OpenSBI)传入的设备树地址保存在 s1
  3. 设置早期 Trap 处理程序 :将 trap_entry 写入 mtvec / stvec
  4. 屏蔽中断 :向 mie / sie 写入 0
  5. 设置栈指针
    • 根据 CONFIG_STACK_SIZE_SHIFT(默认 14,即 16KB)为每个 CPU 分配独立栈
    • SMP 场景下:sp = BASE - (hart_id << shift)
  6. Hart 彩票机制(非 XIP) :通过原子操作 amoswap 选出唯一的初始化主核(Boot Hart),其余核进入 secondary_hart_loop 等待 IPI
  7. 初始化全局数据(GD) :调用 board_init_f_init_reserve() 设置 gd 结构体
  8. 保存启动信息 :将 DTB 地址和 Boot Hart ID 存入 gd->arch
  9. 调用 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_64BITARCH_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 位分别传入 a0a1
  • 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 需要执行若干架构特定的修正:

  1. 保留内存拷贝 :将固件传递的 FDT 中的 /reserved-memory 节点拷贝到最终 FDT
  2. boot-hartid 写入 :在 /chosen 节点中写入 boot-hartid 属性,告知 Linux 哪个核是主核
  3. 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);
}

执行流程

  1. 遍历设备树 /cpus 节点下的所有可用 hart
  2. 跳过 boot hart 和超出 CONFIG_NR_CPUS 范围的核
  3. 将目标地址和参数写入 gd->arch.ipi[hart]
  4. 通过内存屏障 __smp_store_release 标记有效位
  5. 调用 riscv_send_ipi(hart) 触发硬件 IPI
  6. 从核在 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-frequencytimebase-frequencymmu-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 支持代码 展现了高度的工程成熟度:

  1. 优雅的 32/64-bit 共存 通过 CONFIG_32BIT / CONFIG_64BIT 和宏化的汇编指令(LREG/SREG),同一份 start.S 同时支持 RV32 和 RV64,极大减少了代码重复。

  2. SBI 是 RISC-V 软件生态的核心:U-Boot 在 S-mode 下运行时完全依赖 SBI 访问特权功能,这使得 U-Boot 本身不需要直接操作大量 M-mode CSR,提升了安全性和可移植性。SPL 通常运行在 M-mode,而主 U-Boot 可以运行在 S-mode。

  3. 设备树驱动化 :与旧版将 DRAM 时序、GPIO 配置硬编码在 C 文件中的做法不同,现代 RISC-V U-Boot 几乎完全依赖设备树进行硬件描述,板级代码量极少(如 qemu-riscv.c 仅有 58 行)。

  4. SMP 原生支持 :RISC-V 多核启动机制(Hart Lottery + IPI)被优雅地实现在 start.Ssmp.c 中,从核通过 wfi 休眠等待主核调度。

  5. 活跃的社区生态:从 QEMU 仿真到 SiFive、StarFive、平头哥、算能等商业芯片,U-Boot 的 RISC-V 支持覆盖了从微控制器到高性能应用处理器的全谱系。


相关推荐
Eloudy3 个月前
CHI 开发备忘 12 记 -- CHI spec 12 链路层
人工智能·算法·arch·hpc
Eloudy3 个月前
CHI 开发备忘 08 记 -- CHI spec 08
人工智能·arch·hpc
Eloudy3 个月前
稀疏矩阵的 CSR 格式(Compressed Sparse Row)
人工智能·算法·arch·hpc
Eloudy3 个月前
CHI 开发备忘 06 记 -- CHI spec 06 独占访问
人工智能·arch·hpc
Eloudy3 个月前
直接法 读书笔记 07 第7章 减少填充的排序
人工智能·arch·hpc
Eloudy3 个月前
CHI 开发备忘 05 记 -- CHI spec 05 互连协议流程
人工智能·ai·arch·hpc
Eloudy3 个月前
CHI 开发备忘 03 记 -- CHI spec 03 网络层
人工智能·ai·arch·hpc
Eloudy3 个月前
CHI 开发备忘 02 记 -- CHI spec 02 事务
人工智能·ai·arch·hpc
Eloudy3 个月前
CHI 开发备忘 01 记 -- CHI spec 01章 简介
arch