summaryrefslogtreecommitdiff
path: root/src/kernel/fdt.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/fdt.cc')
-rw-r--r--src/kernel/fdt.cc246
1 files changed, 206 insertions, 40 deletions
diff --git a/src/kernel/fdt.cc b/src/kernel/fdt.cc
index e9f8422..ae09c84 100644
--- a/src/kernel/fdt.cc
+++ b/src/kernel/fdt.cc
@@ -1,13 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Copyright (C) 2026 Richard QIn
+ * Copyright (C) 2026 Richard Qin
+ * LumeOS - FDT Parser
+ *
+ * Parses the Flattened Device Tree to discover hardware.
+ * Populates g_devices for use by all drivers.
*/
#include "common/types.h"
-#include "drivers/uart.h"
-#include "kernel/config.h"
+#include "kernel/fdt.h"
#include "lib/string.h"
-inline uint32_t bswap(uint32_t x)
+// Global device registry
+DeviceRegistry g_devices;
+
+// Keep g_dtb_addr for PMM to protect DTB pages
+// g_dtb_addr is defined in entry.S, g_uart_base in config.cc
+extern "C" uint64 g_dtb_addr;
+extern uint64 g_uart_base;
+
+static inline uint32 bswap(uint32 x)
{
return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) |
((x & 0xFF0000) >> 8) | ((x >> 24) & 0xFF);
@@ -34,74 +45,229 @@ struct fdt_header
#define FDT_NOP 4
#define FDT_END 9
+// Check if 'str' starts with 'prefix'
+static bool starts_with(const char *str, const char *prefix)
+{
+ while (*prefix)
+ {
+ if (*str != *prefix)
+ return false;
+ str++;
+ prefix++;
+ }
+ return true;
+}
+
+// Check if 'haystack' contains 'needle' as a substring within 'len' bytes
+// (compatible strings may have multiple null-separated entries)
+static bool compat_match(const uint8 *data, uint32 len, const char *needle)
+{
+ uint32 nlen = strlen(needle);
+ for (uint32 i = 0; i + nlen <= len; i++)
+ {
+ if (memcmp(data + i, needle, nlen) == 0)
+ return true;
+ }
+ return false;
+}
+
+// Add a MMIO region to the registry (avoids duplicates)
+static void add_mmio_region(uint64 base, uint64 size)
+{
+ if (base == 0 || size == 0)
+ return;
+ // Only track I/O regions below RAM
+ if (base >= 0x80000000ULL)
+ return;
+ if (g_devices.mmio_count >= DeviceRegistry::MAX_MMIO)
+ return;
+
+ // Check for overlap/duplicate
+ for (int i = 0; i < g_devices.mmio_count; i++)
+ {
+ if (g_devices.mmio_regions[i].base == base)
+ return;
+ }
+
+ g_devices.mmio_regions[g_devices.mmio_count].base = base;
+ g_devices.mmio_regions[g_devices.mmio_count].size = size;
+ g_devices.mmio_count++;
+}
-void fdt_init(uint64 dtb)
+void fdt_parse(uint64 dtb)
{
- if (dtb == 0) return;
+ // Clear registry
+ memset(&g_devices, 0, sizeof(g_devices));
+
+ if (dtb == 0)
+ return;
- struct fdt_header* header = (struct fdt_header*)dtb;
+ g_dtb_addr = dtb;
+
+ struct fdt_header *header = (struct fdt_header *)dtb;
if (bswap(header->magic) != FDT_MAGIC)
return;
-
- uint8* struct_ptr = (uint8*)(dtb + bswap(header->off_dt_struct));
- char* strings_ptr = (char *)(dtb + bswap(header->off_dt_strings));
+ uint8 *struct_ptr = (uint8 *)(dtb + bswap(header->off_dt_struct));
+ char *strings_ptr = (char *)(dtb + bswap(header->off_dt_strings));
+
+ // Parsing state
+ char current_node_name[64];
+ current_node_name[0] = '\0';
+
+ // Per-node state
uint64 current_reg_addr = 0;
+ uint64 current_reg_size = 0;
+ uint32 current_irq = 0;
+ const uint8 *current_compat = nullptr;
+ uint32 current_compat_len = 0;
+ bool has_reg = false;
+ bool has_compat = false;
+ // bool has_irq = false; // Unused
+
+ // Depth tracking (for #address-cells / #size-cells)
+ // Default for root: 2 address-cells, 2 size-cells
+ int depth = 0;
while (1)
{
- uint32 token = bswap(*(uint32*)struct_ptr);
+ uint32 token = bswap(*(uint32 *)struct_ptr);
struct_ptr += 4;
- if(token == FDT_END)break;
- if(token == FDT_NOP)continue;
+ if (token == FDT_END)
+ break;
+ if (token == FDT_NOP)
+ continue;
- if(token == FDT_BEGIN_NODE)
+ if (token == FDT_BEGIN_NODE)
{
- while (*struct_ptr != 0)struct_ptr++;
+ // Save node name
+ // char *name = (char *)struct_ptr;
+ int i = 0;
+ while (*struct_ptr != 0 && i < 63)
+ {
+ current_node_name[i++] = *struct_ptr;
+ struct_ptr++;
+ }
+ current_node_name[i] = '\0';
+ while (*struct_ptr != 0)
+ struct_ptr++;
struct_ptr++;
- struct_ptr = (uint8*)(((uint64)struct_ptr + 3) & ~3);
+ struct_ptr = (uint8 *)(((uint64)struct_ptr + 3) & ~3);
+
+ // Reset per-node state
+ current_reg_addr = 0;
+ current_reg_size = 0;
+ current_irq = 0;
+ current_compat = nullptr;
+ current_compat_len = 0;
+ has_reg = false;
+ has_compat = false;
+ // has_irq = false;
+
+ depth++;
}
- else if(token == FDT_END_NODE)
+ else if (token == FDT_END_NODE)
{
-
+ // Process the node we just finished
+ if (has_reg && has_compat)
+ {
+ // UART: ns16550a
+ if (compat_match(current_compat, current_compat_len, "ns16550"))
+ {
+ g_devices.uart.base_addr = current_reg_addr;
+ g_devices.uart.size = current_reg_size ? current_reg_size : 0x100;
+ g_devices.uart.irq = current_irq;
+ add_mmio_region(current_reg_addr, g_devices.uart.size);
+ }
+ // PLIC
+ else if (compat_match(current_compat, current_compat_len, "plic"))
+ {
+ g_devices.plic.base_addr = current_reg_addr;
+ g_devices.plic.size = current_reg_size ? current_reg_size : 0x4000000;
+ g_devices.plic.irq = 0;
+ add_mmio_region(current_reg_addr, g_devices.plic.size);
+ }
+ // CLINT
+ else if (compat_match(current_compat, current_compat_len, "clint"))
+ {
+ g_devices.clint.base_addr = current_reg_addr;
+ g_devices.clint.size = current_reg_size ? current_reg_size : 0x10000;
+ g_devices.clint.irq = 0;
+ add_mmio_region(current_reg_addr, g_devices.clint.size);
+ }
+ // VirtIO
+ else if (compat_match(current_compat, current_compat_len, "virtio"))
+ {
+ if (g_devices.virtio_count < DeviceRegistry::MAX_VIRTIO)
+ {
+ int idx = g_devices.virtio_count++;
+ g_devices.virtio[idx].base_addr = current_reg_addr;
+ g_devices.virtio[idx].size = current_reg_size ? current_reg_size : 0x1000;
+ g_devices.virtio[idx].irq = current_irq;
+ add_mmio_region(current_reg_addr, g_devices.virtio[idx].size);
+ }
+ }
+ }
+
+ // Memory node: name starts with "memory"
+ if (has_reg && starts_with(current_node_name, "memory"))
+ {
+ g_devices.mem_base = current_reg_addr;
+ g_devices.mem_size = current_reg_size;
+ }
+
+ depth--;
}
else if (token == FDT_PROP)
{
- uint32 len = bswap(*(uint32*)struct_ptr);
+ uint32 len = bswap(*(uint32 *)struct_ptr);
struct_ptr += 4;
- uint32 nameoff = bswap(*(uint32*)struct_ptr);
+ uint32 nameoff = bswap(*(uint32 *)struct_ptr);
struct_ptr += 4;
- char* name = strings_ptr + nameoff;
- uint8* data = struct_ptr;
+ char *name = strings_ptr + nameoff;
+ uint8 *data = struct_ptr;
struct_ptr += len;
- struct_ptr = (uint8*)(((uint64)struct_ptr + 3) & ~3);
+ struct_ptr = (uint8 *)(((uint64)struct_ptr + 3) & ~3);
- if(strcmp(name, "reg") == 0 && len >= 8)
+ if (strcmp(name, "reg") == 0 && len >= 8)
{
- uint32 hi = bswap(*(uint32*)data);
- uint32 lo = bswap(*(uint32*)(data + 4));
+ // Assume 2 address-cells, 2 size-cells (standard for QEMU virt)
+ uint32 hi = bswap(*(uint32 *)data);
+ uint32 lo = bswap(*(uint32 *)(data + 4));
current_reg_addr = ((uint64)hi << 32) | lo;
- }
- if(strcmp(name, "compatible") == 0)
- {
- if (g_uart_base == 0)
+ if (len >= 16)
{
- for(uint32 i = 0; i < len; i++)
- {
- if (data[i] == 'n' && data[i + 1] == 's' && data[i + 2] == '1')
- {
- g_uart_base = current_reg_addr;
- break;
- }
- }
+ uint32 shi = bswap(*(uint32 *)(data + 8));
+ uint32 slo = bswap(*(uint32 *)(data + 12));
+ current_reg_size = ((uint64)shi << 32) | slo;
}
-
+ has_reg = true;
+ }
+ else if (strcmp(name, "compatible") == 0)
+ {
+ current_compat = data;
+ current_compat_len = len;
+ has_compat = true;
+ }
+ else if (strcmp(name, "interrupts") == 0 && len >= 4)
+ {
+ current_irq = bswap(*(uint32 *)data);
+ // has_irq = true;
+ }
+ else if (strcmp(name, "interrupts-extended") == 0 && len >= 8)
+ {
+ // interrupts-extended = <&phandle irq_num>
+ // IRQ number is typically the second cell
+ current_irq = bswap(*(uint32 *)(data + 4));
+ // has_irq = true;
}
}
}
-
+
+ // Legacy compat
+ g_uart_base = g_devices.uart.base_addr;
} \ No newline at end of file