aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTravis Geiselbrecht <geist@foobox.com>2024-04-16 23:08:15 -0700
committerTravis Geiselbrecht <geist@foobox.com>2024-04-16 23:11:59 -0700
commit4a97f932fdaa51bf23108f86ff2978c573625036 (patch)
treefe5bd656fbe133a92ee41e099d26b5ab1c8c4be1
parent05540c992a2d2443904cebb7f465be3688eca1bb (diff)
downloadlk-4a97f932fdaa51bf23108f86ff2978c573625036.tar.gz
[lib][fdtwalk] restructure the fdtwalker helper routines
Instead of one large routine that takes a list of optional callbacks, build some helper routines that do the work that a few platforms have implemented on their own to share some code between them. Future enhancements: move some of the helpers out of this library into the library that implements the thing it's helping with (ie, PCI bringup, bootstrapping arch specific cores). For now just leave them in helper.cc which is conditionally compiled.
-rw-r--r--lib/fdtwalk/fdtwalk.cpp270
-rw-r--r--lib/fdtwalk/helpers.cpp263
-rw-r--r--lib/fdtwalk/include/lib/fdtwalk.h37
-rw-r--r--lib/fdtwalk/rules.mk1
4 files changed, 460 insertions, 111 deletions
diff --git a/lib/fdtwalk/fdtwalk.cpp b/lib/fdtwalk/fdtwalk.cpp
index 8f39a83a..17c8a515 100644
--- a/lib/fdtwalk/fdtwalk.cpp
+++ b/lib/fdtwalk/fdtwalk.cpp
@@ -9,19 +9,23 @@
#include <assert.h>
#include <libfdt.h>
-#include <stdio.h>
+#include <lk/cpp.h>
#include <lk/err.h>
#include <lk/trace.h>
+#include <stdio.h>
#include <sys/types.h>
#define LOCAL_TRACE 0
-static const int MAX_DEPTH = 16;
+
+namespace {
+
+const int MAX_DEPTH = 16;
/* read the #address-cells and #size-cells properties at the current node to
* see if there are any overriding sizes at this level. It's okay to not
* find the properties.
*/
-static void read_address_size_cells(const void *fdt, int offset, int depth,
+void read_address_size_cells(const void *fdt, int offset, int depth,
uint32_t *address_cells, uint32_t *size_cells) {
LTRACEF_LEVEL(3, "fdt %p, offset %d depth %d\n", fdt, offset, depth);
@@ -43,7 +47,7 @@ static void read_address_size_cells(const void *fdt, int offset, int depth,
LTRACEF_LEVEL(3, "address-cells %u size-cells %u\n", address_cells[depth], size_cells[depth]);
}
-static status_t read_base_len_pair(const uint8_t *prop_ptr, size_t prop_len,
+status_t read_base_len_pair(const uint8_t *prop_ptr, size_t prop_len,
size_t address_cell_size, size_t size_cell_size,
uint64_t *base, uint64_t *len) {
*base = 0;
@@ -78,7 +82,7 @@ static status_t read_base_len_pair(const uint8_t *prop_ptr, size_t prop_len,
}
// returns true or false if a particular property is a particular value
-static bool check_prop_is_val_string(const void *fdt, int offset, const char *prop, const char *val) {
+bool check_prop_is_val_string(const void *fdt, int offset, const char *prop, const char *val) {
int lenp;
const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, prop, &lenp);
if (!prop_ptr || lenp <= 0) {
@@ -92,87 +96,174 @@ static bool check_prop_is_val_string(const void *fdt, int offset, const char *pr
return false;
}
-status_t fdt_walk(const void *fdt, const struct fdt_walk_callbacks *cb) {
+struct fdt_walk_state {
+ const void *fdt;
+ int offset;
+ int depth;
+ uint32_t address_cells[MAX_DEPTH];
+ uint32_t size_cells[MAX_DEPTH];
+
+ uint32_t curr_address_cell() const { return address_cells[depth]; }
+ uint32_t curr_size_cell() const { return size_cells[depth]; }
+};
+
+// Inner page table walker routine. Takes a callback in the form of a function or lambda
+// and calls on every node in the tree.
+template <typename callback>
+status_t _fdt_walk(const void *fdt, callback cb) {
int err = fdt_check_header(fdt);
if (err != 0) {
return ERR_NOT_FOUND;
}
/* walk the nodes */
- int depth = 0;
- int offset = 0;
- uint32_t address_cells[MAX_DEPTH];
- uint32_t size_cells[MAX_DEPTH];
-
- /* if >= 0, we're inside /reserved-memory */
- int reserved_memory_depth = -1;
+ fdt_walk_state state = {};
+ state.fdt = fdt;
/* read the address/size cells properties at the root, if present */
- address_cells[0] = 2;
- size_cells[0] = 1;
- read_address_size_cells(fdt, offset, 0, address_cells, size_cells);
+ state.address_cells[0] = 2;
+ state.size_cells[0] = 1;
+ read_address_size_cells(fdt, state.offset, 0, state.address_cells, state.size_cells);
for (;;) {
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < 0 || depth < 0) {
+ state.offset = fdt_next_node(fdt, state.offset, &state.depth);
+ if (state.offset < 0 || state.depth < 0) {
break;
}
- LTRACEF_LEVEL(3, "fdt_next node offset %d, depth %d\n", offset, depth);
+ LTRACEF_LEVEL(3, "fdt_next node offset %d, depth %d\n", state.offset, state.depth);
- if (depth >= MAX_DEPTH) {
+ if (state.depth >= MAX_DEPTH) {
printf("FDTWALK: exceeded max depth %d\n", MAX_DEPTH);
return ERR_NO_MEMORY;
}
+ // TODO: fix the way address and size cells are inherited, they're not exactly correct
+ // here.
+
/* copy the address/size cells from the parent depth and then see if we
* have local properties to override it. */
- if (depth > 0) {
- address_cells[depth] = address_cells[depth - 1];
- size_cells[depth] = size_cells[depth - 1];
+ if (state.depth > 0) {
+ state.address_cells[state.depth] = state.address_cells[state.depth - 1];
+ state.size_cells[state.depth] = state.size_cells[state.depth - 1];
}
- read_address_size_cells(fdt, offset, depth, address_cells, size_cells);
+ read_address_size_cells(fdt, state.offset, state.depth, state.address_cells, state.size_cells);
/* get the name */
- const char *name = fdt_get_name(fdt, offset, NULL);
+ const char *name = fdt_get_name(fdt, state.offset, NULL);
if (!name)
continue;
LTRACEF_LEVEL(2, "name '%s', depth %d, address cells %u, size cells %u\n",
- name, depth, address_cells[depth], size_cells[depth]);
+ name, state.depth, state.address_cells[state.depth], state.size_cells[state.depth]);
- /* look for the 'memory@*' property */
- if (cb->mem && strncmp(name, "memory@", 7) == 0 && depth == 1) {
+ // Callback
+ cb(state, name);
+ }
+
+ return NO_ERROR;
+}
+
+} // anonymous namespace
+
+status_t fdt_walk_dump(const void *fdt) {
+ auto cb = [](const fdt_walk_state &state, const char *name) {
+ for (auto i = 0; i < state.depth; i++) {
+ printf(" ");
+ }
+ printf("offset %d depth %d acells %u scells %u name '%s'\n", state.offset, state.depth,
+ state.curr_address_cell(), state.curr_size_cell(), name);
+ };
+
+ printf("FDT dump: address %p total size %#x\n", fdt, fdt_totalsize(fdt));
+
+ return _fdt_walk(fdt, cb);
+}
+
+status_t fdt_walk_find_cpus(const void *fdt, struct fdt_walk_cpu_info *cpu, size_t *cpu_count) {
+ const size_t max_cpu_count = *cpu_count;
+ *cpu_count = 0;
+
+ auto walker = [&](const fdt_walk_state &state, const char *name) {
+ /* look for a cpu leaf and count the number of cpus */
+ if (*cpu_count < max_cpu_count && strncmp(name, "cpu@", 4) == 0 && state.depth == 2) {
int lenp;
- const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, "reg", &lenp);
+ const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(state.fdt, state.offset, "reg", &lenp);
+ LTRACEF("%p, lenp %u\n", prop_ptr, lenp);
if (prop_ptr) {
LTRACEF_LEVEL(2, "found '%s' reg prop len %d, ac %u, sc %u\n", name, lenp,
- address_cells[depth], size_cells[depth]);
- /* we're looking at a memory descriptor */
- uint64_t base;
- uint64_t len;
- err = read_base_len_pair(prop_ptr, lenp, address_cells[depth], size_cells[depth], &base, &len);
- if (err != NO_ERROR) {
- TRACEF("error reading base/length from memory@ node\n");
- /* continue on */
+ state.curr_address_cell(), state.curr_size_cell());
+ uint32_t id = 0;
+ if (state.curr_address_cell() == 1 && lenp >= 4) {
+ id = fdt32_to_cpu(*(const uint32_t *)prop_ptr);
+ prop_ptr += 4;
+ lenp -= 4;
+ } else {
+ PANIC_UNIMPLEMENTED;
+ }
+
+ // is it disabled?
+ if (check_prop_is_val_string(state.fdt, state.offset, "status", "disabled")) {
+ LTRACEF("cpu id %#x is disabled, skipping...\n", id);
} else {
- LTRACEF("calling mem callback with base %#llx len %#llx\n", base, len);
- cb->mem(base, len, cb->memcookie);
+ LTRACEF("calling cpu callback with id %#x\n", id);
+ cpu[*cpu_count].id = id;
+ (*cpu_count)++;
+ }
+ }
+ }
+ };
+
+ return _fdt_walk(fdt, walker);
+}
+
+status_t fdt_walk_find_memory(const void *fdt, struct fdt_walk_memory_region *memory, size_t *mem_count,
+ struct fdt_walk_memory_region *reserved_memory, size_t *reserved_mem_count) {
+ /* if >= 0, we're inside /reserved-memory */
+ int reserved_memory_depth = -1;
+ const size_t max_memory_index = *mem_count;
+ const size_t max_reserved_index = *reserved_mem_count;
+ *mem_count = *reserved_mem_count = 0;
+
+ auto walker = [&](const fdt_walk_state &state, const char *name) {
+ int err;
+
+ /* look for the 'memory@*' property */
+ if (memory && *mem_count < max_memory_index) {
+ if (strncmp(name, "memory@", 7) == 0 && state.depth == 1) {
+ int lenp;
+ const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(state.fdt, state.offset, "reg", &lenp);
+ if (prop_ptr) {
+ LTRACEF_LEVEL(2, "found '%s' reg prop len %d, ac %u, sc %u\n", name, lenp,
+ state.curr_address_cell(), state.curr_size_cell());
+ /* we're looking at a memory descriptor */
+ uint64_t base;
+ uint64_t len;
+ err = read_base_len_pair(prop_ptr, lenp, state.curr_address_cell(), state.curr_size_cell(), &base, &len);
+ if (err != NO_ERROR) {
+ TRACEF("error reading base/length from memory@ node\n");
+ /* continue on */
+ } else {
+ LTRACEF("mem base %#llx len %#llx\n", base, len);
+ memory[*mem_count].base = base;
+ memory[*mem_count].len = len;
+ (*mem_count)++;
+ }
}
}
}
/* look for the 'reserved-memory' tree */
- if (cb->reserved_memory) {
+ if (reserved_memory && *reserved_mem_count < max_reserved_index) {
/* once we see the reserved-memory first level node, track that we are inside
* it until we step out to a node at the same or higher depth.
*/
- if (strncmp(name, "reserved-memory", 15) == 0 && depth == 1) {
+ if (strncmp(name, "reserved-memory", 15) == 0 && state.depth == 1) {
LTRACEF_LEVEL(2, "found reserved memory node\n");
- reserved_memory_depth = depth;
+ reserved_memory_depth = state.depth;
} else if (reserved_memory_depth >= 0) {
- if (depth <= reserved_memory_depth) {
+ if (state.depth <= reserved_memory_depth) {
/* we have exited the reserved memory tree, so clear our tracking depth */
LTRACEF_LEVEL(2, "exiting reserved memory node\n");
reserved_memory_depth = -1;
@@ -180,90 +271,71 @@ status_t fdt_walk(const void *fdt, const struct fdt_walk_callbacks *cb) {
/* if we're inside the reserved meory tree, so this node must
* be a reserved memory region */
int lenp;
- const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, "reg", &lenp);
+ const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(state.fdt, state.offset, "reg", &lenp);
if (prop_ptr) {
LTRACEF_LEVEL(2, "found '%s' reg prop len %d, ac %u, sc %u\n", name, lenp,
- address_cells[depth], size_cells[depth]);
+ state.curr_address_cell(), state.curr_size_cell());
/* we're looking at a memory descriptor */
uint64_t base;
uint64_t len;
- err = read_base_len_pair(prop_ptr, lenp, address_cells[depth], size_cells[depth], &base, &len);
+ err = read_base_len_pair(prop_ptr, lenp, state.curr_address_cell(), state.curr_size_cell(), &base, &len);
if (err != NO_ERROR) {
TRACEF("error reading base/length from reserved-memory node\n");
/* continue on */
} else {
- LTRACEF("calling reserved memory callback with base %#llx len %#llx\n", base, len);
- cb->reserved_memory(base, len, cb->reserved_memory_cookie);
+ LTRACEF("reserved memory base %#llx len %#llx\n", base, len);
+ reserved_memory[*reserved_mem_count].base = base;
+ reserved_memory[*reserved_mem_count].len = len;
+ (*reserved_mem_count)++;
}
}
}
}
}
+ };
- /* look for a cpu leaf and count the number of cpus */
- if (cb->cpu && strncmp(name, "cpu@", 4) == 0 && depth == 2) {
- int lenp;
- const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, "reg", &lenp);
- LTRACEF("%p, lenp %u\n", prop_ptr, lenp);
- if (prop_ptr) {
- LTRACEF_LEVEL(2, "found '%s' reg prop len %d, ac %u, sc %u\n", name, lenp,
- address_cells[depth], size_cells[depth]);
- uint32_t id = 0;
- if (address_cells[depth] == 1 && lenp >= 4) {
- id = fdt32_to_cpu(*(const uint32_t *)prop_ptr);
- prop_ptr += 4;
- lenp -= 4;
- } else {
- PANIC_UNIMPLEMENTED;
- }
-
- // is it disabled?
- if (check_prop_is_val_string(fdt, offset, "status", "disabled")) {
- LTRACEF("cpu id %#x is disabled, skipping...\n", id);
- } else {
- LTRACEF("calling cpu callback with id %#x\n", id);
- cb->cpu(id, cb->cpucookie);
- }
- }
- }
+ return _fdt_walk(fdt, walker);
+}
+status_t fdt_walk_find_pcie_info(const void *fdt, struct fdt_walk_pcie_info *info, size_t *count) {
+ size_t info_len = *count;
+ *count = 0;
+ auto walker = [info, info_len, &count](const fdt_walk_state &state, const char *name) {
/* look for a pcie leaf and pass the address of the ecam and other info to the callback */
- if (cb->pcie && (strncmp(name, "pcie@", 5) == 0 || strncmp(name, "pci@", 4) == 0)) {
- struct fdt_walk_pcie_info info = {};
-
+ if (*count < info_len && (strncmp(name, "pcie@", 5) == 0 || strncmp(name, "pci@", 4) == 0)) {
/* find the range of the ecam */
int lenp;
- const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, "reg", &lenp);
+ const uint8_t *prop_ptr = (const uint8_t *)fdt_getprop(state.fdt, state.offset, "reg", &lenp);
LTRACEF("%p, lenp %u\n", prop_ptr, lenp);
if (prop_ptr) {
LTRACEF_LEVEL(2, "found '%s' prop 'reg' len %d, ac %u, sc %u\n", name, lenp,
- address_cells[depth], size_cells[depth]);
+ state.curr_address_cell(), state.curr_size_cell());
/* seems to always be full address cells 2, size cells 2, despite it being 3/2 */
- info.ecam_base = fdt64_to_cpu(*(const uint64_t *)prop_ptr);
+ info[*count].ecam_base = fdt64_to_cpu(*(const uint64_t *)prop_ptr);
prop_ptr += 8;
- info.ecam_len = fdt64_to_cpu(*(const uint64_t *)prop_ptr);
+ info[*count].ecam_len = fdt64_to_cpu(*(const uint64_t *)prop_ptr);
}
/* find which bus range the ecam covers */
- prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, "bus-range", &lenp);
+ prop_ptr = (const uint8_t *)fdt_getprop(state.fdt, state.offset, "bus-range", &lenp);
LTRACEF("%p, lenp %u\n", prop_ptr, lenp);
if (prop_ptr) {
LTRACEF_LEVEL(2, "found '%s' prop 'bus-range' len %d, ac %u, sc %u\n", name, lenp,
- address_cells[depth], size_cells[depth]);
+ state.curr_address_cell(), state.curr_size_cell());
if (lenp == 8) {
- info.bus_start = fdt32_to_cpu(*(const uint32_t *)prop_ptr);
+ info[*count].bus_start = fdt32_to_cpu(*(const uint32_t *)prop_ptr);
prop_ptr += 4;
- info.bus_end = fdt32_to_cpu(*(const uint32_t *)prop_ptr);
+ info[*count].bus_end = fdt32_to_cpu(*(const uint32_t *)prop_ptr);
}
}
- prop_ptr = (const uint8_t *)fdt_getprop(fdt, offset, "ranges", &lenp);
+ prop_ptr = (const uint8_t *)fdt_getprop(state.fdt, state.offset, "ranges", &lenp);
LTRACEF("%p, lenp %u\n", prop_ptr, lenp);
if (prop_ptr) {
LTRACEF_LEVEL(2, "found '%s' prop 'ranges' len %d, ac %u, sc %u\n", name, lenp,
- address_cells[depth], size_cells[depth]);
+ state.curr_address_cell(), state.curr_size_cell());
/* iterate this packed property */
const uint8_t *prop_end = prop_ptr + lenp;
@@ -283,19 +355,19 @@ status_t fdt_walk(const void *fdt, const struct fdt_walk_callbacks *cb) {
switch (type) {
case 0x1000000: // io range
LTRACEF_LEVEL(2, "io range\n");
- info.io_base = base1;
- info.io_base_mmio = base2;
- info.io_len = size;
+ info[*count].io_base = base1;
+ info[*count].io_base_mmio = base2;
+ info[*count].io_len = size;
break;
case 0x2000000: // mmio range
LTRACEF_LEVEL(2, "mmio range\n");
- info.mmio_base = base1;
- info.mmio_len = size;
+ info[*count].mmio_base = base1;
+ info[*count].mmio_len = size;
break;
case 0x3000000: // mmio range (64bit)
LTRACEF_LEVEL(2, "mmio range (64bit)\n");
- info.mmio64_base = base1;
- info.mmio64_len = size;
+ info[*count].mmio64_base = base1;
+ info[*count].mmio64_len = size;
break;
default:
LTRACEF_LEVEL(2, "unhandled type %#x\n", type);
@@ -305,14 +377,10 @@ status_t fdt_walk(const void *fdt, const struct fdt_walk_callbacks *cb) {
}
}
- if (info.ecam_len > 0) {
- LTRACEF("calling pci callback with ecam base %#llx size %#llx\n", info.ecam_base, info.ecam_len);
- cb->pcie(&info, cb->pciecookie);
- }
+ (*count)++;
}
+ };
- }
-
- return NO_ERROR;
+ return _fdt_walk(fdt, walker);
}
diff --git a/lib/fdtwalk/helpers.cpp b/lib/fdtwalk/helpers.cpp
new file mode 100644
index 00000000..fae5c6c6
--- /dev/null
+++ b/lib/fdtwalk/helpers.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2024 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <lib/fdtwalk.h>
+
+#include <inttypes.h>
+#include <assert.h>
+#include <libfdt.h>
+#include <lk/cpp.h>
+#include <lk/err.h>
+#include <lk/trace.h>
+#include <stdio.h>
+#include <sys/types.h>
+#if WITH_KERNEL_VM
+#include <kernel/vm.h>
+#else
+#include <kernel/novm.h>
+#endif
+#if ARCH_RISCV
+#include <arch/riscv.h>
+#endif
+#if ARCH_ARM || ARCH_ARM64
+#include <dev/power/psci.h>
+#endif
+#if WITH_DEV_BUS_PCI
+#include <dev/bus/pci.h>
+#endif
+
+// A few helper routines to configure subsystems such as cpu, memory, and pci busses based
+// on the information in a device tree.
+//
+// May eventually move some of these to other locations, but for now dump the helpers here.
+
+#define LOCAL_TRACE 0
+
+status_t fdtwalk_reserve_fdt_memory(const void *fdt, paddr_t fdt_phys) {
+ if (fdt_check_header(fdt) != 0) {
+ return ERR_NOT_FOUND;
+ }
+
+ unsigned long length = fdt_totalsize(fdt);
+
+ paddr_t base = fdt_phys;
+ base = PAGE_ALIGN(base);
+ length = ROUNDUP(length, PAGE_SIZE);
+
+ printf("FDT: reserving physical range for FDT: [%#lx, %#lx]\n", base, base + length - 1);
+
+#if WITH_KERNEL_VM
+ struct list_node list = LIST_INITIAL_VALUE(list);
+ pmm_alloc_range(base, length / PAGE_SIZE, &list);
+#else
+ novm_alloc_specific_pages((void *)base, length / PAGE_SIZE);
+#endif
+ return NO_ERROR;
+}
+
+status_t fdtwalk_setup_memory(const void *fdt, paddr_t fdt_phys, paddr_t default_mem_base, size_t default_mem_size) {
+#if WITH_KERNEL_VM
+ // TODO: consider having pmm make a copy so that this doesn't have to be static
+ static pmm_arena_t arenas[8];
+#endif
+
+ struct fdt_walk_memory_region mem[8];
+ struct fdt_walk_memory_region reserved_mem[16];
+ size_t mem_count = countof(mem);
+ size_t reserved_mem_count = countof(reserved_mem);
+
+ // find memory from the FDT
+ status_t err = fdt_walk_find_memory(fdt, mem, &mem_count, reserved_mem, &reserved_mem_count);
+ if (err < NO_ERROR || mem_count == 0) {
+ /* add a default memory region if we didn't find it in the FDT */
+ printf("FDT: could not find memory, using default base %#lx size %#zx\n", default_mem_base, default_mem_size);
+#if WITH_KERNEL_VM
+ mem[0].base = default_mem_base;
+ mem[0].len = default_mem_size;
+ mem_count = 1;
+#endif
+ }
+
+ for (size_t i = 0; i < mem_count; i++) {
+ LTRACEF("base %#llx len %#llx\n", mem[i].base, mem[i].len);
+ printf("FDT: found memory bank range [%#llx, %#llx] (length %#llx)\n", mem[i].base, mem[i].base + mem[i].len - 1, mem[i].len);
+
+ /* trim size on certain platforms */
+#if ARCH_ARM
+ /* only use the first 1GB on ARM32 */
+ const auto GB = 1024*1024*1024UL;
+ if (mem[i].base - MEMBASE > GB) {
+ printf("trimming memory to 1GB\n");
+ continue;
+ }
+ if (mem[i].base - MEMBASE + mem[i].len > GB) {
+ printf("trimming memory to 1GB\n");
+ mem[i].len = MEMBASE + GB - mem[i].base;
+ printf("range is now [%#llx, %#llx]\n", mem[i].base, mem[i].base + mem[i].len - 1);
+ }
+#endif
+
+#if WITH_KERNEL_VM
+ if (i >= countof(arenas)) {
+ printf("FDT: found too many arenas, max is %zu\n", countof(arenas));
+ break;
+ }
+
+ /* add a vm arena */
+ arenas[i].name = "fdt";
+ arenas[i].base = mem[i].base;
+ arenas[i].size = mem[i].len;
+ arenas[i].flags = PMM_ARENA_FLAG_KMAP;
+ pmm_add_arena(&arenas[i]);
+#else
+ novm_add_arena("fdt", mem[i].base, mem[i].len);
+#endif
+ }
+
+ /* reserve memory described by the FDT */
+ for (size_t i = 0; i < reserved_mem_count; i++) {
+ printf("FDT: reserving memory range [%#llx, %#llx]\n",
+ reserved_mem[i].base, reserved_mem[i].base + reserved_mem[i].len - 1);
+
+#if WITH_KERNEL_VM
+ // allocate the range and place on a list
+ struct list_node list = LIST_INITIAL_VALUE(list);
+ pmm_alloc_range(reserved_mem[i].base, reserved_mem[i].len / PAGE_SIZE, &list);
+#else
+ novm_alloc_specific_pages((void *)reserved_mem[i].base, reserved_mem[i].len / PAGE_SIZE);
+#endif
+ }
+
+ // TODO: deal with fdt reserved memory sections with
+ // fdt_num_mem_rsv and
+ // fdt_get_mem_rsv
+
+ // reserve the memory the device tree itself uses
+ fdtwalk_reserve_fdt_memory(fdt, fdt_phys);
+
+ return NO_ERROR;
+}
+
+#if ARCH_RISCV
+status_t fdtwalk_setup_cpus_riscv(const void *fdt) {
+#if WITH_SMP
+ struct fdt_walk_cpu_info cpus[SMP_MAX_CPUS];
+ size_t cpu_count = countof(cpus);
+
+ status_t err = fdt_walk_find_cpus(fdt, cpus, &cpu_count);
+ if (err >= NO_ERROR) {
+
+ if (cpu_count > 0) {
+ printf("FDT: found %zu cpu%c\n", cpu_count, cpu_count == 1 ? ' ' : 's');
+ uint harts[SMP_MAX_CPUS - 1];
+
+ // copy from the detected cpu list to an array of harts, excluding the boot hart
+ size_t hart_index = 0;
+ for (size_t i = 0; i < cpu_count; i++) {
+ if (cpus[i].id != riscv_current_hart()) {
+ harts[hart_index++] = cpus[i].id;
+ }
+
+ // we can start MAX CPUS - 1 secondaries
+ if (hart_index >= SMP_MAX_CPUS - 1) {
+ break;
+ }
+ }
+
+ // tell the riscv layer how many cores we have to start
+ if (hart_index > 0) {
+ riscv_set_secondary_harts_to_start(harts, hart_index);
+ }
+ }
+ }
+#endif
+
+ return err;
+}
+#endif
+
+#if ARCH_ARM || ARCH_ARM64
+status_t fdtwalk_setup_cpus_arm(const void *fdt) {
+#if WITH_SMP
+ struct fdt_walk_cpu_info cpus[SMP_MAX_CPUS];
+ size_t cpu_count = countof(cpus);
+
+ status_t err = fdt_walk_find_cpus(fdt, cpus, &cpu_count);
+ if (err >= NO_ERROR) {
+ if (cpu_count > 0) {
+ printf("FDT: found %zu cpu%c\n", cpu_count, cpu_count == 1 ? ' ' : 's');
+
+ if (cpu_count > SMP_MAX_CPUS) {
+ cpu_count = MIN(cpu_count, SMP_MAX_CPUS);
+ printf("FDT: clamping max cpus to %zu\n", cpu_count);
+ }
+
+ LTRACEF("booting %zu cpus\n", cpu_count);
+
+ /* boot the secondary cpus using the Power State Coordintion Interface */
+ for (size_t i = 1; i < cpu_count; i++) {
+ /* note: assumes cpuids are numbered like MPIDR 0:0:0:N */
+ printf("ARM: starting cpu %#x\n", cpus[i].id);
+ int ret = psci_cpu_on(cpus[i].id, MEMBASE + KERNEL_LOAD_OFFSET);
+ if (ret != 0) {
+ printf("ERROR: psci CPU_ON returns %d\n", ret);
+ }
+ }
+ }
+ }
+#endif
+
+ return err;
+}
+#endif
+
+#if WITH_DEV_BUS_PCI
+status_t fdtwalk_setup_pci(const void *fdt) {
+ /* detect pci */
+ struct fdt_walk_pcie_info pcie_info[4] = {};
+
+ size_t count = countof(pcie_info);
+ status_t err = fdt_walk_find_pcie_info(fdt, pcie_info, &count);
+ LTRACEF("fdt_walk_find_pcie_info returns %d, count %zu\n", err, count);
+ if (err == NO_ERROR) {
+ for (size_t i = 0; i < count; i++) {
+ LTRACEF("ecam base %#" PRIx64 ", len %#" PRIx64 ", bus_start %hhu, bus_end %hhu\n", pcie_info[i].ecam_base,
+ pcie_info[i].ecam_len, pcie_info[i].bus_start, pcie_info[i].bus_end);
+
+ // currently can only handle the first segment
+ if (i > 0) {
+ printf("skipping pci segment %zu, not supported (yet)\n", i);
+ continue;
+ }
+
+ if (pcie_info[i].ecam_len > 0) {
+ printf("PCIE: initializing pcie with ecam at %#" PRIx64 " found in FDT\n", pcie_info[i].ecam_base);
+ err = pci_init_ecam(pcie_info[i].ecam_base, pcie_info[i].ecam_len, pcie_info[i].bus_start, pcie_info[i].bus_end);
+ if (err == NO_ERROR) {
+ // add some additional resources to the pci bus manager in case it needs to configure
+ if (pcie_info[i].io_len > 0) {
+ // we can only deal with a mapping of io base 0 to the mmio base
+ DEBUG_ASSERT(pcie_info[i].io_base == 0);
+ pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_info[i].io_base, pcie_info[i].io_len);
+
+ // TODO: set the mmio base somehow so pci knows what to do with it
+ }
+ if (pcie_info[i].mmio_len > 0) {
+ pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_info[i].mmio_base, pcie_info[i].mmio_len);
+ }
+ if (pcie_info[i].mmio64_len > 0) {
+ pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_info[i].mmio64_base, pcie_info[i].mmio64_len);
+ }
+ }
+ }
+ }
+ }
+
+ return err;
+}
+#endif
+
diff --git a/lib/fdtwalk/include/lib/fdtwalk.h b/lib/fdtwalk/include/lib/fdtwalk.h
index a48e29a5..b0f2d644 100644
--- a/lib/fdtwalk/include/lib/fdtwalk.h
+++ b/lib/fdtwalk/include/lib/fdtwalk.h
@@ -34,17 +34,34 @@ struct fdt_walk_pcie_info {
uint64_t mmio64_len;
};
-struct fdt_walk_callbacks {
- void (*mem)(uint64_t base, uint64_t len, void *cookie);
- void *memcookie;
- void (*reserved_memory)(uint64_t base, uint64_t len, void *cookie);
- void *reserved_memory_cookie;
- void (*cpu)(uint64_t id, void *cookie);
- void *cpucookie;
- void (*pcie)(const struct fdt_walk_pcie_info *info, void *cookie);
- void *pciecookie;
+struct fdt_walk_memory_region {
+ uint64_t base;
+ uint64_t len;
};
-status_t fdt_walk(const void *fdt, const struct fdt_walk_callbacks *);
+struct fdt_walk_cpu_info {
+ uint32_t id;
+};
+
+status_t fdt_walk_dump(const void *fdt);
+
+// New style walkers, finds a single topic at a time
+status_t fdt_walk_find_pcie_info(const void *fdt, struct fdt_walk_pcie_info *, size_t *count);
+status_t fdt_walk_find_memory(const void *fdt, struct fdt_walk_memory_region *memory, size_t *mem_count,
+ struct fdt_walk_memory_region *reserved_memory, size_t *reserved_mem_count);
+status_t fdt_walk_find_cpus(const void *fdt, struct fdt_walk_cpu_info *cpu, size_t *cpu_count);
+
+// Helper routines that initialize various subsystems based on device tree info
+status_t fdtwalk_setup_memory(const void *fdt, paddr_t fdt_phys, paddr_t default_mem_base, size_t default_mem_size);
+#if ARCH_RISCV
+status_t fdtwalk_setup_cpus_riscv(const void *fdt);
+#endif
+#if ARCH_ARM || ARCH_ARM64
+status_t fdtwalk_setup_cpus_arm(const void *fdt);
+#endif
+#if WITH_DEV_BUS_PCI
+status_t fdtwalk_setup_pci(const void *fdt);
+#endif
+status_t fdtwalk_reserve_fdt_memory(const void *fdt, paddr_t fdt_phys);
__END_CDECLS
diff --git a/lib/fdtwalk/rules.mk b/lib/fdtwalk/rules.mk
index ff8d1f6b..7723e05d 100644
--- a/lib/fdtwalk/rules.mk
+++ b/lib/fdtwalk/rules.mk
@@ -5,5 +5,6 @@ MODULE := $(LOCAL_DIR)
MODULE_DEPS := lib/fdt
MODULE_SRCS := $(LOCAL_DIR)/fdtwalk.cpp
+MODULE_SRCS += $(LOCAL_DIR)/helpers.cpp
include make/module.mk