diff options
Diffstat (limited to 'fdt/fdt.cc')
| -rw-r--r-- | fdt/fdt.cc | 625 |
1 files changed, 0 insertions, 625 deletions
diff --git a/fdt/fdt.cc b/fdt/fdt.cc deleted file mode 100644 index d44ebda..0000000 --- a/fdt/fdt.cc +++ /dev/null @@ -1,625 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -/* - * fdt.cc — Flattened Device Tree parser implementation - * - * Implements the two-phase FDT parsing strategy described in fdt.md: - * - * Phase 1 (Early Scan): - * Static methods that linearly walk the raw FDT blob in-place, - * performing zero memory allocation. Called before PMM/Slab exist. - * - * Phase 2 (Unflatten): - * Builds a persistent DeviceNode C++ object tree using Slab. - * Stub implementation — will be completed when Slab is available. - * - * Reference: docs/specs/fdt.md - */ - -#include <lume/addr.h> -#include <lume/fdt.h> -#include <lume/klog.h> -#include <lume/new.h> - -/* ------------------------------------------------------------------ */ -/* Global singleton (Placement New pattern, see global_init.md §3.1) */ -/* ------------------------------------------------------------------ */ - -alignas(FdtManager) uint8 g_fdt_buf[sizeof(FdtManager)]; -FdtManager *g_fdt = nullptr; - -/* ------------------------------------------------------------------ */ -/* FDT binary format constants (DTSpec v0.4) */ -/* ------------------------------------------------------------------ */ - -struct fdt_header { - uint32 magic; - uint32 totalsize; - uint32 off_dt_struct; - uint32 off_dt_strings; - uint32 off_mem_rsvmap; - uint32 version; - uint32 last_comp_version; - uint32 boot_cpuid_phys; - uint32 size_dt_strings; - uint32 size_dt_struct; -}; - -#define FDT_MAGIC 0xd00dfeed -#define FDT_BEGIN_NODE 1 -#define FDT_END_NODE 2 -#define FDT_PROP 3 -#define FDT_NOP 4 -#define FDT_END 9 - -/* ------------------------------------------------------------------ */ -/* Minimal string helpers (no lib/ dependency for early boot) */ -/* ------------------------------------------------------------------ */ - -static inline bool str_starts_with(const char *str, const char *prefix) { - while (*prefix) { - if (*str++ != *prefix++) - return false; - } - return true; -} - -static inline bool str_equals(const char *s1, const char *s2) { - while (*s1 && *s1 == *s2) { - s1++; - s2++; - } - return *s1 == *s2; -} - -/* - * Check whether a null-separated string list (e.g. the "compatible" - * property) contains @target. - */ -static bool prop_contains_string(const char *prop_val, uint32 len, - const char *target) { - uint32 i = 0; - while (i < len) { - const char *s = prop_val + i; - if (str_equals(s, target)) - return true; - while (i < len && prop_val[i] != '\0') - i++; - i++; // skip '\0' separator - } - return false; -} - -/* ------------------------------------------------------------------ */ -/* Byte-swap helpers */ -/* ------------------------------------------------------------------ */ - -/* - * Manual 32-bit big-endian to little-endian conversion. - * We cannot use __builtin_bswap32 because on RISC-V without the Zbb - * extension the compiler emits a call to libgcc's __bswapsi2, which - * is unavailable in a freestanding (-nostdlib) environment. - */ -static inline uint32 bswap32(uint32 x) { - return ((x & 0xFF000000u) >> 24) | ((x & 0x00FF0000u) >> 8) | - ((x & 0x0000FF00u) << 8) | ((x & 0x000000FFu) << 24); -} - -/* - * Read a big-endian cell value from the FDT struct block. - * @cells: number of 32-bit cells (1 or 2). - * 2-cell values are common for 64-bit addresses/sizes. - */ -static inline uint64 read_cells(const uint8 *data, uint32 cells) { - const uint32 *p = reinterpret_cast<const uint32 *>(data); - if (cells == 2) { - uint64 high = bswap32(p[0]); - uint64 low = bswap32(p[1]); - return (high << 32) | low; - } - return bswap32(p[0]); -} - -/* ------------------------------------------------------------------ */ -/* FdtManager construction */ -/* ------------------------------------------------------------------ */ - -FdtManager::FdtManager(uint64 fdt_paddr) - : fdt_paddr_(fdt_paddr) {} - -/* ------------------------------------------------------------------ */ -/* Phase 1: Early scan — memory layout */ -/* ------------------------------------------------------------------ */ - -/* - * Walk the raw FDT struct block to locate the /memory node and - * extract its "reg" property (base address + size). - * - * Cell sizes (#address-cells, #size-cells) are tracked per depth - * level to correctly decode the "reg" value under any bus topology. - * - * Panics on invalid magic or missing /memory node — there is no - * recovery path this early in boot. - */ -void FdtManager::early_scan_mem(uint64 fdt_paddr, uint64 *base, uint64 *size) { - const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_paddr); - if (bswap32(hdr->magic) != FDT_MAGIC) - kernel_panic("FDT early_scan_mem: invalid magic number", nullptr); - - const uint8 *dt_struct = - reinterpret_cast<const uint8 *>(fdt_paddr) + bswap32(hdr->off_dt_struct); - const char *dt_strings = - reinterpret_cast<const char *>(fdt_paddr) + bswap32(hdr->off_dt_strings); - - uint32 offset = 0; - int depth = -1; - - // Per-depth cell sizes (inherit from parent on entry) - uint32 address_cells[16] = {2}; - uint32 size_cells[16] = {1}; - bool in_memory_node = false; - - while (true) { - uint32 token = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - offset += 4; - - if (token == FDT_BEGIN_NODE) { - const char *name = reinterpret_cast<const char *>(dt_struct + offset); - uint32 name_len = 0; - while (name[name_len] != '\0') - name_len++; - offset = (offset + name_len + 1 + 3) & ~3u; // align to 4 bytes - - depth++; - - // Inherit parent's cell sizes - if (depth > 0 && depth < 16) { - address_cells[depth] = address_cells[depth - 1]; - size_cells[depth] = size_cells[depth - 1]; - } - - // Match top-level "memory" or "memory@..." nodes - in_memory_node = (depth == 1 && str_starts_with(name, "memory")); - - } else if (token == FDT_END_NODE) { - if (depth >= 0) - depth--; - in_memory_node = false; - - } else if (token == FDT_PROP) { - uint32 len = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - uint32 nameoff = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4)); - offset += 8; - - const char *prop_name = dt_strings + nameoff; - const uint8 *prop_val = dt_struct + offset; - - if (depth >= 0 && depth < 16) { - // Track cell size overrides at the current depth - if (str_equals(prop_name, "#address-cells")) { - address_cells[depth] = - bswap32(*reinterpret_cast<const uint32 *>(prop_val)); - } else if (str_equals(prop_name, "#size-cells")) { - size_cells[depth] = - bswap32(*reinterpret_cast<const uint32 *>(prop_val)); - } - // Decode the "reg" property using the parent's cell sizes - else if (in_memory_node && str_equals(prop_name, "reg")) { - uint32 p_depth = (depth > 0) ? depth - 1 : 0; - *base = read_cells(prop_val, address_cells[p_depth]); - *size = read_cells(prop_val + address_cells[p_depth] * 4, - size_cells[p_depth]); - return; // Success - } - } - - offset = (offset + len + 3) & ~3u; // align to 4 bytes - - } else if (token == FDT_NOP) { - continue; - } else if (token == FDT_END) { - break; - } else { - kernel_panic("FDT early_scan_mem: unknown token", nullptr); - } - } - - kernel_panic("FDT early_scan_mem: /memory node not found", nullptr); -} - -/* ------------------------------------------------------------------ */ -/* Phase 1: Early scan — UART address */ -/* ------------------------------------------------------------------ */ - -/* - * Locate a UART node by matching compatible = "ns16550a" and return - * its base address from the "reg" property. - * - * Unlike early_scan_mem, failure here is non-fatal: we fall back to - * the platform's default UART address (0x10000000 for QEMU virt). - */ -void FdtManager::early_scan_uart(uint64 fdt_paddr, uint64 *uart_addr, - uint64 *uart_size) { - if (!uart_addr || !uart_size) - return; - - auto set_default_uart = [uart_addr, uart_size]() { - *uart_addr = 0x10000000ULL; - *uart_size = 0x100; - }; - - const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_paddr); - if (bswap32(hdr->magic) != FDT_MAGIC) { - set_default_uart(); - return; - } - - const uint8 *dt_struct = - reinterpret_cast<const uint8 *>(fdt_paddr) + bswap32(hdr->off_dt_struct); - const char *dt_strings = - reinterpret_cast<const char *>(fdt_paddr) + bswap32(hdr->off_dt_strings); - - uint32 offset = 0; - int depth = -1; - uint32 address_cells[16] = {2}; - uint32 size_cells[16] = {1}; - bool in_uart_node = false; - bool has_current_node_reg = false; - uint64 current_node_reg = 0; - uint64 current_node_size = 0; - - while (true) { - uint32 token = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - offset += 4; - - if (token == FDT_BEGIN_NODE) { - const char *name = reinterpret_cast<const char *>(dt_struct + offset); - uint32 name_len = 0; - while (name[name_len] != '\0') - name_len++; - offset = (offset + name_len + 1 + 3) & ~3u; - - depth++; - if (depth > 0 && depth < 16) { - address_cells[depth] = address_cells[depth - 1]; - size_cells[depth] = size_cells[depth - 1]; - } - in_uart_node = false; - has_current_node_reg = false; - - } else if (token == FDT_END_NODE) { - if (depth >= 0) - depth--; - in_uart_node = false; - has_current_node_reg = false; - - } else if (token == FDT_PROP) { - uint32 len = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - uint32 nameoff = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4)); - offset += 8; - - const char *prop_name = dt_strings + nameoff; - const uint8 *prop_val = dt_struct + offset; - - if (depth >= 0 && depth < 16) { - if (str_equals(prop_name, "#address-cells")) { - address_cells[depth] = - bswap32(*reinterpret_cast<const uint32 *>(prop_val)); - } else if (str_equals(prop_name, "#size-cells")) { - size_cells[depth] = - bswap32(*reinterpret_cast<const uint32 *>(prop_val)); - } else if (str_equals(prop_name, "compatible")) { - if (prop_contains_string(reinterpret_cast<const char *>(prop_val), - len, "ns16550a")) { - in_uart_node = true; - if (has_current_node_reg) { - *uart_addr = current_node_reg; - *uart_size = current_node_size; - return; - } - } - } else if (str_equals(prop_name, "reg")) { - uint32 p_depth = (depth > 0) ? depth - 1 : 0; - current_node_reg = read_cells(prop_val, address_cells[p_depth]); - current_node_size = read_cells(prop_val + address_cells[p_depth] * 4, - size_cells[p_depth]); - has_current_node_reg = true; - if (in_uart_node) { - *uart_addr = current_node_reg; - *uart_size = current_node_size; - return; // Found it - } - } - } - - offset = (offset + len + 3) & ~3u; - - } else if (token == FDT_NOP) { - continue; - } else if (token == FDT_END) { - break; - } else { - break; // Unknown token — give up silently - } - } - - set_default_uart(); -} - -/* ------------------------------------------------------------------ */ -/* Phase 2: Unflatten (stub — requires Slab) */ -/* ------------------------------------------------------------------ */ - -void FdtManager::unflatten() { - const uint64 fdt_va = pa_to_va(phys_addr(fdt_paddr_)).raw; - const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_va); - if (bswap32(hdr->magic) != FDT_MAGIC) - kernel_panic("fdt unflatten: invalid magic", nullptr); - - const uint8 *dt_struct = - reinterpret_cast<const uint8 *>(fdt_va) + bswap32(hdr->off_dt_struct); - const char *dt_strings = - reinterpret_cast<const char *>(fdt_va) + bswap32(hdr->off_dt_strings); - - uint32 offset = 0; - - /* Stack to track current node at each depth (max 16 levels) */ - DeviceNode *stack[16] = {}; - int depth = -1; - - while (true) { - uint32 token = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - offset += 4; - - if (token == FDT_BEGIN_NODE) { - const char *name = reinterpret_cast<const char *>(dt_struct + offset); - uint32 name_len = 0; - while (name[name_len] != '\0') - name_len++; - offset = (offset + name_len + 1 + 3) & ~3u; - - depth++; - if (depth >= 16) - kernel_panic("fdt unflatten: tree too deep", nullptr); - - DeviceNode *parent = (depth > 0) ? stack[depth - 1] : nullptr; - DeviceNode *node = new DeviceNode(parent, name); - if (!node) - kernel_panic("fdt unflatten: OOM creating DeviceNode", nullptr); - - stack[depth] = node; - - if (parent) - parent->add_child(node); - else - root_ = node; /* Root node */ - - } else if (token == FDT_END_NODE) { - if (depth >= 0) - depth--; - - } else if (token == FDT_PROP) { - uint32 len = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - uint32 nameoff = - bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4)); - offset += 8; - - const char *prop_name = dt_strings + nameoff; - const void *prop_val = dt_struct + offset; - - if (depth >= 0 && depth < 16 && stack[depth]) { - FdtProperty *prop = new FdtProperty; - if (!prop) - kernel_panic("fdt unflatten: OOM creating FdtProperty", nullptr); - prop->name = prop_name; - prop->value = prop_val; - prop->len = static_cast<int>(len); - prop->link.init(); - stack[depth]->add_prop(prop); - } - - offset = (offset + len + 3) & ~3u; - - } else if (token == FDT_NOP) { - continue; - } else if (token == FDT_END) { - break; - } else { - kernel_panic("fdt unflatten: unknown token", nullptr); - } - } - - is_unflattened_ = true; -} - -/* Recursive helper: find first node with matching compatible string */ -static DeviceNode *find_compat_recursive(DeviceNode *node, const char *compat) { - const FdtProperty *prop = node->get_prop("compatible"); - if (prop) { - const char *val = static_cast<const char *>(prop->value); - uint32 i = 0; - while (i < static_cast<uint32>(prop->len)) { - const char *s = val + i; - if (str_equals(s, compat)) - return node; - while (i < static_cast<uint32>(prop->len) && val[i] != '\0') - i++; - i++; - } - } - - /* Search children */ - list_node *cur = node->children_list_head(); - list_node *end = node->children_list_sentinel(); - while (cur != end) { - DeviceNode *child = list_entry<DeviceNode, &DeviceNode::sibling_link_>(cur); - DeviceNode *found = find_compat_recursive(child, compat); - if (found) - return found; - cur = cur->next; - } - return nullptr; -} - -DeviceNode *FdtManager::find_compatible(const char *compat_string) { - if (!is_unflattened_) - kernel_panic("fdt: tree not unflattened yet", nullptr); - if (!root_) - return nullptr; - return find_compat_recursive(root_, compat_string); -} - -DeviceNode *FdtManager::get_node_by_path(const char *path) { - if (!is_unflattened_) - kernel_panic("fdt: tree not unflattened yet", nullptr); - if (!root_ || !path) - return nullptr; - - /* Skip leading '/' */ - if (*path == '/') - path++; - if (*path == '\0') - return root_; - - DeviceNode *cur = root_; - while (*path && cur) { - /* Extract next path component */ - const char *end = path; - while (*end && *end != '/') - end++; - - /* Search children for a name matching this component */ - bool found = false; - list_node *lc = cur->children_list_head(); - list_node *le = cur->children_list_sentinel(); - while (lc != le) { - DeviceNode *child = - list_entry<DeviceNode, &DeviceNode::sibling_link_>(lc); - /* Compare name up to the component length */ - const char *cn = child->get_name(); - const char *p = path; - const char *e = end; - bool match = true; - while (p < e) { - if (*cn != *p) { - match = false; - break; - } - cn++; - p++; - } - if (match && *cn == '\0') { - cur = child; - found = true; - break; - } - lc = lc->next; - } - - if (!found) - return nullptr; - - path = (*end == '/') ? end + 1 : end; - } - return cur; -} - -/* ------------------------------------------------------------------ */ -/* Public free-function API */ -/* ------------------------------------------------------------------ */ - -void fdt_init(uint64 fdt_paddr) { - const uint32 *magic_ptr = reinterpret_cast<const uint32 *>(fdt_paddr); - if (bswap32(*magic_ptr) != FDT_MAGIC) - kernel_panic("fdt_init: invalid FDT magic number", nullptr); - - g_fdt = new (g_fdt_buf) FdtManager(fdt_paddr); -} - -void fdt_unflatten() { g_fdt->unflatten(); } - -DeviceNode *fdt_find_compatible(const char *compat_string) { - return g_fdt->find_compatible(compat_string); -} - -DeviceNode *fdt_get_node_by_path(const char *path) { - return g_fdt->get_node_by_path(path); -} - -void fdt_early_get_mem_info(uint64 *base, uint64 *size) { - FdtManager::early_scan_mem(g_fdt->get_paddr(), base, size); -} - -void fdt_early_get_uart_info(uint64 *uart_addr, uint64 *uart_size) { - FdtManager::early_scan_uart(g_fdt->get_paddr(), uart_addr, uart_size); -} - -void FdtManager::early_scan_kaslr(uint64 fdt_paddr, uint64 *seed) -{ - if (!seed) return; - *seed = 0; - - const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_paddr); - if (bswap32(hdr->magic) != FDT_MAGIC) return; - - const uint8 *dt_struct = reinterpret_cast<const uint8 *>(fdt_paddr) + bswap32(hdr->off_dt_struct); - const char *dt_strings = reinterpret_cast<const char *>(fdt_paddr) + bswap32(hdr->off_dt_strings); - - uint32 offset = 0; - int depth = -1; - bool in_chosen = false; - - while (true) { - uint32 token = bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - offset += 4; - - if (token == FDT_BEGIN_NODE) { - const char *name = reinterpret_cast<const char *>(dt_struct + offset); - uint32 name_len = 0; - while (name[name_len] != '\0') name_len++; - offset = (offset + name_len + 1 + 3) & ~3u; - - depth++; - if (depth == 1 && str_starts_with(name, "chosen")) { - in_chosen = true; - } else if (depth == 1) { - in_chosen = false; - } - } else if (token == FDT_END_NODE) { - if (depth >= 0) depth--; - if (depth < 1) in_chosen = false; - } else if (token == FDT_PROP) { - uint32 len = bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset)); - uint32 nameoff = bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4)); - offset += 8; - - if (in_chosen) { - const char *prop_name = dt_strings + nameoff; - if (str_starts_with(prop_name, "kaslr-seed") && len == 8) { - *seed = read_cells(dt_struct + offset, 2); - return; - } - } - offset = (offset + len + 3) & ~3u; - } else if (token == FDT_NOP) { - continue; - } else if (token == FDT_END) { - break; - } else { - break; - } - } -} - -void fdt_early_get_kaslr_seed(uint64 *seed) { - FdtManager::early_scan_kaslr(g_fdt->get_paddr(), seed); -} |
