Page 1 of 3

Paging Triple Fault

Posted: Sun Mar 02, 2008 12:57 am
by jzgriffin
I'm back and OSdev'ing again. This time my OS isn't as radically different from existing operating systems in hopes of not failing as epically as my past projects have. :-)

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);
}
paging.h

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 */
Even more relevant:

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));
}
Edit: Source code updated, proper panic() function

Posted: Sun Mar 02, 2008 2:06 am
by jtlb
hello, your post is quite huge so it's hard to figure out were your problem stands. Does placement_address refere to the begin or the end of your kernel? If it's the begin it means that you won't ever map your kernel. So when you enable paging, you willl get a page fault since the CPU does'nt see your code, since it won't find the handler's code you will get a double fault and then a triple fault.

good luck

Posted: Sun Mar 02, 2008 2:20 am
by jzgriffin
Thanks for the reply. :-)

placement_address refers to the end of the kernel; the last two lines of linker.ld:

Code: Select all

	end = .;
}

Posted: Sun Mar 02, 2008 2:55 am
by jtlb
Are your tables aligned to a 0x10000 boundaries? else i don't see, sorry. I took me about 6 month to write my memory manager so...

Posted: Sun Mar 02, 2008 3:06 am
by JamesM
Have you got the bochs error dump? The code looks to be taken mainly from my tutorials so I know it *should* work. :-)

Posted: Sun Mar 02, 2008 3:15 am
by jzgriffin
Yes, it is from your tutorial. :-)

And, no, I don't. Ubuntu's Bochs seems to be broken. Give me a few minutes to compile a new one.

Posted: Sun Mar 02, 2008 3:29 am
by jzgriffin
Okay, here's the contents of the Bochs log file:

Code: Select all

00000000000i[     ] Bochs x86 Emulator 2.3.6
00000000000i[     ]   Build from CVS snapshot, on December 24, 2007
00000000000i[     ] System configuration
00000000000i[     ]   processors: 1 (cores=1, HT threads=1)
00000000000i[     ]   A20 line support: yes
00000000000i[     ]   APIC support: yes
00000000000i[     ] CPU configuration
00000000000i[     ]   level: 5
00000000000i[     ]   TLB enabled: yes
00000000000i[     ]   SMP support: no
00000000000i[     ]   FPU support: yes
00000000000i[     ]   MMX support: yes
00000000000i[     ]   SSE support: no
00000000000i[     ]   CLFLUSH support: no
00000000000i[     ]   VME support: yes
00000000000i[     ]   3dnow! support: yes
00000000000i[     ]   PAE support: no
00000000000i[     ]   PGE support: no
00000000000i[     ]   PSE support: yes
00000000000i[     ]   x86-64 support: no
00000000000i[     ]   SEP support: no
00000000000i[     ]   MWAIT support: no
00000000000i[     ] Optimization configuration
00000000000i[     ]   Guest2HostTLB support: no
00000000000i[     ]   RepeatSpeedups support: no
00000000000i[     ]   Icache support: no
00000000000i[     ]   Trace cache support: no
00000000000i[     ]   Fast function calls: no
00000000000i[     ] Devices configuration
00000000000i[     ]   ACPI support: no
00000000000i[     ]   NE2000 support: yes
00000000000i[     ]   PCI support: yes
00000000000i[     ]   SB16 support: no
00000000000i[     ]   USB support: no
00000000000i[     ]   VGA extension support: vbe 
00000000000i[MEM0 ] allocated memory at 0x405ab008. after alignment, vector=0x405ac000
00000000000i[MEM0 ] 32.00MB
00000000000i[MEM0 ] rom at 0xfffe0000/131072 ('/usr/share/bochs/BIOS-bochs-latest')
00000000000i[MEM0 ] rom at 0xc0000/38400 ('/usr/share/bochs/VGABIOS-lgpl-latest')
00000000000i[APIC?] set APIC ID to 0
00000000000i[APIC0] 80586
00000000000i[APIC0] local apic in CPU apicid=00 initializing
00000000000i[IOAP ] initializing I/O APIC
00000000000i[IOAP ] set APIC ID to 1
00000000000i[MEM0 ] Register memory access handlers: fec00000-fec00fff
00000000000i[CMOS ] Using local time for initial clock
00000000000i[CMOS ] Setting initial clock to: Sun Mar  2 01:34:32 2008 (time0=1204450472)
00000000000i[DMA  ] channel 4 used by cascade
00000000000i[DMA  ] channel 2 used by Floppy Drive
00000000000i[FDD  ] fd0: 'xettix.img' ro=0, h=2,t=80,spt=18
00000000000i[PCI  ] 440FX Host bridge present at device 0, function 0
00000000000i[PCI  ] PIIX3 PCI-to-ISA bridge present at device 1, function 0
00000000000i[MEM0 ] Register memory access handlers: 000a0000-000bffff
00000000000i[VGA  ] interval=40000
00000000000i[MEM0 ] Register memory access handlers: e0000000-e07fffff
00000000000i[VGA  ] VBE Bochs Display Extension Enabled
00000000000i[     ] init_mem of 'harddrv' plugin device by virtual method
00000000000i[     ] init_mem of 'keyboard' plugin device by virtual method
00000000000i[     ] init_mem of 'serial' plugin device by virtual method
00000000000i[     ] init_mem of 'parallel' plugin device by virtual method
00000000000i[     ] init_mem of 'extfpuirq' plugin device by virtual method
00000000000i[     ] init_mem of 'speaker' plugin device by virtual method
00000000000i[     ] init_mem of 'pci_ide' plugin device by virtual method
00000000000i[     ] init_dev of 'harddrv' plugin device by virtual method
00000000000i[HD   ] Using boot sequence floppy, none, none
00000000000i[HD   ] Floppy boot signature check is enabled
00000000000i[     ] init_dev of 'keyboard' plugin device by virtual method
00000000000i[KBD  ] will paste characters every 1000 keyboard ticks
00000000000i[     ] init_dev of 'serial' plugin device by virtual method
00000000000i[SER  ] com1 at 0x03f8 irq 4
00000000000i[     ] init_dev of 'parallel' plugin device by virtual method
00000000000i[PAR  ] parallel port 1 at 0x0378 irq 7
00000000000i[     ] init_dev of 'extfpuirq' plugin device by virtual method
00000000000i[     ] init_dev of 'speaker' plugin device by virtual method
00000000000i[SPEAK] Failed to open /dev/console: Success
00000000000i[SPEAK] Deactivating beep on console
00000000000i[     ] init_dev of 'pci_ide' plugin device by virtual method
00000000000i[PCI  ] PIIX3 PCI IDE controller present at device 1, function 1
00000000000i[     ] register state of 'harddrv' plugin device by virtual method
00000000000i[     ] register state of 'keyboard' plugin device by virtual method
00000000000i[     ] register state of 'serial' plugin device by virtual method
00000000000i[     ] register state of 'parallel' plugin device by virtual method
00000000000i[     ] register state of 'extfpuirq' plugin device by virtual method
00000000000i[     ] register state of 'speaker' plugin device by virtual method
00000000000i[     ] register state of 'pci_ide' plugin device by virtual method
00000000000i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00000000000i[CPU0 ] cpu hardware reset
00000000000i[APIC0] local apic in CPU 0 initializing
00000000000i[     ] reset of 'harddrv' plugin device by virtual method
00000000000i[     ] reset of 'keyboard' plugin device by virtual method
00000000000i[     ] reset of 'serial' plugin device by virtual method
00000000000i[     ] reset of 'parallel' plugin device by virtual method
00000000000i[     ] reset of 'extfpuirq' plugin device by virtual method
00000000000i[     ] reset of 'speaker' plugin device by virtual method
00000000000i[     ] reset of 'pci_ide' plugin device by virtual method
00000003445i[BIOS ] $Revision: 1.193 $ $Date: 2007/12/20 18:12:11 $
00000317060i[KBD  ] reset-disable command received
00000335566i[BIOS ] Starting rombios32
00000336278i[BIOS ] ram_size=0x02000000
00000336794i[BIOS ] Found 1 cpu(s)
00000352266i[BIOS ] bios_table_addr: 0x000faff8 end=0x000fe05b
00000352340i[PCI  ] 440FX PMC write to PAM register 59 (TLB Flush)
00000680075i[PCI  ] 440FX PMC write to PAM register 59 (TLB Flush)
00001008522i[P2I  ] PCI IRQ routing: PIRQA# set to 0x0b
00001008571i[P2I  ] PCI IRQ routing: PIRQB# set to 0x09
00001008620i[P2I  ] PCI IRQ routing: PIRQC# set to 0x0b
00001008669i[P2I  ] PCI IRQ routing: PIRQD# set to 0x09
00001008684i[P2I  ] write: ELCR2 = 0x0a
00001009485i[BIOS ] PIIX3 init: elcr=00 0a
00001030312i[BIOS ] PCI: bus=0 devfn=0x00: vendor_id=0x8086 device_id=0x1237
00001033285i[BIOS ] PCI: bus=0 devfn=0x08: vendor_id=0x8086 device_id=0x7000
00001035726i[BIOS ] PCI: bus=0 devfn=0x09: vendor_id=0x8086 device_id=0x7010
00001036208i[PIDE ] new BM-DMA address: 0xc000
00001036962i[BIOS ] region 4: 0x0000c000
00001061037i[BIOS ] MP table addr=0x000fb0d0 MPC table addr=0x000fb000 size=0xd0
00001061114i[PCI  ] 440FX PMC write to PAM register 59 (TLB Flush)
00001311455i[VBIOS] VGABios $Id: vgabios.c,v 1.66 2006/07/10 07:47:51 vruppert Exp $

00001311526i[VGA  ] VBE known Display Interface b0c0
00001311558i[VGA  ] VBE known Display Interface b0c4
00001314483i[VBIOS] VBE Bios $Id: vbe.c,v 1.58 2006/08/19 09:39:43 vruppert Exp $
00001758742i[BIOS ] Booting from 0000:7c00
00014149461i[CPU0 ] CPU is in protected mode (active)
00014149461i[CPU0 ] CS.d_b = 32 bit
00014149461i[CPU0 ] SS.d_b = 32 bit
00014149461i[CPU0 ] | EAX=80000011  EBX=00020000  ECX=00000000  EDX=00100f37
00014149461i[CPU0 ] | ESP=00105f90  EBP=00105f90  ESI=0010b42c  EDI=0010788c
00014149461i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf SF zf af PF cf
00014149461i[CPU0 ] | SEG selector     base    limit G D
00014149461i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00014149461i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] | EIP=00100294 (00100294)
00014149461i[CPU0 ] | CR0=0x80000011 CR1=0 CR2=0x80000011
00014149461i[CPU0 ] | CR3=0x00109000 CR4=0x00000000
00014149461i[CPU0 ] >> add byte ptr ds:[eax], al : 0000
00014149461p[CPU0 ] >>PANIC<< exception(): 3rd (13) exception with no resolution
00014149461i[CPU0 ] CPU is in protected mode (active)
00014149461i[CPU0 ] CS.d_b = 32 bit
00014149461i[CPU0 ] SS.d_b = 32 bit
00014149461i[CPU0 ] | EAX=80000011  EBX=00020000  ECX=00000000  EDX=00100f37
00014149461i[CPU0 ] | ESP=00105f90  EBP=00105f90  ESI=0010b42c  EDI=0010788c
00014149461i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf SF zf af PF cf
00014149461i[CPU0 ] | SEG selector     base    limit G D
00014149461i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00014149461i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00014149461i[CPU0 ] | EIP=00100294 (00100294)
00014149461i[CPU0 ] | CR0=0x80000011 CR1=0 CR2=0x80000011
00014149461i[CPU0 ] | CR3=0x00109000 CR4=0x00000000
00014149461i[CPU0 ] >> add byte ptr ds:[eax], al : 0000
00014149461i[CMOS ] Last time is 1204450479 (Sun Mar  2 01:34:39 2008)
00014149461i[     ] restoring default signal behavior
00014149461i[CTRL ] quit_sim called with exit code 1
Edit: Removed extra message crap

Posted: Sun Mar 02, 2008 4:25 am
by JamesM
Hi,
exception(): 3rd (13) exception with no resolution
The interesting thing is that it's throwing a GPF (exception 13), not a page fault. I don't have time to help debug atm, but that should help you, I think.

Posted: Sun Mar 02, 2008 4:07 pm
by pcmattman
Actually, if I'm not mistaken it's probably throwing a page fault, then a double fault, and then a GPF (I've had similar problems) - you can find out for sure by enabling debug logging for the CPU device.

Posted: Sun Mar 02, 2008 5:04 pm
by jzgriffin
Right now I'm reorganizing and cleaning up a lot of the code. I removed the paging for now (entirely), but will add it again when I'm done, and try it with debugging for the CPU. I've already found several potential bugs and fixed them.

Thanks for the suggestions.

Posted: Sun Mar 02, 2008 7:46 pm
by jzgriffin
Done. Here's the log with CPU debugging enabled.

Could "PDE: entry not present" be the problem?

Posted: Mon Mar 03, 2008 12:17 am
by pcmattman
Have you made sure that the present bit (bit 0, 0x1) is set on all page directory and table entries?

Posted: Mon Mar 03, 2008 1:08 pm
by Combuster
Is it just me, or do I find it a strange coincidence that CR2 equals CR0 :?

Posted: Mon Mar 03, 2008 2:00 pm
by jzgriffin
pcmattman, no, I don't believe so; I'll try that now.
Combuster, that does seem a bit strange, doesn't it...

Posted: Fri Mar 07, 2008 8:04 am
by zaleschiemilgabriel
I'll bet the problem is the good ol' missing "cli" instruction before paging is enabled. :P