So far I've got quite a bit of work done. There's a timer, an IRQ system, an IDT, GDT, rudimentary allocation functions, screen output, etc. Right now I'm working on paging and, as you may have noticed from the topic, it's not going too well. I've uploaded a copy of my kernel's source code here. Here is a copy of the relevant code (or, as far as I've gathered):
paging.c
Code: Select all
/**
* Xettix operating system
* Copyright (c) 2008, Xettex Studios
*
* Created on 2008-03-01 by jeremiahzg
*/
/**
* Includes
*/
#include "paging.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 */
unsigned int *frames;
unsigned int nframes;
/* Defined in allocate.c */
extern unsigned int 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(unsigned int frame_addr)
{
unsigned int frame = frame_addr / 0x1000;
unsigned int idx = INDEX_FROM_BIT(frame);
unsigned int off = OFFSET_FROM_BIT(frame);
frames[idx] |= (0x01 << off);
}
/**
* Static function to clear a bit in the frames bitset
*/
static void clear_frame(unsigned int frame_addr)
{
unsigned int frame = frame_addr / 0x1000;
unsigned int idx = INDEX_FROM_BIT(frame);
unsigned int off = OFFSET_FROM_BIT(frame);
frames[idx] &= ~(0x01 << off);
}
/**
* Static function to test if a bit is set
*/
static unsigned int test_frame(unsigned int frame_addr)
{
unsigned int frame = frame_addr / 0x1000;
unsigned int idx = INDEX_FROM_BIT(frame);
unsigned int off = OFFSET_FROM_BIT(frame);
return (frames[idx] & (0x01 << off));
}
/**
* Static function to find the first free frame
*/
static unsigned int first_frame()
{
unsigned int idx, idx2;
for (idx = 0; idx < INDEX_FROM_BIT(nframes); idx++) {
if (frames[idx] != 0xFFFFFFFF) { /* Nothing free, exit early */
/* At least one bit is free here */
for (idx2 = 0; idx2 < 32; idx2++) {
unsigned int to_test = 0x01 << idx;
if (!(frames[idx] & to_test)) {
return idx * 4 * 8 + idx2;
}
}
}
}
return 0;
}
/**
* Function to allocate a frame
*/
void allocate_frame(page_t *page, int is_kernel, int is_writeable)
{
if (page->frame != 0) {
return;
} else {
unsigned int idx = first_frame();
if (idx == (unsigned int)-1) {
panic("No free frames", __FILE__, __LINE__);
}
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)
{
unsigned int frame;
if (!(frame = page->frame)) {
return;
} else {
clear_frame(frame);
page->frame = 0x0;
}
}
/**
* Causes the specified page directory to be loaded into the CR3 register
*/
void switch_page_directory(page_directory_t *dir)
{
current_directory = dir;
__asm__ __volatile__ ("mov %0, %%cr3" : : "r" (&dir->tables_phys));
unsigned int cr0;
__asm__ __volatile__ ("mov %%cr0, %0" : "=r" (cr0));
/* Okay, here's the "problem" line. When this is uncommented, Qemu throws a
triple page fault */
//cr0 |= 0x80000000; /* Enable paging! */
__asm__ __volatile__ ("mov %0, %%cr0" : : "r" (cr0));
}
/**
* 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(unsigned int address, int make, page_directory_t *dir)
{
/* Turn the address into an index */
address /= 0x1000;
/* Find the page table containing this address */
unsigned int 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) {
unsigned int tmp;
dir->tables[table_idx] = (page_table_t *)allocate_ap(sizeof(page_table_t), &tmp);
dir->tables_phys[table_idx] = tmp | 0x7; /* PRESENT, RW, US */
return &dir->tables[table_idx]->pages[address % 1024];
}
return 0;
}
/**
* Handler for page faults
*/
void page_fault(struct registers *r)
{
/* A page fault has occurred. The faulting address is stored in the CR2
register */
unsigned int faulting_address;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r" (faulting_address));
/* The error code gives us details of what happened */
int present = !(r->err_code & 0x01); /* Page not present */
int rw = r->err_code & 0x02; /* Write operation? */
int us = r->err_code & 0x04; /* Processor was in user-mode? */
int reserved = r->err_code & 0x08; /* Overwritten CPU-reserved bits of page entry? */
int id = r->err_code & 0x10; /* Caused by an instruction fetch? */
/* Output an error message */
char flags[64] = {0,};
if (present) {
format(flags, "%spresent ", flags);
}
if (rw) {
format(flags, "%sread-only ", flags);
}
if (us) {
format(flags, "%suser-mode ", flags);
}
if (reserved) {
format(flags, "%sreserved ", flags);
}
if (id) {
format(flags, "%sinstruction-fetch ", flags);
}
flags[strlen(flags) - 1] = 0;
char message[64];
format(message, "Page fault (%s) at 0x%X", flags, faulting_address);
panic(message, __FILE__, __LINE__);
}
/**
* Sets up the environment, page directories etc and enables paging
*/
void paging_init(unsigned int mem_end_page)
{
nframes = mem_end_page / 0x1000;
frames = (unsigned int *)allocate(INDEX_FROM_BIT(nframes));
memset(frames, 0, INDEX_FROM_BIT(nframes));
/* Let's make a page directory */
kernel_directory = (page_directory_t*)allocate_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: We use a while loop here deliberately. Inside the loop body we
actually change placement_address by calling allocate(). A while loop
causes this to be computed on-the-fly rather than once at the start */
int idx = 0;
while (idx < placement_address) {
/* Kernel code is readable but not writeable from userspace */
allocate_frame(get_page(idx, 1, kernel_directory), 0, 0);
idx += 0x1000;
}
/* Before we enable paging, we must register our page fault handler */
irq_install_handler(14, page_fault);
/* Now, enable paging! */
switch_page_directory(kernel_directory);
}
Code: Select all
/**
* Xettix operating system
* Copyright (c) 2008, Xettex Studios
*
* Created on 2008-03-01 by jeremiahzg
*/
#ifndef PAGING_H
#define PAGING_H
/**
* Includes
*/
#include <memory.h>
#include <assembly.h>
#include "panic.h"
#include "isr.h"
#include "irq.h"
/**
* Typedefs
*/
typedef struct page
{
unsigned int present : 1; /* Page present in memory */
unsigned int rw : 1; /* Read-only if clear, readwrite if set */
unsigned int user : 1; /* Supervisor level only if clear */
unsigned int accessed : 1; /* Has the page been accessed since last refresh? */
unsigned int dirty : 1; /* Has the page been written to since last refresh? */
unsigned int unused : 7; /* Amalgamation of unused and reserved bits */
unsigned int 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 */
unsigned int tables_phys[1024];
/* The physical address of tables_phys. This comes into play when we get
our kernel heap allocated and the directory may be in a different
location in virtual memory */
unsigned int physical_addr;
} page_directory_t;
/**
* Routines
*/
extern void switch_page_directory(page_directory_t *new);
extern page_t *get_page(unsigned int address, int make, page_directory_t *dir);
extern void page_fault(struct registers *r);
extern void paging_init(unsigned int mem_end_page);
#endif /* !PAGING_H */
Code: Select all
/**
* Causes the specified page directory to be loaded into the CR3 register
*/
void switch_page_directory(page_directory_t *dir)
{
current_directory = dir;
__asm__ __volatile__ ("mov %0, %%cr3" : : "r" (&dir->tables_phys));
unsigned int cr0;
__asm__ __volatile__ ("mov %%cr0, %0" : "=r" (cr0));
/* Okay, here's the "problem" line. When this is uncommented, Qemu throws a
triple page fault */
//cr0 |= 0x80000000; /* Enable paging! */
__asm__ __volatile__ ("mov %0, %%cr0" : : "r" (cr0));
}