RISC-V 内核启动要求和约束¶
- 作者:
Alexandre Ghiti <alexghiti@rivosinc.com>
- 日期:
2023 年 5 月 23 日
本文档描述了 RISC-V 内核对引导加载程序和固件的期望,以及任何开发人员在接触早期启动过程时必须牢记的约束。 就本文档而言,早期启动过程
是指在最终虚拟映射设置之前运行的任何代码。
内核前的要求和约束¶
RISC-V 内核对引导加载程序和平台固件有以下期望
寄存器状态¶
RISC-V 内核期望
$a0
包含当前核心的 hartid。
$a1
包含内存中设备树的地址。
CSR 状态¶
RISC-V 内核期望
$satp = 0
: MMU(如果存在)必须禁用。
为驻留固件保留内存¶
RISC-V 内核不得在直接映射中映射任何驻留内存或受 PMP 保护的内存,因此固件必须根据设备树规范和/或 UEFI 规范正确标记这些区域。
内核位置¶
RISC-V 内核希望放置在 PMD 边界(rv64 为 2MB 对齐,rv32 为 4MB 对齐)。 请注意,如果不是这种情况,EFI stub 将在物理上重新定位内核。
硬件描述¶
固件可以将设备树或 ACPI 表传递给 RISC-V 内核。
设备树要么使用 $a1
寄存器从上一阶段直接传递给内核,要么在使用 UEFI 启动时,可以使用 EFI 配置表传递。
ACPI 表使用 EFI 配置表传递给内核。 在这种情况下,仍然由 EFI stub 创建一个小的设备树。 有关此设备树的详细信息,请参阅下面的“EFI stub 和设备树”部分。
内核入口¶
在 SMP 系统上,有两种方法进入内核
RISCV_BOOT_SPINWAIT
:固件释放内核中的所有 hart,一个 hart 赢得彩票并执行早期启动代码,而其他 hart 则停放在那里等待初始化完成。 此方法主要用于支持没有 SBI HSM 扩展和 M 模式 RISC-V 内核的旧固件。有序启动
:固件仅释放一个将执行初始化阶段的 hart,然后使用 SBI HSM 扩展启动所有其他 hart。 有序启动方法是启动 RISC-V 内核的首选启动方法,因为它支持 CPU 热插拔和 kexec。
UEFI¶
UEFI 内存映射¶
使用 UEFI 启动时,RISC-V 内核将仅使用 EFI 内存映射来填充系统内存。
UEFI 固件必须解析 /reserved-memory
设备树节点的子节点,并遵守设备树规范,以将这些子节点的属性(no-map
和 reusable
)转换为其正确的 EFI 等效项(请参阅设备树规范 v0.4-rc1 的“3.5.4 /reserved-memory 和 UEFI”部分)。
RISCV_EFI_BOOT_PROTOCOL¶
使用 UEFI 启动时,EFI stub 需要启动 hartid,以便将其传递给 $a1
中的 RISC-V 内核。 EFI stub 使用以下方法之一检索启动 hartid
RISCV_EFI_BOOT_PROTOCOL
(首选)。boot-hartid
设备树子节点 (已弃用)。
任何新固件都必须实现 RISCV_EFI_BOOT_PROTOCOL
,因为现在基于设备树的方法已弃用。
早期启动要求和约束¶
RISC-V 内核的早期启动过程在以下约束下运行
EFI stub 和设备树¶
使用 UEFI 启动时,设备树由 EFI stub 补充(或创建),其参数与 arm64 相同,如 统一可扩展固件接口 (UEFI) 中的“ARM 上的 UEFI 内核支持”段落中所述。
虚拟映射安装¶
虚拟映射的安装在 RISC-V 内核中分 2 步完成
setup_vm()
在early_pg_dir
中安装临时内核映射,允许发现系统内存。 此时仅映射内核文本/数据。 建立此映射时,无法进行分配(因为系统内存尚未知),因此静态分配early_pg_dir
页表(每级仅使用一个表)。setup_vm_final()
在swapper_pg_dir
中创建最终内核映射,并利用发现的系统内存来创建线性映射。 建立此映射时,内核可以分配内存但无法直接访问它(因为直接映射尚不存在),因此它使用 fixmap 区域中的临时映射来访问新分配的页表级别。
为了使 virt_to_phys()
和 phys_to_virt()
能够正确地将直接映射地址转换为物理地址,它们需要知道 DRAM 的起始位置。 这发生在步骤 1 之后,就在步骤 2 安装直接映射之前(请参阅 arch/riscv/mm/init.c 中的 setup_bootmem()
函数)。 在安装最终虚拟映射之前,必须仔细检查这些宏的任何用法。
通过 fixmap 映射设备树¶
由于 reserved_mem
数组使用由 setup_vm()
建立的虚拟地址初始化,并与由 setup_vm_final()
建立的映射一起使用,因此 RISC-V 内核使用 fixmap 区域来映射设备树。 这确保了设备树仍然可以通过两种虚拟映射访问。
MMU 前执行¶
甚至在建立第一个虚拟映射之前,都需要运行一些代码。 这些是第一个虚拟映射本身的安装、早期备选项的修补以及内核命令行早期解析。 该代码必须非常小心地编译为
-fno-pie
:这对于使用-fPIE
的可重定位内核是必需的,因为否则,对全局符号的任何访问都将通过仅虚拟重定位的 GOT。-mcmodel=medany
:对全局符号的任何访问都必须是 PC 相关的,以避免在 MMU 设置之前发生任何重定位。所有 检测也必须禁用(包括 KASAN、ftrace 和其他)。
由于从不同的编译单元使用符号需要使用这些标志编译该单元,因此我们建议尽可能不要使用外部符号。