Kernel: Add working paging
This commit is contained in:
parent
01725789fd
commit
d5a4e32b35
|
@ -191,3 +191,24 @@ irq_common_stub:
|
||||||
add $0x8, %esp //Cleans up the pushed error code and pushed ISR number
|
add $0x8, %esp //Cleans up the pushed error code and pushed ISR number
|
||||||
sti
|
sti
|
||||||
iret
|
iret
|
||||||
|
|
||||||
|
.global loadPageDirectory
|
||||||
|
loadPageDirectory:
|
||||||
|
push %ebp
|
||||||
|
mov %esp, %ebp
|
||||||
|
mov 8(%esp), %eax
|
||||||
|
mov %eax, %cr3
|
||||||
|
mov %ebp, %esp
|
||||||
|
pop %ebp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global enable_paging
|
||||||
|
enable_paging: // FIXME
|
||||||
|
push %ebp
|
||||||
|
mov %esp, %ebp
|
||||||
|
mov %cr0, %eax
|
||||||
|
or $0x80000000, %eax
|
||||||
|
mov %eax, %cr0
|
||||||
|
mov %ebp, %esp
|
||||||
|
pop %ebp
|
||||||
|
ret
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "interrupts.c"
|
#include "interrupts.c"
|
||||||
//#include "timer.c"
|
//#include "timer.c"
|
||||||
#include "keyboard.c"
|
#include "keyboard.c"
|
||||||
//#include "paging.h"
|
#include "paging.h"
|
||||||
|
|
||||||
void kernel_early_main(void) {
|
void kernel_early_main(void) {
|
||||||
gdt_init();
|
gdt_init();
|
||||||
|
@ -16,9 +16,10 @@ void kernel_early_main(void) {
|
||||||
idt_init();
|
idt_init();
|
||||||
serial_printf("idt initialized!\n");
|
serial_printf("idt initialized!\n");
|
||||||
//init_timer(50); // Initialise timer to 50Hz
|
//init_timer(50); // Initialise timer to 50Hz
|
||||||
serial_printf("timer initialized!\n");
|
//serial_printf("timer initialized!\n");
|
||||||
//init_paging();
|
init_paging();
|
||||||
//serial_printf("paging initialized!\n");
|
serial_printf("paging initialized!\n");
|
||||||
terminal_initialize();
|
|
||||||
init_keyboard();
|
init_keyboard();
|
||||||
|
serial_printf("keyboard initialized!\n");
|
||||||
|
terminal_initialize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,13 +183,13 @@ void idt_init() {
|
||||||
|
|
||||||
void isr_handler(registers_t regs)
|
void isr_handler(registers_t regs)
|
||||||
{
|
{
|
||||||
printf("Received Interrupt: %s\n", regs.int_no); // TODO: Fix from %s to %X
|
|
||||||
serial_printf("Received interrupt: %s\n", regs.int_no); // TODO: Fix %s to %X
|
|
||||||
|
|
||||||
if (interrupt_handlers[regs.int_no] != 0)
|
if (interrupt_handlers[regs.int_no] != 0)
|
||||||
{
|
{
|
||||||
isr_t handler = interrupt_handlers[regs.int_no];
|
isr_t handler = interrupt_handlers[regs.int_no];
|
||||||
handler(regs);
|
handler(regs);
|
||||||
|
} else {
|
||||||
|
//printf("Unhandled interrupt: %c\n", regs.int_no); // FIXME: Fix from %s to %X
|
||||||
|
//serial_printf("Unhandled interrupt: %c\n", regs.int_no); // FIXME: Fix from %s to %X
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +209,8 @@ void irq_handler(registers_t regs)
|
||||||
{
|
{
|
||||||
isr_t handler = interrupt_handlers[regs.int_no];
|
isr_t handler = interrupt_handlers[regs.int_no];
|
||||||
handler(regs);
|
handler(regs);
|
||||||
|
} else {
|
||||||
|
//serial_printf("Unhandled IRQ interrupt: %c\n", regs.int_no); // FIXME: Fix from %s to %X
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@ void kernel_main(void) {
|
||||||
serial_printf("Newline test\n");
|
serial_printf("Newline test\n");
|
||||||
char * test_string = "test";
|
char * test_string = "test";
|
||||||
serial_printf("This is string from variable: %s\n", test_string);
|
serial_printf("This is string from variable: %s\n", test_string);
|
||||||
|
|
||||||
printf("Test finished success!\n");
|
printf("Test finished success!\n");
|
||||||
asm volatile("sti");
|
asm volatile("sti");
|
||||||
|
uint32_t *ptr = (uint32_t*)0xA0000000;
|
||||||
|
uint32_t do_page_fault = *ptr;
|
||||||
|
printf("%s", do_page_fault);
|
||||||
for(;;) {
|
for(;;) {
|
||||||
asm("hlt");
|
asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,98 +1,4 @@
|
||||||
#ifndef PAGING_H
|
#include <stdio.h>
|
||||||
#define PAGING_H
|
|
||||||
|
|
||||||
// A bitset of frames - used or free.
|
|
||||||
uint32_t *frames;
|
|
||||||
uint32_t nframes;
|
|
||||||
|
|
||||||
/*extern */uint32_t placement_address;
|
|
||||||
|
|
||||||
// Macros used in the bitset algorithms.
|
|
||||||
#define INDEX_FROM_BIT(a) (a/(8*4))
|
|
||||||
#define OFFSET_FROM_BIT(a) (a%(8*4))
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
uint32_t physicalAddr;
|
|
||||||
} page_directory_t;
|
|
||||||
|
|
||||||
uint32_t kmalloc(uint32_t sz)
|
|
||||||
{
|
|
||||||
uint32_t tmp = placement_address;
|
|
||||||
placement_address += sz;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t kmalloc_a(uint32_t sz)
|
|
||||||
{
|
|
||||||
uint32_t tmp = placement_address;
|
|
||||||
placement_address += sz;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
uint32_t kmalloc_a(uint32_t sz, int align)
|
|
||||||
{
|
|
||||||
if (align == 1 && (placement_address & 0xFFFFF000)) // If the address is not already page-aligned
|
|
||||||
{
|
|
||||||
// Align it.
|
|
||||||
placement_address &= 0xFFFFF000;
|
|
||||||
placement_address += 0x1000;
|
|
||||||
}
|
|
||||||
uint32_t tmp = placement_address;
|
|
||||||
placement_address += sz;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
uint32_t kmalloc_ap(uint32_t sz, int align, uint32_t *phys)
|
|
||||||
{
|
|
||||||
if (align == 1 && (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
//uint32_t kmalloc_a(uint32_t sz); // page aligned.
|
|
||||||
uint32_t kmalloc_p(uint32_t sz, uint32_t *phys); // returns a physical address.
|
|
||||||
//uint32_t kmalloc_ap(uint32_t sz, uint32_t *phys); // page aligned and returns a physical address.
|
|
||||||
//uint32_t kmalloc(uint32_t sz); // vanilla (normal).
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Handler for page faults.
|
Handler for page faults.
|
||||||
|
@ -116,170 +22,42 @@ void page_fault(registers_t regs){
|
||||||
if (rw) {printf("read-only ");}
|
if (rw) {printf("read-only ");}
|
||||||
if (us) {printf("user-mode ");}
|
if (us) {printf("user-mode ");}
|
||||||
if (reserved) {printf("reserved ");}
|
if (reserved) {printf("reserved ");}
|
||||||
printf(") at 0x%s\n", faulting_address);
|
printf(") at 0x%c\n", faulting_address); // FIXME: fix %c to %X
|
||||||
panic("PAGE FAULT");
|
printf("PAGE FAULT\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Retrieves a pointer to the page required.
|
uint32_t page_directory[1024] __attribute__((aligned(4096)));
|
||||||
If make == 1, if the page-table in which this page should
|
uint32_t first_page_table[1024] __attribute__((aligned(4096)));
|
||||||
reside isn't created, create it!
|
|
||||||
*/
|
extern void loadPageDirectory(unsigned int*);
|
||||||
page_t *get_page(uint32_t address, int make, page_directory_t *dir){
|
extern void enable_paging();
|
||||||
// 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), 1, &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() {
|
void init_paging() {
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
|
||||||
// The size of physical memory. For the moment we
|
//we will fill all 1024 entries in the table, mapping 4 megabytes
|
||||||
// assume it is 16MB big.
|
for(i = 0; i < 1024; i++)
|
||||||
uint32_t mem_end_page = 0x1000000;
|
{
|
||||||
|
// 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.
|
||||||
|
}
|
||||||
|
|
||||||
nframes = mem_end_page / 0x1000;
|
// attributes: supervisor level, read/write, present
|
||||||
frames = (uint32_t*)kmalloc(INDEX_FROM_BIT(nframes));
|
page_directory[0] = ((unsigned int)first_page_table) | 3;
|
||||||
memset(frames, 0, INDEX_FROM_BIT(nframes));
|
|
||||||
|
|
||||||
// Let's make a page directory.
|
register_interrupt_handler(14, &page_fault);
|
||||||
void* kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t));
|
|
||||||
memset(kernel_directory, 0, sizeof(page_directory_t));
|
|
||||||
void* current_directory = kernel_directory;
|
|
||||||
|
|
||||||
// We need to identity map (phys addr = virt addr) from
|
loadPageDirectory(page_directory);
|
||||||
// 0x0 to the end of used memory, so we can access this
|
enable_paging();
|
||||||
// transparently, as if paging wasn't enabled.
|
|
||||||
// NOTE that we use a while loop here deliberately.
|
|
||||||
// inside the loop body we actually change placement_address
|
|
||||||
// by calling kmalloc(). A while loop causes this to be
|
|
||||||
// computed on-the-fly rather than once at the start.
|
|
||||||
int i = 0;
|
|
||||||
while (i < placement_address)
|
|
||||||
{
|
|
||||||
// Kernel code is readable but not writeable from userspace.
|
|
||||||
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
|
|
||||||
i += 0x1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before we enable paging, we must register our page fault handler.
|
|
||||||
register_interrupt_handler(14, page_fault);
|
|
||||||
|
|
||||||
// Now, enable paging!
|
|
||||||
switch_page_directory(kernel_directory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Causes the specified page directory to be loaded into the
|
|
||||||
CR3 register.
|
|
||||||
*/
|
|
||||||
void switch_page_directory(page_directory_t *dir) {
|
|
||||||
void* current_directory = dir;
|
|
||||||
asm volatile("mov %0, %%eax":: "r"(&dir->tablesPhysical));
|
|
||||||
asm("mov %eax, %cr3;");
|
|
||||||
asm("mov %cr0, %eax;");
|
|
||||||
asm("or 0x80000000, %eax;");
|
|
||||||
asm("mov %eax, %cr0;");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 test if a bit is set.
|
|
||||||
static uint32_t test_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);
|
|
||||||
return (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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to allocate a frame.
|
|
||||||
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("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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
Loading…
Reference in New Issue