Triple fault when enabling paging.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
TylerH
Member
Member
Posts: 285
Joined: Tue Apr 13, 2010 8:00 pm
Contact:

Triple fault when enabling paging.

Post by TylerH »

I'm doing a higher half kernel and I'm at the step of setting up paging before changing to the normal GDT. I've printed and looked at a sample of my PD and PT and everything looks correct and all physical addresses are corrected for the 0xc0000000 virtual address difference. But when I set debug in mmap to false and remove the hlt before I enable paging, I get a triple fault. I've been trying to get this to work for a few hours now and would really appreciate some insight on what I'm doing wrong.

Here's the code:

Code: Select all

#include "vmem.h"
#include "kprint.h"

#include <stdint.h>

#define BASE_VADDR					0xc0000000
#define	PDE_ENTRIES					1024
#define	PTE_ENTRIES					1024
#define PAGE_TABLES					1024
typedef uint32_t pde_t;
typedef uint32_t pte_t;
typedef pde_t pd_t[PDE_ENTRIES];
typedef pte_t pt_t[PTE_ENTRIES];
typedef pt_t pt_arr[PDE_ENTRIES];

extern "C" pd_t _pd;
extern "C" pt_t _pt_arr[PAGE_TABLES];

template<typename T>
void inline clear_bit(T &block, int bit){
	block &= ~(T)(1u << bit);
}

template<typename T>
void inline set_bit(T &block, int bit){
	block |= 1u << bit;
}

template<typename T>
void inline set_bit(T &block, int bit, bool val){
	if(val)
		set_bit(block, bit);
	else
		clear_bit(block, bit);
}

uint32_t get_pd_index(void *vaddr){
	return (uint32_t)vaddr >> 22;
}

uint32_t get_pt_index(void *vaddr){
	return ((uint32_t)vaddr >> 12) & 0x3ff;
}

void clear_pde(pde_t &pde){
	pde = 0u;
}

void clear_pte(pte_t &pte){
	pte = 0u;
}

void clear_pd(pd_t pd){
	for(int i = 0;i < PDE_ENTRIES;i++)
		clear_pde(pd[i]);
}

void clear_pt(pt_t pt){
	for(int i = 0;i < PTE_ENTRIES;i++)
		clear_pte(pt[i]);
}

void set_pde_addr(uint32_t &pde, void *paddr){
	pde = (pde & 0xfffu) | ((uint32_t)paddr & ~0xfffu);
}

void set_pte_addr(uint32_t &pte, void *paddr){
	pte = (pte & 0xfffu) | ((uint32_t)paddr & ~0xfffu);
}

void inline set_pde_present(pde_t &pde, bool present){
	set_bit(pde, 0, present);
}

void inline set_pde_user(pde_t &pde, bool user){
	set_bit(pde, 2, user);
}

void inline set_pde_writable(pde_t &pde, bool writable){
	set_bit(pde, 1, writable);
}

void inline set_pte_present(pte_t &pte, bool present){
	set_bit(pte, 0, present);
}

void inline set_pte_user(pte_t &pte, bool user){
	set_bit(pte, 2, user);
}

void inline set_pte_writable(pte_t &pte, bool writable){
	set_bit(pte, 1, writable);
}


void mmap(void *vaddr, void *paddr, unsigned int length, 
		bool user, 
		bool write){
	int i = 0;
	while(length >= PAGESIZE){
		int pd_index = get_pd_index(vaddr);
		int pt_index = get_pt_index(vaddr);
		
		set_pde_present(_pd[pd_index], true);
		set_pde_user(_pd[pd_index], user);
		set_pde_writable(_pd[pd_index], write);
		
		set_pte_present(_pt_arr[pd_index][pt_index], true);
		set_pte_user(_pt_arr[pd_index][pt_index], user);
		set_pte_writable(_pt_arr[pd_index][pt_index], write);
		
		set_pte_addr(_pt_arr[pd_index][pt_index], paddr);
		
		bool debug = true;
		if(debug){
			kprintf("PDE: %x PD Index: %x\n", (unsigned int)_pd[pd_index], pd_index);
			kprintf("PTE: %x PT Index: %x\n", (unsigned int)_pt_arr[pd_index][pt_index], 
				pt_index);
		
			i++;
			if(i > 4) return;
		}
		
		length -= PAGESIZE;
		vaddr = (void*)((uintptr_t)vaddr + PAGESIZE);
		paddr = (void*)((uintptr_t)paddr + PAGESIZE);
	}
}

void load_pd(uintptr_t paddr){
	kprintf("PD Addr: %x ", (unsigned int)paddr);
	asm("hlt");
	asm(
		"mov eax, %0\n"
		"mov cr3, eax\n"
		"mov eax, cr0\n"
		"or  eax, 0x80000000\n"
		"mov cr0, eax\n"
	:: "r"(paddr));
	asm("hlt");
};

void init_vmem(){
	clear_pd(_pd);
	for(int i = 0;i < PAGE_TABLES;i++){
		uintptr_t pt_addr = (uintptr_t)&_pt_arr[i] + 0x40000000;
		clear_pt(_pt_arr[i]);
		set_pde_addr(_pd[i], (void*)pt_addr);
		//kprintf("PT: %x ", (unsigned int)pt_addr);
	}
	
	// Direct map first MB.
	mmap((void*)0, (void*)0, 0x100000, false, true);
	
	// Map 64 MB (ie kernel) from vaddr 0xc0000000 to paddr 0
	mmap((void*)0xc0000000, (void*)0, 0x4000000);
	
	load_pd((uintptr_t)_pd + 0x40000000);
}
The nm of related _pd and _pt_arr (so you can see that the physical addresses are actually correct, given it is loaded at 0x100000):

Code: Select all

c0b36000 B _pd
c0b37000 B _pt_arr
And the output showing aforementioned correct PDEs, PTEs, and PD address to be loaded into cr3:Image
User avatar
Velko
Member
Member
Posts: 153
Joined: Fri Oct 03, 2008 4:13 am
Location: Ogre, Latvia, EU

Re: Triple fault when enabling paging.

Post by Velko »

You're using Tim Robinson's GDT Trick, don't you? You did not mention that.

You also need to identity map the kernel (currently you identity map area only up to 1st MiB, not the area where kernel is).

After you enable paging, segmentation still works and still translates your addresses from high to low. But since nothing is mapped at
low address, you get a page fault.
If something looks overcomplicated, most likely it is.
TylerH
Member
Member
Posts: 285
Joined: Tue Apr 13, 2010 8:00 pm
Contact:

Re: Triple fault when enabling paging.

Post by TylerH »

Yeah, the GDT trick. And that fixes it! Oh man you have no idea how thankful I am! Do you have a BTC address? I'll send you .025 BTC (that sounds cheap considering the current price, but I bought at ~$1000/BTC like a dumb***.)
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: Triple fault when enabling paging.

Post by alexfru »

TylerH wrote:Do you have a BTC address?
I have two:

Code: Select all

label1: btc dword [var1], 1 ;)
var1 dd 0
Post Reply