Page 1 of 1

OSDev Wiki paging code

Posted: Fri May 05, 2017 11:22 pm
by Agola
Hello.

There's a strange thing in the Paging wiki page code in the "Manipulation" section. (http://wiki.osdev.org/Paging#Manipulation)
The CR3 value, that is, the value containing the address of the page directory, is in physical form. Once, then, the computer is in paging mode, only recognizing those virtual addresses mapped into the paging tables, how can the tables be edited and dynamically changed?
Many prefer to map the last PDE to itself. The page directory will look like a page table to the system. To get the physical address of any virtual address in the range 0x00000000-0xFFFFF000 is then just a matter of:

Code: Select all

void * get_physaddr(void * virtualaddr)
{
    unsigned long pdindex = (unsigned long)virtualaddr >> 22;
    unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;
 
    unsigned long * pd = (unsigned long *)0xFFFFF000;
    // Here you need to check whether the PD entry is present.
 
    unsigned long * pt = ((unsigned long *)0xFFC00000) + (0x400 * pdindex);
    // Here you need to check whether the PT entry is present.
 
    return (void *)((pt[ptindex] & ~0xFFF) + ((unsigned long)virtualaddr & 0xFFF));
}
To map a virtual address to a physical address can be done as follows:

Code: Select all

void map_page(void * physaddr, void * virtualaddr, unsigned int flags)
{
    // Make sure that both addresses are page-aligned.
 
    unsigned long pdindex = (unsigned long)virtualaddr >> 22;
    unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;
 
    unsigned long * pd = (unsigned long *)0xFFFFF000;
    // Here you need to check whether the PD entry is present.
    // When it is not present, you need to create a new empty PT and
    // adjust the PDE accordingly.
 
    unsigned long * pt = ((unsigned long *)0xFFC00000) + (0x400 * pdindex);
    // Here you need to check whether the PT entry is present.
    // When it is, then there is already a mapping present. What do you do now?
 
    pt[ptindex] = ((unsigned long)physaddr) | (flags & 0xFFF) | 0x01; // Present
 
    // Now you need to flush the entry in the TLB
    // or you might not notice the change.
}
Unmapping an entry is essentially the same as above, but instead of assigning the pt[ptindex] a value, you set it to 0x00000000 (i.e. not present). When the entire page table is empty, you may want to remove it and mark the page directory entry 'not present'. Of course you don't need the 'flags' or 'physaddr' for unmapping.

Code: Select all

unsigned long * pt = ((unsigned long *)0xFFC00000) + (0x400 * pdindex);
Isn't that calculation wrong? I thought (0x400 * pdindex) should be (0x1000 * pdindex) because each page table is one page long in that mapping.

Also it gives wrong addresses. The last page table's address is in this calculation:
0xFFC00000 + (0x400 * 1023) = 0xFFCFFC00

Using 0x1000 instead of 0x400 gives the correct address:
0xFFC00000 + (0x1000 * 1023) = 0xFFFFF000

If it's correct, could you explain why the calculation is (0x400 * pdindex), please?

Thanks in advance.

Edit: I forgot taking care about (unsigned long*), fixed.
A really noobish mistake, maybe I should sleep more :|

Re: OSDev Wiki paging code

Posted: Fri May 05, 2017 11:47 pm
by iansjack

Re: OSDev Wiki paging code

Posted: Fri May 05, 2017 11:52 pm
by Agola
iansjack wrote:https://www.tutorialspoint.com/cprogramming/c_pointer_arithmetic.htm

Code: Select all

((unsigned long*) 0xFFC00000) + (0x400 * pdindex);
Ah, I forgot taking care about (unsigned long*) before calculating. Such a noobish mistake. Maybe I should sleep more.
Even after thinking this about 2 hours, how couldn't I see it.

That code in my os is like:

Code: Select all

(uint32_t*) (0xFFC00000 + (0x1000 * pdindex))
Probably that's why I couldn't see it :|

Re: OSDev Wiki paging code

Posted: Sat May 06, 2017 12:24 am
by iansjack
I think it's a mistake that catches everyone out from time to time. It's certainly bitten me a few times.