forked from xamidev/pepperOS
148 lines
4.0 KiB
C
148 lines
4.0 KiB
C
/*
|
|
* @author xamidev <xamidev@riseup.net>
|
|
* @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 <mem/paging.h>
|
|
#include <limine.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <kernel.h>
|
|
#include <mem/utils.h>
|
|
#include <mem/pmm.h>
|
|
|
|
/*
|
|
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; i<memmap->entry_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:
|
|
* <addr> - 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; i<memmap->entry_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);
|
|
}
|