10#pragma GCC diagnostic push
11#pragma GCC diagnostic ignored "-Wpedantic"
17#pragma GCC diagnostic pop
21#include <etl/singleton.h>
32#include "kstd_cstring"
55 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
72 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
78 if (
strcmp(method,
"smc") != 0) {
94 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
97 [
this](
int offset) ->
Expected<std::pair<uint64_t, size_t>> {
99 [](std::pair<uint64_t, size_t> reg) {
return reg; });
110 ->
Expected<
std::tuple<uint64_t,
size_t, uint32_t>> {
111 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
113 auto chosen_offset =
FindNode(
"/chosen");
114 if (!chosen_offset.has_value()) {
115 return std::unexpected(chosen_offset.error());
119 const auto* prop = fdt_get_property(
fdt_header_, chosen_offset.value(),
120 "stdout-path", &len);
121 if (prop ==
nullptr || len <= 0) {
125 const char* stdout_path =
reinterpret_cast<const char*
>(prop->data);
126 std::array<char, 256> path_buffer;
127 kstd::strncpy(path_buffer.data(), stdout_path, path_buffer.max_size() - 1);
129 char* colon =
strchr(path_buffer.data(),
':');
130 if (colon !=
nullptr) {
134 int stdout_offset = -1;
135 if (path_buffer[0] ==
'&') {
136 const char* alias = path_buffer.data() + 1;
137 const char* aliased_path = fdt_get_alias(
fdt_header_, alias);
138 if (aliased_path !=
nullptr) {
139 stdout_offset = fdt_path_offset(
fdt_header_, aliased_path);
142 stdout_offset = fdt_path_offset(
fdt_header_, path_buffer.data());
145 if (stdout_offset < 0) {
150 if (!reg.has_value()) {
151 return std::unexpected(reg.error());
154 prop = fdt_get_property(
fdt_header_, stdout_offset,
"interrupts", &len);
155 if (prop ==
nullptr) {
160 const auto* interrupts =
reinterpret_cast<const uint32_t*
>(prop->data);
161 if (interrupts !=
nullptr && len != 0) {
162 irq = fdt32_to_cpu(*interrupts);
165 return std::tuple{reg.value().first, reg.value().second, irq};
174 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
178 const auto* prop =
reinterpret_cast<const uint32_t*
>(
179 fdt_getprop(
fdt_header_, offset,
"timebase-frequency", &len));
180 if (prop ==
nullptr) {
184 if (len !=
sizeof(uint32_t)) {
188 return fdt32_to_cpu(*prop);
199 ->
Expected<
std::tuple<uint64_t,
size_t, uint64_t,
size_t>> {
200 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
203 if (!offset.has_value()) {
204 return std::unexpected(offset.error());
209 fdt_get_property(
fdt_header_, offset.value(),
"reg", &len);
210 if (prop ==
nullptr) {
214 uint64_t dist_base = 0;
215 size_t dist_size = 0;
216 uint64_t redist_base = 0;
217 size_t redist_size = 0;
219 const auto* reg =
reinterpret_cast<const uint64_t*
>(prop->data);
220 if (
static_cast<unsigned>(len) >= 2 *
sizeof(uint64_t)) {
221 dist_base = fdt64_to_cpu(reg[0]);
222 dist_size = fdt64_to_cpu(reg[1]);
224 if (
static_cast<unsigned>(len) >= 4 *
sizeof(uint64_t)) {
225 redist_base = fdt64_to_cpu(reg[2]);
226 redist_size = fdt64_to_cpu(reg[3]);
229 return std::tuple{dist_base, dist_size, redist_base, redist_size};
238 return GetGIC().transform(
239 [](std::tuple<uint64_t, size_t, uint64_t, size_t> gic) {
240 return std::pair{std::get<0>(gic), std::get<1>(gic)};
250 return GetGIC().transform(
251 [](std::tuple<uint64_t, size_t, uint64_t, size_t> gic) {
252 return std::pair{std::get<2>(gic), std::get<3>(gic)};
264 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
267 if (!offset.has_value()) {
268 return std::unexpected(offset.error());
273 fdt_get_property(
fdt_header_, offset.value(),
"interrupts", &len);
274 if (prop ==
nullptr) {
278 const auto* interrupts =
reinterpret_cast<const uint32_t*
>(prop->data);
280#ifdef SIMPLEKERNEL_DEBUG
281 for (uint32_t i = 0; i < fdt32_to_cpu(prop->len);
282 i += 3 *
sizeof(uint32_t)) {
283 auto type = fdt32_to_cpu(interrupts[i /
sizeof(uint32_t) + 0]);
284 auto intid = fdt32_to_cpu(interrupts[i /
sizeof(uint32_t) + 1]);
285 auto trigger = fdt32_to_cpu(interrupts[i /
sizeof(uint32_t) + 2]) & 0xF;
287 fdt32_to_cpu(interrupts[i /
sizeof(uint32_t) + 2]) & 0xFF00;
288 klog::Debug(
"type: {}, intid: {}, trigger: {}, cpuid_mask: {}", type,
289 intid, trigger, cpuid_mask);
294 if (
strcmp(compatible,
"arm,armv8-timer") == 0) {
295 intid = fdt32_to_cpu(interrupts[7]);
296 }
else if (
strcmp(compatible,
"arm,pl011") == 0) {
297 intid = fdt32_to_cpu(interrupts[1]);
311 ->
Expected<
std::tuple<uint64_t, uint64_t, uint32_t, uint32_t>> {
312 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
315 if (!offset.has_value()) {
318 if (!offset.has_value()) {
319 return std::unexpected(offset.error());
324 const auto* prop = fdt_get_property(
fdt_header_, offset.value(),
325 "interrupts-extended", &len);
326 if (prop ==
nullptr) {
330 uint32_t num_entries = len /
sizeof(uint32_t);
331 uint32_t context_count = num_entries / 2;
333 prop = fdt_get_property(
fdt_header_, offset.value(),
"riscv,ndev", &len);
334 if (prop ==
nullptr) {
338 fdt32_to_cpu(*
reinterpret_cast<const uint32_t*
>(prop->data));
341 if (!reg.has_value()) {
342 return std::unexpected(reg.error());
345 return std::tuple{reg.value().first, reg.value().second, ndev,
364 template <
typename Callback>
366 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
372 offset = fdt_next_node(
fdt_header_, offset, &depth);
374 if (offset == -FDT_ERR_NOTFOUND) {
380 const char* node_name = fdt_get_name(
fdt_header_, offset,
nullptr);
381 if (node_name ==
nullptr) {
386 const auto* status_prop =
387 fdt_get_property(
fdt_header_, offset,
"status", &status_len);
388 if (status_prop !=
nullptr) {
389 const char* status =
reinterpret_cast<const char*
>(status_prop->data);
390 if (
strcmp(status,
"okay") != 0 &&
strcmp(status,
"ok") != 0) {
395 const char* compatible_data =
nullptr;
396 size_t compatible_len = 0;
398 const auto* compat_prop =
399 fdt_get_property(
fdt_header_, offset,
"compatible", &compat_len);
400 if (compat_prop !=
nullptr && compat_len > 0) {
401 compatible_data =
reinterpret_cast<const char*
>(compat_prop->data);
402 compatible_len =
static_cast<size_t>(compat_len);
405 uint64_t mmio_base = 0;
406 size_t mmio_size = 0;
408 const auto* reg_prop =
409 fdt_get_property(
fdt_header_, offset,
"reg", ®_len);
410 if (reg_prop !=
nullptr &&
411 static_cast<size_t>(reg_len) >= 2 *
sizeof(uint64_t)) {
412 const auto* reg =
reinterpret_cast<const uint64_t*
>(reg_prop->data);
413 mmio_base = fdt64_to_cpu(reg[0]);
414 mmio_size = fdt64_to_cpu(reg[1]);
419 const auto* irq_prop =
420 fdt_get_property(
fdt_header_, offset,
"interrupts", &irq_len);
421 if (irq_prop !=
nullptr &&
422 static_cast<size_t>(irq_len) >=
sizeof(uint32_t)) {
423 const auto* interrupts =
424 reinterpret_cast<const uint32_t*
>(irq_prop->data);
425 irq = fdt32_to_cpu(interrupts[0]);
428 if (!callback(node_name, compatible_data, compatible_len, mmio_base,
450 template <
typename Callback>
452 Callback&& callback)
const
454 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
458 offset = fdt_node_offset_by_compatible(
fdt_header_, offset, compatible);
460 if (offset == -FDT_ERR_NOTFOUND) {
466 const char* node_name = fdt_get_name(
fdt_header_, offset,
nullptr);
467 if (node_name ==
nullptr) {
471 uint64_t mmio_base = 0;
472 size_t mmio_size = 0;
474 const auto* reg_prop =
475 fdt_get_property(
fdt_header_, offset,
"reg", ®_len);
476 if (reg_prop !=
nullptr &&
477 static_cast<size_t>(reg_len) >= 2 *
sizeof(uint64_t)) {
478 const auto* reg =
reinterpret_cast<const uint64_t*
>(reg_prop->data);
479 mmio_base = fdt64_to_cpu(reg[0]);
480 mmio_size = fdt64_to_cpu(reg[1]);
485 const auto* irq_prop =
486 fdt_get_property(
fdt_header_, offset,
"interrupts", &irq_len);
487 if (irq_prop !=
nullptr &&
488 static_cast<size_t>(irq_len) >=
sizeof(uint32_t)) {
489 const auto* interrupts =
490 reinterpret_cast<const uint32_t*
>(irq_prop->data);
491 irq = fdt32_to_cpu(interrupts[0]);
494 if (!callback(offset, node_name, mmio_base, mmio_size, irq)) {
518 template <
typename Callback>
521 assert(
fdt_header_ !=
nullptr &&
"ForEachDeviceNode: fdt_header_ is null");
527 offset = fdt_next_node(
fdt_header_, offset, &depth);
529 if (offset == -FDT_ERR_NOTFOUND) {
535 const char* node_name = fdt_get_name(
fdt_header_, offset,
nullptr);
536 if (node_name ==
nullptr) {
542 const auto* status_prop =
543 fdt_get_property(
fdt_header_, offset,
"status", &status_len);
544 if (status_prop !=
nullptr) {
545 const char* status =
reinterpret_cast<const char*
>(status_prop->data);
546 if (
strcmp(status,
"okay") != 0 &&
strcmp(status,
"ok") != 0) {
552 if (fdt_getprop(
fdt_header_, offset,
"interrupt-controller",
nullptr) !=
556 if (fdt_getprop(
fdt_header_, offset,
"#clock-cells",
nullptr) !=
562 const auto* dt_prop =
563 fdt_get_property(
fdt_header_, offset,
"device_type", &dt_len);
564 if (dt_prop !=
nullptr) {
565 const char* dt =
reinterpret_cast<const char*
>(dt_prop->data);
566 if (
strcmp(dt,
"cpu") == 0 ||
strcmp(dt,
"memory") == 0) {
573 const char* compatible_data =
nullptr;
574 size_t compatible_len = 0;
576 const auto* compat_prop =
577 fdt_get_property(
fdt_header_, offset,
"compatible", &compat_len);
578 if (compat_prop !=
nullptr && compat_len > 0) {
579 compatible_data =
reinterpret_cast<const char*
>(compat_prop->data);
580 compatible_len =
static_cast<size_t>(compat_len);
584 uint64_t mmio_base = 0;
585 size_t mmio_size = 0;
587 const auto* reg_prop =
588 fdt_get_property(
fdt_header_, offset,
"reg", ®_len);
589 if (reg_prop !=
nullptr &&
590 static_cast<size_t>(reg_len) >= 2 *
sizeof(uint64_t)) {
591 const auto* reg =
reinterpret_cast<const uint64_t*
>(reg_prop->data);
592 mmio_base = fdt64_to_cpu(reg[0]);
593 mmio_size = fdt64_to_cpu(reg[1]);
599 const auto* irq_prop =
600 fdt_get_property(
fdt_header_, offset,
"interrupts", &irq_len);
601 if (irq_prop !=
nullptr &&
602 static_cast<size_t>(irq_len) >=
sizeof(uint32_t)) {
603 const auto* interrupts =
604 reinterpret_cast<const uint32_t*
>(irq_prop->data);
605 irq = fdt32_to_cpu(interrupts[0]);
608 if (!callback(node_name, compatible_data, compatible_len, mmio_base,
627 :
fdt_header_(reinterpret_cast<fdt_header*>(header)) {
629 klog::Err(
"KernelFdt init failed: {}", err.message());
637 static_cast<uint64_t
>(
reinterpret_cast<uintptr_t
>(
fdt_header_)),
638 static_cast<uint64_t
>(fdt32_to_cpu(
fdt_header_->totalsize)));
665 assert(
fdt_header_ !=
nullptr &&
"fdt_header_ is null");
692 auto offset = fdt_node_offset_by_compatible(
fdt_header_, -1, compatible);
708 offset = fdt_node_offset_by_compatible(
fdt_header_, offset, compatible);
714 const auto* status_prop =
715 fdt_get_property(
fdt_header_, offset,
"status", &len);
717 if (status_prop ==
nullptr) {
721 const char* status =
reinterpret_cast<const char*
>(status_prop->data);
722 if (
strcmp(status,
"okay") == 0 ||
strcmp(status,
"ok") == 0) {
737 const auto* prop = fdt_get_property(
fdt_header_, offset,
"reg", &len);
738 if (prop ==
nullptr) {
742 const auto* reg =
reinterpret_cast<const uint64_t*
>(prop->data);
743 if (
static_cast<size_t>(len) < 2 *
sizeof(uint64_t)) {
747 uint64_t base = fdt64_to_cpu(reg[0]);
748 size_t size = fdt64_to_cpu(reg[1]);
749 return std::pair{base, size};
763 offset = fdt_next_node(
fdt_header_, offset,
nullptr);
765 if (offset != -FDT_ERR_NOTFOUND) {
772 fdt_get_property(
fdt_header_, offset,
"device_type",
nullptr);
773 if (prop !=
nullptr) {
774 const char* type =
reinterpret_cast<const char*
>(prop->data);
775 if (
strcmp(type, device_type) == 0) {
791 const auto* prop = fdt_get_property(
fdt_header_, offset,
"method", &len);
792 if (prop ==
nullptr) {
795 return reinterpret_cast<const char*
>(prop->data);
805 auto validate_id = [
this, offset](
const char* name,
808 const auto* prop = fdt_get_property(
fdt_header_, offset, name, &len);
809 if (prop !=
nullptr &&
static_cast<size_t>(len) >=
sizeof(uint32_t)) {
811 fdt32_to_cpu(*
reinterpret_cast<const uint32_t*
>(prop->data));
812 klog::Debug(
"PSCI {} function ID: {:#X}", name,
id);
813 if (
id != expected) {
814 klog::Err(
"PSCI {} function ID mismatch: expected {:#X}, got {:#X}",
FDT(Flattened Device Tree)解析器
auto GetGicDist() const -> Expected< std::pair< uint64_t, size_t > >
获取 GIC Distributor 信息
auto ForEachDeviceNode(Callback &&callback) const -> Expected< void >
遍历 FDT 中所有"叶设备"节点,自动跳过基础设施节点。
auto GetTimebaseFrequency() const -> Expected< uint32_t >
获取 CPU 时钟频率
static constexpr uint64_t kPsciCpuOnFuncId
auto operator=(KernelFdt &&) -> KernelFdt &=default
auto GetRegProperty(int offset) const -> Expected< std::pair< uint64_t, size_t > >
获取 reg 属性(返回第一个 reg 条目)
KernelFdt(uint64_t header)
构造函数
auto GetGicCpu() const -> Expected< std::pair< uint64_t, size_t > >
获取 GIC CPU Interface (Redistributor) 信息
auto CountNodesByDeviceType(const char *device_type) const -> Expected< size_t >
按 device_type 统计节点数量
auto FindNode(const char *path) const -> Expected< int >
按路径查找节点
auto GetAarch64Intid(const char *compatible) const -> Expected< uint64_t >
获取 aarch64 中断号
auto GetPlic() const -> Expected< std::tuple< uint64_t, uint64_t, uint32_t, uint32_t > >
获取 PLIC 信息
KernelFdt(const KernelFdt &)=default
auto FindCompatibleNode(const char *compatible) const -> Expected< int >
按 compatible 查找第一个匹配的节点
static constexpr uint64_t kPsciCpuOffFuncId
auto GetGIC() const -> Expected< std::tuple< uint64_t, size_t, uint64_t, size_t > >
获取 GIC 信息
static constexpr uint64_t kPsciCpuSuspendFuncId
auto GetPsciMethod(int offset) const -> Expected< const char * >
获取 PSCI method 属性
auto ValidateFdtHeader() const -> Expected< void >
验证 FDT header
auto GetCoreCount() const -> Expected< size_t >
获取 CPU 核心数量
KernelFdt(KernelFdt &&)=default
auto FindEnabledCompatibleNode(const char *compatible) const -> Expected< int >
根据 compatible 查找已启用的节点(跳过 status="disabled")
auto GetSerial() const -> Expected< std::tuple< uint64_t, size_t, uint32_t > >
获取串口信息
auto ForEachCompatibleNode(const char *compatible, Callback &&callback) const -> Expected< void >
遍历所有匹配指定 compatible 的 FDT 节点
auto operator=(const KernelFdt &) -> KernelFdt &=default
auto ForEachNode(Callback &&callback) const -> Expected< void >
遍历 FDT 中所有设备节点
fdt_header * fdt_header_
FDT header 指针
auto GetMemory() const -> Expected< std::pair< uint64_t, size_t > >
获取内存信息
auto ValidatePsciFunctionIds(int offset) const -> Expected< void >
验证 PSCI 函数 ID
auto CheckPSCI() const -> Expected< void >
判断 PSCI 信息
@ kFdtInvalidPropertySize
std::expected< T, Error > Expected
std::expected 别名模板
etl::singleton< KernelFdt > KernelFdtSingleton
auto Err(etl::format_string< Args... > fmt, Args &&... args) -> void
以 ERROR 级别记录日志
auto Debug(etl::format_string< Args... > fmt, Args &&... args) -> void
以 DEBUG 级别记录日志(SIMPLEKERNEL_MIN_LOG_LEVEL > 0 时编译期消除)