/* * @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 "config.h" #include #include #include #include #include #include #include /* First we'll have to discover the physical memory layout, and for that we can use a Limine request. */ // DEPRECATED struct limine_memmap_entry* biggest_entry; /* * pmm_find_biggest_usable_region - Finding the biggest free memory region (DEPRECATED) * @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(struct limine_memmap_response* memmap) { uint64_t total_pages = 0; for (size_t i=0; ientry_count; i++) { struct limine_memmap_entry* entry = memmap->entries[i]; if (entry->type == LIMINE_MEMMAP_USABLE) { uint64_t base = ALIGN_UP(entry->base, PAGE_SIZE); uint64_t end = ALIGN_DOWN(entry->base + entry->length, PAGE_SIZE); if (end > PAGING_MAX_PHYS) { end = PAGING_MAX_PHYS; } // Region above PAGING_MAX_PHYS if (base >= end) continue; for (uint64_t addr = base; addr < end; addr += PAGE_SIZE) { pmm_free(addr); total_pages++; } } } DEBUG("%u frames in freelist, %u bytes available (%u MB)", total_pages, total_pages*PAGE_SIZE, total_pages*PAGE_SIZE/1000000); } /* * 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(boot_ctx.mmap); }