[CLOSED] Creating a new page table

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
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

[CLOSED] Creating a new page table

Post by ExeTwezz »

Hi,

I'm trying to implement a function that creates a new page table (its number is taken as an argument).
Here is a list of actions it does:
  • Request a free physical page.
  • Calculate an address of the first page that is covered by the page table.
  • Fill in the page table.
    • Calculate a 4KB-aligned address of the current page .
    • OR that address with 3 (011b).
    • Write the result into the page table at offset "i" (0-1023).
  • Calculate an address of the entry in the page directory for the page table.
  • OR the address of the page table with 3 (011b).
  • Write the result to the calculated address.
I think all is right, except one thing: Do I need to call 'invlpg()'? If yes, what should I give as argument: all the addresses of 1024 pages (in loop) or something else?

The code I have:

Code: Select all

// Create a page table.
int create_page_table (int num)
{
    // `num` is the number of a new page table (counting from 1).

    // Get the physical address of the current page directory.
    int page_dir = read_cr3 ();

    if (num < 1)
    {
        return CPT_ZERONUM;
    }
    if (num > 1024)
    {
        // The maximum value of page tables in one page directory is 1024.
        return CPT_BIGNUM;
    }

    // Request a free physical page.
    u32 *ppage = (u32 *) pop_physical_page ();
    // The new page table will be located in this physical page.

    /*
    puts ("ppage = 0x");
    char str[32];
    puts (itoa ((int) ppage, str, 16));
    puts ("\n");
    */

    // Convert `num` to the address of the first page that will be pushed into
    // the first entry of the new page table.
    int first = (num - 1) * 4194304;

    /*
    puts ("first = 0x");
    puts (itoa (first, str, 16));
    puts ("\n");
    */

    // Fill in the page table.
    int i;
    for (i = 0; i < 1024; ++i)
    {
        int page = first + (i * 4096);

        // Fill in the entry "i".
        ppage[i] = page | 3; // End with 011 (supervisor level, read and write
		// access, the page is present).
    }

    // Update the page directory entry.
    int page_dir_entry_num = num - 1;
    u32 *page_dir_entry_addr = (u32 *) (page_dir + page_dir_entry_num * 32);
    *page_dir_entry_addr = (u32) ppage | 3; // End with 011 (supervisor level,
    // read and write access, the page table is present).

    for (i = 0; i < 1024; ++i)
    {
        int page = first + (i * 4096);
        invlpg ((void *) ppage[i]);
    }

    return CPT_SUCCESS;
}
Last edited by ExeTwezz on Wed Oct 08, 2014 5:20 am, edited 1 time in total.
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Creating a new page table

Post by max »

You should only use INVLPG when you make small changes, like adding or removing a page at an address. To this instruction you only pass the virtual address of the changed page (you'd have to do once it for each page).

But when you do bigger modifications, like creating a page table, you just set the page directories address into CR3 again, so the entire TLB will be flushed; see the TLB wiki page.

Greets, Max
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Creating a new page table

Post by ExeTwezz »

Thanks.

But why do I get "Page Fault" when trying to write to 0x400000 (with calling `create_page_table(2)` before)? It seems like the address to the page table is incorrect in the page table or something else. However what?
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Creating a new page table

Post by max »

I'd highly recommend you to start your indices at 0, not 1. Passing "1" to your function should create a table in the second entry of the page directory. That will make life much easier.

That said, the first page table entries range starts at address 0x0; the second ones starts at 0x400000 (1024 * 0x1000); so thats correct. But, did you actually map a page into that page table?

You have to map an actual page into the page table. Mapping the page table entry 0 to a physical page would then give you the area 0x400000-0x401000, mapping the entry 1 would give you the area 0x401000-0x402000 and so on :)
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Creating a new page table

Post by ExeTwezz »

max wrote:I'd highly recommend you to start your indices at 0, not 1. Passing "1" to your function should create a table in the second entry of the page directory. That will make life much easier.

That said, the first page table entries range starts at address 0x0; the second ones starts at 0x400000 (1024 * 0x1000); so thats correct. But, did you actually map a page into that page table?

You have to map an actual page into the page table. Mapping the page table entry 0 to a physical page would then give you the area 0x400000-0x401000, mapping the entry 1 would give you the area 0x401000-0x402000 and so on :)
Yeah,

I've made it counting from 0, not 1. Of course, I've also removed (num - 1)s.

As you can see the code below, the pages are wrote to the needed entry of the specified page table address (an address that the physical allocator returned (P.S. don't worry, my virtual pages are mapped to the corresponding physical pages)).

Also, I wrote a function call that puts an address of the page directory to the CR3 register (the end of the function).

Code: Select all

// Create a page table.
int create_page_table (int num)
{
    // `num` is the number of a new page table (counting from 0).

    // Get the physical address of the current page directory.
    int page_dir = read_cr3 ();

    if (num < 0)
    {
        return CPT_ZERONUM;
    }
    if (num > 1023)
    {
        // The maximum value of page tables in one page directory is 1024.
        return CPT_BIGNUM;
    }

    // Request a free physical page.
    u32 *ppage = (u32 *) pop_physical_page ();
    // The new page table will be located in this physical page.

    char str[32];
    puts ("ppage = 0x");
    puts (itoa ((int) ppage, str, 16));
    puts ("\n");

    // Convert `num` to the address of the first page that will be pushed into
    // the first entry of the new page table.
    int first = num * 4194304;

    // Fill in the page table.
    int i;
    for (i = 0; i < 1024; ++i)
    {
        int page = first + (i * 4096);

        if (i == 1023)
        {
            puts ("first: 0x");
            puts (itoa (first, str, 16));
            puts ("\nlast: 0x");
            puts (itoa (page, str, 16));
            puts ("\n");
        }

        // Fill in the entry "i".
        ppage[i] = page | 3; // End with 011 (supervisor level, read and write
		// access, the page is present).

        if (i % 100)
        {
            puts ("ppage[i] = 0x");
            puts (itoa (ppage[i], str, 16));
            puts ("\n");
        }
    }

    // Update the page directory entry.
    u32 *page_dir_entry_addr = (u32 *) (page_dir + num * 32);
    *page_dir_entry_addr = (u32) ppage | 3; // End with 011 (supervisor level,
    // read and write access, the page table is present).

    puts ("*page_dir_entry_addr = 0x");
    puts (itoa ((int) *page_dir_entry_addr, str, 16));
    puts ("\n");

    puts ("page_dir = 0x");
    puts (itoa (page_dir, str, 16));
    puts ("\n");

    // Flush the whole page directory to update the TLB.
    write_cr3 (page_dir);

    puts ("*page_dir_entry_addr = 0x");
    puts (itoa (*page_dir_entry_addr, str, 16));
    puts ("\n");

    return CPT_SUCCESS;
}
P.S. Don't pay attention to 'puts' function callings, I'm just debugging ;).
ExeTwezz
Member
Member
Posts: 104
Joined: Sun Sep 21, 2014 7:16 am
Libera.chat IRC: exetwezz

Re: Creating a new page table

Post by ExeTwezz »

Oh,

I've found the error:

Code: Select all

    // Update the page directory entry.
    u32 *page_dir_entry_addr = (u32 *) (page_dir + num * 32);
    *page_dir_entry_addr = (u32) ppage | 3; // End with 011 (supervisor level,
    // read and write access, the page table is present).
I've changed it to:

Code: Select all

    // Update the page directory entry.
    u32 *page_dir_ptr = (u32 *) page_dir;
    page_dir_ptr[num] = (u32) ppage | 3; // End with 011 (supervisor level,
    // read and write access, the page talbe is present).
Thank you ;)
Post Reply