diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-04-17 00:54:20 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-04-17 00:54:20 +0800 |
| commit | 81563cb415d8214f1d1e34f73d49a404424f8f2c (patch) | |
| tree | ce4926b598f8eef00fe2fb2eff62766688c7bad9 /docs/specs/linker_and_entry.md | |
| parent | ab83dfb9a607807a6373f9bd47190848b822cf6e (diff) | |
| download | lume-81563cb415d8214f1d1e34f73d49a404424f8f2c.tar.gz lume-81563cb415d8214f1d1e34f73d49a404424f8f2c.tar.xz | |
docs: add global initialization, linker and entry specifications, and spinlock documentation
- Introduced global initialization sequence documentation outlining the objectives, dependencies, core data structures, public APIs, concurrency contracts, error paths, and state machine for LumeOS.
- Created linker and entry documentation defining early memory layout and assembly startup sequence compatible with multiple bootloaders, including API specifications and error handling.
- Added spinlock documentation detailing the implementation of spinlocks, concurrency synchronization, memory ordering guarantees, and error handling mechanisms.
Diffstat (limited to 'docs/specs/linker_and_entry.md')
| -rw-r--r-- | docs/specs/linker_and_entry.md | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/docs/specs/linker_and_entry.md b/docs/specs/linker_and_entry.md new file mode 100644 index 0000000..a59d28d --- /dev/null +++ b/docs/specs/linker_and_entry.md @@ -0,0 +1,208 @@ +# 链接器与引导入口 (Linker & Entry) + +## 1. 目标 (Objective) + +定义 LumeOS 在 RISC-V 64 架构上的早期内存布局与汇编启动序列。负责兼容多种 Bootloader(OpenSBI、GRUB、U-Boot),建立高半区内核(Higher-Half Kernel)映射,初始化早期 C++ 运行环境(BSS、GP 寄存器、全局构造函数),并安全地将各个 CPU 同步导入虚拟地址空间的 `kernel_main`。 + +## 2. 前置依赖 (Dependencies) + +- **引导协议契约 (Boot Protocol):** 为了同时支持 OpenSBI(直接跳转偏移 0)和 GRUB/U-Boot(解析元数据后引导),LumeOS 采用 **RISC-V Linux Boot Protocol**。内核文件起始处必须包含标准的 64 字节 Image Header。 + +- **寄存器传参契约:** 无论哪种引导器,在跳转至内核执行的第一条指令时,必须保证 `a0` = 当前 CPU ID (Hart ID),`a1` = FDT (Device Tree) 的物理基址。 + +- **编译模型契约:** 内核必须使用 `-mcmodel=medany` 编译,以支持基于 PC 的相对寻址,确保在 MMU 开启前后代码均能正确执行。 + +- **硬件 MMU 契约:** 目标硬件必须支持 RISC-V SV39 分页模式。 + + +## 3. 核心数据结构与内存布局 (Data Structures & Memory Map) + +### 3.1 虚拟与物理地址基址 + +- **物理加载基址 (Physical Base):** `0x80200000` (跳过低地址的 OpenSBI 固件区,对齐 2MB 以支持大页映射)。 + +- **内核虚拟基址 (Virtual Base):** `0xFFFFFFC080200000` (SV39 地址空间顶部,硬编码偏移量 `PAGE_OFFSET = 0xFFFFFFC000000000`)。 + +- **虚拟-物理偏移常量:** `KERNEL_VA_OFFSET = 0xFFFFFFC000000000`,用于虚拟/物理地址互转:`VA = PA + KERNEL_VA_OFFSET`,`PA = VA - KERNEL_VA_OFFSET`。 + + +### 3.2 链接脚本段分布 (`linker.ld`) + +必须在所有代码之前放置 `.head.text` 段,以确保 Image Header 绝对位于二进制文件的第 0 字节处。所有段强制 4KB 对齐: + +``` ++-----------------------+ 0xFFFFFFC080200000 (Virtual) / 0x80200000 (Physical) +| .head.text (镜像头) | -> 严格位于内核入口 0 字节处,包含 Linux Boot Header ++-----------------------+ +| .text (执行代码) | -> ALIGN(4096), 权限: RX ++-----------------------+ +| .rodata (只读数据) | -> ALIGN(4096), 权限: R- +| (含 __init_array) | -> 全局构造函数指针表 ++-----------------------+ +| .data (已初始化数据) | -> ALIGN(4096), 权限: RW +| (含 __global_pointer$)| -> 位于 .sdata 附近,用于优化全局变量访问 +| (含 early_pgdir) | -> 4KB 静态早期页表 ++-----------------------+ +| .bss (未初始化数据) | -> ALIGN(4096), 权限: RW ++-----------------------+ +| boot_stack (早期栈) | -> ALIGN(4096), 为每个 CPU 预留 8KB 栈空间 ++-----------------------+ +``` + +**链接脚本必须导出的符号:** + +| 符号名 | 含义 | +|--------|------| +| `_start` | 内核入口点(Image Header 首字节) | +| `_stext` / `_etext` | .text 段边界 | +| `_srodata` / `_erodata` | .rodata 段边界 | +| `_sdata` / `_edata` | .data 段边界 | +| `_sbss` / `_ebss` | .bss 段边界(用于启动清零) | +| `__global_pointer$` | GP 寄存器锚点 | +| `__init_array_start` / `__init_array_end` | C++ 全局构造函数表边界 | +| `_kernel_end` | 内核镜像结束地址(PMM 初始化起点) | + +### 3.3 早期页表 (Early Page Table) + +- **`early_pgdir`:** 一块静态分配的 4KB `.data` 内存,用作初始顶级页表 (Root Page Table)。 + +- 内含两个 1GB 的超级大页(Giga Page)映射: + + 1. **恒等映射 (Identity Map):** `0x80000000 -> 0x80000000`,保证开启 MMU 瞬间下一条指令能正确取指。 + + 2. **高半区映射 (Higher-Half Map):** `0xFFFFFFC080000000 -> 0x80000000`,供后续 C++ 代码正常运行。 + +- **生命周期:** 此恒等映射为临时性过渡,在 VMM 建立完整页表后必须被撤销。高半区映射将被 VMM 接管并细化为 4KB 粒度的精确权限映射。 + + +## 4. 公共 API 与核心符号 (Public APIs & Symbols) + +### 4.1 RISC-V OS Image Header 规范 + +在 `entry.S` 的最开始(`.head.text` 段),必须按照以下结构手写 64 字节的元数据。 +定义参照 Linux `arch/riscv/include/asm/image.h` 中的 `struct riscv_image_header`: + +```asm + .section .head.text, "ax" + .globl _start +_start: + /* code0 (u32, offset 0): 可执行跳转指令,OpenSBI 裸启入口 */ + j real_start + /* code1 (u32, offset 4): 保留 */ + .word 0 + /* text_offset (u64, offset 8): Image 加载偏移 */ + .dword 0x200000 + /* image_size (u64, offset 16): 镜像大小,链接后由脚本填充,初期可写 0 */ + .dword 0 + /* flags (u64, offset 24): 内核标志位 (bit 0: 0=LE, 1=BE) */ + .dword 0 + /* version (u32, offset 32): Header 版本 (major << 16 | minor),当前 0.2 */ + .word 0x0002 + /* res1 (u32, offset 36): 保留 */ + .word 0 + /* res2 (u64, offset 40): 保留 */ + .dword 0 + /* magic (u64, offset 48): RISCV_IMAGE_MAGIC = "RISCV\0\0\0" (LE) */ + .dword 0x5643534952 + /* magic2 (u32, offset 56): RISCV_IMAGE_MAGIC2 = "RSC\x05" (LE) */ + .word 0x05435352 + /* res3 (u32, offset 60): 预留给 PE/COFF 偏移 (UEFI),目前填 0 */ + .word 0 +``` + +### 4.2 汇编函数签名 + +```cpp +// 定义于 arch/riscv/boot/entry.S +// _start 中的 `j real_start` 跳转目标,执行早期硬件初始化。 +void real_start() __attribute__((noreturn)); // 汇编实现,无 C++ 调用约定 + +// 定义于 arch/riscv/boot/entry.S 或 kernel/main.cc +// 仅由 CPU 0 调用,遍历 .init_array 执行全局构造函数。 +void early_cpp_init(); + +// 定义于 kernel/main.cc +// C++ 内核总入口。各 CPU 在开启 MMU 后汇聚于此。 +// 参数:cpu_id = 当前 CPU 编号,fdt_paddr = FDT 物理基址。 +void kernel_main(uint64 cpu_id, uint64 fdt_paddr) __attribute__((noreturn)); +``` + +## 5. 并发与锁契约 (Concurrency & Locks) + +此时 `Spinlock` 尚未初始化,必须使用汇编级的极简同步机制来避免多核访问冲突。 + +- **本模块持有的锁:** 无高级锁。 + +- **本模块可能获取的外部锁:** 无。 + +- **原子同步契约 (SMP Sync):** + + 1. 在 `.data` 段定义一个全局标志 `uint32 boot_sync_flag = 0;`。 + + 2. 只有 **CPU 0** (或 `a0` 读取到的第一个 CPU) 作为主核心 (BSP),获准执行 BSS 清零、早期页表填充等破坏性操作。 + + 3. **副核心 (AP, Application Processors)** 必须使用原子加载指令在汇编层盲等 `boot_sync_flag` 变为 1。具体实现为 `lw` + 循环比较(不需要 `amoswap`,因为只有 BSP 写入,AP 只读)。 + + 4. BSP 完成 `early_pgdir` 写入并唤醒 AP 前,必须执行 `fence rw, rw` 以保证所有先前的内存写入对其他 CPU 可见,随后使用 `fence.i` 刷新指令缓存(如有需要),最后将 `boot_sync_flag` 设为 1。 + + +## 6. 错误路径 (Error Paths) + +在早期的裸机启动阶段,内核没有任何错误恢复能力。 + +- **检测点:** 所有 CPU 在 `real_start` 入口处可选地检查 `misa` 或 `marchid` 判断基本硬件兼容性(如 64 位检查)。 + +- **失败行为:** 立即关闭本 CPU 的中断使能(`csrc sstatus, SSTATUS_SIE`),进入 `wfi` 死循环。无串口输出(此时 UART 尚未初始化)。 + +- **无回滚:** 早期启动无任何资源可回收,失败即死。 + + +## 7. 状态机 / 时序图 (State Machine / Sequence) + +以下描述了从底层固件直到进入 `kernel_main` 的全流程: + +``` +[引导分流] + ├──> OpenSBI 裸启: 直接跳转到内存基址 (0x80200000) + │ └─ 命中 _start 处的第一条指令 `j real_start` + │ + └──> GRUB / U-Boot 引导: 解析位于 0x80200000 处的 64 字节 Header + ├─ 验证 magic ("RISCV\0\0\0") 与 magic2 ("RSC\x05") + ├─ 验证 text_offset + └─ 跳转到 0x80200000 (此时 a0=cpu_id, a1=fdt_paddr) + └─ 同样命中 _start 处的第一条指令 `j real_start` + + │ (汇聚点) + ▼ +[real_start] + │ + ├─> 1. 设置 gp 寄存器 (la gp, __global_pointer$) + │ // 极其关键:medany 模型下访问全局变量依赖此寄存器 + │ + ├─> 2. 设置早期内核栈 (la sp, boot_stack + (cpu_id + 1) * 8192) + │ + ├─> 3. 判断是否为主核心 (CPU 0)? + │ ├── [是 (BSP)] + │ │ ├─ 3.1 循环清理 BSS 段 (_sbss 至 _ebss 填 0) + │ │ ├─ 3.2 填充 early_pgdir (建立恒等映射与高半区映射) + │ │ ├─ 3.3 fence rw, rw (确保内存修改对其他 CPU 可见) + │ │ └─ 3.4 释放副核心 (boot_sync_flag = 1) + │ │ + │ └── [否 (AP)] + │ └─ 3.1 汇编级自旋等待,直到 boot_sync_flag == 1 + │ + ├─> 4. 高半区蹦床 (Higher-Half Trampoline - 所有 CPU 执行) + │ ├─ 4.1 将 early_pgdir 物理地址写入 satp 寄存器 (MODE=SV39) + │ ├─ 4.2 执行 sfence.vma 刷新 TLB + │ ├─ 4.3 计算 kernel_main 的高半区虚拟地址 + │ └─ 4.4 jr 绝对跳转至虚拟地址,彻底抛弃低位物理 PC + │ + ▼ +[进入虚拟世界: call early_cpp_init] (仅 BSP 执行) + │ + └─> 遍历 __init_array_start 到 __init_array_end + 依次调用全局构造函数,完成 C++ early runtime setup + │ + ▼ +[跳转至 C++ 逻辑: call kernel_main(a0, a1)] (所有 CPU 汇合) +```
\ No newline at end of file |
