forked from xamidev/pepperOS
Move headers to include/
This commit is contained in:
132
src/mem/pmm.c
Normal file
132
src/mem/pmm.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* @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 <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.
|
||||
*/
|
||||
|
||||
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; 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()
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
Reference in New Issue
Block a user