diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-04-14 22:47:15 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-04-14 22:47:15 +0800 |
| commit | ac9741e12222e72bad3414bc6f588e70a3d130cc (patch) | |
| tree | 6aace9f63a2ea54c7c3fd1e49e98ff81b638296f /src/kernel/fdt.cc | |
| parent | 1e862099a13266da57ff7c6de11c1a11c42a8112 (diff) | |
| download | lume-ac9741e12222e72bad3414bc6f588e70a3d130cc.tar.gz lume-ac9741e12222e72bad3414bc6f588e70a3d130cc.tar.xz | |
feat: implement FDT-based memory discovery and add comprehensive debug logging to exec syscalls
Diffstat (limited to 'src/kernel/fdt.cc')
| -rw-r--r-- | src/kernel/fdt.cc | 246 |
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 |
