2026-06-16 18:44:31 -05:00
|
|
|
#include "memory/mm.h"
|
2026-06-13 20:43:09 -04:00
|
|
|
#include "multiboot.h"
|
|
|
|
|
|
2026-06-14 00:34:11 -05:00
|
|
|
#include <stddef.h>
|
2026-07-07 18:52:22 -05:00
|
|
|
#include <lib/memory.h>
|
2026-06-14 00:34:11 -05:00
|
|
|
|
|
|
|
|
extern uint8_t __kernel_start;
|
|
|
|
|
extern uint8_t __kernel_end;
|
|
|
|
|
|
2026-06-15 23:35:01 -05:00
|
|
|
extern uint32_t page_directory[1024];
|
|
|
|
|
|
2026-06-13 20:43:09 -04:00
|
|
|
static uint32_t max_address = 0;
|
|
|
|
|
static uint32_t total_available_memory = 0;
|
2026-06-14 00:34:11 -05:00
|
|
|
static uint8_t memory_bitmap[BITMAP_SIZE];
|
|
|
|
|
|
|
|
|
|
static void mark_free(uint32_t addr) {
|
|
|
|
|
uint32_t page_index = addr / PAGE_SIZE;
|
2026-06-17 18:16:47 -05:00
|
|
|
uint32_t byte_index = page_index / 8;
|
2026-06-14 00:34:11 -05:00
|
|
|
uint32_t bit_index = page_index % 8;
|
|
|
|
|
memory_bitmap[byte_index] &= ~(1 << bit_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mark_used(uint32_t addr) {
|
|
|
|
|
uint32_t page_index = addr / PAGE_SIZE;
|
|
|
|
|
uint32_t byte_index = page_index / 8;
|
|
|
|
|
uint32_t bit_index = page_index % 8;
|
|
|
|
|
memory_bitmap[byte_index] |= (1 << bit_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t align(uint32_t addr, uint32_t len, uint32_t* usable_start, uint32_t* usable_end) {
|
|
|
|
|
uint32_t start = addr;
|
|
|
|
|
uint32_t end = addr + len;
|
|
|
|
|
*usable_start = (start + 4095) & ~0xFFF;
|
|
|
|
|
*usable_end = end & ~0xFFF;
|
|
|
|
|
return *usable_end - *usable_start;
|
|
|
|
|
}
|
2026-06-13 20:43:09 -04:00
|
|
|
|
2026-06-14 00:34:11 -05:00
|
|
|
static void parse_multiboot_mmap(void* base, uint32_t limit) {
|
2026-06-13 20:43:09 -04:00
|
|
|
multiboot_mmap_entry* entry = (multiboot_mmap_entry*)base;
|
|
|
|
|
void* end = (void*)((uint32_t)base + limit);
|
2026-06-14 00:34:11 -05:00
|
|
|
total_available_memory = 0;
|
2026-06-13 20:43:09 -04:00
|
|
|
while ((void*)entry < end) {
|
2026-06-14 00:34:11 -05:00
|
|
|
uint32_t entry_size = entry->size + sizeof(entry->size);
|
2026-06-13 20:43:09 -04:00
|
|
|
if (entry->type == 1) {
|
|
|
|
|
uint32_t region_start = (uint32_t)(entry->addr);
|
|
|
|
|
uint32_t region_end = (uint32_t)(entry->addr + entry->len);
|
|
|
|
|
if (region_end > max_address) {
|
|
|
|
|
max_address = region_end;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-14 00:34:11 -05:00
|
|
|
uint32_t usable_start, usable_end, usable_size;
|
|
|
|
|
usable_size = align(region_start, entry->len, &usable_start, &usable_end);
|
|
|
|
|
if (usable_size <= 4096) {
|
2026-06-24 21:52:05 -05:00
|
|
|
entry = (multiboot_mmap_entry*)((uint32_t)entry + entry_size);
|
2026-06-13 20:43:09 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 18:09:04 -04:00
|
|
|
uint32_t kernel_phys_start = (uint32_t)&__kernel_start - 0xC0000000;
|
|
|
|
|
uint32_t kernel_phys_end = (uint32_t)&__kernel_end - 0xC0000000;
|
2026-06-14 00:34:11 -05:00
|
|
|
for (uint32_t addr = usable_start; addr < usable_end; addr += 4096) {
|
2026-06-15 23:35:01 -05:00
|
|
|
if (addr <= kernel_phys_end && addr >= kernel_phys_start) {
|
2026-06-14 00:34:11 -05:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (addr >= (uint32_t)entry && addr <= (uint32_t)entry + entry_size) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-06-13 20:43:09 -04:00
|
|
|
|
2026-06-14 00:34:11 -05:00
|
|
|
if (addr <= 0x200000) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-06-13 20:43:09 -04:00
|
|
|
|
2026-06-14 00:34:11 -05:00
|
|
|
total_available_memory += 4096;
|
|
|
|
|
|
|
|
|
|
mark_free(addr);
|
2026-06-13 20:43:09 -04:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-14 00:34:11 -05:00
|
|
|
entry = (multiboot_mmap_entry*)((uint32_t)entry + entry_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 18:44:31 -05:00
|
|
|
void* p_alloc_frame() {
|
2026-06-14 00:34:11 -05:00
|
|
|
for (uint32_t i = 0; i < BITMAP_SIZE; i++) {
|
|
|
|
|
uint8_t b = memory_bitmap[i];
|
|
|
|
|
if (b == 0xFF) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int bit = 0; bit < 8; bit++) {
|
|
|
|
|
if ((b & (1 << bit)) == 0) {
|
|
|
|
|
uint32_t page_addr = (i * 8 + bit) * PAGE_SIZE;
|
|
|
|
|
mark_used(page_addr);
|
2026-06-15 23:35:01 -05:00
|
|
|
return (void*)page_addr;
|
2026-06-14 00:34:11 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-29 17:00:07 -05:00
|
|
|
void p_free_frame(void* addr) {
|
2026-06-14 00:34:11 -05:00
|
|
|
mark_free((uint32_t)addr);
|
2026-06-14 18:53:54 -05:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 10:04:49 -04:00
|
|
|
// MODIFIES THE ACTIVE PAGE TABLE AT THE PHYSICAL ADDRESS LOADED INTO CR3 VIA RECURSIVE MAPPING
|
2026-06-16 18:44:31 -05:00
|
|
|
void map_page(uint32_t vaddr, uint32_t paddr, uint32_t flags) {
|
2026-06-15 23:35:01 -05:00
|
|
|
uint32_t pde_entry = vaddr >> 22;
|
|
|
|
|
uint32_t pte_entry = (vaddr >> 12) & 0x3FF;
|
2026-07-08 19:11:39 -05:00
|
|
|
uint32_t* pd = (uint32_t*)CUR_PAGE_DIRECTORY;
|
|
|
|
|
uint32_t* tablev = (uint32_t*)(CUR_PAGE_TABLES + (pde_entry * PAGE_SIZE));
|
2026-06-16 10:04:49 -04:00
|
|
|
|
|
|
|
|
if ((pd[pde_entry] & PAGE_PRESENT) == 0) {
|
|
|
|
|
uint32_t new_table = (uint32_t)p_alloc_frame();
|
2026-06-16 18:44:31 -05:00
|
|
|
pd[pde_entry] = new_table | flags;
|
2026-06-16 10:04:49 -04:00
|
|
|
flush_tlb();
|
|
|
|
|
for (int i = 0; i < 1024; i++) {
|
|
|
|
|
tablev[i] = 0;
|
|
|
|
|
}
|
2026-06-15 23:35:01 -05:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 10:04:49 -04:00
|
|
|
tablev[pte_entry] = paddr | flags;
|
|
|
|
|
flush_tlb();
|
2026-06-15 23:35:01 -05:00
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
void unmap_page(uint32_t vaddr) {
|
|
|
|
|
uint32_t pde_entry = vaddr >> 22;
|
|
|
|
|
uint32_t pte_entry = (vaddr >> 12) & 0x3FF;
|
|
|
|
|
uint32_t* tablev = (uint32_t*)(CUR_PAGE_TABLES + (pde_entry * PAGE_SIZE));
|
|
|
|
|
tablev[pte_entry] = 0;
|
|
|
|
|
flush_tlb();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int is_cow(uint32_t vaddr) {
|
|
|
|
|
uint32_t pde_entry = vaddr >> 22;
|
|
|
|
|
uint32_t pte_entry = (vaddr >> 12) & 0x3FF;
|
|
|
|
|
uint32_t* pd = (uint32_t*)CUR_PAGE_DIRECTORY;
|
|
|
|
|
uint32_t* tablev = (uint32_t*)(CUR_PAGE_TABLES + (pde_entry * PAGE_SIZE));
|
|
|
|
|
|
|
|
|
|
if (!(pd[pde_entry] & PAGE_PRESENT)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tablev[pte_entry] & PAGE_WRITABLE) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(tablev[pte_entry] & PAGE_COW)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 23:35:01 -05:00
|
|
|
void init_pmm(void* mmap_base, uint32_t mmap_limit) {
|
|
|
|
|
for (int i = 0; i < BITMAP_SIZE; i++) {
|
|
|
|
|
memory_bitmap[i] = 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mmap_base != NULL && mmap_limit > 0) {
|
|
|
|
|
parse_multiboot_mmap(mmap_base, mmap_limit);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 18:09:04 -04:00
|
|
|
void init_page_tables() {
|
2026-06-16 18:44:31 -05:00
|
|
|
uint32_t kernel_size = (uint32_t)&__kernel_end - (uint32_t)&__kernel_start;
|
|
|
|
|
uint32_t num_pages = kernel_size / PAGE_SIZE + 1;
|
|
|
|
|
uint32_t num_page_tables = (num_pages + 1023) / 1024;
|
|
|
|
|
for (uint32_t table = 0; table < num_page_tables; table++) {
|
|
|
|
|
uint32_t* kernel_table = p_alloc_frame();
|
|
|
|
|
for (int i = 0; i < 1024; i++) {
|
|
|
|
|
kernel_table[i] = (i * PAGE_SIZE) | 0x3;
|
|
|
|
|
}
|
|
|
|
|
page_directory[768 + table] = (uint32_t)(kernel_table) | 0x3;
|
2026-06-14 18:53:54 -05:00
|
|
|
}
|
2026-06-15 23:35:01 -05:00
|
|
|
page_directory[0] = 0;
|
|
|
|
|
disable_pae();
|
2026-06-15 18:09:04 -04:00
|
|
|
flush_tlb();
|
2026-06-15 23:35:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t total_free_memory() {
|
|
|
|
|
return total_available_memory;
|
2026-06-16 10:04:49 -04:00
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
void* create_new_pd() {
|
|
|
|
|
uint32_t task_pd_addr = (uint32_t)p_alloc_frame();
|
|
|
|
|
if (task_pd_addr == 0) {
|
|
|
|
|
return NULL; // outta mem
|
2026-06-16 10:04:49 -04:00
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
map_page(TEMP_TASKPD_VADDR, task_pd_addr, PAGE_PRESENT | PAGE_WRITABLE);
|
2026-06-16 10:04:49 -04:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
uint32_t* task_pd = (uint32_t*)TEMP_TASKPD_VADDR;
|
|
|
|
|
uint32_t* parent_pd = (uint32_t*)CUR_PAGE_DIRECTORY;
|
|
|
|
|
|
|
|
|
|
for(int i = 768; i < 1023; i++) {
|
|
|
|
|
task_pd[i] = parent_pd[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task_pd[1023] = task_pd_addr | PAGE_PRESENT | PAGE_WRITABLE;
|
2026-06-16 10:04:49 -04:00
|
|
|
|
2026-06-16 18:44:31 -05:00
|
|
|
for(int i = 0; i < 768; i++) {
|
2026-06-26 20:05:01 -05:00
|
|
|
task_pd[i] = 0;
|
2026-06-16 10:04:49 -04:00
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
unmap_page(TEMP_TASKPD_VADDR);
|
|
|
|
|
|
|
|
|
|
return (void*)task_pd_addr;
|
2026-07-07 00:13:24 -05:00
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
void* clone_current_pd() {
|
|
|
|
|
uint32_t task_pd_addr = (uint32_t)p_alloc_frame();
|
|
|
|
|
if (task_pd_addr == 0) {
|
|
|
|
|
return NULL; // outta mem
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
map_page(TEMP_TASKPD_VADDR, task_pd_addr, PAGE_PRESENT | PAGE_WRITABLE);
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
uint32_t* task_pd = (uint32_t*)TEMP_TASKPD_VADDR;
|
|
|
|
|
uint32_t* parent_pd = (uint32_t*)CUR_PAGE_DIRECTORY;
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
for(int i = 768; i < 1023; i++) {
|
|
|
|
|
task_pd[i] = parent_pd[i];
|
2026-07-07 00:13:24 -05:00
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
task_pd[1023] = task_pd_addr | PAGE_PRESENT | PAGE_WRITABLE;
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
for(int i = 0; i < 768; i++) {
|
|
|
|
|
task_pd[i] = 0;
|
2026-07-07 00:13:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 768; i++) {
|
|
|
|
|
uint32_t pde = parent_pd[i];
|
|
|
|
|
if (!(pde & PAGE_PRESENT)) continue;
|
2026-07-08 19:11:39 -05:00
|
|
|
|
|
|
|
|
uint32_t task_pt = (uint32_t)p_alloc_frame();
|
|
|
|
|
task_pd[i] = task_pt | (pde & 0xFFF);
|
|
|
|
|
|
|
|
|
|
uint32_t parent_pt = pde & 0xFFFFF000;
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
map_page(TEMP_PT_DST, task_pt, PAGE_PRESENT | PAGE_WRITABLE);
|
|
|
|
|
map_page(TEMP_PT_SRC, parent_pt, PAGE_PRESENT | PAGE_WRITABLE);
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
uint32_t* pt_dst = (uint32_t*)TEMP_PT_DST;
|
|
|
|
|
uint32_t* pt_src = (uint32_t*)TEMP_PT_SRC;
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
for(int j = 0; j < 1024; j++) {
|
|
|
|
|
uint32_t pte_src = pt_src[j];
|
|
|
|
|
if (!(pte_src & PAGE_PRESENT)) continue;
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-16 23:19:54 -05:00
|
|
|
if (!(pte_src & PAGE_WRITABLE) || (pte_src & PAGE_MMIO)) {
|
2026-07-08 19:11:39 -05:00
|
|
|
pt_dst[j] = pt_src[j];
|
2026-07-07 00:13:24 -05:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
// the src page is writable, mark the pte src as read only and COW, then copy to dst pte
|
|
|
|
|
pt_src[j] &= ~(PAGE_WRITABLE);
|
|
|
|
|
pt_src[j] |= PAGE_COW;
|
|
|
|
|
flush_tlb(); // huge performance hit here, and probably everywhere else too where we aren't switching cr3s
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
pt_dst[j] = pt_src[j];
|
2026-07-07 00:13:24 -05:00
|
|
|
}
|
|
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
unmap_page(TEMP_PT_DST);
|
|
|
|
|
unmap_page(TEMP_PT_SRC);
|
|
|
|
|
}
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
unmap_page(TEMP_TASKPD_VADDR);
|
2026-07-07 00:13:24 -05:00
|
|
|
|
2026-07-08 19:11:39 -05:00
|
|
|
return (void*)task_pd_addr;
|
2026-07-08 21:15:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void clear_current_pd() {
|
|
|
|
|
|
2026-06-13 20:43:09 -04:00
|
|
|
}
|