summaryrefslogtreecommitdiff
path: root/docs/specs/global_init.md
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-04-17 00:54:20 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-04-17 00:54:20 +0800
commit81563cb415d8214f1d1e34f73d49a404424f8f2c (patch)
treece4926b598f8eef00fe2fb2eff62766688c7bad9 /docs/specs/global_init.md
parentab83dfb9a607807a6373f9bd47190848b822cf6e (diff)
downloadlume-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/global_init.md')
-rw-r--r--docs/specs/global_init.md208
1 files changed, 208 insertions, 0 deletions
diff --git a/docs/specs/global_init.md b/docs/specs/global_init.md
new file mode 100644
index 0000000..cd14dae
--- /dev/null
+++ b/docs/specs/global_init.md
@@ -0,0 +1,208 @@
+# 全局初始化序列 (Global Initialization)
+
+## 1. 目标 (Objective)
+
+统筹 LumeOS 从汇编跃迁至 C++ 后的全局初始化流程。定义子系统间的严格启动顺序、全局对象的生命周期管理范式、以及 BSP/AP 的两阶段同步协议。
+
+## 2. 前置依赖 (Dependencies)
+
+- **汇编环境契约:** 依赖 `linker_and_entry.md` 保证的执行环境:BSS 段已被清零、高半区虚拟内存(`early_pgdir`)已映射、`tp` 寄存器已保存当前 CPU ID、`gp` 寄存器已就绪。注意:`early_cpp_init()` 已在 `entry.S` 中由 BSP 调用完毕(在 `kernel_main` 之前),不在本模块的管辖范围内。
+
+- **全局基础契约:** 依赖 `00_conventions.md` 中禁用了 C++ 异常与 RTTI 的环境。
+
+- **并发同步契约:** 依赖 `spinlock.md` 提供的 `Spinlock`(但在 BSP 独占期不使用)。
+
+
+## 3. 核心数据结构与生命周期范式 (Data Structures & Lifecycle)
+
+在内核早期,物理内存管理 (PMM) 和字节级分配器 (Slab) 尚未就绪,传统的 `new` 和隐式的全局构造函数极易引发缺页崩溃或顺序未定义行为。
+
+### 3.1 显式生命周期模型 (Placement New 范式)
+
+对于所有复杂的内核全局单例(如 `PhysicalMemoryManager`, `Scheduler`),**严禁**直接定义为全局变量依赖 `.init_array` 隐式构造。必须采用以下静态缓冲区分配 + 显式 Placement New 的范式:
+
+```cpp
+// 示例:kernel/mm/pmm_global.cc
+
+// Placement new 声明(freestanding 环境无 <new> 头文件,自行提供)
+// 定义于 include/lume/new.h
+inline void* operator new(size_t, void* ptr) noexcept { return ptr; }
+inline void operator delete(void*, void*) noexcept {}
+
+// 1. 在 .bss 段静态分配对齐的内存区块,不触发任何构造函数
+alignas(PhysicalMemoryManager) uint8 g_pmm_buf[sizeof(PhysicalMemoryManager)];
+
+// 2. 暴露全局指针供外部访问
+PhysicalMemoryManager* g_pmm = nullptr;
+
+// 3. 在 kernel_main 的严格时序中被显式调用
+void pmm_init(uint64 fdt_paddr) {
+ // 使用 Placement New 在预分配的内存上执行构造函数
+ g_pmm = new (g_pmm_buf) PhysicalMemoryManager(fdt_paddr);
+}
+```
+
+**关于 `<new>` 头文件:** 在 `-ffreestanding -nostdlib` 编译环境下,`<new>` 可能不可用或行为不一致。LumeOS 在 `include/lume/new.h` 中自行提供 placement new 声明,不依赖任何标准库头文件。
+
+### 3.2 简单数据结构的按需初始化
+
+对于没有复杂依赖的 Plain Old Data (POD) 或简单锁对象:
+
+- `Spinlock` 等对象直接作为 BSS 段全局变量,由 `entry.S` 清零。随后在所属子系统的 `init()` 方法中调用 `lock.init("name")` 赋予身份。
+
+### 3.3 `.init_array` 的使用边界
+
+`early_cpp_init()` 在 `entry.S` 中遍历 `.init_array`。只有满足以下**全部**条件的全局构造函数才允许出现在此表中:
+
+- 构造函数不依赖任何子系统(PMM、Slab、VMM、Scheduler 均未初始化)。
+- 构造函数不分配动态内存。
+- 构造函数不获取任何锁。
+
+实际上,应该尽量让 `.init_array` 为空。绝大多数全局对象应走 §3.1 的 Placement New 路径。
+
+
+## 4. 公共 API (Public APIs)
+
+### 4.1 内核 C++ 唯一入口
+
+```cpp
+// 定义于 kernel/main.cc
+// 被 entry.S 中的 real_start 在开启 MMU、调用 early_cpp_init 后跳转
+// 所有 CPU 从此入口进入 C++ 世界
+// a0 = cpu_id, a1 = fdt_paddr
+extern "C" void kernel_main(uint64 cpu_id, uint64 fdt_paddr) __attribute__((noreturn));
+```
+
+### 4.2 子系统初始化签名规范
+
+所有子系统的初始化入口必须遵循统一签名,并在 `kernel/main.cc` 内部按图谱依次调用。每个 `xxx_init()` 函数成功时无返回值,失败时直接 `kernel_panic()`:
+
+```cpp
+void console_init(); // 1. UART 就绪,此后 kprintf/kernel_panic 可输出
+void fdt_init(uint64 fdt_paddr); // 2. 解析 FDT,构建设备节点表
+void pmm_init(); // 3. 从 FDT 获取内存布局,建立 Buddy 分配器
+void slab_init(); // 4. 建立 kmem_cache,此后 operator new 可用
+void vmm_init(); // 5. 建立精细化内核页表,废弃 early_pgdir
+void trap_init(); // 6. 设置 stvec,注册 IRQ 分发器
+void plic_init(); // 7. 配置 PLIC 中断控制器(阈值、优先级、使能位)
+void timer_init(); // 8. 配置定时器中断(调度器时钟源)
+void sched_init(); // 9. 构造 BSP 的 Idle Task 和初始就绪队列
+```
+
+### 4.3 AP 初始化签名
+
+AP 在 BSP 完成全局初始化后执行,仅限 Per-CPU 私有数据:
+
+```cpp
+void vmm_init_ap(); // 切换 satp 至 BSP 建好的内核页表
+void trap_init_ap(); // 设置本 CPU 的 stvec
+void plic_init_ap(); // 配置本 CPU 在 PLIC 上的 Context 使能
+void timer_init_ap(); // 启动本 CPU 的定时器中断
+void sched_init_ap(); // 初始化本 CPU 的 O(1) 就绪队列、创建 Idle Task
+```
+
+## 5. 并发与锁契约 (Concurrency & Locks)
+
+这是整个内核生命周期中**唯一一段不受自旋锁约束的特殊时期**,必须严格遵循以下阶段隔离:
+
+### 5.1 两阶段同步协议
+
+LumeOS 启动过程中存在**两个独立的同步屏障**,不要混淆:
+
+| 同步点 | 位置 | 变量 | 含义 |
+|--------|------|------|------|
+| 第一道 | `entry.S` (汇编层) | `boot_sync_flag` | AP 等待 BSP 建立 `early_pgdir`,完成后所有 CPU 齐步进入虚拟地址空间 |
+| 第二道 | `kernel_main` (C++ 层) | `ap_boot_sync` | AP 等待 BSP 完成全部子系统初始化,完成后 AP 开始 Per-CPU 初始化 |
+
+### 5.2 绝对单核期 (BSP 独占)
+
+- 在 `kernel_main` 启动初期,AP 卡在第二道屏障 `ap_boot_sync` 处自旋。
+
+- BSP 拥有对全局数据结构的**绝对独占权**。在此期间调用 `pmm_init`、`vmm_init` 等函数时,**无需获取该子系统的 `Spinlock`**(也不应获取,因为锁可能尚未初始化)。
+
+### 5.3 多核唤醒与并发期 (AP 启动)
+
+- BSP 完成所有全局初始化后,通过 `__atomic_store_n(&ap_boot_sync, 1, __ATOMIC_RELEASE)` 唤醒 AP。`RELEASE` 语义保证 AP 读到 `ap_boot_sync == 1` 时,所有全局单例对象必然已构造完毕。
+
+- AP 进入后,**只能执行 Per-CPU 的局部初始化**(如设置自身的 `stvec`、初始化 PLIC Context、创建 Per-CPU Idle Task)。
+
+- 一旦 AP 的 Per-CPU 初始化完成并开启中断,所有子系统对全局数据的访问必须严格遵守 `spinlock.md` 契约。
+
+
+## 6. 错误路径 (Error Paths)
+
+初始化阶段的错误处理极度苛刻:
+
+- **console_init 之前的 Panic:** 此时 UART 尚未就绪,`kernel_panic()` 无法输出任何信息。唯一行为是关中断 + `wfi` 死循环(静默死亡)。可通过 JTAG/GDB 附加调试。
+
+- **console_init 之后的 Panic:** 可输出 Panic 消息和调用栈信息后死循环。
+
+- 任何子系统的 `init` 方法如果检测到硬件缺失(如 FDT 解析不到内存节点)或关键资源耗尽(Slab 无法建立早期缓存),必须**立即调用 `kernel_panic()`**。
+
+- 此阶段系统尚未具备任务调度与资源回收能力,无后路可退。
+
+
+## 7. 状态机 / 时序图 (State Machine / Sequence)
+
+### 7.1 全局初始化依赖 DAG 图
+
+`kernel_main` 必须严格按照自顶向下的单向依赖顺序执行,任何逆序调用将导致 `kernel_panic` 或缺页崩溃。
+
+```
+ entry.S (汇编世界)
+ │
+ early_cpp_init() ← 遍历 .init_array (应尽量为空)
+ │
+ ════════════════
+ kernel_main()
+ ════════════════
+ │
+ ┌─────────────┴──────────────┐
+ │ │
+ [BSP (cpu_id == 0)] [AP (cpu_id != 0)]
+ │ │
+ 1. console_init() 自旋等待
+ └─ UART 就绪 ap_boot_sync == 1
+ └─ 此后 Panic 可输出 │
+ │ │
+ 2. fdt_init(fdt_paddr) │
+ └─ 解析 FDT 设备树 │
+ │ │
+ 3. pmm_init() │
+ └─ Buddy 分配器就绪 │
+ │ │
+ 4. slab_init() │
+ └─ operator new 可用 │
+ │ │
+ 5. vmm_init() │
+ └─ 完整内核页表就绪 │
+ └─ 废弃 early_pgdir 恒等映射 │
+ │ │
+ 6. trap_init() │
+ └─ stvec 挂载 trap_vector │
+ │ │
+ 7. plic_init() │
+ └─ PLIC 中断控制器就绪 │
+ │ │
+ 8. timer_init() │
+ └─ 定时器中断就绪 │
+ │ │
+ 9. sched_init() │
+ └─ Idle Task (Task 0) 创建 │
+ └─ Per-CPU 就绪队列初始化 │
+ │ │
+ ──[RELEASE 屏障]──────────────────> 读到 1
+ ap_boot_sync = 1 │
+ │ 1. vmm_init_ap()
+ │ 2. trap_init_ap()
+ │ 3. plic_init_ap()
+ │ 4. timer_init_ap()
+ │ 5. sched_init_ap()
+ │ │
+ ▼ ▼
+ arch::cpu::intr_on() arch::cpu::intr_on()
+ BSP 变为 Idle Task AP 变为 Idle Task
+ 进入调度循环 进入调度循环
+```
+
+**BSP 进入调度的方式:** BSP 在 `sched_init()` 中将自身的执行上下文注册为 CPU 0 的 Idle Task (Task 0)。开启中断后,第一个定时器中断触发调度器接管执行流。不是通过 `sched_yield()` 主动让出,而是被定时器中断被动抢占进入调度循环。 \ No newline at end of file