Triple fault on 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
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Triple fault on enabling paging

Post by velikiyv4 »

I'm trying to use paging in my OS and I've implemented the paging manager, but it crashes when I enable paging. I've googled it and the only clue I've found that it is a triple fault. I don't know what to do because everything seems correct to me. I'm sorry for providing so much code, but I don't know where is the problem. Here is the code:

paging.h

Code: Select all

/*
 * paging.h
 *
 *  Created on: Jul 2, 2016
 *      Author: alexander
 */

#ifndef INCLUDE_MEM_PAGING_H_
#define INCLUDE_MEM_PAGING_H_

typedef union {
	struct {
		uint16_t offset : 12;
		uint16_t table_entry : 10;
		uint16_t directory_entry : 10;
	} __attribute__((packed)) page_addr;
	uint32_t linear_addr;
} vaddr_t;

typedef struct {
	uint8_t present : 1;
	uint8_t read_write : 1;
	uint8_t user_supervisor : 1;
	uint8_t write_through : 1;
	uint8_t cache_disabled : 1;
	uint8_t accessed : 1;
} __attribute__((packed)) paging_common_flags_t;

typedef struct {
	struct {
		paging_common_flags_t common;
		uint8_t zero : 1;
		uint8_t page_size : 1;
		uint8_t ignored : 1;
	} flags;
	uint8_t available : 3;
	uint32_t page_table_addr : 20;
} __attribute__((packed)) page_directory_entry_t;

typedef struct {
	struct {
		paging_common_flags_t common;
		uint8_t dirty : 1;
		uint8_t zero : 1;
		uint8_t ignored : 1;
	} flags;
	uint8_t available : 3;
	uint32_t page_phys_addr;
} __attribute__((packed)) page_table_entry_t;

void init_paging();

void identity_page(void* addr, uint32_t frames, paging_common_flags_t flags);

void map_frame_range(vaddr_t virt_addr, void* phys_addr, uint32_t frames, paging_common_flags_t flags);

void map_page(vaddr_t virt_addr, void* phys_addr, paging_common_flags_t flags);

void* get_phys_addr(vaddr_t virt_addr);

#endif /* INCLUDE_MEM_PAGING_H_ */
paging.c

Code: Select all

/*
 * paging.c
 *
 *  Created on: Jul 2, 2016
 *      Author: alexander
 */

#include "general/includes.h"

page_directory_entry_t* page_directory;

void init_paging() {
	page_directory = kalloc(sizeof(page_directory_entry_t) * 1024, 0x1000); // kalloc() zeroes it
	paging_common_flags_t flags;
	flags.present = 1;
	flags.user_supervisor = 1;
	flags.read_write = 0;
	identity_page(0, HEAP_START / 0x1000, flags);
	flags.read_write = 1;
	identity_page((void*) HEAP_START, HEAP_SIZE / 0x1000, flags);
	__asm__(
			"movl %0, %%cr3\n"
			"movl %%cr0, %%eax\n"
			"orl $0x80000000, %%eax\n"
			"movl %%eax, %%cr0" :
			:
			"r"(page_directory)
			);
}

void identity_page(void* addr, uint32_t frames, paging_common_flags_t flags) {
	vaddr_t virt_addr;
	virt_addr.linear_addr = (uint32_t)(size_t) addr;
	map_frame_range(virt_addr, addr, frames, flags);
}

void map_frame_range(vaddr_t virt_addr, void* phys_addr, uint32_t frames, paging_common_flags_t flags) {
	for(uint32_t frame = 0; frame < frames; frame++) {
		map_page(virt_addr, (void*)((size_t) phys_addr + frame * 0x1000), flags);
		virt_addr.linear_addr += 0x1000;
	}
}

void map_page(vaddr_t virt_addr, void* phys_addr, paging_common_flags_t flags) {
	uint32_t pd_entry_ix = virt_addr.page_addr.directory_entry;
	page_directory_entry_t pd_entry = page_directory[pd_entry_ix];
	page_table_entry_t* page_table;
	if(!pd_entry.flags.common.present) {
		page_table_entry_t* table = kalloc(1024 * sizeof(page_table_entry_t), 0x1000);
		pd_entry.page_table_addr = (uint32_t)(size_t) table >> 12;
		pd_entry.flags.common.read_write = 1;
		pd_entry.flags.common.user_supervisor = 1;
		pd_entry.flags.common.present = 1;
		page_table = table;
		page_directory[pd_entry_ix] = pd_entry;
	} else {
		page_table = (void*)(size_t)(pd_entry.page_table_addr << 12);
	}
	uint32_t pt_entry_ix = virt_addr.page_addr.table_entry;
	page_table_entry_t pt_entry = page_table[pt_entry_ix];
	pt_entry.page_phys_addr = (uint32_t)(size_t) phys_addr >> 12;
	pt_entry.flags.common = flags;
	page_table[pt_entry_ix] = pt_entry;
}

void* get_phys_addr(vaddr_t virt_addr) {
	uint32_t pd_entry_ix = virt_addr.page_addr.directory_entry;
	page_directory_entry_t pd_entry = page_directory[pd_entry_ix];
	if(!pd_entry.flags.common.present) {
		return NULL;
	}
	page_table_entry_t* page_table = (void*)(size_t)(pd_entry.page_table_addr << 12);
	uint32_t pt_entry_ix = virt_addr.page_addr.table_entry;
	page_table_entry_t pt_entry = page_table[pt_entry_ix];
	if(!pt_entry.flags.common.present) {
		return NULL;
	}
	uint32_t phys_offset = pt_entry.page_phys_addr << 12;
	void* phys_addr = (void*)(size_t)(phys_offset + virt_addr.page_addr.offset);
	return phys_addr;
}
kalloc

Code: Select all


void* __heap = (void*) 2097152;

#define HEAP_SIZE (24 * 1048576)

void* kalloc(size_t size, size_t align) {
#ifdef DEBUG
    printf("Allocating %d bytes aligned by 0x%x, __heap = 0x%x\n", size, align, __heap);
#endif // DEBUG
    if(align != 0 && (size_t) __heap % align) {
	__heap += align - (size_t) __heap % align;
    }
    void* addr = __heap;
    __heap += size;
    memset(addr, 0x00, size);
    return addr;
}
I was trying to debug it, and this is what's happening before the reboot (I'm using QEMU which reboots on fault)

Code: Select all

; EAX contains 2097152 (page directory is the first thing that gets allocated on heap)
<0x10113C> MOV CR3, EAX
<0x10113F> MOV EAX, CR0
; EAX contains 0x11
<0x101142> OR EAX, 0x80000000
; EAX contains 0x80000011
<0x101147> MOV CR0, EAX
; (gdb) Cannot access memory at address 0x101193
If this matters, I load the kernel to 1M. Could you help me solve it?
You can see the full OS at https://github.com/velikiyv4/VV4OS, branch 'paging'.
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Re: Triple fault on enabling paging

Post by velikiyv4 »

Here is what QEMU prints on fault.

Code: Select all

Triple fault
CPU Reset (CPU 0)
EAX=80000011 EBX=00010000 ECX=00000007 EDX=000003d5
ESI=00000000 EDI=00000000 EBP=00109ccc ESP=00109cb4
EIP=0010114a EFL=00200286 [--S--P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00105180 00000027
IDT=     001051c0 000007ff
CR0=80000011 CR2=00105200 CR3=00200000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000010 CCD=80000011 CCO=LOGICL  
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Triple fault on enabling paging

Post by Octocontrabass »

Right before paging is enabled, examine your page tables in QEMU's memory. Are they correct? Do they identity-map the portion of code that enables paging?
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Re: Triple fault on enabling paging

Post by velikiyv4 »

Octocontrabass wrote:Right before paging is enabled, examine your page tables in QEMU's memory. Are they correct? Do they identity-map the portion of code that enables paging?
When I run get_phys_addr() with EIP value, it returns the same and when I manually navigate through tables using struct values (printing their members and doing necessary calculations) I also get the valid result. Seems like I either have invalid structs or I've forgotten to do something important. Any suggestions?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Triple fault on enabling paging

Post by Combuster »

You should check sizeof(page_directory_entry_t) and sizeof(page_table_entry_t) to see if they are what you expect.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Re: Triple fault on enabling paging

Post by velikiyv4 »

I've found an interesting moment: the faulting address is 0x00105200, and it's the IDT table (it actually starts at 0x001051C0). I think that descriptor tables have something to do with it.
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Re: Triple fault on enabling paging

Post by velikiyv4 »

Combuster wrote:You should check sizeof(page_directory_entry_t) and sizeof(page_table_entry_t) to see if they are what you expect.
Thank you! They both aren't DWORDs, but the former is 5 bytes and the latter is 7 bytes. I don't know how this happened yet, but I'll report if changing them fixes the problem. I think that's because I use nested bitfield structs that are expanded to byte size.
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Re: Triple fault on enabling paging

Post by velikiyv4 »

Combuster wrote:You should check sizeof(page_directory_entry_t) and sizeof(page_table_entry_t) to see if they are what you expect.
I've fixed them, and now the physical address of next instruction I get with get_phys_addr() is NULL, looks like I'll fix it soon.
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Re: Triple fault on enabling paging

Post by velikiyv4 »

Ehm... now the paging code gets mapped to some incorrect area, full of zeroes. But it has already made a lot of progress.
velikiyv4
Posts: 11
Joined: Tue Feb 16, 2016 11:38 am
Libera.chat IRC: jan_alesande

Re: Triple fault on enabling paging

Post by velikiyv4 »

Okay, I've found what the bug does. It somehow makes 8 frames point to one, instead of one-to-one mapping. I mean, the frames must be mapped like this:

Code: Select all

0x0000 -> 0x0000
0x1000 -> 0x1000
0x2000 -> 0x2000
...
And they are actually mapped like this:

Code: Select all

0x0000 -> 0x0000
0x1000 -> 0x0000
0x2000 -> 0x0000
...
0x7000 -> 0x0000
0x8000 -> 0x1000
0x9000 -> 0x1000
0xA000 -> 0x1000
...
I'll try to solve it today.
Post Reply