From b2497d1ee0105e2fefe7536aa58dca249b9c0d95 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Fri, 21 Feb 2025 12:31:03 -0500 Subject: [PATCH] HIPPO: store memory regions in addition to individual free pages --- src/hulk/hippo.d | 83 ++++++++++++++++++++++++++++++++++++++++++-- src/hulk/hurl/hurl.d | 82 ++++++++++++++++++++++++++++++++----------- 2 files changed, 141 insertions(+), 24 deletions(-) diff --git a/src/hulk/hippo.d b/src/hulk/hippo.d index 57d77e7..c21862d 100644 --- a/src/hulk/hippo.d +++ b/src/hulk/hippo.d @@ -7,21 +7,38 @@ */ module hulk.hippo; +import hulk.hurl; +import hulk.klog; + struct Hippo { /** - * Linked list node entry for a physical page. + * Linked list node entry for a single page. */ private static struct PhysicalPage { PhysicalPage * next; } + /** + * Linked list node entry for a region. + */ + private static struct Region + { + ulong size; + Region * next; + } + /** * Linked list of free physical pages. */ private static __gshared PhysicalPage * free_pages; + /** + * Linked list of free regions. + */ + private static __gshared Region * regions; + /** * Number of free physical pages. */ @@ -48,22 +65,82 @@ struct Hippo public static void * allocate_page() { PhysicalPage * pp; - if (free_pages != null) + if (free_pages !is null) { pp = free_pages; free_pages = free_pages.next; m_n_free_pages--; } + else if (regions !is null) + { + pp = cast(PhysicalPage *)(cast(ulong)regions + regions.size - PAGE_SIZE); + regions.size -= PAGE_SIZE; + if (regions.size == 0) + { + regions = regions.next; + } + m_n_free_pages--; + } return cast(void *)pp; } + /** + * Free a physical memory region. + */ + public static void free_region(T)(T start, ulong size) + { + if (regions is null) + { + /* The free regions list is empty. Append this new region. */ + regions = cast(Region *)start; + regions.size = size; + regions.next = null; + } + else if (size < regions.size) + { + /* The new region is smaller than the first one in the list. + * Add the new region to the beginning of the list. */ + Region * new_region = cast(Region *)start; + new_region.size = size; + new_region.next = regions; + regions = new_region; + } + else + { + /* Find the last free region that is smaller than this one. */ + Region * region = regions; + while (region.next !is null && region.next.size < size) + { + region = region.next; + } + + /* Add the new region after this entry. */ + Region * new_region = cast(Region *)start; + new_region.size = size; + new_region.next = region.next; + region.next = new_region; + } + + m_n_free_pages += size >> 12; + } + /** * Get the number of free pages. * * @return The number of free pages. */ - public @property size_t n_free_pages() const + public static @property size_t n_free_pages() { return m_n_free_pages; } + + public static void dump_regions() + { + Region * region = regions; + while (region !is null) + { + Klog.writefln("HIPPO free region: %p %uKB", region, region.size >> 10); + region = region.next; + } + } } diff --git a/src/hulk/hurl/hurl.d b/src/hulk/hurl/hurl.d index 9e41bf3..0e6e5b0 100644 --- a/src/hulk/hurl/hurl.d +++ b/src/hulk/hurl/hurl.d @@ -157,13 +157,14 @@ struct Hurl size_t usable_memory; size_t physical_address_limit; const(size_t) fb_size = round_up_power_2(header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u, PAGE_SIZE); - ulong[2][2] reserved = [ + ulong[2][2] reserved_regions = [ [header.bootinfo.hulk_phys, LinkerAddresses.hulk_binary_size], [cast(ulong)header.bootinfo.fb.buffer, fb_size], ]; - for (size_t ri = 0u; ri < reserved.length; ri++) + for (size_t ri = 0u; ri < reserved_regions.length; ri++) { - reserved[ri][1] += reserved[ri][0]; + Klog.writefln("Reserved region %p %uKB", reserved_regions[ri][0], reserved_regions[ri][1] >> 10); + reserved_regions[ri][1] += reserved_regions[ri][0]; } for (size_t bii = 0u; bii < header.bootinfo.memory_map_count; bii++) { @@ -171,29 +172,14 @@ struct Hurl (header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Conventional)) { ulong phys = header.bootinfo.memory_map[bii].base; - ulong phys_end = phys + header.bootinfo.memory_map[bii].size; + ulong size = header.bootinfo.memory_map[bii].size; + ulong phys_end = phys + size; if (phys_end > physical_address_limit) { physical_address_limit = phys_end; } usable_memory += header.bootinfo.memory_map[bii].size; - while (phys < phys_end) - { - bool is_reserved = false; - for (size_t ri = 0u; ri < reserved.length; ri++) - { - if ((reserved[ri][0] <= phys) && (phys < reserved[ri][1])) - { - is_reserved = true; - break; - } - } - if (!is_reserved) - { - Hippo.free_page(phys); - } - phys += PAGE_SIZE; - } + add_region(phys, size, reserved_regions); } } @@ -223,6 +209,60 @@ struct Hurl usable_memory += fb_size; Klog.writefln("Usable memory: %uKB", usable_memory >> 10u); Klog.writefln("Kernel size: %uKB", (LinkerAddresses.hulk_binary_size + LinkerAddresses.hulk_bss_size + header.stack_size) >> 10u); + Klog.writefln("Free pages: %u", Hippo.n_free_pages); +// Hippo.dump_regions(); + } + + /** + * Add a region of available physical memory. + * + * @param start Start address of region. + * @param size Size of region. + * @param reserved_regions List of reserved regions to exclude. + */ + private static void add_region(ulong start, ulong size, ulong[2][] reserved_regions) + { + ulong end = start + size; + bool split; + + /* Skip empty regions. */ + if (size == 0) + { + return; + } + + for (size_t rri = 0; rri < reserved_regions.length; rri++) + { + /* Skip this region if it is entirely within a reserved region. */ + if (reserved_regions[rri][0] <= start && reserved_regions[rri][1] >= end) + { + return; + } + + /* Check if the region is partly reserved but the beginning is free. */ + if (reserved_regions[rri][0] > start && reserved_regions[rri][0] < end) + { + split = true; + add_region(start, reserved_regions[rri][0] - start, reserved_regions); + } + + /* Check if the region is partly reserved but the end is free. */ + if (reserved_regions[rri][1] > start && reserved_regions[rri][1] < end) + { + split = true; + add_region(reserved_regions[rri][1], end - reserved_regions[rri][1], reserved_regions); + } + + if (split) + { + return; + } + } + + /* If a region makes it here, it did not overlap with any reserved + * region, so the entire region can be used. */ +// Klog.writefln("HURL: freeing region %p %uKB", start, size >> 10); + Hippo.free_region(start, size); } public static void map(ulong virtual, ulong physical, ulong flags)