summaryrefslogtreecommitdiff
path: root/docs/specs/fdt.md
blob: 2a1d5426a805a028828bbe5341b4ea611c294dfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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 驱动)
```