Nucleus
Barry Kernel threads + threads share address space 6217f0d (3 years, 1 month ago)
/*
* This file contains all the functions used to manipulate the virtual address
* spaces. It controls all of the system's paging and virtual memory from a low
* level perspective. On initialisation, a page directory is allocated for the
* kernel that identity maps everything.
*/
#include <stdint.h>
#include <nucleus/lib.h>
#include <nucleus/memory.h>
void copy_page_frame(uintptr_t src, uintptr_t dest);
void page_fault_handler(struct InterruptFrame *frame);
page_t zeroFrame;
static page_dir_t kernelDir;
/* Switch page directory */
void
switch_dir(page_dir_t dir)
{
asm volatile("mov %0, %%cr3" :: "r" (dir));
}
/* Get a page mapping */
page_t
get_page(uintptr_t vaddr)
{
page_t *mappings = (void *) 0xFFC00000;
page_table_t *tables = (void *) 0xFFFFF000;
uintptr_t address = vaddr >> 12;
uint32_t tbl = address / 1024;
if (!(tables[tbl] & PDE_PRESENT))
return 0x00000000;
return mappings[address];
}
/* Set a page mapping */
void
set_page(uintptr_t vaddr, page_t page)
{
page_t *mappings = (void *) 0xFFC00000;
page_table_t *tables = (void *) 0xFFFFF000;
uintptr_t address = vaddr >> 12;
uint32_t tbl = address / 1024;
if (!(tables[tbl] & PDE_PRESENT) && !page)
return;
/* Create table if not present */
if (!(tables[tbl] & PDE_PRESENT)) {
tables[tbl] = alloc_frame() | PDE_PRESENT | PDE_WRITE;
if (page & PTE_USER)
tables[tbl] |= PDE_USER;
memset(mappings + (tbl * 1024), 0, PAGE_SIZE);
}
mappings[address] = page;
}
/* Clone an entire page directory */
page_dir_t
clone_dir(void)
{
page_table_t *oldTables = (void *) 0xFFFFF000;
page_table_t *newTables = (void *) 0xFFFFE000;
page_t *oldTable, *newTable;
page_dir_t dir = alloc_frame();
uint16_t tbl, pg;
/* Temporarily link new paging structures into current directory */
page_table_t restore = oldTables[1022];
oldTables[1022] = dir | PDE_PRESENT | PDE_WRITE;
flush_tlb((uintptr_t) newTables);
/* Iterate tables */
for (tbl = 0; tbl < 1022; tbl++) {
if (!(oldTables[tbl] & PDE_PRESENT))
continue;
/* Link kernel tables */
if (tbl < 2 || tbl >= 1008) {
newTables[tbl] = oldTables[tbl];
continue;
}
/* Copy everything else */
newTables[tbl] = alloc_frame() | PAGE_ATTR(oldTables[tbl]);
oldTable = (page_t *) 0xFFC00000 + (tbl * 1024);
newTable = (page_t *) 0xFF800000 + (tbl * 1024);
flush_tlb((uintptr_t) newTable);
for (pg = 0; pg < 1024; pg++) {
if (!(oldTable[pg] & PTE_PRESENT)) {
newTable[pg] = 0;
continue;
}
/* Link the pages for Copy-On-Write */
oldTable[pg] &= ~PTE_WRITE;
flush_tlb(((tbl * 1024) + pg) << 12);
newTable[pg] = oldTable[pg];
}
}
newTables[1023] = oldTables[1022];
/* Unlink paging structures from current directory */
oldTables[1022] = restore;
flush_tlb((uintptr_t) newTables);
return dir;
}
/* Initialise paging */
void
init_paging(void)
{
uint16_t tbl, pg;
kernelDir = alloc_frame();
page_table_t *kernelTables = (page_table_t *) kernelDir;
page_t *table;
for (tbl = 0; tbl < 1024; tbl++)
kernelTables[tbl] = 0x00000000 | PDE_WRITE;
for (tbl = 0; tbl < 2; tbl++) {
table = (page_t *) alloc_frame();
kernelTables[tbl] = ((page_table_t) table)
| PDE_WRITE | PDE_PRESENT;
for (pg = 0; pg < 1024; pg++) {
/* Skip bottom page - catches NULL dereferences */
if (!tbl && !pg)
continue;
table[pg] = (((tbl * 1024) + pg) << 12)
| PTE_WRITE | PTE_PRESENT | PTE_GLOBAL;
}
}
/* Map the directory into itself */
kernelTables[1023] = kernelDir | PDE_WRITE | PDE_PRESENT;
/*
* By mapping the page directory as the last page table, the page
* directory entries are read as page table entries, and the page
* tables become the pages. This means that each page contains the
* contents of a page table, and the region in memory represented by the
* last page table contains a contiguous list of all pages in memory.
* The very last page contains the contents of the page directory
* itself. This means that each virtual address space contains it's own
* paging structures.
*/
/* Use kernel directory */
register_exception(14, page_fault_handler);
cpu_load_paging();
zeroFrame = alloc_frame();
}
/* Enable paging on the current CPU */
void
cpu_load_paging(void)
{
switch_dir(kernelDir);
asm volatile (
"movl %%cr0, %%eax;"
"orl $0x80000000, %%eax;"
"movl %%eax, %%cr0"
::: "%eax"
);
}