Setting Up Paging: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(→‎Creating a Blank Page Directory: added page align notes)
(→‎Creating Your First Page Table: Page alignment notes)
Line 53: Line 53:
The second step is to create a basic page table. In this example we choose to fill up the whole first page table with addresses for the MMU. Because each page is 4 kilobytes large, and because each page table has exactly 1024 entries, filling up the whole table causes us to map 4 megabytes of memory.
The second step is to create a basic page table. In this example we choose to fill up the whole first page table with addresses for the MMU. Because each page is 4 kilobytes large, and because each page table has exactly 1024 entries, filling up the whole table causes us to map 4 megabytes of memory.


We start like we did with the page directory, by finding a piece of free memory where we can keep our page table.
We start like we did with the page directory, by finding a piece of free memory where we can keep our page table. If we properly page-aligned the page directory, then the page table should also be properly page aligned.
<pre>
<pre>
//our first page table comes right after the page directory
//our first page table comes right after the page directory

Revision as of 05:28, 1 May 2008

This is a guide to setting up paging. It will teach you the basic concepts behind paging and how it can help you with your OS.

This example will concentrate on Legacy Non-PSE Non-PAE paging.

Basic Paging

Paging allows you to have more than one address space mapped into the real address space. The MMU uses what is called a Page Directory to map virtual addresses to physical addresses.


Page Directory - A table in memory which the MMU uses to find the page tables.


Each index in the Page Directory is a pointer to a Page table.


Page Table - A table in memory that describes how the MMU should translate a certain range of addresses.


Each index in a Page Table contains the physical memory address to which a certain page should be mapped.

Creating a Blank Page Directory

The first step is to create a blank page directory. The page directory is blank because we have not yet created any page tables where the entries in the page directory can point.

First we need a piece of free memory where we can keep the page directory. If you know where the end of your kernel is, then you can put the page directory right after it.

Note that all of your paging structures need to be at page aligned addresses, so it's probably a good idea to make a page allocator first and have that dish out pages for your paging code.

//the page directory comes right after the kernel - NOTE: make sure the address is page aligned!
unsigned int *page_directory = (unsigned int*)end;

If you don't know where the end of your kernel is, then you need to edit your linker script to tell you where the end of your kernel is. In the meantime, use something like this.

//this is only a temporary solution. Find out where your kernel ends!
unsigned int *page_directory = (unsigned int*) 0x9C000;

Now that we have a piece of free memory for the page directory, we need to blank it. The page directory should have exactly 1024 entries. We will set each entry to not present so that if the MMU looks for that page table, it will see that it is not there (...yet. We will add the first page table in a moment).

//set each entry to not present
int i = 0;
for(i = 0; i < 1024; i++)
{
    //attribute: supervisor level, read/write, not present.
    page_directory[i] = 0 | 2; 
}

Creating Your First Page Table

The second step is to create a basic page table. In this example we choose to fill up the whole first page table with addresses for the MMU. Because each page is 4 kilobytes large, and because each page table has exactly 1024 entries, filling up the whole table causes us to map 4 megabytes of memory.

We start like we did with the page directory, by finding a piece of free memory where we can keep our page table. If we properly page-aligned the page directory, then the page table should also be properly page aligned.

//our first page table comes right after the page directory
unsigned int *first_page_table = page_directory + 0x1000;

We now need to fill each index in the table with an address to which the MMU will map that page. Index 0 (zero) holds the address where the first page will be mapped. Likewise, index 1 (one) holds the address for the second page and index 1023 holds the address of the 1024'th page.

// holds the physical address where we want to start mapping these pages to.
// in this case, we want to map these pages to the very beginning of memory.
unsigned int address = 0; 
unsigned int i;

//we will fill all 1024 entries, mapping 4 megabytes
for(i = 0; i < 1024; i++)
{
    first_page_table[i] = address | 3; // attributes: supervisor level, read/write, present.
    address = address + 4096; //advance the address to the next page boundary
}

Put the Page Table in the Page Directory

The third step is to put the newly created page table into our blank page directory. We do this by setting the first entry in the page directory to the address of our page table.

page_directory[0] = first_page_table; 
page_directory[0] |= 3;// attributes: supervisor level, read/write, present

Enable Paging

The final step is to actually enable paging. First we tell the processor where to find our page table by putting it's address into the cr3 register. Because C code can not directly access the computer's registers, we will need to use inline assembly to set cr3.

asm volatile("mov %0, %%cr3":: "b"(page_directory));

Finally we switch the paging bit on the cr0 register. This operation also requires inline assembly code.

unsigned int cr0;
asm volatile("mov %%cr0, %0": "=b"(cr0));
cr0 |= 0x80000000;
asm volatile("mov %0, %%cr0":: "b"(cr0));

Paging should now be enabled.

More Advanced Paging Example

Add sections on how to dynamically get and free pages...