1. 课程导入

欢迎进入《RISC-V Linux系统移植工程》课程。RISC-V 作为开放指令集架构,正在从嵌入式 MCU、边缘计算网关一路走向服务器与桌面平台;而 Linux 作为最主流的开源操作系统,是承载 RISC-V 生态应用的关键底座。本课程的目标,不是让你停留在“会编译一个内核”的层面,而是建立一套完整的系统移植工程能力:能够从一块 RISC-V 开发板或一个 RISC-V 虚拟平台出发,独立完成引导加载、内核适配、设备树描述、根文件系统构建、驱动调试与系统启动优化,最终让 Linux 在目标硬件上稳定运行。

一、学习目标:从“能启动”到“可交付”

本课程围绕四个层次递进的能力目标展开。第一层是认知层:理解 RISC-V 特权架构、Linux 启动链路、设备树机制与交叉编译工具链的基本原理。第二层是操作层:能够独立配置和编译 U-Boot、Linux 内核、BusyBox/Buildroot 根文件系统,并完成镜像烧录与串口调试。第三层是适配层:能够根据具体 SoC 或 FPGA 平台修改设备树、裁剪内核配置、适配时钟、中断控制器、串口、存储与网络等关键外设。第四层是工程层:能够定位启动失败、内核 panic、驱动 probe 失败等典型问题,形成可复现的调试记录与移植文档。

学习本课程后,你应当能够回答:RISC-V Linux 从上电到用户态 shell 之间经历了哪些阶段?每个阶段由谁负责?设备树如何把硬件信息传递给内核?当串口没有输出时,应该按什么顺序排查?

二、核心概念:五个必须建立的心智模型

RISC-V Linux 系统移植涉及大量术语,但真正决定移植成败的核心概念可以归纳为五个。理解它们之间的关系,比死记命令更重要。

  • RISC-V 特权级与启动模式:M 模式负责最底层硬件初始化,S 模式运行 Linux 内核,U 模式运行用户程序。移植工作通常从 M 模式的 BootROM/SPL 开始,最终把控制权交给 S 模式内核。
  • 交叉编译工具链:宿主机通常是 x86_64,目标机是 RISC-V。工具链前缀如 riscv64-linux-gnu- 或 riscv64-unknown-linux-gnu-,决定了编译出的指令集、ABI 和浮点调用约定。
  • 设备树(Device Tree):以 .dts 描述硬件,编译为 .dtb 后由引导程序传给内核。它是“内核与板级硬件解耦”的关键机制。
  • 引导链:典型链路为 BootROM → SPL → U-Boot → Linux Kernel → init → 用户态。每一级都可能成为移植的瓶颈。
  • 根文件系统:内核启动后需要挂载 rootfs,包含 /init、/bin、/etc、/dev 等。Buildroot、Yocto、BusyBox 是常见构建方式。

三、整体知识框架:一张图看清移植工程的全貌

下图给出本课程的知识框架与实战路线图。左侧是宿主机开发环境,中间是 RISC-V Linux 启动链路,右侧是目标硬件与调试手段。图中同时标出了各阶段的关键产物与常见故障点,便于你在后续章节中定位自己的位置。

RISC-V Linux 系统移植工程 · 知识框架与实战路线图 宿主机开发环境 交叉编译工具链 riscv64-linux-gnu-gcc 源码与配置 U-Boot / Linux / Buildroot 设备树源文件 board.dts → board.dtb 镜像打包与烧录 mkimage / dd / openocd 版本管理与文档 git / patch / 移植记录 RISC-V Linux 启动链路 BootROM / 固化引导 上电复位,加载 SPL SPL / 二级引导 初始化 DDR、时钟、串口 U-Boot 加载内核与 dtb,传递启动参数 Linux Kernel 解压、建页表、初始化驱动 init / 用户态 挂载 rootfs,启动 shell 关键产物:u-boot.bin / Image / dtb / rootfs 每一级都需验证后再进入下一级 目标硬件与调试 RISC-V 开发板 / FPGA SoC、DDR、Flash、外设 串口 / JTAG 调试 minicom / openocd / gdb 启动日志分析 earlycon / panic / oops 外设验证 UART、SD、Ethernet、USB 性能与稳定性 启动时间、内存占用、压力测试 调试反馈:根据启动日志回溯修改设备树、内核配置或引导参数

四、实战路线图:按阶段推进,避免“一上来就改内核”

很多初学者拿到 RISC-V 开发板后,第一反应是直接编译内核并烧录,结果串口没有任何输出,陷入无从下手的困境。正确的实战路线应当遵循“先通链路,再调外设,最后做优化”的原则。

  1. 环境搭建阶段:安装交叉编译工具链,准备串口终端、烧录工具和源码仓库,确认宿主机可以正常编译出 RISC-V 可执行文件。
  2. 引导链路打通阶段:先让 BootROM/SPL 有输出,再让 U-Boot 进入命令行,最后通过 U-Boot 的 tftpboot 或 load 命令加载内核与 dtb。
  3. 内核启动阶段:配置 defconfig,确认串口驱动、时钟驱动、中断控制器已编译进内核,观察 Starting kernel ... 之后的日志。
  4. 根文件系统阶段:使用 Buildroot 或 BusyBox 生成 rootfs,配置 bootargs 中的 root= 与 console=,确保内核能挂载并执行 /init。
  5. 外设适配阶段:逐个验证 GPIO、I2C、SPI、SD、以太网等设备,修改设备树节点与 pinctrl 配置。
  6. 工程化收尾阶段:整理移植文档、固化启动参数、优化启动时间与镜像体积,形成可重复构建的脚本。

五、常见误区与复盘要点

误区一:忽略工具链 ABI 匹配。 如果内核使用 lp64d 而根文件系统使用 lp64,用户态程序可能无法运行。务必统一 --with-abi 与 --with-arch 配置。
误区二:设备树与硬件不一致。 串口寄存器地址、时钟频率、中断号写错,都会导致内核启动到某一阶段后静默死机。建议先用已知可用的 dtb 验证,再逐步修改。
复盘建议: 每次移植失败后,记录“最后一行日志 + 当前阶段 + 怀疑对象 + 验证方法”。例如最后一行是 Uncompressing Linux... done, booting the kernel.,说明问题多半在内核早期初始化或串口驱动,而不是 U-Boot。

六、关键阶段与验证清单

阶段 关键产物 验证方式 常见故障
工具链 riscv64-linux-gnu-gcc 编译 hello.c 并 file 查看架构 ABI 不匹配、缺少动态库
SPL/U-Boot u-boot.bin / u-boot.itb 串口出现 U-Boot 版本与命令行 DDR 初始化失败、时钟配置错误
内核 Image / Image.gz 打印内核版本与设备树模型 串口无输出、earlycon 未配置
设备树 board.dtb 内核日志中 model 与 compatible 正确 地址/中断号错误、节点缺失
根文件系统 rootfs.cpio / rootfs.ext4 进入 shell,执行 ls /proc root= 参数错误、init 不存在
外设 驱动模块与设备节点 ifconfig、i2cdetect、cat /dev/ttyS0 pinctrl 冲突、时钟未使能

七、一个最小可用的启动参数示例

在 U-Boot 中,典型的 bootargs 与启动命令如下。注意 console= 必须与设备树中串口节点一致,root= 必须指向实际根文件系统所在设备或内存地址。

setenv bootargs "console=ttyS0,115200 earlycon=sbi root=/dev/mmcblk0p2 rw rootwait"
setenv kernel_addr_r 0x84000000
setenv fdt_addr_r    0x88000000
load mmc 0:1 ${kernel_addr_r} Image
load mmc 0:1 ${fdt_addr_r} board.dtb
booti ${kernel_addr_r} - ${fdt_addr_r}

这段命令体现了 RISC-V Linux 移植的核心逻辑:引导程序负责把内核镜像和设备树放到内存的正确位置,内核负责解析设备树并初始化硬件,根文件系统负责提供用户态运行环境。三者缺一不可,任何一环出错都会导致启动失败。