It hasn't been going too well - I tried tossing everything and tried to reimplement a standard two-level paging system, which led to a dead end. So now I'm dealing with 4MB pages and things seem to have gone carrot-shaped as well. The kernel's loaded, and the moment I enable the paging bit in cr0 - boom, triple-fault. Best-case scenario, I've misread the intel docs on 4MB paging and something's pearshaped with my struct; I'll need fresh eyes for that, though. Worst-case - I've thoroughly misunderstood something about C++, which happens more often than not.
Some searching on the forum says you'll want a Bochs log so that's up at http://pastebin.com/79tiUvb7. It indicates a GPF, but I'm not sure what to make of *that*.
This is the crux of the paging code - the equivalent of the "kernel_main()" that the bootloader jumps into calls install_paging() in order to set things up. Hopefully it's pretty vanilla stuff, except written with some higher-level boilerplate. You'll see I've stolen some bits from JamesM as well...
paging.h
Code: Select all
namespace stage1 {
// Setup - for the kernel, remember, we'll swap this out for user processes via the TSS if needed -
// page directory, page tables, and page frames somewhere where the sun don't shine
// and then enable paging.
void installPaging();
// The page directory (to 4MB pages).
// We use two-step construction here, because a significant amount of memory is required for
// these tables so we can't just (1) first implicitly construct and then (2) re-assign.
class page_directory {
class entry {
u32int inMemoryP : 1; // Page present in memory
u32int writeableP : 1; // Read-only if clear, readwrite if set
u32int userAccessibleP : 1; // Supervisor level only if clear
u32int unused0 : 2; // PWT and PCD, which we conveniently ignore
u32int accessedP : 1; // Has the page been accessed since last refresh?
u32int dirtyP : 1; // Has the page been written to since last refresh?
u32int always1 : 2; // Nominally the "page size" bit and "G" bit, but is always 1 since we use 4MB pages.
u32int unused1 : 3; // Ignored bytes.
u32int always0 : 1; // PAT index - always 0.
u32int unused2 : 9; // The highest 4 bits of the address... we ignore.
u32int pageFrameAddr : 10; // The next least-significant 10 bits of the addres... remember we lop off the other LSBs.
public:
entry():
inMemoryP(0), writeableP(0), userAccessibleP(0), unused0(0), accessedP(0),
dirtyP(0), always1(3), unused1(0), always0(0), unused2(0), pageFrameAddr(0) {};
// Initialize as inaccessible.
void initAsInaccessible() {
inMemoryP = 0;
};
// Initalize this frame as an identity mapped page - for the kernel, you see, since we can't
// retcon all our pointers.
void initAsKernelPage(u32int newTableAddr) {
inMemoryP = 1; writeableP = 1; userAccessibleP = 1; pageFrameAddr = newTableAddr;
}
// Perhaps add another initializer that allocates from the pagetable allocator.
} __attribute__((packed));
entry entries[1024];
public:
// Identity-map the first 4MB.
void init();
} __attribute__((aligned (4096), packed));
}
Code: Select all
#include "paging.h"
using namespace stage1;
static page_directory our_page_directory;
void stage1::installPaging() {
our_page_directory.init();
// page directory swapping implementation copied off JamesM.
asm volatile("mov %0, %%cr3":: "r"(&our_page_directory));
u32int cr0;
asm volatile("mov %%cr0, %0": "=r"(cr0));
cr0 |= 0x80000000; // Enable paging!
asm volatile("mov %0, %%cr0":: "r"(cr0));
}
void page_directory::init() {
entries[0].initAsKernelPage(0);
}