1. QEMU RISC-V virt 机器最小固件启动

QEMU RISC-V virt 机器最小固件启动 学习卡片

任务说明

本案例目标是在Linux主机上安装QEMU系统模拟器与RISC-V交叉编译工具链,并验证QEMU能够启动一个最小的RISC-V固件镜像。完成本案例后,你将拥有一个可复用的RISC-V固件开发与调试基础环境。

环境准备

本案例以Ubuntu 22.04 LTS为参考环境。需要安装的软件包包括:qemu-system-misc(提供RISC-V 64位系统模拟)、gcc-riscv64-unknown-elf(交叉编译器)、gdb-multiarch(用于后续调试)。执行以下命令安装:

sudo apt update
sudo apt install -y qemu-system-misc gcc-riscv64-unknown-elf gdb-multiarch

安装完成后,验证工具版本:

qemu-system-riscv64 --version
riscv64-unknown-elf-gcc --version

最小固件实现

创建一个最小固件,该固件在启动后向主机串口输出一行字符,然后进入死循环。这是验证QEMU启动流程的最简可执行程序。

新建文件 start.S:

.section .text.init
.globl _start
_start:
    # 设置栈指针(使用链接脚本中定义的栈顶)
    la sp, _stack_top

    # 调用C入口
    call main

loop:
    wfi
    j loop

新建文件 main.c:

#define UART0_BASE 0x10000000L
#define UART0_THR  (*(volatile unsigned char *)(UART0_BASE + 0x00))

void uart_putc(char c) {
    UART0_THR = c;
}

void uart_puts(const char *s) {
    while (*s) {
        uart_putc(*s++);
    }
}

void main(void) {
    uart_puts("Hello RISC-V QEMU!\n");
    while (1) {
        // 空循环,等待中断或复位
    }
}

新建链接脚本 link.ld:

OUTPUT_ARCH(riscv)
ENTRY(_start)

MEMORY
{
    RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M
}

SECTIONS
{
    .text : {
        *(.text.init)
        *(.text)
    } > RAM

    .rodata : {
        *(.rodata)
    } > RAM

    .data : {
        *(.data)
    } > RAM

    .bss : {
        *(.bss)
    } > RAM

    . = ALIGN(16);
    . = . + 0x1000;  /* 预留4KB栈空间 */
    _stack_top = .;
}

编译与链接

执行以下命令生成固件镜像 firmware.elf:

riscv64-unknown-elf-gcc -march=rv64gc -mabi=lp64d -nostdlib -fno-builtin -nostartfiles \
    -T link.ld -o firmware.elf start.S main.c

参数说明:-march=rv64gc 指定64位RISC-V指令集(含乘法除法与原子指令),-mabi=lp64d 使用64位整数与双精度浮点ABI,-nostdlib 与 -nostartfiles 表示不使用标准库与标准启动文件,-T link.ld 指定链接脚本。

QEMU启动验证

使用以下命令启动QEMU并运行固件:

qemu-system-riscv64 -machine virt -nographic -bios none -kernel firmware.elf

参数说明:-machine virt 选择virt虚拟开发板(该板型将UART0映射在0x10000000地址),-nographic 将串口重定向到当前终端,-bios none 表示不加载BIOS,直接使用 -kernel 指定的固件。

验证方法与判定标准

执行上述QEMU命令后,终端应显示以下输出:

Hello RISC-V QEMU!

随后程序进入死循环,QEMU进程保持运行状态(不会自动退出)。判定标准:

  1. 终端出现上述字符串,说明UART驱动与启动流程正确;
  2. QEMU进程不退出,说明固件正常进入循环,没有触发异常复位;
  3. 按 Ctrl+A 然后按 X 可退出QEMU。
QEMU RISC-V 启动验证拓扑 Linux 主机 riscv64-unknown-elf-gcc 交叉编译 → firmware.elf qemu-system-riscv64 -machine virt -nographic -bios none -kernel firmware.elf QEMU virt 机器 RISC-V CPU rv64gc RAM 0x80000000 firmware.elf 加载执行 UART0 @ 0x10000000 输出 "Hello RISC-V QEMU!" 终端显示 Hello RISC-V QEMU! (进程保持运行) virt机器内存映射:ROM 0x1000 | 固件入口 0x80000000 | UART 0x10000000

常见问题排查

  • QEMU提示无法打开设备:确认已安装 qemu-system-misc 包,且当前用户有权限访问终端设备。
  • 编译报错找不到头文件:本案例未使用任何标准库头文件,若添加了 #include 语句,需使用 -nostdinc 并自行提供头文件,或改用 riscv64-unknown-elf-gcc 自带的freestanding头文件。
  • 无输出且QEMU卡死:检查链接脚本中 _start 是否被正确放置在入口地址,可使用 riscv64-unknown-elf-objdump -d firmware.elf 查看反汇编确认入口代码位置。

至此,QEMU RISC-V开发环境搭建完成,后续案例将在此基础上进行固件开发与调试。