跳至主要内容

RISC-V Linux Boot Flow(RISC-V Linux 開機流程)

RISC-V 是一個開放的指令集架構(ISA),其開機流程與 x86 或 ARM 有顯著差異,引入了 SBI(Supervisor Binary Interface) 作為韌體與 OS 之間的標準介面。本文整理完整的 RISC-V Linux 開機流程,以主流平台(SiFive HiFive、StarFive VisionFive、QEMU virt)為範例說明。

整體流程概覽

Power On / Reset


ZSBL(Zero Stage Bootloader)
│ ROM 內建,將 FSBL 從 Flash 載入 SRAM

FSBL(First Stage Bootloader)
│ e.g., SiFive FSBL、oreboot

OpenSBI(Open Source Supervisor Binary Interface)
│ 提供 M-mode 韌體服務,初始化 SBI 介面

U-Boot SPL(可選)
│ 初始化 DRAM(若 FSBL 未完成)

U-Boot Proper
│ 載入 Kernel + DTB + initramfs

Linux Kernel(riscv64)
│ 進入 S-mode,讀取 DTB

initramfs


RootFS + init(systemd)


Userspace

RISC-V 特權層級架構

RISC-V 定義了三個特權層級,開機流程在不同層級間切換:

┌─────────────────────────────────────────────────────┐
│ U-mode(User Mode) │ 應用程式
├─────────────────────────────────────────────────────┤
│ S-mode(Supervisor Mode) │ Linux Kernel、Hypervisor
├─────────────────────────────────────────────────────┤
│ M-mode(Machine Mode) │ OpenSBI、Bootloader
└─────────────────────────────────────────────────────┘
最低特權 最高特權 →
層級全名誰運行可存取資源
M-modeMachine ModeOpenSBI、FSBL所有硬體暫存器、CSR
S-modeSupervisor ModeLinux Kernel部分 CSR、透過 SBI 呼叫 M-mode
U-modeUser Mode應用程式無直接硬體存取

CSR(Control and Status Registers)重要暫存器

mhartid    — 目前 hart(硬體執行緒)的 ID
mstatus — 全域狀態(中斷啟用、前一特權層級)
mepc — M-mode 例外返回位址
mtvec — M-mode 中斷向量表基址
satp — 分頁模式(Sv39 / Sv48 / Sv57)+ 頁表基址
sstatus — S-mode 狀態暫存器
sepc — S-mode 例外返回位址

1. ZSBL(Zero Stage Bootloader)

說明

上電後 CPU hart 0 從固定位址執行片上 ROM(ZSBL),這是最原始的一段不可修改的程式碼。

主要工作

  • 初始化最基本的時鐘與 SRAM
  • 從 SPI Flash / SD Card / eMMC 讀取 FSBL
  • 將 FSBL 複製到 SRAM 後跳轉執行

多 hart 處理

Hart 0(Primary Hart)→ 執行開機流程
Hart 1, 2, 3...(Secondary Harts)→ 等待 WFI(Wait For Interrupt)
由 OpenSBI 的 HSM(Hart State Management)喚醒

2. FSBL(First Stage Bootloader)

常見實作

FSBL使用平台說明
SiFive Freedom U540 FSBLSiFive HiFive UnleashedSiFive 官方 FSBL
oreboot實驗性平台Rust 實作的開源 FSBL
coreboot + RISC-V payload部分開發板
U-Boot SPLStarFive VisionFive 2直接由 U-Boot SPL 扮演 FSBL

主要工作

  • DRAM 初始化(DDR training,若 ZSBL 未完成)
  • 從儲存裝置載入 OpenSBIfw_dynamic.binfw_jump.bin
  • 可能同時載入 U-Boot 或直接跳轉至 OpenSBI

3. OpenSBI(Open Source Supervisor Binary Interface)

SBI 是什麼?

SBI(Supervisor Binary Interface)是 RISC-V 特有的韌體規範,定義了 S-mode(Kernel)與 M-mode(韌體)之間的標準介面,類似於 x86 的 BIOS INT 呼叫或 ARM 的 PSCI。

Linux Kernel(S-mode)
│ ecall 指令(類似 syscall,但往下呼叫韌體)

OpenSBI(M-mode)
│ 處理硬體操作後返回

回傳結果給 Kernel

OpenSBI 三種韌體映像

韌體類型說明使用場景
fw_payload.bin將 U-Boot / Kernel 打包在 OpenSBI 內簡單部署,QEMU 常用
fw_jump.binOpenSBI 初始化後跳轉至固定位址U-Boot 放置在已知位址
fw_dynamic.bin由前一階段動態傳遞下一階段資訊最靈活,現代主流方式

OpenSBI 主要工作

  1. 初始化 M-mode 環境(設定 mtvec、PMP 保護)
  2. 實作 SBI 擴充功能
    • sbi_console_putchar:序列埠輸出
    • sbi_send_ipi:處理器間中斷(IPI)
    • sbi_set_timer:定時器設定
    • sbi_system_reset:系統重置
    • sbi_hsm:Hart State Management(多核心管理)
  3. 傳遞 FDT(Flattened Device Tree) 位址給下一階段(放在 a1 暫存器)
  4. 切換至 S-mode 並跳轉至 U-Boot 或 Kernel

SBI 呼叫慣例(RISC-V Calling Convention)

a7 = SBI Extension ID(EID)
a6 = SBI Function ID(FID)
a0~a5 = 參數
─────────────────────────
ecall
─────────────────────────
a0 = 錯誤碼(0 = 成功)
a1 = 返回值

4. U-Boot(RISC-V)

說明

U-Boot 在 RISC-V 平台上執行於 S-mode,透過 SBI ecall 呼叫 OpenSBI 的硬體服務。

U-Boot SPL vs U-Boot Proper

(若平台需要)U-Boot SPL
│ 執行於 M-mode 或 S-mode
│ 初始化 DRAM、載入 OpenSBI + U-Boot Proper

U-Boot Proper(S-mode)

├─ 初始化儲存裝置(SD / eMMC / NVMe)
├─ 讀取 /boot/extlinux/extlinux.conf 或 FIT Image
├─ 載入 Linux Kernel(Image 或 Image.gz)
├─ 載入 DTB(.dtb)
├─ 載入 initramfs(可選)
└─ booti / bootm 啟動 Kernel

典型啟動指令

# 從 SD Card 啟動
load mmc 0:1 ${kernel_addr_r} /boot/Image
load mmc 0:1 ${fdt_addr_r} /boot/dtbs/starfive/jh7110-starfive-visionfive-2-v1.3b.dtb
load mmc 0:1 ${ramdisk_addr_r} /boot/initrd.img

booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}

extlinux.conf(類 GRUB 設定)

DEFAULT linux
LABEL linux
KERNEL /boot/Image
FDT /boot/dtbs/starfive/jh7110-visionfive2.dtb
APPEND root=/dev/mmcblk1p4 rw console=ttyS0,115200 earlycon rootwait

5. Linux Kernel 初始化(RISC-V)

Kernel 進入點

U-Boot 透過 booti 命令跳轉至 Kernel,並依照 RISC-V Linux Boot Protocol 傳遞參數:

a0 = hartid(執行 Kernel 的 hart ID,通常為 0)
a1 = FDT 位址(Flattened Device Tree,DTB 的記憶體位址)

Kernel Image 格式

RISC-V Linux Kernel Image 標頭(64 bytes)
┌────────────────────────────────────────┐
│ jump instruction(跳過 header) │ 8 bytes
│ magic number(0x5643534952 "RISCV") │ 8 bytes
│ text offset │ 8 bytes
│ image size │ 8 bytes
│ flags │ 8 bytes
│ version │ 4 bytes
│ reserved │ 12 bytes
│ magic2(0x05435352) │ 4 bytes
│ reserved │ 4 bytes
└────────────────────────────────────────┘

早期初始化流程

_start(arch/riscv/kernel/head.S)

├─ 設定初始頁表(setup_vm)
├─ 啟用 MMU(satp 暫存器設定分頁模式)
├─ 設定 trap handler(stvec)
├─ 呼叫 start_kernel()(init/main.c)

start_kernel()
├─ setup_arch() ← 解析 DTB、初始化記憶體
├─ mm_init() ← 頁面分配器(buddy system)
├─ sched_init() ← CFS 排程器
├─ irq_init() ← PLIC(Platform-Level Interrupt Controller)
├─ time_init() ← CLINT / Timer 驅動
└─ rest_init() ← 啟動 kernel thread,呼叫 init 程序

RISC-V 分頁模式(MMU)

Sv39(39-bit 虛擬位址,512 GB 空間)← 64-bit Linux 預設
│ 3 層頁表(Page Global Directory → Page Middle Directory → Page Table)

Sv48(48-bit 虛擬位址,256 TB 空間)
│ 4 層頁表

Sv57(57-bit 虛擬位址,128 PB 空間)
│ 5 層頁表(最新規範,部分平台支援)

中斷控制器架構

CLINT(Core Local Interruptor)
│ 軟體中斷(IPI)、計時器中斷
│ 每個 hart 各自有一組

PLIC(Platform-Level Interrupt Controller)
│ 外部中斷(UART、GPIO、NVMe...)
│ 可設定各 hart 的中斷優先權與 mask

6. DTB(Device Tree Blob)在 RISC-V 的角色

RISC-V 平台高度依賴 DTB 描述硬體拓樸,因為 RISC-V 目標是支援各種不同硬體,沒有像 x86 的 ACPI 標準。

DTB 傳遞鏈

FSBL / U-Boot
│ 將 DTB 位址存入 a1 暫存器

OpenSBI
│ 可修改 DTB(加入 /chosen 節點,如 bootargs)

Linux Kernel
│ 透過 early_init_fdt() 解析 DTB

驅動程式
│ 透過 of_*() API 讀取硬體描述

DTB 重要節點範例

/ {
cpus {
cpu@0 {
compatible = "sifive,u74-mc";
riscv,isa = "rv64imafdc";
mmu-type = "riscv,sv39";
};
};

soc {
plic: interrupt-controller@c000000 {
compatible = "sifive,plic-1.0.0";
reg = <0xc000000 0x4000000>;
riscv,ndev = <53>;
};

uart0: serial@10000000 {
compatible = "ns16550a";
reg = <0x10000000 0x100>;
interrupts = <10>;
interrupt-parent = <&plic>;
};
};

chosen {
bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rw";
stdout-path = &uart0;
};
};

7. initramfs 與 RootFS 掛載

流程與 x86 相同,但注意 RISC-V 平台的儲存驅動:

initramfs 載入

├─ 載入必要模組:
│ riscv_sbi.ko(SBI 驅動)
│ plic.ko(中斷控制器)
│ sifive_uart.ko / ns16550.ko(序列埠)
│ dwmac.ko(網路,StarFive 平台)

├─ 掛載真正 RootFS(ext4 / XFS / btrfs)

└─ switch_root → 執行 /sbin/init

8. systemd 與 Userspace

與 x86 Linux 一致,RISC-V 上的現代 Linux 發行版同樣使用 systemd 管理服務。

# 查看開機 log
journalctl -b
dmesg | grep -i "riscv\|plic\|clint\|sbi"

# 確認 SBI 版本
cat /sys/firmware/devicetree/base/firmware/sbi/compatible
dmesg | grep -i "OpenSBI"

各平台開機流程比較

QEMU virt(開發/模擬)

QEMU 直接載入 fw_jump.bin(OpenSBI)
│ -bios fw_jump.bin

OpenSBI 初始化,跳轉至 U-Boot 或直接跳轉 Kernel
│ -kernel Image -dtb virt.dtb

Linux Kernel(無需真實 Bootloader 鏈)

QEMU 啟動範例:

qemu-system-riscv64 \
-machine virt \
-cpu rv64 \
-m 4G \
-smp 4 \
-bios /usr/lib/riscv64-linux-gnu/opensbi/qemu/virt/fw_jump.elf \
-kernel /boot/vmlinuz \
-initrd /boot/initrd.img \
-append "root=/dev/vda2 rw console=ttyS0" \
-drive file=ubuntu-riscv.img,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-nographic

SiFive HiFive Unmatched

ZSBL(ROM)→ FSBL(SPI Flash)→ OpenSBI + U-Boot SPL → U-Boot Proper → Linux

StarFive VisionFive 2

ZSBL(ROM)→ SPL(eMMC/SD)→ OpenSBI(fw_dynamic)→ U-Boot Proper → Linux

Milk-V Pioneer(SG2042,64 核心)

ZSBL → FSBL → OpenSBI → EDK2(UEFI)→ GRUB → Linux

Flash / 儲存裝置 Layout(典型)

SD Card / eMMC
┌──────────────────────────────────────────┐ 0 MB
│ GPT Header / MBR │
├──────────────────────────────────────────┤ 1 MB
│ U-Boot SPL + OpenSBI(fw_dynamic.bin) │ partition 1(raw)
├──────────────────────────────────────────┤ 4 MB
│ U-Boot Proper(u-boot.itb) │ partition 2(raw)
├──────────────────────────────────────────┤ 8 MB
│ /boot(ext4) │ partition 3
│ ├─ Image(Kernel) │
│ ├─ *.dtb │
│ └─ initrd.img │
├──────────────────────────────────────────┤ 512 MB
│ /(RootFS,ext4 / btrfs) │ partition 4
└──────────────────────────────────────────┘

與其他架構開機流程比較

特性x86 (UEFI)ARM (ATF)RISC-V (OpenSBI)
韌體規範UEFI / PI SpecPSCI / TF-ASBI / OpenSBI
最高特權層SMMEL3M-mode
OS 執行層Ring 0EL1S-mode
硬體描述ACPI + SMBIOSACPI / DTBDTB(主要)
標準 BootloaderGRUB / systemd-bootU-Boot / GRUBU-Boot
多核啟動ACPI MADT / MP TablePSCI CPU_ONSBI HSM Extension
安全開機Secure Boot(UEFI)TrustZone / Secure Boot尚在發展中

常見除錯方法

查看 OpenSBI 資訊

# 從 dmesg 確認 OpenSBI 版本與功能
dmesg | grep -i "opensbi\|sbi"
# 輸出範例:
# [ 0.000000] riscv: base ISA extensions acdfim
# [ 0.000000] SBI specification v1.0 detected
# [ 0.000000] SBI implementation ID=0x1 Version=0x10004

# 查看支援的 SBI 擴充
cat /proc/cpuinfo | grep isa

UART 序列除錯

# 在 U-Boot 中透過 UART 觀察
# 連接 USB-to-UART 模組(3.3V)至開發板 Debug UART 腳位
screen /dev/ttyUSB0 115200
# 或
minicom -D /dev/ttyUSB0 -b 115200

常見開機失敗原因

現象可能原因對策
UART 無任何輸出ZSBL 無法載入 FSBL、SPI Flash 損壞確認 Flash 燒錄、Boot Mode 引腳
OpenSBI 啟動後卡住DTB 錯誤、DRAM 初始化失敗確認 DTB 版本相符
U-Boot 找不到 Kernel分割區錯誤、檔案系統損壞確認 bootcmd 與 partition 設定
Kernel panic: no initinitramfs 損壞、root= 參數錯誤確認 UUID/裝置名
多核心 hart 無法啟動SBI HSM 問題、CLINT 設定錯誤確認 OpenSBI 版本、DTB 的 cpus 節點

參考資料