I have time...
I'll talk you through it. This is the exact same code I use to map my kernel (and I use other address spaces, so I know it works).
Three guesses, first 2 don't count. For this code alone, which is setting up the kernel address space, this'll hold the
physical addresses.
Code: Select all
dword i;
for(i=0;i<1024;i++)
{
page_table[i]= address | 3 ; //supervisor ,read/write ,present
address= address+4096 ;
}
Each page table has 1024 4 byte entries (4 kb, 1 page). This basically maps each address 1:1 (so 0 == 0, 0x100000 == 0x100000).
By OR-ing it by 3, you are setting attributes. 3 is 11b, so the page is present (1b) and has read/write privileges (10b). It's currently set as supervisor, so ring3 tasks will page fault on access to this page. If you want it to be available to all, you could set the fourth bit (USER privileges).
If you read the intel manuals you can get better descriptions of these bits.
Notice how the address goes up 4 kb each time? That's because paging maps pages (funnily enough) and pages are generally 4 kb (you can get 2 mb and 4 mb pages as well). I'll come back to mapping other addresses later.
Code: Select all
page_directory[0]= page_table;
page_directory[0]= page_directory[0] | 3;
This just maps the first entry in the page directory (the one that maps 0-4 MB) to the page table and ORs it by 3 to have proper privileges.
Code: Select all
for(i=1;i<1024;i++)
page_directory[i]= 0 | 2;
This sets all other pages to non-present. You do this so that the CPU doesn't try to access random memory (you can never rely on what was there before you decided it was useful to you).
Code: Select all
write_cr3(page_directory); // put that page directory address into CR3
write_cr0(read_cr0() | 0x80000000); // set the paging bit in CR0 to 1
This sets CR3 to your page directory, and then enables paging (bit 31 iirc). CR3 will always hold the current page directory. Never set it to NULL unless you disable paging.
Now that I've explained how that all works...
Also if I want to map a new page directory entry,
but without identity mapping how can I do that ?
Say we have the address 0x400000 (the 4 MB mark). Watch this:
Code: Select all
uint_t pagedir_offset = 0x400000 >> 22;
uint_t pagetab_offset = 0x400000 << 10 >> 10 >> 12;
Then, using such code as this:
Code: Select all
uint_t virt-addr = 0x400000;
((uint_t*) (pagedir[virt_addr >> 22] & ~0xFFF ))[virt_addr << 10 >> 10 >> 12] = phys_addr | flags;
You can set mapping exactly the way you want it. By the way, you AND with ~0xFFF because you want to remove the privilege bits (remember OR-ing by 3 earlier?).
I may as well provide some utility functions, but remember that these won't just integrate straight away. You'll need to make it allocate pages the way you need it to (pm_alloc should be replaced with whatever function you use to allocate pages).
Code: Select all
// gets the kernel page directory
uint_t GetKernelPageDir()
{
return (uint_t) pagedir;
}
// allocates space for a page table
void AllocatePageTable( uint_t pdir, uint_t virt_addr, uint_t flags )
{
// get a pointer to the page directory
uint_t* local_pagedir = (uint_t*) pdir;
// is there a page directory for it?
// if not, then create it
if( local_pagedir[virt_addr >> 22] == 0 )
{
local_pagedir[virt_addr >> 22] = ((uint_t) pm_alloc( 1 )) | flags;
memset( (uint_t*) local_pagedir[virt_addr >> 22], 0, 4096 );
}
}
// sets a physical address mapping
void MapPhysicalAddress( uint_t pdir, uint_t virt_addr, uint_t phys_addr, uint_t flags )
{
// get a pointer to the page directory
uint_t* local_pagedir = (uint_t*) pdir;
// set the mapping
if( local_pagedir[virt_addr >> 22] == 0 )
AllocatePageTable( pdir, virt_addr, flags );
((uint_t*) (local_pagedir[virt_addr >> 22] & ~0xFFF ))[virt_addr << 10 >> 10 >> 12] = phys_addr | flags;
}
// gets the physical address for a virtual address
uint_t GetPhysicalAddress( uint_t pdir, uint_t virt_addr )
{
// get a pointer to the page directory
uint_t* local_pagedir = (uint_t*) pdir;
// get the physical address
uint_t phys_addr = ((uint_t*) (local_pagedir[virt_addr >> 22] & ~0xFFF))[virt_addr << 10 >> 10 >> 12] & ~0xFFF;
// return it
return phys_addr;
}
// creates a new address space with a certain size, returning the page directory location
uint_t CreateAddressSpace( uint_t pagecount )
{
// allocate a page directory for the address space
uint_t* local_pagedir = (uint_t*) pm_alloc( pagecount );
// zero it out so that no pages are present within it
memset( local_pagedir, 0, 4096 );
// and then set it up so we have the first 4 MB mapped into the directory
// the page table is always at 0x9D000 so this will not fail
memcpy( local_pagedir, pagedir, sizeof( unsigned int ) );
// return the address
return (uint_t) local_pagedir;
}
In the above code 'pagedir' is the equivalent of 'page_directory' in your code.
You call CreateAddressSpace to get a page directory with the kernel address space already mapped into it. Then you can do this to map addresses:
Code: Select all
MapPhysicalAddress( mypagedir, <virt_addr>, pm_alloc( 1 ), <flags> );
Remember that MapPhysicalAddress only maps 1 page. I'm sure you can figure out how to do it over more than 1 page
I hope this helps. I also hope that some time in the future someone stumbles across this when they were as confused as I was and is able to understand how to get basic paging working.
Good luck.