Persistent paging/multitasking problem

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
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Persistent paging/multitasking problem

Post by Creature »

Hello,

For quite some time I've been having this problem now. At first I presumed it was my heap, which was coded quickly and not all too well. But now, after fully rewriting my kernel, I still have the same problem; as soon as I copy a page directory (to keep the kernel one consistent so I can enable multitasking later) and switch to that copy, my kernel triple faults (Bochs reports 3rd exception (14) with no resolution). I've checked my code over and over again, but it still doesn't work. I'm also using JamesM's tuts as a guide (I'm using the downloadable, mostly fixed, source-code instead of the on-site code). So I was hoping someone can stop some error somewhere that causes this strange issue.

Here's the code for respectively the CopyPhysicalFrame, ClonePageTable and ClonePageDirectory functions:

Code: Select all

; ========================================
; CopyPhysicalPage
;   - Copies the contents of the source page frame address
;	  to the destination page frame address.
; ========================================
[GLOBAL CopyPhysicalPage]

CopyPhysicalPage:
	push ebx					; Save the contents of EBX.
	pushf						; Push common registers (including EFLAGS, to re-enable interrupts).
	
	cli							; Disable interrupts.
	
	mov ebx, [ESP + 12]			; Grab source address (sourceAddress).
	mov ecx, [ESP + 16]			; Grab destination address (destAddress).
	
	mov edx, CR0				; |
	and edx, 0x7FFFFFFF			; |> Use CR0 to disable paging.
	mov CR0, edx				; |
	
	mov edx, 1024				; Make sure the loop runs 1024 times (using 4 bytes each time = 4096 bytes = 0x1000 bytes).
	
	.Loop:
		mov eax, [EBX]			; Grab 16-bits from the source address.
		mov [ECX], eax			; Put it in the 16-bits from the destination address.
		
		add ebx, 4				; Increment the source address with sizeof(dword)
		add ecx, 4				; Increment the destination address with sizeof(dword)
		
		dec edx					; Decrement edx (used by the loop).
		
		jnz .Loop				; If edx is not zero yet, start back at '.Loop:'.
		
	mov edx, CR0				; |
	or edx, 0x80000000			; |> Use CR0 to enable paging again.
	mov CR0, edx				; |
	
	popf						; Pop common registers (including EFLAGS, to re-enable interrupts).
	pop ebx						; Pop ebx (pushed in the beginning).
	ret							; All done!

Code: Select all

/*
 *	ClonePageTable
 *		Clones the specified page table.
 */
static PageTable *ClonePageTable(PageTable *source, unsigned *physAddr)
{
	/* Create a new page table (page-aligned). */
	PageTable *Ret = (PageTable *) MM->AllocAlignGetPhys(sizeof(PageTable), physAddr);
	MM->Set(Ret, 0, sizeof(PageTable));//For some reason, JamesM uses sizeof(PageDir) here.

	for(int i(0); i < 1024; ++i)
	{
		/* If there is a frame address available. */
		if(source->PageArray[i].FrameAddress)
		{
			/* Create a new frame, clone the flags and physically copy the data. */
			AllocateFrame(&Ret->PageArray[i], false, false);

			/*
			 * Use these if-statements because if a field should be clear,
			 * it must not have been written to. So only write if necessary.
			 */
			if(source->PageArray[i].Present)		Ret->PageArray[i].Present = 1;
			if(source->PageArray[i].ReadWrite)		Ret->PageArray[i].ReadWrite = 1;
			if(source->PageArray[i].UserMode)		Ret->PageArray[i].UserMode = 1;
			if(source->PageArray[i].Accessed)		Ret->PageArray[i].Accessed = 1;
			if(source->PageArray[i].Dirty)			Ret->PageArray[i].Dirty = 1;

			CopyPhysicalPage(source->PageArray[i].FrameAddress * HEAP_PAGESIZE, Ret->PageArray[i].FrameAddress * HEAP_PAGESIZE);
		}
	}

	return Ret;
}

Code: Select all

/*
 *	ClonePageDir
 *		Clones the specified paging directory.
 */
PageDir *ClonePageDir(PageDir *source)
{
	unsigned PhysAddr;

	/* Create a new page directory (page-aligned). */
	PageDir *Ret = (PageDir *) MM->AllocAlignGetPhys(sizeof(PageDir), &PhysAddr);
	MM->Set(Ret, 0, sizeof(PageDir));

	/* Grab the offset of 'TablesPhys' starting from the paging directory. */
	unsigned Offset = (unsigned) Ret->TablesPhys - (unsigned) Ret;

	/* This is the physical address of the tables. */
	Ret->Address = (PhysAddr + Offset);

	/*
	 * Search for the page table in the kernel directory, if
	 * it is already there, it does not need to be copied.
	 */
	for(int i(0); i < 1024; ++i)
	{
		/* Null pointer? Skip. */
		if(!source->PageTables[i])
			continue;

		/* Does the entry already exist in the kernel directory? */
		if(KernelDir->PageTables[i] == source->PageTables[i])
		{
			/* Point to that table. */
			Ret->PageTables[i] = source->PageTables[i];
			Ret->TablesPhys[i] = source->TablesPhys[i];
		}

		else
		{
			/* It isn't, copy the table. */
			unsigned PhysAddress;

			Ret->PageTables[i] = ClonePageTable(source->PageTables[i], &PhysAddress);
			Ret->TablesPhys[i] = PhysAddress | 0x07; /* 0x07 = Present, Read-Write and User-mode. */
		}
	}

	return Ret;
}
And last, but not least, my function that initializes paging.

Code: Select all

/*
 *	InitPaging
 *		Initializes paging.
 */
void InitPaging()
{
	/* Physical memory size, GRUB detects this in kB, we need bytes. */
	size_t MemSize = GRUB->HigherMem * 1024;

	FrameCount = MemSize / HEAP_PAGESIZE;
	Frames = (unsigned *) MM->Alloc(GET_ID_FROM_BIT(FrameCount));
	MM->Set(Frames, 0, GET_ID_FROM_BIT(FrameCount));

	/* Make a page directory. */
           
           //James also puts an 'unsigned phys' variable here which is never used?

	KernelDir = (PageDir *) MM->AllocAlign(sizeof(PageDir));
	MM->Set(KernelDir, 0, sizeof(PageDir));
	KernelDir->Address = (unsigned) KernelDir->TablesPhys;
	
	//CurrentDir = KernelDir;

	unsigned i;

	/* Map kernel heap pages, creating them if necessary. */
	for(i = HEAP_START; i < HEAP_START + HEAP_STARTSIZE; i += HEAP_PAGESIZE)
		GetPage(i, true, KernelDir);

	i = 0;

	/* Identity map. */
	while(i < CurAddress + HEAP_PAGESIZE)
	{
		AllocateFrame(GetPage(i, true, KernelDir), false, false);
		i += HEAP_PAGESIZE;
	}

	/* Allocate previously mapped pages (now that everything is identity mapped). */
	for(i = HEAP_START; i < HEAP_START + HEAP_STARTSIZE; i += HEAP_PAGESIZE)
		AllocateFrame(GetPage(i, true, KernelDir), false, false);

	/* Switch page directories and enable paging. */
	SwitchPageDir(KernelDir);

	/* Create the kernel heap. */
	KernelHeap = CreateHeap(HEAP_START, HEAP_START + HEAP_STARTSIZE, 0xCFFFF000, false, false);

	/* Clone the kernel directory, so it can remain constant. Then switch to the cloned directory. */
	CurrentDir = ClonePageDir(KernelDir);
	SwitchPageDir(CurrentDir); //<--- This is what causes the crash.
}
As you can see in the code, the very last line is causing the triple fault. I've tried about everything; numerous times of running over the code, replacing my code with JamesM's code, but nothing seems to work. I am using C++ in my kernel, which shouldn't be a problem, but maybe it's somehow interfering?

Thanks for your time and effort,
Creature
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Persistent paging/multitasking problem

Post by AJ »

Hi,

Have you got the Bochs register dump? Could you obtain it and post it here, please?

Cheers,
Adam
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Re: Persistent paging/multitasking problem

Post by Creature »

Here it is:

Code: Select all

00023207036i[CPU0 ] CPU is in protected mode (active)
00023207036i[CPU0 ] CS.d_b = 32 bit
00023207036i[CPU0 ] SS.d_b = 32 bit
00023207036i[CPU0 ] EFER   = 0x00000000
00023207036i[CPU0 ] | RAX=000000000000100c  RBX=00000000001045e0
00023207036i[CPU0 ] | RCX=0000000000000300  RDX=0000000000107000
00023207036i[CPU0 ] | RSP=0000000000067e3c  RBP=0000000000067e4c
00023207036i[CPU0 ] | RSI=000000000002bb7a  RDI=000000000002bb7b
00023207036i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00023207036i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00023207036i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00023207036i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00023207036i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00023207036i[CPU0 ] | SEG selector     base    limit G D
00023207036i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00023207036i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00023207036i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00023207036i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00023207036i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00023207036i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00023207036i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00023207036i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00023207036i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00023207036i[CPU0 ] | RIP=00000000001020ac (00000000001020ac)
00023207036i[CPU0 ] | CR0=0xe0000011 CR1=0x0 CR2=0x0000000000105600
00023207036i[CPU0 ] | CR3=0x0000100c CR4=0x00000000
00023207036i[CPU0 ] (instruction unavailable) page not present
00023207036e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Persistent paging/multitasking problem

Post by AJ »

Hi,

I think the problem must be with MM->AllocAlignGetPhys(). I assume this should return a page-aligned pointer, but look at your value of CR3 - 0x0000100c.

Cheers,
Adam
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Re: Persistent paging/multitasking problem

Post by Creature »

I suppose there should be something minor wrong, or JamesM has an error in his code.

This is the AllocAlignGetPhys member of the MM object's class:

Code: Select all

/*
 *	AllocAlignGetPhys
 *		Allocates memory, page-align it if necessary and retrieves the
 *		physical address of where the memory was allocated.
 */
void *MemoryManager::AllocAlignGetPhys(size_t size, unsigned *physAddr)
{
	return this->_AllocEx(size, true, physAddr);
}
Which calls the extended function:

Code: Select all

inline void *MemoryManager::_AllocEx(size_t _Size, bool _AlignAddress, unsigned *_PhysAddrStorage)
{
	if(KernelHeap)
	{
		void *Address = HeapAlloc(_Size, _AlignAddress, KernelHeap);

		if(_PhysAddrStorage)
		{
			Page *_Page = GetPage((unsigned) Address, false, KernelDir);
			(*_PhysAddrStorage) = _Page->FrameAddress * HEAP_PAGESIZE + ((unsigned) Address & 0xFFF);
		}

		return Address;
	}

	else
	{
		/* Is page-alignment on the address needed and isn't the address already aligned? */
		if(_AlignAddress && (CurAddress & 0xFFFFF000))
		{
			CurAddress &= 0xFFFFF000;
			CurAddress += HEAP_PAGESIZE;
		}

		/* Save the physical address into the pointer? */
		if(_PhysAddrStorage)
			(*_PhysAddrStorage) = CurAddress;

		/* Move to the next position and return where the pointer was. */
		CurAddress += _Size;
		return ((void *) (CurAddress - _Size));
	}

	return 0;
}
The first switch to the paging directory occurs with a paging directory not allocated with the heap, the second one is allocated with the heap, so the problem must lie within the 'if(KernelHeap)' statement. Even though this code is exactly JamesM's code. I checked it over again, but everything seems to be the same, maybe there is something wrong with this statement:

Code: Select all

(*_PhysAddrStorage) = _Page->FrameAddress * HEAP_PAGESIZE + ((unsigned) Address & 0xFFF);
That brings me to another question, why does it say '& 0xFFF' anyway?
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
User avatar
Steve the Pirate
Member
Member
Posts: 152
Joined: Fri Dec 15, 2006 7:01 am
Location: Brisbane, Australia
Contact:

Re: Persistent paging/multitasking problem

Post by Steve the Pirate »

Is your heap management based on JamesM's tutorials? I've seen this a lot in people's kernels who (like mine) have memory management heavily based on them, and I haven't been able to solve it. The problem is that when you try to get kmalloc to return a physical address, you don't get one - so when you try to set CR3, you're giving it an address that points nowhere, and then I suppose the processor can't find your code or IDT anymore - hence the triple fault. In mine, It's either this line in kmalloc_int doing something wrong:

Code: Select all

*phys = page->frame*0x1000 + (u32int)addr&0xFFF;
or possibly get_page not working properly.

EDIT: I see you've come to the same conclusion!
My Site | My Blog
Symmetry - My operating system.
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Re: Persistent paging/multitasking problem

Post by Creature »

Steve the Pirate wrote:Is your heap management based on JamesM's tutorials? I've seen this a lot in people's kernels who (like mine) have memory management heavily based on them, and I haven't been able to solve it. The problem is that when you try to get kmalloc to return a physical address, you don't get one - so when you try to set CR3, you're giving it an address that points nowhere, and then I suppose the processor can't find your code or IDT anymore - hence the triple fault. In mine, It's either this line in kmalloc_int doing something wrong:

Code: Select all

*phys = page->frame*0x1000 + (u32int)addr&0xFFF;
or possibly get_page not working properly.

EDIT: I see you've come to the same conclusion!
Yes, it's heavily based on JamesM's code and yes, I've come to the same conclusion :P.

Boy, ain't that sucky. It's probably that line of code since the 'else' part (when the kernel heap hasn't been created yet) works perfectly fine for creating page directories and switching. Also, when I DON'T use the heap to create the second page directory, it works fine I believe, so it MUST be that statement. The question is, what is wrong?

I remember that the frame address is always shifted 12 bits to the right, maybe I should try and experiment a bit with the statement. Unless anyone else has a solution to this problem.
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Re: Persistent paging/multitasking problem

Post by Creature »

Update: I've found something that fixes it, but I need your feedback on this if this is correct, I tried adding a statement that page-aligns the address retrieved by the heap if necessary. Is that correct?

Code: Select all

if(KernelHeap)
	{
		unsigned Address = (unsigned) HeapAlloc(_Size, _AlignAddress, KernelHeap);

		/* Is page-alignment on the address needed and isn't the address already aligned? */
		if(_AlignAddress && (Address & 0xFFFFF000))
		{
			Address &= 0xFFFFF000;
			Address += HEAP_PAGESIZE;
		}

		if(_PhysAddrStorage)
		{
			Page *_Page = GetPage((unsigned) Address, false, KernelDir);
			(*_PhysAddrStorage) = _Page->FrameAddress * HEAP_PAGESIZE + ((unsigned) Address & 0xFFF);
		}

		return (void *)Address;
	}
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Re: Persistent paging/multitasking problem

Post by Creature »

berkus wrote:You'll be trashing your heap structures that way. Unless you allocate at least one page extra from heap. Afaik, the heap in JamesM tuts already has means for page-aligning memory in malloc requests.
Yes, FIXED! You were wrong and right. You were right as far as you believed JamesM's HeapAlloc already took care of the page-aligning. So I went to investigate if the page-align if-statement inside the HeapAlloc function was actually being called. Guess what? It wasn't. You were also wrong because I wasn't trashing my heap, the statement I just put there was probably correcting the page-alignment the HeapAlloc function was skipping. So I went back to my old code, and it turned out to be this small, futile statement causing all the trouble:

Code: Select all

if(pageAlign && (OldHoleAddress & 0xFFFFF000 != 0))
Had to be:

Code: Select all

if(pageAlign && (OldHoleAddress & 0xFFFFF000))
Which stopped page-alignment from being perforrmed. Changing that fixed my issue.
Thanks for your help everyone.
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
User avatar
Steve the Pirate
Member
Member
Posts: 152
Joined: Fri Dec 15, 2006 7:01 am
Location: Brisbane, Australia
Contact:

Re: Persistent paging/multitasking problem

Post by Steve the Pirate »

Creature wrote:

Code: Select all

if(pageAlign && (OldHoleAddress & 0xFFFFF000))
Awesome! I'll give that a try as soon as possible. Wouldn't it be better C++ to say

Code: Select all

if(pageAlign && (OldHoleAddress & 0xFFFFF000 == 1))
though, because an if expects a boolean value (it will still work your way because true is usually equal to 1). I don't know if this is the case though, maybe I'm just too used to other languages that strictly enforce that (like C#)...
My Site | My Blog
Symmetry - My operating system.
Post Reply