aboutsummaryrefslogtreecommitdiff
path: root/src/pmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmem.c')
-rw-r--r--src/pmem.c178
1 files changed, 90 insertions, 88 deletions
diff --git a/src/pmem.c b/src/pmem.c
index 4da3f4c..bddefa8 100644
--- a/src/pmem.c
+++ b/src/pmem.c
@@ -436,77 +436,89 @@ static void __mark_area_used(pm_t base, pm_t top)
mark_used(BASE_PAGE, runner);
}
+struct avoid_region {
+ pm_t base;
+ pm_t size;
+};
+
+static bool overlaps(pm_t base1, pm_t size1, pm_t base2, pm_t size2)
+{
+ bool b = base1 >= base2 && base1 < base2 + size2;
+ bool t = base1 + size1 > base2 && base1 + size1 <= base2 + size2;
+ return b || t;
+}
+
/**
* Mark reserved memory region used, to avoid it getting accidentally allocated.
*
* @param fdt Global FDT pointer.
*/
-static void __mark_reserved_mem(void *fdt)
+static void __mark_reserved(pm_t ram_base, pm_t ram_size, size_t avoid_count,
+ struct avoid_region avoid[64])
+{
+ for (size_t i = 0; i < avoid_count; ++i) {
+ pm_t base = avoid[i].base;
+ pm_t size = avoid[i].size;
+
+ if (!overlaps(base, size, ram_base, ram_size))
+ continue;
+
+ pm_t top = base + size;
+ __mark_area_used(base, top);
+ info("marked [%lx - %lx] reserved\n", base, top);
+ }
+}
+
+static size_t build_reserved_map(size_t exists, struct avoid_region avoid[64],
+ void *fdt)
{
int rmem_offset = fdt_path_offset(fdt, "/reserved-memory");
struct cell_info ci = get_cellinfo(fdt, rmem_offset);
int node = 0;
fdt_for_each_subnode(node, fdt, rmem_offset) {
- uint8_t *rmem_reg =
- (uint8_t *)fdt_getprop(fdt, node, "reg", NULL);
+ uint8_t *rmem_reg = (uint8_t *)fdt_getprop(fdt, node, "reg",
+ NULL);
pm_t base = (pm_t)fdt_load_reg_addr(ci, rmem_reg, 0);
+ pm_t size = (pm_t)fdt_load_reg_size(ci, rmem_reg, 0);
- /** @todo make sure the top of a reserved memory area doesn't go
- * against our assumptions in FW_MAX_SIZE? */
- pm_t top = (pm_t)fdt_load_reg_size(ci, rmem_reg, 0) + base;
- __mark_area_used((pm_t)__va(base), (pm_t)__va(top));
- info("marked [%lx - %lx] reserved\n",
- (pm_t)__va(base), (pm_t)__va(top));
+ avoid[exists++] = (struct avoid_region){(pm_t)__va(base), size};
+ catastrophic_assert(exists < 64);
}
+
+ return exists;
}
-/**
- * Read top of RAM from FDT.
- *
- * @param fdt Global FDT pointer.
- * @return Physical address of top of RAM.
- */
-static pm_t __get_ramtop(void *fdt)
+static pm_t select_base(pm_t ram_base, pm_t ram_size, pm_t size,
+ pm_t avoid_count,
+ struct avoid_region avoid[64])
{
- int mem_offset = fdt_path_offset(fdt, "/memory");
- const void *mem_reg = fdt_getprop(fdt, mem_offset, "reg", NULL);
+ for (size_t i = 0; i < avoid_count; ++i) {
+ /* try placing things just after each avoidance region */
+ pm_t base = avoid[i].base + avoid[i].size;
- /* here we actually want the root offset because /memory itself doesn't
- * have children, I guess? */
- struct cell_info ci = get_cellinfo(fdt, fdt_path_offset(fdt, "/"));
- pm_t base = (pm_t)fdt_load_reg_addr(ci, mem_reg, 0);
- return (pm_t)fdt_load_reg_size(ci, mem_reg, 0) + base;
-}
+ /* any better alignment than this has to be manually handled
+ * outside of this function */
+ base = align_up(base, sizeof(long));
-/**
- * Read top of FDT.
- *
- * @param fdt Global FDT pointer.
- * @return Physical address of top of FDT.
- */
-static pm_t __get_fdttop(void *fdt)
-{
- const char *b = (const char *)fdt;
- return (pm_t)(b + fdt_totalsize(fdt));
-}
+ if (!overlaps(base, size, ram_base, ram_size))
+ continue;
-/**
- * Return base of FDT.
- *
- * Technically pretty useless, but here mainly for cohesion.
- *
- * @param fdt Global FDT pointer.
- * @return \c fdt.
- */
-static pm_t __get_fdtbase(void *fdt)
-{
- /* lol */
- return (pm_t)fdt;
+ for (size_t a = 0; a < avoid_count; ++a) {
+ if (overlaps(base, size, avoid[a].base, avoid[a].size))
+ goto retry;
+
+ }
+
+ return base;
+retry:
+ }
+
+ return 0;
}
-void init_pmem(void *fdt)
+void init_pmem(void *fdt, uintptr_t load_addr)
{
/** @todo should I keep the info outputs? I suppose it's nice to see
* if any assumption is being broken in the serial log, but in that case
@@ -515,14 +527,10 @@ void init_pmem(void *fdt)
*/
info("initializing pmem\n");
- size_t max_order = 0;
- size_t base_bits = 0;
- size_t bits[NUM_ORDERS] = { 0 };
- stat_pmem_conf(fdt, &max_order, &base_bits, bits);
- init_mem(max_order, bits, base_bits);
-
- pm_t ram_size = __get_ramtop(fdt) - get_ram_base();
+ /* here it's a bit easier to work with virtual RAM addresses, but
+ * physical ones could work just as well. */
pm_t ram_base = (pm_t)__va(get_ram_base());
+ pm_t ram_size = get_ram_size();
info("using ram range [%lx - %lx]\n",
ram_base, ram_base + ram_size);
@@ -530,23 +538,38 @@ void init_pmem(void *fdt)
/** @todo could probably improve error messages on failing to get fdt
* values */
pm_t initrd_base = get_initrdbase(fdt);
- pm_t initrd_top = get_initrdtop(fdt);
- info("found initrd at [%lx - %lx]\n", initrd_base, initrd_top);
+ pm_t initrd_size = get_initrdsize(fdt);
+ info("found initrd at [%lx - %lx]\n", initrd_base,
+ initrd_base + initrd_size);
- pm_t fdt_top = __get_fdttop(fdt);
- pm_t fdt_base = __get_fdtbase(fdt);
- info("found fdt at [%lx - %lx]\n", fdt_base, fdt_top);
+ pm_t fdt_base = (pm_t)fdt;
+ pm_t fdt_size = fdt_totalsize(fdt);
+ info("found fdt at [%lx - %lx]\n", fdt_base, fdt_base + fdt_size);
/* find probably most suitable contiguous region of ram for our physical
* ram map */
- /** @todo this really should check that there's enough space in RAM
- * instead of just forcing the pmap to be populated */
- pm_t pmap_base = align_up(MAX(initrd_top, fdt_top), BASE_PAGE_SIZE);
- info("choosing to place pmem map at %lx\n", pmap_base);
- size_t probe_size = probe_pmap(ram_base, ram_size, pmap_base);
+ size_t probe_size = probe_pmap(0, ram_size, 0);
info("pmem map probe size returned %lu\n", probe_size);
+ /* linker magicry */
+ extern char *__kernel_size;
+ /* avoidance regions, note that stack and so on is included in the
+ * kernel. Addresses can be outside RAM, in which case they are just
+ * ignored. */
+ struct avoid_region avoid[64] = {
+ {(pm_t)__va(load_addr), (pm_t)&__kernel_size},
+ {(pm_t)__va(initrd_base), initrd_size},
+ {(pm_t)__va(fdt_base), fdt_size}
+ };
+
+ size_t avoid_count = build_reserved_map(4, avoid, fdt);
+ pm_t pmap_base = select_base(ram_base, ram_size,
+ probe_size, avoid_count, avoid);
+
+ catastrophic_assert(pmap_base);
+ info("choosing to place pmem map at %lx\n", pmap_base);
+
size_t actual_size = populate_pmap(ram_base, ram_size, pmap_base);
info("pmem map actual size %lu\n", actual_size);
@@ -555,31 +578,10 @@ void init_pmem(void *fdt)
actual_size);
}
- /* mark init stack, this should be unmapped once we get to executing
- * processes */
- __mark_area_used(VM_STACK_BASE, VM_STACK_TOP);
- info("marked stack [%lx - %lx] used\n", VM_STACK_BASE, VM_STACK_TOP);
-
- /* mark kernel */
- /* this could be made more explicit, I suppose. */
- __mark_area_used(VM_KERN, VM_KERN + PM_KERN_SIZE);
- info("marked kernel [%lx - %lx] used\n", VM_KERN,
- VM_KERN + PM_KERN_SIZE);
-
- /* mark fdt and initrd */
- __mark_area_used(initrd_base, initrd_top);
- info("marked initrd [%lx - %lx] used\n", initrd_base, initrd_top);
-
- __mark_area_used(fdt_base, fdt_top);
- info("marked fdt [%lx - %lx] used\n", fdt_base, fdt_top);
-
- /* mark pmap */
- __mark_area_used(pmap_base, pmap_base + actual_size);
- info("marked pmap [%lx - %lx] used\n", pmap_base,
- pmap_base + actual_size);
+ avoid[avoid_count++] = (struct avoid_region){pmap_base, actual_size};
/* mark reserved mem */
- __mark_reserved_mem(fdt);
+ __mark_reserved(ram_base, ram_size, avoid_count, avoid);
init_mem_nodes();