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
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Paging

Post by Matt1223 »

Hi, I try to enable paging but it doesn't work. When I enable it system just stops. It doesn't even print info about what interrupt caused the system to stop although it should.

My kernel is loaded at 0x400000 so when initializing paging I'm identity mapping two first page Directories (8MiB).

That's the code I try to enable paging with. paging_init() is called:

Code: Select all

typedef uint32_t PTE; // Page Table Entry
typedef uint32_t PDE; // Page Directory Entry

// page table
struct pTable
{
	PTE entries[1024];
} __attribute__((packed)); // make sure it's not alligned by compiler

typedef struct pTable pTable;
 
// page directory
struct pDirectoryTable
{
	PDE entries[1024];
} __attribute__((packed)); // make sure it's not alligned by compiler

typedef struct pDirectoryTable pDirectoryTable;
// ...

static void paging_set_pDirectoryTable( pDirectoryTable *dir )
{
	Paging.currPDirectoryTable = dir;

	__asm("mov %0, %%cr3" : : "r" (dir));
}

static void enable_paging()
{
	uint32_t cr0;
	__asm("mov %%cr0, %0" : "=r" (cr0));
	cr0 |= 0x80000001;
	__asm("mov %0, %%cr0" : : "r" (cr0));

	/*__asm("mov %%cr0, %%eax" : : : "%eax");
	__asm("or $0x80000000, %%eax" : : : "%eax");
	__asm("mov %%eax, %%cr0");*/
}

//_________________________________________________________________________________________

void paging_init()
{
	// 0-4 MiB (identity mapped)
	pTable *table1 = (pTable *)memory_alloc_block();
	if (!table1)
	{
		terminal_print(debugTerminal, "paging.c, paging_initialize(): initialization failed no space for Paging Table 1");
		return;
	}
	memsetk(table1, (uint8_t)0x00, sizeof(pTable));
 
	// 4 MiB - 8MiB (identity mapped)
	pTable *table2 = (pTable *)memory_alloc_block();
	if (!table2)
	{
		terminal_print(debugTerminal, "paging.c, paging_initialize(): initialization failed no space for Paging Table 2");
		return;
	}
	memsetk(table2, (uint8_t)0x00, sizeof(pTable));

	//! 1st 4mb are idenitity mapped
	for (int i=0, frame=0x0; i<1024; i++, frame+=4096)
	{
 		// create a new page
		PTE page = 0;
		PTE_set_adress(&page, frame);
		PTE_set_present_flag(&page, true); // present
		PTE_set_rw_flag(&page, true); // writable
		PTE_set_usm_flag(&page, false); // supervisor mode

		// ...and add it to the page table
		table1->entries[i] = page;
	}

	for (int i=0, frame=0x400000; i<1024; i++, frame+=4096)
	{
 		// create a new page
		PTE page = 0;
		PTE_set_adress (&page, frame);
		PTE_set_present_flag(&page, true); // present
		PTE_set_rw_flag(&page, true); // writable
		PTE_set_usm_flag(&page, false); // supervisor mode

		// ...and add it to the page table
		table2->entries[i] = page;
	}

	// create default directory table
	pDirectoryTable *dir = (pDirectoryTable *) memory_alloc_block(); // memory_alloc_block() returns 4096 alligned pointer
	if (!dir)
	{
		terminal_print(debugTerminal, "paging.c, paging_initialize(): initialization failed no space for Paging Directory Table");
		return;
	}
	memsetk(dir, 0, sizeof (pDirectoryTable));

	PDE *entry = &dir->entries[0];
	PDE_set_adress(entry, (uint32_t)table1);
	PDE_set_present_flag(entry, true);
	PDE_set_rw_flag(entry, true);
	PDE_set_usm_flag(entry, false); // supervisor mode

	entry = &dir->entries[1];
	PDE_set_adress(entry, (uint32_t)table2);
	PDE_set_present_flag(entry, true);
	PDE_set_rw_flag(entry, true);
	PDE_set_usm_flag(entry, false); // supervisor mode
 
	//! switch to our page directory
	paging_set_pDirectoryTable(dir);
 
	//! enable paging
	enable_paging();

	terminal_print(debugTerminal, "Paging initialized and enabled!\n");
}
I don't know what's the problem can you help me with that?

Also, I try to compile this function but it gives an error. Do you know what's the problem with this assembly?

Code: Select all

void paging_flush_tlb_entry( uint32_t addr )
{
	disable_interrupts();
	__asm("invlpg %0" : : "r"(addr));
	enable_interrupts();
}
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: Paging

Post by Octocontrabass »

Matt1223 wrote:I don't know what's the problem can you help me with that?
Have you tried stepping through the code in a debugger? Be sure to try stepping individual instructions, not just lines of C code.

If the CPU is throwing an exception, you might have better luck running it under a virtual machine that can log CPU exceptions, such as QEMU with the "-d int" option.
Matt1223 wrote:Do you know what's the problem with this assembly?
The INVLPG instruction requires a memory operand, but you're trying to use a register operand. Also, it's a good idea to use (u)intptr_t for an integer that needs to be the size of a pointer. For example:

Code: Select all

void paging_flush_tlb_entry( uintptr_t addr )
{
	__asm("invlpg %0" : : "m"(*(uint8_t *)addr));
}
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Paging

Post by Matt1223 »

Sorry for such a delay but I didn't have the time recently to work on it.
Octocontrabass wrote: Have you tried stepping through the code in a debugger? Be sure to try stepping individual instructions, not just lines of C code.
I don't really know how to debug kernel. I know there is bochsdbg but I have no idea how to gain control just before enabling paging.
Octocontrabass wrote: The INVLPG instruction requires a memory operand, but you're trying to use a register operand. Also, it's a good idea to use (u)intptr_t for an integer that needs to be the size of a pointer. For example:

Code: Select all

void paging_flush_tlb_entry( uintptr_t addr )
{
	__asm("invlpg %0" : : "m"(*(uint8_t *)addr));
}
Thank you that helped!


I have some news about the error. It looks like calling some functions - for example, memory_alloc_block() - right before calling enable_paging() causes this crush., but some functions called after them can prevent the error from happening. For example, calling terminal_print(...) before enable_paging() eliminates the error:

Code: Select all

paging_set_pDirectoryTable(dir);

terminal_print(debugTerminal, "It makes it doesn't crush\n");

enable_paging();
However, the error doesn't occur, the system behaves as if paging hasn't been enabled. That means writing to virtual memory that hasn't been mapped to physical memory doesn't throw page fault. Weird, right?
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: Paging

Post by Octocontrabass »

Matt1223 wrote:I don't really know how to debug kernel.
Sounds like it's time for you to learn. Here's a good starting point. Be sure to look at the section about using debuggers with VMs.
Matt1223 wrote:I know there is bochsdbg but I have no idea how to gain control just before enabling paging.
Set a breakpoint. Since you're using Bochs, you can embed a magic breakpoint, but every debugger has ways to set breakpoints.
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Paging

Post by Matt1223 »

The problem was that

Code: Select all

__asm("mov %%cr0, %0" : "=r" (cr0));
compiled to

Code: Select all

mov cr0, eax
insted of

Code: Select all

mov eax, cr0
It surprized me as I thought in gcc asm syntax
you give sorce at the beggining and then destiny so opposite to the intel syntax. However it looks like I was wrong.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Paging

Post by iansjack »

Matt1223 wrote: However it looks like I was wrong.
No, you were correct. However, AT&T syntax also mandate the use of '%' before register names; the disassembly you show doesn't have this, so must be Intel syntax.
Post Reply