111 lines
3.1 KiB
C
111 lines
3.1 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 "paging.h"
|
|
#include <limine.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <kernel.h>
|
|
#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.
|
|
*/
|
|
|
|
/*
|
|
We will look for the biggest usable physical memory region
|
|
and use this for the bitmap. The reserved memory will be ignored.
|
|
*/
|
|
|
|
struct limine_memmap_entry* biggest_entry;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
void pmm_free(uintptr_t addr)
|
|
{
|
|
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
|
g_freelist = addr;
|
|
}
|
|
|
|
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);
|
|
//DEBUG("page %u lives at phys 0x%p (virt 0x%p)", page_count, addr, PHYS_TO_VIRT(addr));
|
|
page_count++;
|
|
}
|
|
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
|
}
|
|
|
|
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
|
{
|
|
hhdm_off = hhdm->offset;
|
|
pmm_find_biggest_usable_region(memmap, hhdm);
|
|
//pmm_allocate_bitmap(hhdm); too complicated for my small brain
|
|
|
|
// Now we have biggest USABLE region,
|
|
// so to populate the free list we just iterate through it
|
|
pmm_init_freelist();
|
|
}
|