Files
KatauOS/src/mm/paging.c
T

269 lines
8.6 KiB
C

#include "../include/paging.h"
#include <stdint.h>
#include "../include/string.h"
#include "../include/kheap.h"
#include "../include/liballoc.h"
uint32_t *kpage_dir;
uint32_t setup_tmp_pgdir(uint32_t magic, uint32_t info)
{
int n, pd;
uint32_t pagedir_address, memksize;
uint32_t *page_table;
struct multiboot_info *mbi;
if(magic != MULTIBOOT_BOOTLOADER_MAGIC) {
memksize = 4096;
} else {
mbi = (struct multiboot_info *)(PAGE_OFFSET + info);
if(!(mbi->flags & MULTIBOOT_INFO_MEMORY)) {
memksize = 4096;
} else {
memksize = (uint32_t)mbi->mem_upper + 1024;
if(memksize > ((0xFFFFFFFF - PAGE_OFFSET) / 1024)) {
memksize = (0xFFFFFFFF - PAGE_OFFSET) / 1024;
}
}
}
/* Address is PAGE_OFFSET plus memory size minus 4KB */
pagedir_address = PAGE_OFFSET + (memksize * 1024) - memksize;
pagedir_address = PAGE_ALIGN(pagedir_address);
kpage_dir = (uint32_t *)pagedir_address;
memset(kpage_dir, 0, PAGE_SIZE);
pagedir_address += PAGE_SIZE;
page_table = (uint32_t *)pagedir_address;
memset(page_table, 0, memksize);
for(n = 0; n < memksize / sizeof(uint32_t); n++) {
page_table[n] = (n << PAGE_SHIFT) | PAGE_PRESENT | PAGE_RW;
if(!(n % 1024)) {
pd = n / 1024;
kpage_dir[pd] = (unsigned int)(pagedir_address + (PAGE_SIZE * pd) + GDT_BASE) | PAGE_PRESENT | PAGE_RW;
kpage_dir[GET_PGDIR(PAGE_OFFSET) + pd] = (unsigned int)(pagedir_address + (PAGE_SIZE * pd) + GDT_BASE) | PAGE_PRESENT | PAGE_RW;
}
}
uint32_t pd_phys = (uint32_t)kpage_dir - PAGE_OFFSET;
kpage_dir[1023] = pd_phys | PAGE_PRESENT | PAGE_RW; // Recursive mapping
return (uint32_t)kpage_dir - PAGE_OFFSET;
}
void grub_memory_map(unsigned int magic, struct multiboot_info* mbi)
{
/* Check bit 6 to see if we have a valid memory map */
if(!(mbi->flags >> 6 & 0x1)) {
printf("invalid memory map given by GRUB bootloader\n");
while(1){}
}
/* Loop through the memory map and display the values */
int i;
for(i = 0; i < mbi->mmap_length;
i += sizeof(multiboot_memory_map_t))
{
multiboot_memory_map_t* mmmt =
(multiboot_memory_map_t*) (mbi->mmap_addr + i);
printf("Start Addr: %x | Length: %x | Size: %x | Type: %x\n",
mmmt->addr_low, mmmt->len_low, mmmt->size, mmmt->type);
if(mmmt->type != MULTIBOOT_MEMORY_AVAILABLE) {
/*
* Do something with this memory block!
* BE WARNED that some of memory shown as availiable is actually
* actively being used by the kernel! You'll need to take that
* into account before writing to memory!
*/
printf("not available\n");
uint32_t size_in_pages = mmmt->len_low / PAGE_SIZE;
printf("size_in_pages: 0x%X\n", size_in_pages);
uint32_t base_page = mmmt->addr_low / PAGE_SIZE;
uint32_t page_count = mmmt->len_low / PAGE_SIZE;
printf("page_base: %X, page_count: %X\n", base_page, page_count);
for (uint32_t j = 0; j < page_count; j++) {
set_bit(base_page + j);
}
}
}
extern uint32_t _start, _end; // From linker script
uint32_t kernel_start_phys = (uint32_t)&_start - 0xC0000000;
uint32_t kernel_end_phys = (uint32_t)&_end - 0xC0000000;
uint32_t kernel_start_page = kernel_start_phys / PAGE_SIZE;
uint32_t kernel_end_page = (kernel_end_phys + PAGE_SIZE - 1) / PAGE_SIZE;
printf("Kernel phys range: 0x%X to 0x%X, pages: 0x%X to 0x%X\n",
kernel_start_phys, kernel_end_phys, kernel_start_page, kernel_end_page);
for (uint32_t i = kernel_start_page; i < kernel_end_page; i++) {
if (i < BITMAP_SIZE) {
set_bit(i);
} else {
printf("Bitmap overflow at kernel page %u\n", i);
}
}
}
uint32_t* get_page_dir()
{
uint32_t cr3_value;
asm volatile("mov %%cr3, %0" : "=r" (cr3_value));
return (uint32_t*) cr3_value;
}
void set_page_dir(uint32_t new_page_dir) {
asm volatile("mov %0, %%cr3" : : "r" (new_page_dir));
}
uint32_t phys_to_virt(uint32_t phys)
{
return phys+0xC0000000;
}
void* phys_to_virt2(void* phys)
{
return phys+0xC0000000;
}
uint32_t* create_page_dir()
{
void* ppp = alloc_page();
memset(ppp, 0, 0x1000);
uint32_t* current_pd = (uint32_t*)0xFFFFF000; // Current PD (self-mapped)
uint32_t* new_pd = (uint32_t*)phys_to_virt((uint32_t)ppp);
for(int i = 768; i < 1024; i++)
{
if(current_pd[i] & PAGE_PRESENT) {
new_pd[i] = current_pd[i];
}
}
int vga_pde_index = 0; // Virtual address 0x000B8000 is covered by PDE[0]
if (current_pd[vga_pde_index] & PAGE_PRESENT) {
new_pd[vga_pde_index] = current_pd[vga_pde_index];
}
new_pd[1023] = (uint32_t)ppp | 0x03; // Present + Read/Write
return new_pd;
}
void destroy_page_dir(uint32_t* page_dir_virt) {
if (page_dir_virt == NULL) {
return;
}
// Iterate through user-space page directory entries (PDEs 0-767).
// Kernel space (768+) is shared and should not be freed.
for (int i = 0; i < 768; i++) {
uint32_t pde = page_dir_virt[i];
// Check if the page directory entry is present
if (pde & PAGE_PRESENT) {
// Get the physical address of the page table
uint32_t pt_phys = pde & ~0xFFF;
if(is_page_in_use(pt_phys / PAGE_SIZE)){
// Convert it to a virtual address the kernel can access
uint32_t* page_table_virt = (uint32_t*)phys_to_virt(pt_phys);
// Iterate through all 1024 entries in this page table
for (int j = 0; j < 1024; j++) {
uint32_t pte = page_table_virt[j];
// If the page table entry is present, free the physical page (frame) it points to
if (pte & PAGE_PRESENT) {
//debug_log("freeing page %X\n", pte);
free_page((void*)(pte & ~0xFFF));
}
}
// After freeing all pages within the table, free the page table itself
free_page((void*)pt_phys);
}
}
}
// Finally, free the page directory itself.
// We need its physical address to pass to the physical memory manager.
debug_log("freeing PD page %X\n", virt_to_phys(page_dir_virt));
free_page((void*)virt_to_phys(page_dir_virt));
}
static void* scratch_src = (void*)0xE1000000;
static void* scratch_dst = (void*)0xE1001000;
uint32_t* copy_page_dir(uint32_t* page_dir_virt)
{
if(page_dir_virt == NULL)
return 0;
debug_log("====%X %X\n", scratch_dst, scratch_src);
uint32_t* new_pagedir = create_page_dir();
for(int i = 1; i < 768; i++)
{
uint32_t pde = page_dir_virt[i];
if(pde & PAGE_PRESENT)
{
debug_log("PDE %X present\n", pde);
uint32_t* new_page_table = (uint32_t*)alloc_page();
if(new_page_table == NULL) {
return NULL;
}
uint32_t* new_page_table_virt = (uint32_t*)phys_to_virt((uint32_t)new_page_table);
// Get original page table (physical address from PDE)
uint32_t* orig_page_table = (uint32_t*)phys_to_virt(pde & ~0xFFF);
for(int j = 0; j < 1024; j++)
{
uint32_t pte = orig_page_table[j];
if(pte & PAGE_PRESENT)
{
void* new_phys_page = alloc_page();
if(!new_phys_page)
{
return NULL;
}
void* orig_phys_page = (void*)(pte & ~0xFFF);
map_page(orig_phys_page, scratch_src, PAGE_PRESENT | PAGE_RW);
map_page(new_phys_page, scratch_dst, PAGE_PRESENT | PAGE_RW);
memcpy(scratch_dst, scratch_src, PAGE_SIZE);
unmap_page(scratch_dst);
unmap_page(scratch_src);
new_page_table_virt[j] = (uint32_t)new_phys_page | (pte & 0xFFF);
}
else
{
new_page_table_virt[j] = pte;
}
}
// Set the new PDE in the child's page directory
new_pagedir[i] = (uint32_t)new_page_table | (pde & 0xFFF);
}
else
{
new_pagedir[i] = pde;
}
}
debug_log("ENDENDENDEND\n");
return new_pagedir;
}