diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-07-05 19:38:11 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-07-05 19:38:11 +0300 |
| commit | e09edb5d54e39bd9509a1dc450df5ab320759f97 (patch) | |
| tree | 78e050b0db87df3a3ddfbb6570d44513ac34e02a /src/pmem.c | |
| parent | 98f21e694a6b0964d6d08196cb6bfbc1c2ae2ff4 (diff) | |
| download | kmi-e09edb5d54e39bd9509a1dc450df5ab320759f97.tar.gz kmi-e09edb5d54e39bd9509a1dc450df5ab320759f97.zip | |
allow booting with Qemu's -kernel flag directly
+ Took some fairly significant changes, for one the kernel is no longer
relocated at the start of a boot, instead it sits wherever the user
decides the kernel should sit. Similarly, the initial kernel stack and
page table are stored within the binary, slightly bloating the size
but making it much safer to boot since there's really no chance of us
overwriting the fdt or initrd in memory.
Diffstat (limited to 'src/pmem.c')
| -rw-r--r-- | src/pmem.c | 178 |
1 files changed, 90 insertions, 88 deletions
@@ -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(); |
