/* * @author xamidev * @brief Physical memory manager from freelist * @license GPL-3.0-only */ /* pmm - Physical Memory Manager will manage 4kb pages physically it will probably need to get some info from Limine, to see which pages are used by kernel/bootloader/mmio/fb etc. */ #include "paging.h" #include #include #include #include #include "mem/misc/utils.h" #include "pmm.h" /* First we'll have to discover the physical memory layout, and for that we can use a Limine request. */ struct limine_memmap_entry* biggest_entry; /* * pmm_find_biggest_usable_region - Finding the biggest free memory region * @memmap: Limine memory map * @hhdm: Limine HHDM offset * * This function uses the memory map provided by the bootloader * to find the single biggest free memory region we can use. */ static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm) { // Max length of a usable memory region uint64_t length_max = 0; uint64_t offset = hhdm->offset; DEBUG("Usable Memory:"); for (size_t i=0; ientry_count; i++) { struct limine_memmap_entry* entry = memmap->entries[i]; if (entry->type == LIMINE_MEMMAP_USABLE) { DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", entry->base, entry->base+entry->length, entry->base+offset, entry->base+entry->length+offset); if (entry->length > length_max) { length_max = entry->length; biggest_entry = entry; } } } DEBUG("Biggest usable memory region:"); DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", biggest_entry->base, biggest_entry->base + biggest_entry->length, biggest_entry->base+offset, biggest_entry->base+biggest_entry->length+offset); } // Offset from Higher Half Direct Map uint64_t hhdm_off; static uintptr_t g_freelist = 0; /* * pmm_alloc - Allocate a physical page * * This function allocates a single physical page (frame) * * Return: * - Address for the allocated page */ uintptr_t pmm_alloc() { if (!g_freelist) { panic(NULL, "PMM is out of memory!"); } uintptr_t addr = g_freelist; g_freelist = *(uintptr_t*) PHYS_TO_VIRT(g_freelist); return addr; } /* * pmm_free - Frees a memory page * @addr: Address to the page */ void pmm_free(uintptr_t addr) { *(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist; g_freelist = addr; } /* * pmm_init_freelist - PMM freelist initialization * * This function marks the biggest memory region as * free, so we can use it in pmm_alloc. */ static void pmm_init_freelist() { // We simply call pmm_free() on each page that is marked USABLE // in our big memory region. uint64_t base = ALIGN_UP(biggest_entry->base, PAGE_SIZE); uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE); uint64_t page_count=0; for (uint64_t addr = base; addr < end; addr += PAGE_SIZE) { pmm_free(addr); page_count++; } DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE); } /* * pmm_init - Physical memory manager initialization * @boot_ctx: Boot context structure * * This function prepares the PMM for use. * The PMM works with a freelist. */ void pmm_init(struct boot_context boot_ctx) { hhdm_off = boot_ctx.hhdm->offset; pmm_find_biggest_usable_region(boot_ctx.mmap, boot_ctx.hhdm); // Now we have biggest USABLE region, // so to populate the free list we just iterate through it pmm_init_freelist(); }