当前位置: 首页 > news >正文

RISC-V: 固件与操作系统引导 | eg OpenSBI | 借助AI注释项目代码

引入:计算机没有黑魔法

例如我们都可以,通过指令来查看我们计算机的信息

“Everything is a State Machine”

  • 在许多状态之间不断切换程序就运行了起来
  • Makefile 也是程序;它也是状态机
  • 程序不好读的话,我们还可以调试它!
    • 计算机系统公理:你想到的就一定有人做到
    • “总有人会去发明轮子的”
    • 然后 相信编译器一定是对的

RISC-V: 固件与操作系统引导

灵魂三问

  1. RISC-V 系统是如何复位、执行什么固件、如何加载操作系统的?
  2. OpenSBI 的入口位于什么地方?
  3. _start 开始的 _try_lottery 是做什么的?

一、RISC-V 系统复位与启动流程详解

1. 复位机制与固件执行

RISC-V 系统的复位流程由硬件信号触发,所有核心跳转至复位向量地址(如 FU540 芯片的 0x1004),进入 M-mode 执行固化在 ROM 中的 ZSBL(第零阶段引导程序)

ZSBL 根据 MSEL 引脚配置加载下一阶段固件(如 FSBL),FSBL 负责初始化 DDR 内存、时钟和中断控制器,随后将控制权移交至 OpenSBI

关键步骤

  • ZSBL:从 ROM 中加载 FSBL 到 L2 LIM 缓存(地址 0x0800_0000)。
  • FSBL:配置 DDR 并加载 BBL 或 OpenSBI 到 DDR(地址 0x8000_0000)。
  • OpenSBI:作为 M-mode 运行时服务,通过 SBI 调用为操作系统提供硬件抽象。
2. 操作系统加载

OpenSBI 完成硬件初始化后,将跳转至操作系统的入口地址(如 Linux 内核的 _start,通过设备树(DTB)传递硬件参数。U-Boot 可作为中间加载器,支持从 Flash、网络等介质加载内核镜像。


二、OpenSBI 入口与调试方法

1. 入口地址

OpenSBI 的入口通常位于 0x8000_0000(DDR 起始地址),这是 RISC-V 虚拟化平台(如 QEMU virt)的标准配置。可通过 QEMU 启动参数验证:

qemu-system-riscv64 -machine virt -bios opensbi.bin

启动后 OpenSBI 会输出固件基地址和大小。

2. 调试建议
  • GDB 跟踪:在 QEMU 中启用 GDB 调试,断点设置在 _start 或 OpenSBI 初始化函数。
  • SBI 调用监控:通过修改 OpenSBI 源码添加日志,观察 ecall 指令的参数传递(如 EID 和 FID)。

三、_start_try_lottery 分析

1. 函数作用

_try_lottery 常见于多核启动场景,用于 动态选择主核(Boot HART)。其逻辑通常包括:

  • 检查当前 HART ID 是否为主核。
  • 若非主核,则进入等待循环;主核继续执行后续初始化(如加载设备树、启动调度器)。
2. 优化建议
  • 锁机制使用原子操作或硬件锁避免多核竞争。
  • 性能分析:通过 PMU(性能监控单元)统计各核启动延迟,优化任务分配策略。

四、建议

1. 调试与验证工具
  • QEMU + GDB:模拟 RISC-V 环境,单步跟踪启动流程。
  • RISC-V Trace:使用 SiFive Trace 或 Lauterbach 硬件跟踪器,捕获复位到操作系统的完整指令流。
2. 固件定制
  • OpenSBI 扩展:基于 SBI 规范开发自定义扩展(如安全启动),通过 ecall 提供专属服务。
  • U-Boot 集成:在 S-mode 运行 U-Boot,利用其文件系统和网络协议栈加载复杂内核。
3. 性能优化
  • DDR 初始化加速:预计算 PLL 配置参数,减少 FSBL 阶段的延时。
  • 并行启动:在多核系统中,让从核提前初始化外设,缩短整体启动时间。
4. 安全增强
  • IOPMP 配置:通过内存保护单元限制外设访问权限,防止恶意代码篡改引导链(参考 SpacemiT V100 设计,[1])。
  • 可信执行环境(TEE):集成 OP-TEE,在 OpenSBI 阶段验证内核签名。

五、总结与扩展方向

问题

核心要点

扩展方向

RISC-V 复位与启动

多阶段引导(ZSBL→FSBL→OpenSBI) + SBI 抽象层

安全启动、多核协同

OpenSBI 入口

0x8000_0000(DDR 基址) + SBI 服务网关

自定义 SBI 扩展、性能监控

_try_lottery

主核选择逻辑 + 多核同步机制

锁优化、PMU 分析

推荐实践

  1. 使用 QEMU 模拟不同 MSEL 配置,观察启动路径变化([3])。
  2. 修改 OpenSBI 源码,添加自定义 SBI 调用(如动态频率调节)。
  3. 结合 Lauterbach Trace32 分析 _try_lottery 的执行时序,优化多核启动效率。

OpenSBI

OpenSBI: 一个开源的 RISC-V 引导固件,用于管理 RISC-V 平台的启动过程。

  • 它实现了 SBI(Supervisor Binary Interface)规范,为操作系统提供了硬件抽象层,简化了系统启动和硬件管理。
  • OpenSBI 支持多种 RISC-V 处理器和平台,广泛用于嵌入式系统和开发板。

一、环境准备与依赖安装

1. 安装编译工具链
# 安装基础工具  
sudo apt-get install build-essential git device-tree-compiler  
# 安装 RISC-V 交叉编译器(以 riscv64-unknown-elf 为例)  
sudo apt-get install gcc-riscv64-unknown-elf  

确保交叉编译器路径已添加到环境变量(若未自动添加):

export PATH=$PATH:/usr/local/riscv64-unknown-elf/bin  
2. 获取 OpenSBI 源码
git clone https://github.com/riscv-software-src/opensbi.git  
cd opensbi  
# 切换至稳定版本(例如 v1.2)  
git checkout v1.2  

二、编译流程详解

1. 核心编译命令解析

OpenSBI 的编译通过 Makefile 控制,关键参数如下:

make CROSS_COMPILE=riscv64-unknown-elf- PLATFORM=generic  
  • CROSS_COMPILE:指定交叉编译工具链前缀(如 riscv64-unknown-elf-
  • PLATFORM:目标平台(如 genericqemu/virt,具体平台代码位于 platform/ 目录)
  • FW_JUMP_ADDR:可选参数,指定跳转地址(如 0x80200000 用于 Linux 内核加载)
2. 分步编译操作
# 创建输出目录  
mkdir -p output/opensbi  
# 执行编译(以 QEMU virt 平台为例)  
make CROSS_COMPILE=riscv64-unknown-elf- PLATFORM=qemu/virt  
# 复制生成文件至输出目录  
cp build/platform/qemu/virt/firmware/fw_jump.bin output/opensbi/  

生成文件说明

  • fw_jump.bin:跳转型固件,需手动指定下一阶段地址
  • fw_payload.bin:包含嵌入式内核的固件(如结合 U-Boot)

三、Makefile 关键逻辑解析

1. 平台配置加载

Makefile 会加载 platform/<PLATFORM>/objects.mk,定义平台相关源文件和链接脚本([1])。例如:

# platform/qemu/virt/objects.mk  
platform-objs-y += platform/qemu/virt/sbi_platform.o  
firmware-objs-$(CONFIG_FW_JUMP) += firmware/fw_jump.o  
2. 固件生成流程
  1. 汇编启动代码firmware/fw_base.S 定义入口 _start,处理多核同步和重定位([1])。
  2. C 代码初始化lib/sbi/sbi_init.c 初始化设备树、异常处理、定时器等([1])。
  3. 链接脚本控制firmware/fw_base.lds 指定代码段布局(.text 起始地址为 FW_TEXT_START,默认 0x80000000)。

四、调试与验证

1. QEMU 模拟运行
qemu-system-riscv64 -machine virt -bios output/opensbi/fw_jump.bin -nographic  

输出示例

OpenSBI v1.2  
Platform Name          : QEMU Virt Machine  
Firmware Base          : 0x80000000  
Firmware Size          : 112 KB  
Runtime SBI Version    : 1.0  
2. GDB 调试技巧
# 启动 QEMU 调试模式  
qemu-system-riscv64 -machine virt -bios fw_jump.bin -s -S -nographic  
# 另开终端连接 GDB  
riscv64-unknown-elf-gdb build/platform/qemu/virt/firmware/fw_jump.elf  
(gdb) target remote :1234  
(gdb) b _start       # 在入口点设置断点  
(gdb) c              # 继续执行  

观察点

  • 检查 a0(HART ID)、a1(设备树地址)寄存器初始化值
  • 跟踪 sbi_init 函数调用链([1])

五、常见问题与解决方案

1. 编译错误:未找到交叉编译器
  • 原因CROSS_COMPILE 路径未正确设置
  • 修复:确认编译器可执行文件存在,例如:
ls /usr/local/riscv64-unknown-elf/bin/riscv64-unknown-elf-gcc  
2. 启动时卡在 _wait_for_boot_hart
  • 分析:非主核(Boot HART)未正确释放锁
  • 调试:修改 fw_base.S 添加调试输出(需重新编译):
/* 在 _wait_for_boot_hart 循环前添加 */  
la a0, msg_wait  
call sbi_platform_console_puts  
3. 自定义平台适配
  1. 复制现有平台模板(如 platform/generic/)至新目录
  2. 修改 objects.mk 添加平台特定驱动
  3. 编译时指定 PLATFORM=new_platform

对于分析 Makefile

使用 make -nB > a.log 查看实际执行的命令,

  • grep 过滤干扰项;sed 让输出更易读
  • 然后再 vim 查看可使‘ ' --> /n
  • 逐级分析 Makefile 中的目标依赖关系。

我们也可以借助之前实现的 ag api:通过 output-->program 的想法, 借助 vim -->ai 来让 ai 帮我们实现注释

直接让AI帮你写注释 调格式,读五百多行的makefile文件也不是梦了🙂

相关文章:

  • Java架构师成长之路
  • llama源码学习·model.py[3]ROPE旋转位置编码(2)旋转角度生成代码
  • vue-cli如何正确关闭prefetch和preload
  • 让S7-1200与DeepSeek联动(转)
  • MCU vs SoC
  • vue3 UnwrapRef 与 unref的区别
  • [极客大挑战 2019]BuyFlag-3.23BUUCTF练习day5(3)
  • LeetCode HOT100系列题解之岛屿数量(10/100)
  • 【Keil5-开发技巧】
  • VSCode 生成HTML 基本骨架
  • 【CICD】Ansible知识库
  • 【MySQL数据库】触发器与事件
  • 从失衡到平衡:手撕 AVL 树的插入旋转操作
  • Cursor 一键自动无限续杯(3月24日)亲测有效
  • 黑马点评-UV统计
  • 2025前端面试题记录
  • 23种设计模式-创建型模式-工厂方法
  • 【USTC 计算机网络】第三章:传输层 - 传输层概述及其服务、多路复用与解复用、无连接传输:UDP
  • Python 集合操作大全:从入门到精通,新手学习避坑指南
  • Web PKI技术基础知识
  • 杨建全已任天津市委副秘书长、市委市政府信访办主任
  • 多个“首次”!上市公司重大资产重组新规落地
  • 吉利汽车一季度净利润大增264%,称整合极氪后实现整体效益超5%
  • 黑龙江省政府副秘书长许振宇,拟任正厅级领导
  • 国家卫生健康委通报关于肖某引发舆情事件调查处置进展情况
  • 中央宣传部、全国妇联联合发布2025年“最美家庭”