gem5 运行 risc-v 64bit 的 U-Boot

RISC-V 64位环境下从源码编译 gem5、编译 U-Boot 并用 gem5 运行 U-Boot的操作指南。


环境准备

1. 安装系统依赖

bash 复制代码
# Ubuntu/Debian
sudo apt update
sudo apt install -y build-essential git m4 scons zlib1g zlib1g-dev \
    libprotobuf-dev protobuf-compiler libprotoc-dev \
    libgoogle-perftools-dev python3-dev python3-six \
    python-is-python3 libboost-all-dev pkg-config \
    libncurses5-dev libssl-dev bc flex bison \
    autoconf automake libtool curl gawk \
    libmpc-dev libmpfr-dev libgmp-dev texinfo \
    gperf patchutils zlib1g-dev libexpat-dev

2. 安装 RISC-V 64位交叉编译工具链

有两种方式:apt 直接安装 (推荐,简单)或从源码编译

方式一:apt 直接安装(推荐)
bash 复制代码
sudo apt install -y gcc-riscv64-linux-gnu g++-riscv64-linux-gnu \
    binutils-riscv64-linux-gnu gdb-multiarch

验证安装:

bash 复制代码
riscv64-linux-gnu-gcc --version
# 应显示版本信息,如 gcc 13.x.x
方式二:从源码编译工具链
bash 复制代码
# 下载源码(约6.65GB)
git clone https://github.com/riscv-collab/riscv-gnu-toolchain
cd riscv-gnu-toolchain

# 编译 Linux 工具链(支持 glibc)
./configure --prefix=/opt/riscv
make linux -j$(nproc)

# 添加到环境变量
echo 'export PATH=/opt/riscv/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# 验证
riscv64-unknown-linux-gnu-gcc --version

第一步:从源码编译 gem5

bash 复制代码
# 下载 gem5 源码
git clone https://github.com/gem5/gem5.git
cd gem5

# 编译 RISC-V 版本的 gem5(推荐使用 opt 版本,支持调试符号且速度较快)
scons build/RISCV/gem5.opt -j$(nproc)

# 如需更快的仿真速度,可编译 fast 版本(但不支持调试)
# scons build/RISCV/gem5.fast -j$(nproc)

编译完成后,gem5 可执行文件位于 build/RISCV/gem5.opt

验证 gem5 安装:

bash 复制代码
./build/RISCV/gem5.opt --version

第二步:从源码编译 U-Boot

1. 下载 U-Boot 源码

bash 复制代码
git clone https://github.com/u-boot/u-boot.git
cd u-boot

2. 编译 U-Boot

U-Boot 有多种 RISC-V 配置选项,根据需求选择:

选项 A:Machine 模式 U-Boot(最简配置,适合 baremetal)

U-Boot 直接运行在 Machine 模式,不需要 OpenSBI。

bash 复制代码
# 配置为 RISC-V 64位 QEMU virt 平台的 Machine 模式
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- qemu-riscv64_defconfig

关于EFI CAPSULE

并且在配置中关闭 EFI 胶囊工具

方法一:通过 menuconfig 禁用

bash 复制代码
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- menuconfig

按 / 打开搜索,输入 MKEFICAPSULE

定位到 CONFIG_TOOLS_MKEFICAPSULE,将其设为 n(即取消勾选)

或者更彻底地:搜索 EFI_CAPSULE,关闭 CONFIG_EFI_CAPSULE 及其子项

编译
bash 复制代码
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc)

编译产物:

  • u-boot --- ELF 格式
  • u-boot.bin --- 二进制格式
选项 B:Supervisor 模式 U-Boot(需要 OpenSBI)

U-Boot 运行在 S-mode,需要 M-mode 的 OpenSBI 固件提供 SBI 接口。

bash 复制代码
# 配置为 S-mode
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- qemu-riscv64_smode_defconfig

# 编译
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc)

如需配合 OpenSBI,还需编译 OpenSBI 并将 U-Boot 打包进去:

bash 复制代码
# 编译 OpenSBI
cd ..
git clone https://github.com/riscv-software-src/opensbi.git
cd opensbi
make CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic \
    FW_PAYLOAD_PATH=../u-boot/u-boot.bin -j$(nproc)

# 产物在 build/platform/generic/firmware/fw_payload.elf

第三步:使用 gem5 运行 U-Boot

gem5 运行 RISC-V 全系统仿真使用自己编写的 run_uboot_riscv.py 脚本,直接存储在 gem5/ 下 。

方案一:运行 Machine 模式 U-Boot(Baremetal 方式)

对于 Machine 模式的 U-Boot,使用 --bare-metal 参数直接运行:

bash 复制代码
$ cd gem5/

$ ./build/RISCV/gem5.opt run_uboot_riscv.py \
    --kernel=/home/hipper/ex_gem5_/tmp7/u-boot/u-boot \
    --cpu-type=atomic \
    --mem-size=256MB

参数说明:

参数 说明
--kernel 指定 U-Boot ELF 文件路径
--bare-metal 使用裸机模式运行(RiscvBareMetal workload)
--cpu-type CPU 类型(AtomicSimpleCPU/TimingSimpleCPU/O3CPU)
--mem-size 内存大小
--num-cpus CPU 核心数

效果

其中 run_uboot_riscv.py 脚本内容如下:

python 复制代码
#!/usr/bin/env python3
"""
使用 gem5 Standard Library 运行 RISC-V U-Boot (Baremetal)
关键修复: 手动设置 _is_fullsystem 标记,使 _setup_board() 能正常执行
"""

import argparse
import m5

from gem5.components.boards.riscv_board import RiscvBoard
from gem5.components.cachehierarchies.classic.no_cache import NoCache
from gem5.components.memory import SingleChannelDDR3_1600
from gem5.components.processors.simple_processor import SimpleProcessor
from gem5.components.processors.cpu_types import CPUTypes
from gem5.isas import ISA
from gem5.simulate.simulator import Simulator
from gem5.utils.requires import requires

from m5.objects import CowDiskImage, RawDiskImage
import tempfile
import os

requires(isa_required=ISA.RISCV)

# ========== 命令行参数 ==========
parser = argparse.ArgumentParser(description="Run U-Boot on RISC-V with gem5 stdlib")

parser.add_argument(
    "--kernel", required=True, type=str,
    help="Path to U-Boot ELF file (e.g., /path/to/u-boot/u-boot)"
)
parser.add_argument(
    "--bootloader", default=None, type=str,
    help="Path to OpenSBI bootloader (optional, e.g., fw_jump.elf)"
)
parser.add_argument(
    "--cpu-type", default="atomic", type=str,
    choices=["atomic", "timing", "o3"],
    help="CPU type: atomic, timing, or o3"
)
parser.add_argument(
    "--num-cores", default=1, type=int,
    help="Number of CPU cores"
)
parser.add_argument(
    "--mem-size", default="256MB", type=str,
    help="Memory size (e.g., 256MB, 1GB)"
)

args = parser.parse_args()

# ========== 系统组件配置 ==========
cpu_type_map = {
    "atomic": CPUTypes.ATOMIC,
    "timing": CPUTypes.TIMING,
    "o3": CPUTypes.O3,
}
cpu_type = cpu_type_map[args.cpu_type]

processor = SimpleProcessor(
    cpu_type=cpu_type,
    isa=ISA.RISCV,
    num_cores=args.num_cores,
)

cache_hierarchy = NoCache()
memory = SingleChannelDDR3_1600(size=args.mem_size)

# RISC-V 主板
board = RiscvBoard(
    clk_freq="1GHz",
    processor=processor,
    memory=memory,
    cache_hierarchy=cache_hierarchy,
)

# ========== 关键修复: 正确初始化 Full System 模式 ==========
# _set_fullsystem(True) 会设置 _is_fs=True,并自动调用 _setup_board()
# _setup_board() 会创建 RiscvBootloaderKernelWorkload 和 HiFive 平台
board._set_fullsystem(True)

# _pre_instantiate() 会检查 self._bootloader,但它是 set_kernel_disk_workload 的私有属性
# 对于 baremetal 模式,手动初始化为空列表
board._bootloader = []

# 标记 workload 已设置(某些 board 或 simulator 会检查)
board.set_is_workload_set(True)

# ========== 设置 workload 属性 ==========
if args.bootloader:
    # 方式 A: OpenSBI + U-Boot
    # _pre_instantiate 会检测到 _bootloader 非空并自动设置 bootloader_addr/kernel_addr/entry_point
    board._bootloader = [args.bootloader]
    board.workload.object_file = args.kernel
    print(f"[INFO] Running with OpenSBI bootloader: {args.bootloader}")
    print(f"[INFO] U-Boot (kernel) at: {args.kernel}")
else:
    # 方式 B: 纯 U-Boot Baremetal(推荐)
    # _pre_instantiate 会检测到 _bootloader 为空并设置 kernel_addr=0x80000000, entry_point=0x80000000
    board.workload.object_file = args.kernel
    # generate_device_tree 需要 command_line 不为空,否则 FdtPropertyStrings 会抛异常
    board.workload.command_line = "console=ttyS0"
    print(f"[INFO] Running U-Boot in bare-metal mode: {args.kernel}")

# ========== 提供 dummy disk image 以满足 VirtIOBlock 要求 ==========
# RiscvBoard._setup_board() 总是创建 virtio disk,其 image 参数是必需的
# 对于 baremetal U-Boot,我们不需要真正的 disk,但 gem5 实例化时需要这个参数
dummy_disk_path = os.path.join(tempfile.gettempdir(), "gem5_uboot_dummy.img")
if not os.path.exists(dummy_disk_path):
    with open(dummy_disk_path, "wb") as f:
        f.write(b"\x00" * 512)

dummy_image = CowDiskImage(child=RawDiskImage(read_only=True), read_only=False)
dummy_image.child.image_file = dummy_disk_path
board.disk.vio.image = dummy_image

# 连接 I/O 设备到总线并设置 PMA(正常由 set_kernel_disk_workload -> _add_disk_to_board 完成)
board._setup_io_devices()
board._setup_pma()

# ========== 运行仿真 ==========
simulator = Simulator(board=board)
print("[INFO] Starting simulation...")
simulator.run()
print("[INFO] Simulation finished")

方案二:运行 S-mode U-Boot + OpenSBI

如果编译了 fw_payload.elf(OpenSBI + U-Boot 打包),可以这样运行:

bash 复制代码
./build/RISCV/gem5.opt configs/example/riscv/fs_linux.py \
    --kernel=/path/to/opensbi/build/platform/generic/firmware/fw_payload.elf \
    --bare-metal \
    --cpu-type=AtomicSimpleCPU \
    --mem-size=256MB

或者分别指定 bootloader(OpenSBI)和 kernel(U-Boot):

bash 复制代码
./build/RISCV/gem5.opt configs/example/riscv/fs_linux.py \
    --bootloader=/path/to/opensbi/build/platform/generic/firmware/fw_jump.elf \
    --kernel=/path/to/u-boot/u-boot.bin \
    --cpu-type=AtomicSimpleCPU \
    --mem-size=256MB

查看仿真输出

方式一:使用 telnet 连接终端

gem5 默认在 3456 端口监听终端连接:

bash 复制代码
# 新开一个终端
telnet localhost 3456

方式二:使用 m5term(推荐)

bash 复制代码
# 编译 m5term
cd gem5/util/term
make

# 连接
./gem5term localhost 3456

完整流程汇总

bash 复制代码
# ============ 1. 安装依赖 ============
sudo apt install -y build-essential git m4 scons zlib1g zlib1g-dev \
    libprotobuf-dev protobuf-compiler libprotoc-dev \
    libgoogle-perftools-dev python3-dev python3-six \
    python-is-python3 libboost-all-dev pkg-config \
    libncurses5-dev libssl-dev bc flex bison \
    gcc-riscv64-linux-gnu g++-riscv64-linux-gnu \
    binutils-riscv64-linux-gnu

# ============ 2. 编译 gem5 ============
git clone https://github.com/gem5/gem5.git
cd gem5
scons build/RISCV/gem5.opt -j$(nproc)

# ============ 3. 编译 U-Boot ============
cd ..
git clone https://github.com/u-boot/u-boot.git
cd u-boot
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- qemu-riscv64_defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc)

# ============ 4. 运行 U-Boot ============
cd ../gem5
./build/RISCV/gem5.opt configs/example/riscv/fs_linux.py \
    --kernel=../u-boot/u-boot \
    --bare-metal \
    --cpu-type=AtomicSimpleCPU \
    --mem-size=256MB

# ============ 5. 查看输出(另开终端) ============
telnet localhost 3456
# 或
# ./util/term/m5term localhost 3456

注意事项

问题 解决方案
gem5 编译报错缺少 zlib sudo apt install zlib1g-dev
U-Boot 编译报错缺少 ncurses sudo apt install libncurses5-dev
gem5 运行后终端无输出 确认使用了 --bare-metal 参数,检查 --kernel 路径正确
内存太小 U-Boot 启动失败 增大 --mem-size,建议至少 128MB
需要调试信息 编译 gem5.opt(而非 fast),加 --debug-flags=Exec

如果你需要在 U-Boot 中加载 Linux 内核,还需要准备 Linux 内核镜像和根文件系统,并通过 gem5 的 --kernel--disk-image 参数指定。

相关推荐
木由里予2 天前
RISC-V 32 位基础指令集(RV32I)完整参考手册
risc-v
一枝小雨3 天前
RISC-V架构sp寄存器 & RISC-V架构下FreeRTOS任务上下文保存与恢复
单片机·架构·嵌入式·risc-v·rtos·内核原理
一枝小雨3 天前
RISC-V架构的中断与异常处理机制学习笔记
单片机·架构·嵌入式·risc-v·内核原理·中断与异常
一枝小雨3 天前
什么是标准C函数:以RISC-V架构下的C函数为例
c语言·risc-v·内核原理
咕咚.萌西4 天前
RISCV AS汇编器
risc-v
嵌入式小企鹅5 天前
UiPath推出AI编程“总指挥台”,SiFive发布RISC-V第三代猛兽
人工智能·学习·google·程序员·ai编程·risc-v·开源工具
阿祖_in_coding7 天前
RISC-V ACT测试
risc-v
yusur7 天前
开芯院院长唐丹一行来访中科驭数 共探RISC-V与DPU算力协同创新之路
risc-v
加强洁西卡7 天前
【RISC-V】RVV选摘
risc-v