Page 1 of 1

4MB page size problem

Posted: Thu Apr 19, 2007 3:48 am
by muisei
I decided to use 4MB pages for my kernel and wrote a simple test kernel before I break the original.
The problem is that it doesn't work.When I run the code in QEMU it exits with the following error "qemu: fatal: Trying to execute code outside RAM or ROM at 0x0010066c".When I tested it on a real PC it rebooted.The code is relocated at the first MB.
I've read the Intel's manuals and searched the web a coulpe of times but couldn't found anything related to this problem.Any clues what I'm missing?

Here are the functions I use to initialise the page dir:

Code: Select all

volatile struct pg_dir_t *kpg_dir=NULL;
for(i=0;i<PG_DIR_ENT_NUM;i++)
        {
                // Set page not present.
                kpg_dir[i].p=0;
                /* If the page is within the real memory then set it present.'i' shifted 22 bits left then divided by 1024 is equal to only 12 shifts left.
                  */
                if( mb_info->mem_upper>=(i<<12) )  kpg_dir[i].p=1;
                kpg_dir[i].r_w=1;
                kpg_dir[i].u_s=0;
                kpg_dir[i].pwt=1;
                kpg_dir[i].pcd=0;
                kpg_dir[i].a=0;
                kpg_dir[i].d=0;
                kpg_dir[i].ps=1;
                kpg_dir[i].g=0;
                kpg_dir[i].avl=0;
                kpg_dir[i].res_low=0;
                kpg_dir[i].res_high=0;
                // 'i' is the physical page address.
                kpg_dir[i].base_addr=i;
        };
Here is the code I use to start paging:

Code: Select all

  
u_int cr0=GET_CR0(),cr4=GET_CR4();
cr4|=CR4_PSE;
SET_CR4(cr4);
SET_CR3(kpg_dir);
cr0|=CR0_PG;
SET_CR0(cr0);
for(;;);
Here are the structures and macros used:

Code: Select all

struct pg_dir_t
{
	u_int	p:1;
	u_int	r_w:1;
	u_int	u_s:1;
	u_int	pwt:1;
	u_int	pcd:1;
	u_int	a:1;
	u_int	d:1;
	u_int	ps:1;
	u_int	g:1;
	u_int	avl:3;
	u_int	res_low:4;
	u_int	res_high:6;
	u_int	base_addr:10;
}__attribute__((__packed__));

#define PG_DIR_ENT_NUM 0x400

/**
 * Get and return CR0 register
 */
#define GET_CR0()\
({\
	u_int cr0;\
	__asm__ __volatile__("mov %0,%%cr0":"=r"(cr0):);\
	cr0;\
})

/**
 * Set CR0 register
 */
#define SET_CR0(cr0)\
({\
	__asm__ __volatile__("mov %%cr0,%0"::"r"((u_int)cr0));\
})

/**
 * Set CR3 register
 */
#define SET_CR3(cr3)\
({\
	__asm__ __volatile__("mov %%cr3,%0"::"r"((u_int)cr3));\
})

/**
 * Get and return CR4 register
 */
#define GET_CR4()\
({\
	u_int cr4;\
	__asm__ __volatile__("mov %0,%%cr4":"=r"(cr4):);\
	cr4;\
})

/**
 * Set CR4 register
 */
#define SET_CR4(cr4)\
({\
	__asm__ __volatile__("mov %%cr4,%0"::"r"((u_int)cr4));\
})


Posted: Thu Apr 19, 2007 5:38 am
by Combuster
several remarks:

Code: Select all

#define PG_DIR_ENT_NUM 0x100000
in 386 paging every table has 1024 entries (0x400)

Code: Select all

   u_int   res_low:4;
   u_int   res_high:6;
   u_int   base_addr:10;
this is just confusing use of reserved fields. the middle 10 bits (res_low, res_high) are needed when you are not using 4M pages, which you no doubt will need sometime. Also, with PSE36 part of the reserved area has gotten meaning.
using base_addr:20; would be better (if you want more of bitfields, you could try using an union)

The CR0/CR3/CR4 macro's are ugly as well. static inline functions would be better.

Posted: Thu Apr 19, 2007 7:42 am
by muisei
Combuster wrote:in 386 paging every table has 1024 entries (0x400)
I changed this but it still refuses to run.

Thanks for the remarks Combuster.

Posted: Thu Apr 19, 2007 8:17 am
by Combuster
"surface scan complete, starting mental emulation"

ehm i think your logic is flawed:

Code: Select all

                // Set page not present.
                kpg_dir[i].p=0;
                /* If the page is within the real memory then set it present.'i' shifted 22 bits left then divided by 1024 is equal to only 12 shifts left.
                  */
                if( mb_info->mem_upper>=(i<<12) )  kpg_dir[i].p=1; 
i << 12 equals i * 4kb. you are enabling pages as 4MB, causing you to map 4096 times too much memory...

also (considering you fixed i<<12 to i<<22), if you have 4MB of memory, then i=0 and i=1 would pass the test (4M >=0M, 4M >= 4M), so you end up mapping one page too much.

Also, you are dereferencing a null pointer. Is that done on purpose?

and then we come to the most likely cause:
mov %%cr3, %0
in AT&T syntax, the destination is located at the right, so instead of loading cr3, you are loading your page directory pointer with the undefined contents of cr3.
same way, set_cr0 actually does get_cr0 and the same goes for cr4.

Posted: Thu Apr 19, 2007 9:05 am
by muisei
I forgot to mention that I'm using "-masm=intel" optin of the GCC which enables the Intel's syntax.Sorry for misleading you.I've tested the macros with bochs'es debugger and they work correctly.

EDIT:"The pointer I'm dereferencing is pointing to the end of the kernel.There is a code which find the end of the kernel and set 'kpg_dir' to point to that location.I've put the declaration here for your convenience."

I set the present bit on all pages and I think It should work as long as I don't touch above the real mem.

Here is a picture of bochs with some useful debugging output.

Posted: Thu Apr 19, 2007 9:09 am
by Combuster
Page directory initialised at 0x00107109
Eeeh, that thing must be page-aligned :shock:

Posted: Thu Apr 19, 2007 9:23 am
by muisei
Now it works :P .Thank you very much Combuster.