diff options
Diffstat (limited to 'docs/specs/fdt.md')
| -rw-r--r-- | docs/specs/fdt.md | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/docs/specs/fdt.md b/docs/specs/fdt.md new file mode 100644 index 0000000..2a1d542 --- /dev/null +++ b/docs/specs/fdt.md @@ -0,0 +1,191 @@ +# 设备树 (Flattened Device Tree) + +## 1. 目标 (Objective) + +为 LumeOS 提供一套标准的硬件发现与配置机制,彻底摆脱硬编码的物理地址。本模块负责解析由 Bootloader 传入的 FDT 二进制数据,并分两个阶段将其转化为内核可用的数据结构: + +1. **早期扫描 (Early Scan):** 在内存管理(PMM)初始化之前,以只读、无分配的方式扫描 FDT,提取启动所必需的核心信息(如物理内存布局)。 +2. **对象化展开 (Unflattening):** 在内存管理(PMM/Slab)就绪后,将完整的 FDT 解析为一个常驻内存的 C++ `DeviceNode` 对象树,为后续的驱动探测(Driver Probing)提供面向对象的接口。 + +## 2. 前置依赖 (Dependencies) + +- **引导协议契约 (`linker_and_entry.md`):** 依赖 Bootloader 在跳转至内核时,将 FDT 的物理基址置于 `a1` 寄存器。 +- **全局初始化契约 (`global_init.md`):** `fdt_init()` 在 `console_init()` 之后、`pmm_init()` 之前被 BSP 调用。`pmm_init()` 自身会回调 FDT 的早期扫描接口。 +- **内存管理契约 (`pmm.md`, `slab.md`):** 对象化展开阶段依赖 Slab 分配器(`operator new`)来动态创建 `DeviceNode` 对象。 +- **项目约定 (`00_conventions.md`):** 遵循所有命名、错误处理和 API 设计规范。 + +## 3. 核心数据结构 (Data Structures) + +### 3.1 FDT 属性 (`struct FdtProperty`) + +表示设备节点中的一个属性(如 `reg`, `compatible`)。 + +```cpp +// 定义于 include/lume/fdt_prop.h +struct FdtProperty { + const char* name; // 属性名 + const void* value; // 指向 FDT blob 中原始数据的指针 + int len; // 数据长度(字节) + list_node link; // 链接到下一个属性 +}; +``` + +### 3.2 设备节点 (`class DeviceNode`) + +FDT 对象树的核心,代表一个硬件设备或总线。 + +```cpp +// 定义于 include/lume/device_node.h +class DeviceNode { +public: + // 构造/析构 + DeviceNode(DeviceNode* parent, const char* name); + ~DeviceNode(); + + // 属性访问 + const FdtProperty* get_prop(const char* name) const; + uint32_t get_prop_u32(const char* name, uint32_t default_val = 0) const; + uint64 get_prop_u64(const char* name, uint64 default_val = 0) const; + const char* get_prop_string(const char* name) const; + + // 子节点遍历 + DeviceNode* find_child(const char* name); + void for_each_child(void (*callback)(DeviceNode*)); + + // 内部接口,供 FdtManager 构建树时使用 + void add_child(DeviceNode* child); + void add_prop(FdtProperty* prop); + +private: + const char* name_; + DeviceNode* parent_; + list_node children_list_; // list_head of children + list_node props_list_; // list_head of properties + list_node sibling_link_; // link in parent's children_list_ +}; +``` + +### 3.3 FDT 管理器 (`class FdtManager`) + +全局单例,负责 FDT 的解析与查询。 + +```cpp +// 定义于 include/lume/fdt.h +class FdtManager { +public: + // 由 fdt_init() 通过 Placement New 调用 + FdtManager(uint64 fdt_paddr); + + // 阶段二:对象化展开 + void unflatten(); + + // 驱动探测接口 + DeviceNode* find_compatible(const char* compat_string); + DeviceNode* get_node_by_path(const char* path); + + // 阶段一:早期扫描接口 (静态,无状态) + static void early_scan_mem(uint64 fdt_paddr, uint64* base, uint64* size); + static void early_scan_uart(uint64 fdt_paddr, uint64* uart_addr); + +private: + uint64 fdt_paddr_; + DeviceNode* root_ = nullptr; + bool is_unflattened_ = false; +}; + +// 全局 FDT 管理器实例指针 +extern FdtManager* g_fdt; +``` + +## 4. 公共 API (Public APIs) + +```cpp +// === 初始化 API (由 kernel_main 调用) === + +// 阶段一和阶段二的入口。仅保存 FDT 地址,真正的解析被推迟。 +void fdt_init(uint64 fdt_paddr); + +// 在 Slab 就绪后调用,执行对象化展开。 +void fdt_unflatten(); + + +// === 驱动探测 API (供各驱动 init 函数调用) === + +// 根据 "compatible" 字符串查找第一个匹配的设备节点。 +// 返回值:成功则为 DeviceNode 指针,失败为 nullptr。 +DeviceNode* fdt_find_compatible(const char* compat_string); + +// 根据完整路径查找设备节点。 +DeviceNode* fdt_get_node_by_path(const char* path); + + +// === 早期扫描 API (供 PMM 和早期控制台调用) === + +// 扫描 FDT blob 获取 /memory 节点的 base 和 size。 +// 失败则 panic。 +void fdt_early_get_mem_info(uint64* base, uint64* size); + +// 扫描 FDT blob 获取 /chosen/stdout-path 对应的 UART 物理地址。 +void fdt_early_get_uart_info(uint64* uart_addr); +``` + +## 5. 并发与锁契约 (Concurrency & Locks) + +- **无锁设计:** FDT 的所有初始化和解析操作(`fdt_init`, `fdt_unflatten`)均由 BSP 在多核启动前的**绝对单核期**完成。 + +- **只读安全:** 一旦 `fdt_unflatten()` 完成,`DeviceNode` 对象树即被视为**全局只读**数据。任何 CPU 都可以随时无锁访问它进行驱动探测和属性查询。 + +- **禁止修改:** 任何模块在初始化之后都**不得**修改 `DeviceNode` 树的内容。 + +## 6. 错误路径 (Error Paths) + +- **FDT 损坏:** 在 `fdt_init()` 或 `fdt_unflatten()` 中,如果检测到 FDT 的 magic number 无效或结构损坏,必须立即调用 `kernel_panic()`。 + +- **关键节点缺失:** `fdt_early_get_mem_info()` 如果找不到 `/memory` 节点,说明无法确定物理内存大小,属于致命错误,必须 `kernel_panic()`。 + +- **内存分配失败:** `fdt_unflatten()` 在使用 `new` 创建 `DeviceNode` 时如果 Slab 返回 `nullptr`,说明内存耗尽,必须 `kernel_panic()`。 + +- **查询失败:** `fdt_find_compatible()` 或 `fdt_get_node_by_path()` 在未找到匹配节点时,安全地返回 `nullptr`。调用方(驱动)负责处理此情况(通常意味着该硬件不存在,驱动不进行初始化)。 + +## 7. 状态机 / 时序图 (State Machine / Sequence) + +### FDT 二阶段解析时序 + +``` +[entry.S] -> a1 = fdt_paddr + │ + ▼ +[kernel_main on BSP] + │ + ├─> 1. fdt_init(fdt_paddr) + │ └─ g_fdt = new FdtManager(fdt_paddr); // Placement New + │ (仅保存地址,不进行任何解析) + │ + ├─> 2. pmm_init() + │ └─ fdt_early_get_mem_info(&base, &size); + │ └─ FdtManager::early_scan_mem(fdt_paddr, ...); + │ ├─ 遍历原始 FDT blob (无内存分配) + │ ├─ 找到 /memory 节点 + │ └─ 返回 reg 属性值 + │ + ├─> 3. slab_init() + │ └─ operator new 可用 + │ + ├─> 4. (稍后) fdt_unflatten() + │ └─ g_fdt->unflatten(); + │ ├─ 再次遍历原始 FDT blob + │ ├─ for each node: new DeviceNode(...) + │ ├─ for each prop: new FdtProperty(...) + │ └─ 构建完整的 C++ 对象树 + │ + ▼ +[驱动初始化阶段, e.g., plic_init()] + │ + ├─> node = fdt_find_compatible("riscv,plic0"); + │ └─ g_fdt->find_compatible(...); + │ └─ 遍历已构建的 DeviceNode C++ 对象树 (只读, 无锁) + │ + ├─> reg_prop = node->get_prop("reg"); + │ + └─> (使用 reg_prop->value 初始化 PLIC 驱动) +```
\ No newline at end of file |
