aboutsummaryrefslogtreecommitdiff
path: root/common/pmem.c
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-05-24 13:24:27 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-05-24 18:13:43 +0300
commitbc600ecc3bdf0f189861dfb840f70c2339a7a853 (patch)
treed9840b1dc1b865442a028c03ad7109ac67894f6e /common/pmem.c
parent6a7073e5f262db9a4578ff00b5b28e34335564ce (diff)
downloadkmi-bc600ecc3bdf0f189861dfb840f70c2339a7a853.tar.gz
kmi-bc600ecc3bdf0f189861dfb840f70c2339a7a853.zip
rename common to src
+ I keep starting to type src and wondering why autocomplete won't work, I guess src is just uncounciously a better name
Diffstat (limited to 'common/pmem.c')
-rw-r--r--common/pmem.c587
1 files changed, 0 insertions, 587 deletions
diff --git a/common/pmem.c b/common/pmem.c
deleted file mode 100644
index 4da3f4c..0000000
--- a/common/pmem.c
+++ /dev/null
@@ -1,587 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file pmem.c
- * Physical memory subsystem. Allocates physical memory pages, with support for
- * different ordered pages, depending on the underlying architecture.
- *
- * Effectively, each page order (4096, 2M, 1G...) has a free list of bitmaps for
- * each page size. When an order runs out of free nodes, it just 'allocates' a
- * node from a higher order list, and gives out those maps. This turned out to
- * be around 50% faster than the previous method, with about half the necessary
- * code. Could still probably be cleaned up a little bit, in particular I don't
- * really care for probe_pmap() vs populate_pmap() but I suppose it's fine for
- * now.
- */
-
-#include <kmi/mem_nodes.h>
-#include <kmi/pmem.h>
-#include <kmi/dmem.h>
-#include <kmi/debug.h>
-#include <kmi/initrd.h>
-#include <kmi/string.h> /* memset */
-#include <kmi/bits.h> /* is_nset etc */
-#include <libfdt.h>
-
-/* \todo add memory page counting?
- * To make sure memory is not overcommited at clone, for example.
- */
-
-/**
- * Loop over orders in reverse, starting with highest, giving the iterator the
- * name \p iter.
- *
- * @param iter Name of iterator.
- */
-#define reverse_foreach_order(iter) \
- for (enum mm_order iter = max_order(); iter != MM_MIN; --iter)
-
-/** Page bitmap node */
-struct mm_bmap {
- /** How many bits this node has. This is generally the same as \c bits in
- * mm_bucket, but could be used for trailing nodes with some irregular
- * number of bits. */
- size_t size;
-
- /** How many pages are currently used */
- size_t used;
-
- /** Next node in freelist */
- struct mm_bmap *next;
-
- /** Previous node in freelist */
- struct mm_bmap *prev;
-
- /** Actual bitmap */
- uint8_t bits[];
-};
-
-/** Bucket of bitmaps for some order of pages */
-struct mm_bucket {
- /** How many bits per (regular) bitmap, see \c size in mm_bmap */
- size_t bits;
-
- /** Size in bytes of a page of this order */
- size_t page_size;
-
- /** Current head of freelist */
- struct mm_bmap *head;
-
- /** Bitmaps in contiguous array, to make populating easier. */
- struct mm_bmap bmap[];
-};
-
-/** Physical map. */
-struct mm_pmap {
- /** Base address of our map. Note that this should be the virtual base
- * address of the physical ram. */
- pm_t base;
- /** Buckets, one per order up to maximum order. */
- struct mm_bucket *buckets[MM_NUM];
-};
-
-/** Static physical map address. \note If I support NUMA, this should probably not be
- * static, rather one physical map per NUMA region. */
-static struct mm_pmap *pmap = 0;
-
-/**
- * Zero out memory if \p populate is true.
- * Helper for populate_pmap(), makes it a bit more easy to follow when we're
- * just calculating the size of out physical map versus actually building it.
- *
- * @param populate Whether to write anything.
- * @param cont Where to write.
- * @param size How many bytes to write.
- * @return Address following last written byte.
- */
-static pm_t __zero_if(bool populate, pm_t cont, size_t size)
-{
- if (populate)
- memset((void *)cont, 0, size);
-
- return cont + size;
-}
-
-/**
- * Get size in bytes of (regular) bitmap node in this bucket.
- *
- * @param bucket Bucket.
- * @return Size in bytes of (regular) bitmap node.
- */
-static size_t __get_set_size(struct mm_bucket *bucket)
-{
- return sizeof(bucket->bmap[0]) + bucket->bits / 8;
-}
-
-/**
- * Get pointer to bitmap node at index \p set.
- *
- * @param bucket Bucket.
- * @param set Index of bitmap node to get.
- * @return Pointer to bitmap node.
- */
-static struct mm_bmap *__get_set(struct mm_bucket *bucket, size_t set)
-{
- uintptr_t bmap = (uintptr_t)bucket->bmap;
- return (struct mm_bmap *)(bmap + __get_set_size(bucket) * set);
-}
-
-/**
- * Get index from pointer to \p bmap within \p bucket.
- *
- * @param bucket Bucket.
- * @param bmap Bitmap node.
- * @return Index of \p bmap within \p bucket.
- */
-static size_t __get_set_index(struct mm_bucket *bucket, struct mm_bmap *bmap)
-{
- size_t s = (uintptr_t)bmap - (uintptr_t)bucket->bmap;
- return s / __get_set_size(bucket);
-}
-
-/**
- * Attach bitmap \p bmap to freelist within \p bucket.
- *
- * @param bucket Bucket.
- * @param bmap Bitmap node.
- */
-static void __attach_set(struct mm_bucket *bucket, struct mm_bmap *bmap)
-{
- /* already attached */
- if (bmap->next)
- return;
-
- bmap->next = bucket->head;
- bucket->head = bmap;
- if (bmap->next)
- bmap->next->prev = bmap;
-}
-
-/**
- * Remove bitmap \p bmap from freelist within \p bucket.
- *
- * @param bucket Bucket.
- * @param bmap Bitmap node.
- */
-static void __detach_set(struct mm_bucket *bucket, struct mm_bmap *bmap)
-{
- if (bucket->head == bmap)
- bucket->head = bmap->next;
-
- if (bmap->next)
- bmap->next->prev = bmap->prev;
-
- if (bmap->prev)
- bmap->prev->next = bmap->next;
-}
-
-/**
- * Calculate address of page within bucket.
- *
- * @param bucket Bucket.
- * @param s Index of bitmap node.
- * @param b Bit within bitmap.
- * @return Address of corresponding page.
- */
-static pm_t __page_addr(struct mm_bucket *bucket, size_t s, size_t b)
-{
- return pmap->base
- + s * bucket->page_size * bucket->bits
- + b * bucket->page_size;
-}
-
-/**
- * Calculate which bitmap node index and bit within bitmap an address
- * corresponds to.
- *
- * @param bucket Bucket.
- * @param a Address.
- * @param s Corresponding bitmap node index.
- * @param b Bitmap node bit index.
- */
-static void __get_bit(struct mm_bucket *bucket, pm_t a, size_t *s, size_t *b)
-{
- a -= pmap->base;
- size_t p = a / bucket->page_size;
- *s = p / bucket->bits;
- *b = p % bucket->bits;
-}
-
-void free_page(enum mm_order order, pm_t addr)
-{
- struct mm_bucket *bucket = pmap->buckets[order];
- if (!bucket)
- return;
-
- size_t set = 0, bit = 0;
- __get_bit(bucket, addr, &set, &bit);
-
- struct mm_bmap *bmap = __get_set(bucket, set);
- bmap->used--;
-
- bitmap_clear(bmap->bits, bit);
- __attach_set(bucket, bmap);
-
- if (bmap->used == 0) {
- __detach_set(bucket, bmap);
-
- if (bmap->size == order_width(order + 1))
- free_page(order + 1, __page_addr(bucket, set, bit));
- }
-}
-
-pm_t alloc_page(enum mm_order order)
-{
- struct mm_bucket *bucket = pmap->buckets[order];
- if (!bucket)
- return 0;
-
- struct mm_bmap *bmap = bucket->head;
- if (!bmap) {
- pm_t a = alloc_page(order + 1);
- if (!a)
- return 0;
-
- size_t set = 0, bit = 0;
- __get_bit(bucket, a, &set, &bit);
-
- bmap = __get_set(bucket, set);
- bmap->used = 0;
- bitmap_clear_all(bmap->bits, bmap->size);
- __attach_set(bucket, bmap);
- return alloc_page(order);
- }
-
- bmap->used++;
-
- size_t set = __get_set_index(bucket, bmap);
- size_t bit = bitmap_find_first_unset(bmap->bits, bmap->size);
- bitmap_set(bmap->bits, bit);
-
- if (bmap->used == bmap->size)
- __detach_set(bucket, bmap);
-
- return __page_addr(bucket, set, bit);
-}
-
-void mark_used(enum mm_order order, pm_t addr)
-{
- struct mm_bucket *bucket = pmap->buckets[order];
- if (!bucket)
- return;
-
- size_t set = 0, bit = 0;
- __get_bit(bucket, addr, &set, &bit);
-
- struct mm_bmap *bmap = __get_set(bucket, set);
- if (bmap->used == 0) {
- bitmap_clear_all(bmap->bits, bmap->size);
- __attach_set(bucket, bmap);
- mark_used(order + 1, addr);
- }
-
- /* a page already in use can just be left alone. This MIGHT hide some
- * bugs in case two separate things overlap in memory during
- * initialization, but that scenario should probably be handled outside
- * of this function anyway. */
- if (bitmap_is_set(bmap->bits, bit))
- return;
-
- bmap->used++;
- bitmap_set(bmap->bits, bit);
-
- if (bmap->used == bmap->size)
- __detach_set(bucket, bmap);
-}
-
-/**
- * Helper for probing/populating a bucket.
- *
- * @param n How many pages in total to account for.
- * @todo currently may cut off some pages if \p n is larger than but not a multiple of order
- * width.
- * @param cont Where to place bucket.
- * @param order Order of bucket to populate.
- * @param first First bucket being populated. Top bucket, owns all pages to
- * start with.
- * @param populate Whether to actually write bucket to memory.
- * @return Address right after where last byte of bycket would be.
- */
-static pm_t __maybe_populate_bucket(size_t n, pm_t cont, enum mm_order order,
- bool first, bool populate)
-{
- struct mm_bucket *bucket = (struct mm_bucket *)cont;
- /* todo max order? */
- size_t bits = order_width(order + 1);
- if (bits == 0)
- bits = n;
-
- if (populate) {
- bucket->bits = bits;
- bucket->page_size = order_size(order);
- bucket->head = NULL;
- }
-
- size_t set_size = sizeof(struct mm_bmap) + bits / 8;
-
- cont += sizeof(struct mm_bucket);
-
- size_t sets = n / bits;
- for (size_t i = 0; i < sets; ++i) {
- struct mm_bmap *bmap = (struct mm_bmap *)cont;
- if (populate) {
- memset(bmap, 0, set_size);
- bmap->size = bits;
- }
-
- if (first && populate)
- __attach_set(bucket, bmap);
-
- n -= bits;
- cont += set_size;
- }
-
- if (n) {
- struct mm_bmap *bmap = (struct mm_bmap *)cont;
- if (populate)
- bmap->size = n;
-
- if (first && populate)
- __attach_set(bucket, bmap);
-
- cont += set_size;
- }
-
- return cont;
-}
-
-/**
- * Probe how many bytes the physical map would take up, optionally populate
- * empty physical map if \p populate is given.
- *
- * I realize it sounds like two
- * different functions, and that it might be a good idea to split in twine,
- * but my thinking was that using the same algorithm with a
- * flag to enable writing to memory would decrease chances that I would
- * calculate the size differently from what is actually needed. We need an
- * accurate estimate of the pmap size to know whether we can place it somewhere
- * and not overwrite something else.
- *
- * @param ram_base Address in kernel space where the physical RAM starts.
- * @param ram_size Size of RAM in bytes.
- * @param start Where to start building pmap.
- * @param populate Whether to actually write anything out to memory.
- * @return Size of pmap in bytes.
- */
-static pm_t __maybe_populate_pmap(pm_t ram_base, size_t ram_size, pm_t start,
- bool populate)
-{
- pm_t cont = start;
-
- pmap = (struct mm_pmap *)start;
- cont = __zero_if(populate, cont, sizeof(*pmap));
- if (populate)
- pmap->base = ram_base;
-
- bool first = true;
- reverse_foreach_order(iter) {
- size_t num = ram_size / order_size(iter);
- if (num == 0)
- continue;
-
- if (populate)
- pmap->buckets[iter] = (struct mm_bucket *)cont;
-
- cont = __maybe_populate_bucket(num, cont, iter, first,
- populate);
- first = false;
- }
-
- return cont - start;
-}
-
-pm_t populate_pmap(pm_t ram_base, size_t ram_size, pm_t start)
-{
- return __maybe_populate_pmap(ram_base, ram_size, start, true);
-}
-
-pm_t probe_pmap(pm_t ram_base, size_t ram_size, pm_t start)
-{
- return __maybe_populate_pmap(ram_base, ram_size, start, false);
-}
-
-/**
- * Helper function for marking area used.
- *
- * @param base Base address of area.
- * @param top Top address of top.
- */
-static void __mark_area_used(pm_t base, pm_t top)
-{
- if (top < base) {
- bug("top < base: %lx < %lx\n", top, base);
- return;
- }
-
- size_t area_left = top - base;
- pm_t runner = base;
- while (area_left >= BASE_PAGE_SIZE) {
- mark_used(BASE_PAGE, runner);
- runner += BASE_PAGE_SIZE;
- area_left -= BASE_PAGE_SIZE;
- }
-
- if (area_left != 0)
- mark_used(BASE_PAGE, runner);
-}
-
-/**
- * Mark reserved memory region used, to avoid it getting accidentally allocated.
- *
- * @param fdt Global FDT pointer.
- */
-static void __mark_reserved_mem(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);
-
- pm_t base = (pm_t)fdt_load_reg_addr(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));
- }
-}
-
-/**
- * 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)
-{
- int mem_offset = fdt_path_offset(fdt, "/memory");
- const void *mem_reg = fdt_getprop(fdt, mem_offset, "reg", NULL);
-
- /* 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;
-}
-
-/**
- * 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));
-}
-
-/**
- * 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;
-}
-
-void init_pmem(void *fdt)
-{
- /** @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
- * I should really try adding more of them to other parts of the
- * codebase as well, the pmem subsystem isn't really especially complex.
- */
- 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();
- pm_t ram_base = (pm_t)__va(get_ram_base());
-
- info("using ram range [%lx - %lx]\n",
- ram_base, ram_base + ram_size);
-
- /** @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 fdt_top = __get_fdttop(fdt);
- pm_t fdt_base = __get_fdtbase(fdt);
- info("found fdt at [%lx - %lx]\n", fdt_base, fdt_top);
-
- /* 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);
- info("pmem map probe size returned %lu\n", probe_size);
-
- size_t actual_size = populate_pmap(ram_base, ram_size, pmap_base);
- info("pmem map actual size %lu\n", actual_size);
-
- if (probe_size != actual_size) {
- bug("probe_size (%#lx) != actual_size (%#lx)\n", probe_size,
- 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);
-
- /* mark reserved mem */
- __mark_reserved_mem(fdt);
-
- init_mem_nodes();
-
- init_devmem((pm_t)__pa(ram_base), (pm_t)__pa(ram_base + ram_size));
-}