orion_old/kernel/kernel/paging.h

243 lines
7.0 KiB
C

#include <stdio.h>
#include <string.h>
//#include "kheap.c"
uint32_t placement_address; //FIXME
uint32_t *frames;
uint32_t nframes;
/*
Handler for page faults.
*/
void page_fault(registers_t regs){
// A page fault has occurred.
// The faulting address is stored in the CR2 register.
uint32_t faulting_address;
asm volatile("mov %%cr2, %0" : "=r" (faulting_address));
// The error code gives us details of what happened.
int present = !(regs.err_code & 0x1); // Page not present
int rw = regs.err_code & 0x2; // Write operation?
int us = regs.err_code & 0x4; // Processor was in user-mode?
int reserved = regs.err_code & 0x8; // Overwritten CPU-reserved bits of page entry?
int id = regs.err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
printf("Page fault: ( ");
if (present) {printf("present ");}
if (rw) {printf("read-only ");}
if (us) {printf("user-mode ");}
if (reserved) {printf("reserved ");}
printf(") at 0x%c\n", faulting_address); // FIXME: fix %c to %X
printf("PAGE FAULT\n");
}
uint32_t kmalloc(uint32_t sz) // FIXME
{
uint32_t tmp = placement_address;
placement_address += sz;
return tmp;
}
typedef struct page
{
uint32_t present : 1; // Page present in memory
uint32_t rw : 1; // Read-only if clear, readwrite if set
uint32_t user : 1; // Supervisor level only if clear
uint32_t accessed : 1; // Has the page been accessed since last refresh?
uint32_t dirty : 1; // Has the page been written to since last refresh?
uint32_t unused : 7; // Amalgamation of unused and reserved bits
uint32_t frame : 20; // Frame address (shifted right 12 bits)
} page_t;
typedef struct page_table
{
page_t pages[1024];
} page_table_t;
typedef struct page_directory
{
/**
Array of pointers to pagetables.
**/
page_table_t *tables[1024];
/**
Array of pointers to the pagetables above, but gives their *physical*
location, for loading into the CR3 register.
**/
uint32_t tablesPhysical[1024];
/**
The physical address of tablesPhysical. This comes into play
when we get our kernel heap allocated and the directory
may be in a different location in virtual memory.
**/
uint32_t physicalAddr;
} page_directory_t;
uint32_t page_directory[1024] __attribute__((aligned(4096)));
uint32_t first_page_table[1024] __attribute__((aligned(4096)));
extern void loadPageDirectory(unsigned int*);
extern void enable_paging();
//extern heap_t *kheap;
uint32_t kmalloc_ap(uint32_t sz, uint32_t *phys)
{
if (placement_address & 0xFFFFF000) // If the address is not already page-aligned
{
// Align it.
placement_address &= 0xFFFFF000;
placement_address += 0x1000;
}
if (phys)
{
*phys = placement_address;
}
uint32_t tmp = placement_address;
placement_address += sz;
return tmp;
}
// Macros used in the bitset algorithms.
#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))
// Static function to set a bit in the frames bitset
static void set_frame(uint32_t frame_addr)
{
uint32_t frame = frame_addr/0x1000;
uint32_t idx = INDEX_FROM_BIT(frame);
uint32_t off = OFFSET_FROM_BIT(frame);
frames[idx] |= (0x1 << off);
}
// Static function to clear a bit in the frames bitset
static void clear_frame(uint32_t frame_addr)
{
uint32_t frame = frame_addr/0x1000;
uint32_t idx = INDEX_FROM_BIT(frame);
uint32_t off = OFFSET_FROM_BIT(frame);
frames[idx] &= ~(0x1 << off);
}
// Static function to find the first free frame.
static uint32_t first_frame()
{
uint32_t i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); i++)
{
if (frames[i] != 0xFFFFFFFF) // nothing free, exit early.
{
// at least one bit is free here.
for (j = 0; j < 32; j++)
{
uint32_t toTest = 0x1 << j;
if ( !(frames[i]&toTest) )
{
return i*4*8+j;
}
}
}
}
}
void alloc_frame(page_t *page, int is_kernel, int is_writeable)
{
if (page->frame != 0)
{
return; // Frame was already allocated, return straight away.
}
else
{
uint32_t idx = first_frame(); // idx is now the index of the first free frame.
if (idx == (uint32_t)-1)
{
// PANIC is just a macro that prints a message to the screen then hits an infinite loop.
printf("No free frames!");
}
set_frame(idx*0x1000); // this frame is now ours!
page->present = 1; // Mark it as present.
page->rw = (is_writeable)?1:0; // Should the page be writeable?
page->user = (is_kernel)?0:1; // Should the page be user-mode?
page->frame = idx;
}
}
// Function to deallocate a frame.
void free_frame(page_t *page)
{
uint32_t frame;
if (!(frame=page->frame))
{
return; // The given page didn't actually have an allocated frame!
}
else
{
clear_frame(frame); // Frame is now free again.
page->frame = 0x0; // Page now doesn't have a frame.
}
}
page_t *get_page(uint32_t address, int make, page_directory_t *dir)
{
// Turn the address into an index.
address /= 0x1000;
// Find the page table containing this address.
uint32_t table_idx = address / 1024;
if (dir->tables[table_idx]) // If this table is already assigned
{
return &dir->tables[table_idx]->pages[address%1024];
}
else if(make)
{
uint32_t tmp;
dir->tables[table_idx] = (page_table_t*)kmalloc_ap(sizeof(page_table_t), &tmp);
memset(dir->tables[table_idx], 0, 0x1000);
dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US.
return &dir->tables[table_idx]->pages[address%1024];
}
else
{
return 0;
}
}
void init_paging() {
// The size of physical memory. For the moment we
// assume it is 16MB big.
uint32_t mem_end_page = 0x1000000;
nframes = mem_end_page / 0x1000;
frames = (uint32_t*)kmalloc(INDEX_FROM_BIT(nframes));
memset(frames, 0, INDEX_FROM_BIT(nframes));
//set each entry to not present
int i;
for(i = 0; i < 1024; i++)
{
// This sets the following flags to the pages:
// Supervisor: Only kernel-mode can access them
// Write Enabled: It can be both read from and written to
// Not Present: The page table is not present
page_directory[i] = 0x00000002;
}
//we will fill all 1024 entries in the table, mapping 4 megabytes
for(i = 0; i < 1024; i++)
{
// As the address is page aligned, it will always leave 12 bits zeroed.
// Those bits are used by the attributes ;)
first_page_table[i] = (i * 0x1000) | 3; // attributes: supervisor level, read/write, present.
}
// attributes: supervisor level, read/write, present
page_directory[0] = ((unsigned int)first_page_table) | 3;
register_interrupt_handler(14, &page_fault);
loadPageDirectory(page_directory);
enable_paging();
// Initialise the kernel heap.
//kheap = create_heap(KHEAP_START, KHEAP_START+KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0);
}