Page 1 of 1

Can't Figure out Paging

Posted: Wed May 23, 2007 12:31 am
by pcmattman
I'm now trying to implement paging into my kernel, and it's turning out to be really difficult. ATM I have this code that should map 1:1 the first 8 MB of memory:

Code: Select all

// page directories and tables
ulong_t* pagedir = ( ulong_t* ) 0x9C000;
ulong_t* pagetab = ( ulong_t* ) 0x9D000;
ulong_t* pagetab2 = ( ulong_t* ) 0x9E000;

// initializes paging
void InitPaging()
{
	// necessary variables
	ulong_t address = 0;
	uint_t i;
	
	// map the first 4 MB, first 4K not available
	pagetab[0] = address | 2; // not present
	address += 4096;
	for( i = 1; i < 1024; i++ )
	{
		//kprintf( "Setting up entry at address 0x%8x to point to %d\n", (unsigned int) &pagetab[i], address );
		pagetab[i] = address | 3; // supervisor, r/w, present
		address += 4096; // 4 KB
	}
	
	// fill first entry of the page directory
	pagedir[0] = (unsigned int) pagetab;
	pagedir[0] = pagedir[0] | 3; // supervisor, r/w, present
	
	// now map the second one
	for( i = 0; i < 1024; i++ )
	{
		pagetab2[i] = address | 3;
		address += 4096;
	}
	
	// fill second entry of page dir
	pagedir[1] = (unsigned int) pagetab2;
	pagedir[1] = pagetab[1] | 3;

	// null out the rest
	for( i = 2; i < 1024; i++ )
		pagedir[i] = 0 | 2; // not present

	// fill CR3 and CR0
	write_cr3( (unsigned int) pagedir ); // put page directory address into cr3
	write_cr0( read_cr0() | 0x80000000 ); // set paging bit to 1
}
But when I try to access past 4MB it page faults (below 4MB no problem!), any ideas why this happens? I'm really confused ATM.

Edit: Oops. Works now.

Code: Select all

// fill second entry of page dir
pagedir[1] = (unsigned int) pagetab2;
pagedir[1] = pagetab[1] | 3; <===== Should be pagedir[1]!
Now, new question: how on earth am I meant to allocate memory for each process, so each one can reference 0x0 as the start of its memory (When in fact it's really some other memory location like 0xdeadbeef)?

I've looked around in the wiki, tried Googling, and still nothing! I'm really confused now :? .

Posted: Wed May 23, 2007 12:50 am
by ucosty
Edit: too late, as usual.

The intel manuals show how paging works. Basically the processor splits the memory address (that you use) into three parts. Each part is an offset in the Page Directory, page table and then the page itself. The page at that location refers to the physical address of the page that should be used.

Posted: Wed May 23, 2007 12:54 am
by Kevin McGuire

// fill second entry of page dir
pagedir[1] = (unsigned int) pagetab2;
pagedir[1] = pagedir1[1] | 3;

Now, new question: how on earth am I meant to allocate memory for each process, so each one can reference 0x0 as the start of its memory (When in fact it's really some other memory location like 0xdeadbeef)?
A page directory for each process.

Posted: Wed May 23, 2007 12:54 am
by pcmattman
I already fixed the typo, I edited my post.

What I want to know is how to map memory so a process could write to what it sees as address 0x0 and then the CPU translates that to a write to 0xdeadbeef.

Posted: Wed May 23, 2007 1:03 am
by pcmattman
Kevin McGuire wrote:A page directory for each process.
OK, so I load a new PD into CR3 every time a task switch occurs.

What is put into the page tables in each directory? The physical or virtual address? And how does this then get mapped into the right places?

Edit: Also, where do the page directories go?

Posted: Wed May 23, 2007 1:14 am
by Kevin McGuire
The same way it has always been stored.

((unsigned int*)(pagedir[VIRTUAL_ADDRESS>>22]&0xFFFFF000))[VIRTUAL_ADDRESS<<10>>22] = PHYSICAL_ADDRESS | 0x3;



Store the page directory along side of each process structure.


struct myProcessStruct{
unsigned int *pageDir;
.....
};

struct myProcessStruct *newProcess = (struct myProcessStruct*)kmalloc(sizeof(struct myProcessStruct));
newProcess->pageDir = (unsigned int*)valloc();
/// fill page dir

Posted: Wed May 23, 2007 1:20 am
by pcmattman
Thanks heaps, I'll try to implement that into my kernel and see if it works properly.

One final question, how big are page tables and directories. Are they 4096 bytes?

And this:

Code: Select all

((unsigned int*)(pagedir[VIRTUAL_ADDRESS>>22]&0xFFFFF000))[VIRTUAL_ADDRESS<<10>>22] = PHYSICAL_ADDRESS | 0x3;
Doesn't that mean I have to have a page directory that is the full 4MB big?

Posted: Wed May 23, 2007 1:24 am
by Kevin McGuire
All the tables that you will not access you can set to zero or not present.

pagedir[VIRTUAL_ADDRESS>>22] = NOT_PRESENT // OR ZERO.

It is similar to asking do I have to map all the memory in a table?
No. You can set the unmapped pages to zero or not present also.

You could create a entire directory with nothing mapped, frankly.

Posted: Wed May 23, 2007 2:53 am
by urxae
Kevin McGuire wrote:You could create a entire directory with nothing mapped, frankly.
Well, you could, but the moment you load it into CR3 it'll instantly triple-fault... :roll:
(Unless it can get enough accessed pages from global entries in the TLB; relying on that is a Bad Idea to say the least)

Posted: Wed May 23, 2007 4:34 am
by ucosty
pcmattman wrote:Doesn't that mean I have to have a page directory that is the full 4MB big?
To access the full address space you would. You would only really need one for your entire system though.

Imagine your kernel has a 4mb block of page tables and each process has it's own page directory. You would only need rearrange the page directory entries within the page directory to change the physical->virtual mappings. If you need (for any reason) to map something smaller than 4mb in granularity you can create a page table and remap the appropriate entry in the process' page directory.

Posted: Wed May 23, 2007 3:49 pm
by pcmattman

Code: Select all

	uint_t addr = 0xdeadbeef;
	( (ulong_t*) ( pagedir[addr >> 22] & 0xFFFFF000 ) )[ ( addr << 10 ) >> 22 ] = ( 1024 * 1024 * 2 ) | 3;

Page faults on 0xDEADBEEF?

Posted: Wed May 23, 2007 4:10 pm
by jnc100
Try:

Code: Select all

( (ulong_t*) ( pagedir[virt_addr >> 22] & 0xFFFFF000 ) )[ ( virt_addr >> 12 ) & 0x3FF ] = ( phys_addr ) | 3;
Although that's just off the top of my head. Can't see why it shouldn't work though, although it is rather late here :wink:

Regards,
John.

edit: now I can - this assumes paging isn't enabled otherwise you're trying to write to the physical address of the page table. You really should map your page tables to somewhere in the address space. A good trick is to set the last entry of the page directory to point to itself, then you can write to all the page tables at the last 4 MB of the virtual address space.

Was the page table created before being accessed?

Posted: Wed May 23, 2007 4:10 pm
by Kevin McGuire
Is (pagedir[addr >> 22]) some random memory value or did you actually create a table there first?

Code: Select all

uint_t addr = 0xdeadbeef;
if(pagedir[addr >> 22] == 0){
    pagedir[addr >> 22] = valloc() | 0x3;
    for(uint32_t x = 0; x < 1024; ++x){
        ((uint32_t*)(pagedir[addr >> 22]&0xFFFFF000))[x] = 0;
    }
}
( (ulong_t*) ( pagedir[addr >> 22] & 0xFFFFF000 ) )[ ( addr << 10 ) >> 22 ] = ( 1024 * 1024 * 2 ) | 3;