Page 1 of 1

[SOLVED] Rewriting JamesM's Paging Code

Posted: Mon Feb 25, 2013 6:37 pm
by Marionumber1
After trying to use JamesM's code to help me implement paging in my kernel, I decided that it didn't completely fit my needs, so I decided to rewrite some portions of it. However, when I enable paging, there's an immediate triple fault.

I've looked at the disassembly, and it's definitely crashing on the line where paging gets enabled. Also, I've verified that the physical memory manager works and the page mapping function does what it's supposed to do to the page structure.

Code: Select all

/* A bitmap of used and free pages */
unsigned int *pmm_pages;
unsigned int num_pmm_pages;

/* Allocate a physical memory page */
unsigned int pmm_alloc_page()
{
	/* Find the first free page in physical memory */
	unsigned int i, j;
	for (i = 0; i < num_pmm_pages >> 5; i++)
	{
		for (j = 0; j < 32; j++)
		{
			/* If the bit is 0, set it to 1 and return the address */
			if (!bit_test(pmm_pages[i], j))
			{
				pmm_pages[i] = bit_set(pmm_pages[i], j);
				return (i << 17) + (j << 12);
			}
		}
	}
}

/* Free a physical memory page */
void pmm_free_page(unsigned int address)
{
	/* Find the bit that corresponds to the address and set it to 0 */
	pmm_pages[address >> 5] = bit_clear(pmm_pages[address >> 5], address % 32);
}

/* Initialize the physical memory manager */
void init_pmm(unsigned int size)
{
	/* Create the bitmap of used and free pages */
	num_pmm_pages = size / 0x1000;
	pmm_pages = (unsigned int*) kmalloc(num_pmm_pages >> 5);
}

Code: Select all

/* Map a virtual address to a physical address */
void map_page(page_directory_t *dir, unsigned int virtual_address, unsigned int physical_address, unsigned int flags)
{
	/* Return the page that corresponds to the virtual address, creating it if it doesn't already exist */
	page_t *page = get_page(dir, virtual_address, true, flags);

	/* Map the page in the table to the physical address */
	*((unsigned int*)page) = physical_address | flags | 0x01;
}

/* Unmap a virtual address */
void unmap_page(page_directory_t *dir, unsigned int virtual_address)
{
	/* Return the page that corresponds to the virtual address */
	page_t *page = get_page(dir, virtual_address, false, 0);

	/* If the page already does not exist, return */
	if (!page)
	{
		return;
	}

	/* Free the physical page and set the page to not present */
	pmm_free_page(page->frame * 0x1000);
	*((unsigned int*)page) = 0;

	/* Invalidate the TLB entry */
	asm volatile ("invlpg (%0)" :: "a" (virtual_address));
}

/* Map the kernel into a page directory */
void map_kernel(page_directory_t *dir)
{
	unsigned int i, j;

	/* If this is the kernel directory, we're initializing paging and need to allocate some pages from the physical memory manager */
	if (dir == kernel_directory)
	{
		/* We need to identity map our kernel */
		for (i = 0x100000; i < 0x400000; i += 0x1000)
		{
			map_page(dir, i, pmm_alloc_page(), 0x07);
		}
	}
}

/* Initialize paging and the kernel heap */
void init_vmm()
{
	/* Create the kernel directory */
	kernel_directory = (page_directory_t*) kmalloc_a(sizeof(page_directory_t));
	memset(kernel_directory, 0, sizeof(page_directory_t));
	kernel_directory->physicalAddr = (unsigned int)kernel_directory->tablesPhysical;

	unsigned int i;
	
	/* Identity map the first 1 MB of the address space, so that the VGA framebuffer and VM86 tasks will work */
	for (i = 0; i < 0x100000; i += 0x1000)
	{
		map_page(kernel_directory, i, pmm_alloc_page(), 0x07);
	}

	/* Map our kernel into the kernel directory */
	map_kernel(kernel_directory);

	/* Switch to the the kernel directory */
	switch_page_directory(kernel_directory);

	/* Now, enable paging! */
	unsigned int cr0;
   asm volatile("mov %%cr0, %0" : "=r"(cr0));
   cr0 |= 0x80000000;
   asm volatile("mov %0, %%cr0" :: "r"(cr0));
}

/* Switch the current page directory to a new one */
void switch_page_directory(page_directory_t *dir)
{
    current_directory = dir;
    asm volatile("mov %0, %%cr3" :: "r"(dir->physicalAddr));
}

Re: Rewriting JamesM's Paging Code

Posted: Tue Feb 26, 2013 1:18 am
by Combuster
If this code supposedly works, could you provide a logdump from Bochs? It typically provides some valuable hints as to what went wrong.

Re: Rewriting JamesM's Paging Code

Posted: Sat Mar 02, 2013 8:00 pm
by Marionumber1

Code: Select all

00079312147i[CPU0 ] | EAX=e0000011  EBX=00000000  ECX=0000001f  EDX=00113a43
00079312147i[CPU0 ] | ESP=0010d2e0  EBP=000000ff  ESI=0010f643  EDI=00100000
00079312147i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df IF tf SF zf af PF cf
00079312147i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00079312147i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00079312147i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00079312147i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00079312147i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00079312147i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00079312147i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00079312147i[CPU0 ] | EIP=001050c4 (001050c4)
00079312147i[CPU0 ] | CR0=0xe0000011 CR2=0x0010dd20
00079312147i[CPU0 ] | CR3=0x00110643 CR4=0x00000000
00079312147i[CPU0 ] 0x00000000001050c4: (instruction unavailable) page not present
00079312147p[CPU0 ] >>PANIC<< exception(): 3rd (14) exception with no resolution
From the looks of it, I'm sure that there was a page fault when it tried to get the next instruction as soon as I enabled paging.

Re: Rewriting JamesM's Paging Code

Posted: Sat Mar 02, 2013 8:18 pm
by Nessphoro
If you're still using a loop to find a bit: start again.

Re: Rewriting JamesM's Paging Code

Posted: Sun Mar 03, 2013 1:56 am
by Combuster
CR3=0x00110643
99% chance that's a bug. Page structures must be 4K aligned.

Re: Rewriting JamesM's Paging Code

Posted: Sun Mar 03, 2013 2:06 am
by iansjack
Surely page structures are 4K aligned whatever you put in the last 12 bits?, but I agree it looks suspect. If I were the OP I would use the degugger to examine the page directory and tables; clearly something is wrong with them.

Re: Rewriting JamesM's Paging Code

Posted: Sun Mar 03, 2013 2:19 am
by Combuster
iansjack wrote:Surely page structures are 4K aligned whatever you put in the last 12 bits?
The processor merely assumes that :wink: Also, there are special flags down in the bottom 12 bits you probably don't want to randomly touch.

I also hope that his kmalloc() can do better than 1-byte-alignment, otherwise SSE and performance is going to be a royal pain later as well.

Re: Rewriting JamesM's Paging Code

Posted: Sun Mar 03, 2013 2:43 am
by iansjack
I'd certainly agree with that. And, on reflection, I'm not sure that the value that you put in cr3 - the pointer to the tables rather than the table entries themselves - doesn't have to have the last 12 bits cleared. It's something I've never really thought about, and I haven't looked in the manual. The easiest way to try is to fire up a debugger and try putting a non-aligned value in cr3.

The fact that in the OP's case the value in cr3 doesn't end "000" is definitely suspicious.

Edit: Oh, right. Now I've had a moment to look at the OP's code. I don't know how his kmalloc() works, but what are the chances of:

kernel_directory = (page_directory_t*) kmalloc_a(sizeof(page_directory_t));

ending up as a pointer to the start of a page? 1 chance in 4096 presumably. Once that's wrong the whole house of cards falls over. I use an AllocPage() function which is guaranteed to return a page-aligned pointer to physical memory. kmalloc() should - IMO - be reserved for more general usage. Allocating physical memory pages is a very special case; kmalloc() will surely return logical addresses. Unless the whole of kernel memory is identity mapped that is going to be a very quick road to an instant page fault.

Re: Rewriting JamesM's Paging Code

Posted: Sun Mar 03, 2013 9:43 am
by Marionumber1
I managed to fix the problem. It turned out that the macro I was using the page align addresses wasn't working, so I fixed it. Now paging gets enabled fine and the kernel heap allocations work too. Thanks to everyone who helped.

Also, before the kernel heap is created (which gets enabled after paging is enabled), kmalloc allocates starting from the end of the kernel, using the 'end' symbol in the linker script. kmalloc_a is used to allocate on a page aligned memory address.