Paging triplefaults real computer

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
Crazed123

Paging triplefaults real computer

Post by Crazed123 »

I had said, hurrah, hurrah, I've got paging working. The physical memory manager works (on a real machine, too), page table and directory are filled correctly, IRQs were disabled, paging was set and the segments changed after the IDT is set with the new code segment, IRQs are reenabled. Identity mapping is used at the correct times. Yay. Except that when I put this thing on a real computer it triplefaults.

Code: Select all

function BootstrapKernelPaging(): integer;
var
i: integer;
begin
{Set result to the default error code.}
BootstrapKernelPaging:= -1;
pPageDirectory:= nil;

{Get a page to be page directory.}
i:= AllocatePage();
if i > 0 then
 pPageDirectory:= PLongword(Longword(i * 4096 - Longword(@SegmentationVirtualBase)))
else
 begin
 {Set the page directory pointer to nil.  IMPORTANT.}
 pPageDirectory:= nil;
 Write(PChar('AllocatePage for the page directory failed with error '));
 WriteIntLn(i);
 {-2 means the page directory couldn't be allocated.}
 BootstrapKernelPaging:= -2;
 Exit;
 end;
{This is equivalent to pPageDirectory[all]:= SetPageEntry(0,false,false,false).}
FillDWord(pPageDirectory^,1024,0);

{Get another page to be the first page table.}
i:= AllocatePage();
if i > 0 then
 pPageTable:= PLongword(Longword(i * 4096 - Longword(@SegmentationVirtualBase)))
else
 begin
 {Free the page directory.  VERY IMPORTANT.}
 FreePage(Longword(pPageDirectory) div 4096);
 {Set the page directory pointer to nil.  IMPORTANT.}
 pPageDirectory:= nil;
 Write(PChar('AllocatePage for the first page table failed with error '));
 WriteIntLn(i);
 {-3 means the first page table couldn't be allocated.}
 BootstrapKernelPaging:= -3;
 Exit;
 end;
{This is equivalent to pPageTable[all]:= SetPageEntry(0,false,false,false).}
FillDWord(pPageTable^,1024,0);

{Set every page in the first page table to supervisor, writable and present.}
{This will map the first 4 MB of memory to its place.}
{For now it is assumed that 4 MB is enough to map the entire kernel area.}
i:= KernelZoneEnd + Longword(@SegmentationVirtualBase);
{Map video memory.}
pPageTable[$B8]:= SetPageEntry(Pointer($B8000),false,true,true);
{Address $100 = $100000 div $1000 (page).  The kernel's load point is the first page to get mapped.}
{BIOS and such aren't mapped because they break at the high addresses.}
for i:= $100 to (i div 4096) - 1 do
 pPageTable[i]:= SetPageEntry(Pointer(i * 4096),false,true,true);
{Map the page table and page directory to new locations since they were allocated dynamically.}
{These new locations will allow for planning of page table/directory and kernel heap locations.}
i:= KernelZoneEnd + (4096 - (KernelZoneEnd mod 4096)) + Longword(@SegmentationVirtualBase);
pPageTable[i div 4096]:= SetPageEntry(Pointer(i),false,true,true);
i:= i + 4096;
pPageTable[i div 4096]:= SetPageEntry(Pointer(i),false,true,true);

{Map that page table to virtual address $C0000000.}
i:= Longword(pPageTable) + Longword(@SegmentationVirtualBase);
{Address $300 = $C0000000 div 4096 (page) div 1024 (superpage) and superpage = page directory entry.}
pPageDirectory[$300]:= SetPageEntry(Pointer(i),false,true,true);
{Identity map the page table.  This keeps the kernel from crashing before the segment base can be changed.}
pPageDirectory[0]:= SetPageEntry(Pointer(i),false,true,true);

{Put the address of the page directory into the proper CPU register.}
i:= Longword(pPageDirectory) + Longword(@SegmentationVirtualBase);
SetPageDirectory(Pointer(i));

{If an IRQ fires while paging is on but before the IDT is refilled with exceptions and IRQ handlers there will be a triplefault, so disable IRQs.}
DisableInterrupts();

{Enable paging.  DANGEROUS.}
SetPaging(true);
{With paging enabled set these variables to their new virtual addresses.}
pPageDirectory:= PLongword(KernelZoneEnd + Longword(4096 - (KernelZoneEnd mod 4096)));
pPageTable:= pPageDirectory + 1024;

{The exceptions and IRQs in the IDT need to be bootstrapped for paging.}
BootstrapIDT($20);
BootstrapIRQs($20);

{Now that everything knows what the new environment is, switch the segment registers.}
SetSegmentRegisters($18,$20);

{Now that everything knows the new environment, reenable IRQs.}
EnableInterrupts();

{Now that the segments are changed there is no more need for identity mapping.}
{The page directory and page table must be mapped in their higher half places correctly for this assignment to work.}
pPageDirectory[0]:= 0;
WriteLn(PChar('Kernel now paged to virtual address $C010000.'));
BootstrapKernelPaging:= 1;
end;
BootstrapIDT() and BootstrapIRQs() merely reset the IDT with the new code segment, and other than that every function does what it says. This all works fine on Bochs, but on a real machine it triplefaults somewhere during this function.
Kim

Re:Paging triplefaults real computer

Post by Kim »

GDT is on top of paging, you will have to make sure the address loaded in the gdt base register is mapped in the address space.

Wouldn't it be easy if you had some pascal code linked at 1MB that setups paging and jumps to the real kernel. I did this and i found it very easy to get it working.

Tip you can rename sections:

Code: Select all

objcopy --rename-section .text=.inittext --rename-section .data=.initdata --rename-section .bss=.initbss ..\..\output\kernel\obj\stub\initstub.o ..\..\output\kernel\obj\stub\initstub.o

objcopy --rename-section .text=.inittext --rename-section .data=.initdata --rename-section .bss=.initbss ..\..\output\kernel\obj\init.o ..\..\output\kernel\obj\init.o
When you renamed then, just modify your ld script to link those sections to 1MB and your real kernel to 3GB. You never have to worry about some address translation.
Crazed123

Re:Paging triplefaults real computer

Post by Crazed123 »

When I reset the segment registers the base address loaded is $00000000, which thanks to identity mapping IS mapped in the address space.

I don't want to switch to all that because 1. I like having one page directory for the kernel rather than two, and 2. I like being able to use the physical MM for making paging stuff.
Crazed123

Re:Paging triplefaults real computer

Post by Crazed123 »

To aid in understanding of my code and helping me, I've translated the code into C.

Code: Select all

unsigned int *pPageDirectory,*pPageTable;
extern unsigned int SegmentationVirtualBase;

int BootstrapKernelPaging()
{
/* Set the result to the default error code. */
int result = -1;
pPageDirectory = NULL;

/* Get a page to be page directory */
int i = AllocatePage();
if (i > 0)
 /* The use of SegmentationVirtualBase is for converting a physical address to virtual.*/
 pPageDirectory = (i * 4096) - &SegmentationVirtualBase;
else
 {
 /* Set the page directory pointer to NULL.  IMPORTANT. */
 pPageDirectory = NULL;
 Write("AllocatePage() for the page directory failed with error ");
 WriteIntLn(i);
 /* -2 means the page directory couldn't be allocated. */
 result = -2;
 return result;
 };
/* This is equivalent to pPageDirectory[all]:= SetPageEntry(0,false,false,false). */
memset(pPageDirectory,0,1024);

/* Get another page to be the first page table. */
i = AllocatePage();
if (i > 0)
 pPageTable = (i * 4096) - &SegmentationVirtualBase;
else
 {
 /* Free the page directory. VERY IMPORTANT. */
 FreePage((unsigned int)pPageDirectory / 4096);
 /* Set the page directory pointer to NULL.  IMPORTANT. */
 pPageDirectory = NULL;
 Write("AllocatePage() for the first page table failed with error ");
 WriteIntLn(i);
 /* -3 means the first page table couldn't be allocated. */
 result = -3;
 return result;
 };
/* This is equivalent to pPageTable[all]:= SetPageEntry(0,false,false,false). */
memset(pPageTable,0,1024);

/* Set every page in the first page table to supervisor, writable and present. */
/* This will map the first 4 MB of memory to its place. */
/* For now it is assumed that 4 MB is enough to map the entire kernel area. */
/* "unsigned long KernelZoneEnd;" is a pointer stored as a longword that points to the end of the kernel's code/data/whatnot. */
i = KernelZoneEnd +(unsigned int)&SegmentationVirtualBase;
/* Map video memory. */
pPageTable[0xb8] = SetPageEntry(0xB8000,false,true,true);
/* Address $100 = $100000 div $1000 (page).  The kernel's load point is the first page to get mapped. */
/* BIOS and such aren't mapped because they break at the high addresses. */
for(i=0x100;i<(i div 4096);i++)
 pPageTable[i] = SetPageEntry(i * 4096,false,true,true);
/* Map the page table and page directory to new locations since they were allocated dynamically. */
/* These new locations will allow for planning of page table/directory and kernel heap locations. */
i = KernelZoneEnd + (4096 - (KernelZoneEnd % 4096)) +  (unsigned int)&SegmentationVirtualBase);
pPageTable[i / 4096] = SetPageEntry((void*)i,false,true,true);
i += 4096;
pPageTable[i / 4096] = SetPageEntry((void*)i,false,true,true);

/* Map that page table to virtual address $C0000000. */
i = (unsigned int)pPageTable +(unsigned int)&SegmentationVirtualBase;
/* Address 0x300 = 0xC0000000 / 4096 (page) / 1024 (superpage) and superpage = page directory entry. */
/* Yes, the term "superpage" is one of convenience only.  It might also refer to a 4MB page. */
pPageDirectory[0x300] = SetPageEntry((void*)i,false,true,true);
/* Identity map the page table.  This keeps the kernel from crashing before the segment base can be changed. */
pPageDirectory[0] = SetPageEntry((void*)i,false,true,true);


/* Put the address of the page directory into the proper CPU register. */
i = (unsigned int)pPageDirectory) + (unsigned int)&SegmentationVirtualBase;
SetPageDirectory((void*)i);

/* If an IRQ fires while paging is on but before the IDT is refilled with exceptions and IRQ handlers there will be a triplefault, so disable IRQs. */
DisableInterrupts();

/* Enable paging.  DANGEROUS. */
SetPaging(true);
/* With paging enabled set these variables to their new virtual addresses. */
pPageDirectory = (unsigned int*)(KernelZoneEnd +(unsigned int)(4096 - (KernelZoneEnd % 4096)));
pPageTable = pPageDirectory + 1024;

/* The exceptions and IRQs in the IDT need to be bootstrapped for paging. */
BootstrapIDT(0x20);
BootstrapIRQs(0x20);


/* Now that everything knows what the new environment is, switch the segment registers. */
/* void SetSegmentRegisters(unsigned short data,unsigned short code) */
SetSegmentRegisters(0x18,0x20);

/* Now that everything knows the new environment, reenable IRQs. */
/* This function is an assembly wrapper. */
EnableInterrupts();

/* Now that the segments are changed there is no more need for identity mapping. */
/* The page directory and page table must be mapped in their higher half places correctly for this assignment to work. */
pPageDirectory[0] = 0;
WriteLn("Kernel now paged to virtual address $C010000.");
result = 1;
return result;
};
I hope that satisfies all of you who for some reason can't read Pascal!
AR

Re:Paging triplefaults real computer

Post by AR »

I can't read either because both are frankly just ugly. Please use proper coding practice and indent.
Crazed123

Re:Paging triplefaults real computer

Post by Crazed123 »

I'm not going to get into an indentation flame war because I use spaces rather than tabs, which I think take up too much screen space and am not going to use just because every C programmer who can get his hands on a C Primer is willing to waste pixels on tab indentation.

In other words, I ain't doing it just 'cause you people do it.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Re:Paging triplefaults real computer

Post by Colonel Kernel »

Crazed123 wrote: I'm not going to get into an indentation flame war because I use spaces rather than tabs, which I think take up too much screen space and am not going to use just because every C programmer who can get his hands on a C Primer is willing to waste pixels on tab indentation.

In other words, I ain't doing it just 'cause you people do it.
Uhhh... this isn't a spaces-versus-tabs issue. That code is just about unreadable because there seems to be no indentation whatsoever. Without indentation, it's impossible to see the logical structure of code... as such, it isn't worth the bother to try and read.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Paging triplefaults real computer

Post by Pype.Clicker »

@crazed: when you got such long stuff to post, would you mind _attach_ it to your post rather than posting the whole stuff. For plenty of reasons, it's barely readable and almost everyone here feels like complaining rather than helping you.

So my advice for your triple fault problem would be to check out you don't assume too hard the memory you allocate is already filled with zeroes.

In other times, i'd have editted the stuff, put a decent attachement and wiped out the flames, but i unfortunately don't have the time for such stuff by now, so i just lock this. Feel free to redo from scratch, btw, if the problem still appear.
Post Reply