I tried to implement his paging tutorial (http://www.jamesmolloy.co.uk/tutorial_h ... aging.html) into my OS after reading it and the paging article on here. For some reason, my code is crashing right when enabling paging (filling the %cr0 register after ORing it).
Code: Select all
void switch_page_directory(page_directory_t *dir)
{
current_directory = dir;
_write_cr3(&(dir->tablesPhysical));
for(;;);
_enable_paging(); // PROBLEM HAPPENS HERE
}
Code: Select all
.globl _enable_paging
_enable_paging:
push %ebp
mov %esp, %ebp
mov %cr0, %eax
or $0x80000000, %eax
mov %eax, %cr0 // OS CRASHES HERE
pop %ebp
ret
Here is "paging.c"
Code: Select all
#include "lib/stdio.h"
#include "paging.h"
#include "kheap.h"
#include <stdint.h>
// The kernel's page directory
page_directory_t *kernel_directory=0;
// The current page directory;
page_directory_t *current_directory=0;
// A bitset of frames - used or free.
uint32_t *frames;
uint32_t nframes;
// Defined in kheap.c
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))
// 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;
}
}
}
}
}
void test_page_fault()
{
// Allocate a page of memory
void* page = kmalloc(0x1000);
// Map the page to virtual address 0x1000
page_t* page_table_entry = get_page(0x1000, 1, kernel_directory);
// Clear the present bit to trigger a page fault
clear_frame(page_table_entry->frame);
// Try to access the virtual address 0x1000
uint32_t* ptr = (uint32_t*)0x1000;
*ptr = 0xDEADBEEF;
}
// Function to allocate a frame.
void alloc_frame(page_t *page, int is_kernel, int is_writeable)
{
if (page->frame != 0)
{
return;
}
else
{
uint32_t idx = first_frame();
if (idx == (uint32_t)-1)
{
// PANIC! no free frames!!
}
set_frame(idx*0x1000);
page->present = 1;
page->rw = (is_writeable)?1:0;
page->user = (is_kernel)?0:1;
page->frame = idx;
}
}
// Function to deallocate a frame.
void free_frame(page_t *page)
{
uint32_t frame;
if (!(frame=page->frame))
{
return;
}
else
{
clear_frame(frame);
page->frame = 0x0;
}
}
void initialize_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));
// Let's make a page directory.
kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t));
current_directory = kernel_directory;
// We need to identity map (phys addr = virt addr) from
// 0x0 to the end of used memory, so we can access this
// 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);
}
void switch_page_directory(page_directory_t *dir)
{
current_directory = dir;
_write_cr3(&(dir->tablesPhysical));
for(;;);
_enable_paging();
}
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);
dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US.
return &dir->tables[table_idx]->pages[address%1024];
}
else
{
return 0;
}
}
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.
nanos_printf("Page fault! ( ");
if (present) {nanos_printf("present ");}
if (rw) {nanos_printf("read-only ");}
if (us) {nanos_printf("user-mode ");}
if (reserved) {nanos_printf("reserved ");}
nanos_printf(") at 0x%h\n", faulting_address);
PANIC("Page fault");
}
Code: Select all
#ifndef PAGING_H
#define PAGING_H
#include <stdint.h>
#include "idt.h"
#include "lib/stdio.h"
#include "lib/stdlib.h"
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;
/**
Sets up the environment, page directories etc and
enables paging.
**/
void initialize_paging();
/**
Causes the specified page directory to be loaded into the
CR3 register.
**/
void switch_page_directory(page_directory_t *new);
/**
Retrieves a pointer to the page required.
If make == 1, if the page-table in which this page should
reside isn't created, create it!
**/
page_t *get_page(uint32_t address, int make, page_directory_t *dir);
/**
Handler for page faults.
**/
void page_fault(registers_t regs);
/**
Tester to examine whether the page fault system is working
IS SOON TO BE REMOVED
**/
void test_page_fault();
/**
Stubs from paging_stubs.s
**/
extern void _write_cr0(uint32_t data);
extern void _write_cr3(uint32_t data);
extern uint32_t _read_cr0();
extern uint32_t _read_cr3();
extern void _enable_paging();
#endif
Code: Select all
ENTRY(_start)
SECTIONS
{
. = 1M;
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
/DISCARD/ : {
*(.comment)
*(.eh_frame)
*(.note*)
*(.iplt)
*(.igot)
}
end = .; _end = .; __end = .;
}