#include <sys/memory.h>
#include <sys/paging.h>
#include <sys/types.h>
#include <stdlib.h>
#define PD_SIZE (4 * 1024)
#define PT_SIZE (4 * 1024)
#define PD_ENTRY(pt_addr, flags) ((((uint32_t)(pt_addr)) & 0xfffff000) | (flags))
#define PT_ENTRY(phys_addr, flags) ((((uint32_t)(phys_addr)) & 0xfffff000) | (flags))
static uint32_t pt_init(void);
static void pd_proto_init(void);
pd_t kernel_paging_directory;
/* Contains page mappings shared by all page directories. */
static pd_t pd_proto;
#define KERNEL_MEM_START 0x100000
#define KERNEL_MEM_END 0x400000
#define VIDEO_MEM_START 0xB8000
#define VIDEO_MEM_SIZE 4000
#define KMEM_MAX (256 * 1024 * 1024)
static inline void
pd_switch(pd_t pd) {
asm volatile ("movl %0, %%cr3\n"
"movl %%cr0, %%eax\n"
"or $0x80000000, %%eax\n"
"movl %%eax, %%cr0\n"
:
: "r" (pd)
: "memory", "eax");
}
void
paging_init(void) {
pd_proto_init();
pd_t paging_directory = pd_create();
kernel_paging_directory = paging_directory;
pd_switch(kernel_paging_directory);
}
void
pd_proto_init(void) {
pd_proto = kmalloc_align(PAGE_SIZE, PD_SIZE);
memset(pd_proto, 0, PD_SIZE);
void *sentry_page_table = kmalloc_align(PAGE_SIZE, PT_SIZE);
memset(sentry_page_table, 0, PT_SIZE);
for(int i=0; i < 1024; ++i) {
pd_proto[i] = PD_ENTRY(sentry_page_table, 0);
}
int video_pages = (VIDEO_MEM_SIZE + PAGE_SIZE - 1) / PAGE_SIZE;
pd_map_area(pd_proto, (void *)VIDEO_MEM_START,
(void *)VIDEO_MEM_START, video_pages, 0);
int kernel_pages = KMEM_MAX / PAGE_SIZE;
pd_map_area(pd_proto, (void *)KERNEL_MEM_START,
(void *)KERNEL_MEM_START, kernel_pages, 0);
}
pd_t
pd_create(void) {
pd_t pd = kmalloc_align(PAGE_SIZE, PD_SIZE);
memcpy(pd, pd_proto, PD_SIZE);
return pd;
}
void
pd_destroy(pd_t pd) {
// TODO: free allocated page tables
kfree(pd);
}
void pd_map_area(pd_t pd_base, void *virt_addr_p, void *phys_addr_p, int num_pages, uint32_t flags) {
for (int i=0; i<num_pages; ++i) {
pd_map_page(pd_base, virt_addr_p, phys_addr_p, flags);
virt_addr_p += PAGE_SIZE;
phys_addr_p += PAGE_SIZE;
}
}
void pd_map_page(pd_t pd_base, void *virt_addr_p, void *phys_addr_p, uint32_t flags) {
uint32_t virt_addr = (uint32_t)virt_addr_p;
uint32_t phys_addr = (uint32_t)phys_addr_p;
uint32_t pd_offset = virt_addr >> (PAGE_SIZE_SHIFT + 10);
uint32_t pd_entry = pd_base[pd_offset];
if (!(pd_entry & PD_FLAG_PRESENT)) {
pd_entry = pd_base[pd_offset] = pt_init();
}
uint32_t *pt_base = (uint32_t *)(pd_entry & 0xfffff000);
uint32_t pt_offset = (virt_addr >> PAGE_SIZE_SHIFT) & 0x3ff;
pt_base[pt_offset] = PT_ENTRY(phys_addr, flags | PT_FLAG_PRESENT);
asm volatile ("invlpg (%0)" : : "r" (virt_addr) : "memory");
}
static uint32_t pt_init(void) {
void *pt_addr = kmalloc_align(PAGE_SIZE, PT_SIZE);
memset(pt_addr, 0, PT_SIZE);
return PD_ENTRY(pt_addr, PD_FLAG_PRESENT | PD_FLAG_USER | PD_FLAG_WRITABLE);
}
uint32_t
pd_get_phys(pd_t pd_base, void *virt_addr_p) {
uint32_t virt_addr = (uint32_t)virt_addr_p;
uint32_t pd_offset = virt_addr >> (PAGE_SIZE_SHIFT + 10);
uint32_t pd_entry = pd_base[pd_offset];
if (!(pd_entry & PD_FLAG_PRESENT)) {
return 0;
}
uint32_t *pt_base = (uint32_t *)(pd_entry & 0xfffff000);
uint32_t pt_offset = (virt_addr >> PAGE_SIZE_SHIFT) & 0x3ff;
uint32_t phys_addr_base = pt_base[pt_offset] & 0xfffff000;
return phys_addr_base | (virt_addr & 0xfff);
}