Page Table
The code in pagedir.c
is an abstract interface to the 80x86 hardware page table, also called a “page directory” by Intel processor documentation.
- The page table interface uses a
uint32_t *
to represent a page table because this is convenient for accessing their internal structure. - The sections below describe the page table interface and internals.
Creation, Destruction, and Activation
These functions create, destroy, and activate page tables. The base Pintos code already calls these functions where necessary, so it should not be necessary to call them yourself.
- Function:
uint32_t *pagedir_create (void)
- Creates and returns a new page table.
- The new page table contains Pintos’s normal kernel virtual page mappings, but no user virtual mappings.
- Returns a null pointer if memory cannot be obtained.
- Function:
void pagedir_destroy (uint32_t *pd)
- Frees all of the resources held by
pd
, including the page table itself and the frames that it maps.
- Frees all of the resources held by
- Function:
void pagedir_activate (uint32_t *pd)
- Activates
pd
. - The active page table is the one used by the CPU to translate memory references.
- Activates
Inspection and Updates
These functions examine or update the mappings from pages to frames encapsulated by a page table. They work on both active and inactive page tables (that is, those for running and suspended processes), flushing the TLB as necessary.
- Function:
bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable)
- Adds to
pd
a mapping from user pageupage
to the frame identified by kernel virtual addresskpage
. Ifwritable
is true, the page is mapped read/write; otherwise, it is mapped read-only. - User page
upage
must not already be mapped inpd
. - Kernel page
kpage
should be a kernel virtual address obtained from the user pool withpalloc_get_page(PAL_USER)
. - Returns true if successful, false on failure. Failure will occur if additional memory required for the page table cannot be obtained.
- Adds to
- Function:
void *pagedir_get_page (uint32_t *pd, const void *uaddr)
- Looks up the frame mapped to
uaddr
inpd
. - Returns the kernel virtual address for that frame, if
uaddr
is mapped, or a null pointer if it is not.
- Looks up the frame mapped to
- Function:
void pagedir_clear_page (uint32_t *pd, void *page)
- Marks
page
“not present” inpd
. Later accesses to thepage
will fault. - Other bits in the page table for
page
are preserved, permitting the accessed and dirty bits (see the next section) to be checked. - This function has no effect if
page
is not mapped.
- Marks
Accessed and Dirty Bits
80x86 hardware provides some assistance for implementing page replacement algorithms, through a pair of bits in the page table entry (PTE) for each page.
- On any read or write to a page, the CPU sets the accessed bit to 1 in the page’s PTE, and on any write, the CPU sets the dirty bit to 1.
- The CPU never resets these bits to 0, but the OS may do so.
- Proper interpretation of these bits requires understanding of aliases, that is, two (or more) pages that refer to the same frame. When an aliased frame is accessed, the accessed and dirty bits are updated in only one page table entry (the one for the page used for access). The accessed and dirty bits for the other aliases are not updated.
- In project 3, you will apply these bits in implementing page replacement algorithms.
The followings are some functions related to accessed and dirty bits:
- Function:
bool pagedir_is_dirty (uint32_t *pd, const void *page)
- Function:
bool pagedir_is_accessed (uint32_t *pd, const void *page)
- Returns true if
page
directorypd
contains a page table entry forpage
that is marked dirty (or accessed). Otherwise, returns false.
- Returns true if
- Function:
void pagedir_set_dirty (uint32_t *pd, const void *page, bool value)
- Function:
void pagedir_set_accessed (uint32_t *pd, const void *page, bool value)
- If the page directory
pd
has a page table entry forpage
, then its dirty (or accessed) bit is set tovalue
.
- If the page directory
Page Table Details
The functions provided with Pintos are sufficient to implement the projects. However, you may still find it worthwhile to understand the hardware page table format, so we’ll go into a little detail in this section.
Structure
- The top-level paging data structure is a page called the “page directory” (PD) arranged as an array of 1,024 32-bit page directory entries (PDEs), each of which represents 4 MB of virtual memory.
- Each PDE may point to the physical address of another page called a “page table” (PT) arranged, similarly, as an array of 1,024 32-bit page table entries (PTEs), each of which translates a single 4 kB virtual page to a physical page.
Address Translation
Translation of a virtual address into a physical address follows the three-step process illustrated in the diagram below:
Actually, virtual to physical translation on the 80x86 architecture occurs via an intermediate “linear address,” but Pintos (and most modern 80x86 OSes) set up the CPU so that linear and virtual addresses are one and the same. Thus, you can effectively ignore this CPU feature.
- The most-significant 10 bits of the virtual address (bits 22…31) index the page directory.
- If the PDE is marked “present,” the physical address of a page table is read from the PDE thus obtained.
- If the PDE is marked “not present,” then a page fault occurs.
- The next 10 bits of the virtual address (bits 12…21) index the page table.
- If the PTE is marked “present,” the physical address of a data page is read from the PTE thus obtained.
- If the PTE is marked “not present,” then a page fault occurs.
- The least-significant 12 bits of the virtual address (bits 0…11) are added to the data page’s physical base address, yielding the final physical address.
31 22 21 12 11 0
+----------------------+----------------------+----------------------+
| Page Directory Index | Page Table Index | Page Offset |
+----------------------+----------------------+----------------------+
| | |
_______/ _______/ _____/
/ / /
/ Page Directory / Page Table / Data Page
/ .____________. / .____________. / .____________.
|1,023|____________| |1,023|____________| | |____________|
|1,022|____________| |1,022|____________| | |____________|
|1,021|____________| |1,021|____________| __\|____________|
|1,020|____________| |1,020|____________| /|____________|
| | | | | | | |
| | | ____\| |_ | |
| | . | /| . | \ | . |
____\| . |_ | . | | | . |
/| . | \ | . | | | . |
| . | | | . | | | . |
| | | | | | | |
|____________| | |____________| | |____________|
4|____________| | 4|____________| | |____________|
3|____________| | 3|____________| | |____________|
2|____________| | 2|____________| | |____________|
1|____________| | 1|____________| | |____________|
0|____________| __\0|____________| ____\|____________|
/ /
Pintos provides some macros and functions that are useful for working with raw page tables:
Macros and Functions for Page Tables
- Macro:
PTSHIFT
- Macro:
PTBITS
- The starting bit index (12) and number of bits (10), respectively, in a page table index.
- Macro:
PTMASK
- A bit mask with the bits in the page table index set to 1 and the rest set to 0 (0x3ff000).
- Macro:
PTSPAN
- The number of bytes of virtual address space that a single page table page covers (4,194,304 bytes, or 4 MB).
- Macro:
PDSHIFT
- Macro:
PDBITS
- The starting bit index (22) and number of bits (10), respectively, in a page directory index.
- Macro:
PDMASK
- A bit mask with the bits in the page directory index set to 1 and other bits set to 0 (0xffc00000).
- Function:
uintptr_t pd_no (const void *va)
- Function:
uintptr_t pt_no (const void *va)
- Returns the page directory index or page table index, respectively, for virtual address va. These functions are defined in
threads/pte.h
.
- Returns the page directory index or page table index, respectively, for virtual address va. These functions are defined in
- Function:
unsigned pg_ofs (const void *va)
- Returns the page offset for virtual address va. This function is defined in
threads/vaddr.h
.
- Returns the page offset for virtual address va. This function is defined in
Page Table Entry Format
You do not need to understand the PTE format to do the Pintos projects, unless you wish to incorporate the page table into your supplemental page table in project 3.
The actual format of a page table entry is summarized below. For complete information, refer to section 3.7, “Page Translation Using 32-Bit Physical Addressing,” in IA32-v3a.
31 12 11 9 6 5 2 1 0
+---------------------------------------+----+----+-+-+---+-+-+-+
| Physical Address | AVL| |D|A| |U|W|P|
+---------------------------------------+----+----+-+-+---+-+-+-+
Some more information on each bit is given below. The names are threads/pte.h
macros that represent the bits’ values:
Macros and Functions for Page Table Entry
- Macro:
PTE_P
- Bit 0, the “present” bit.
- When this bit is 1, the other bits are interpreted as described below. When this bit is 0, any attempt to access the page will page fault. The remaining bits are then not used by the CPU and may be used by the OS for any purpose.
- Macro:
PTE_W
- Bit 1, the “read/write” bit.
- When it is 1, the page is writable. When it is 0, write attempts will page fault.
- Macro:
PTE_U
- Bit 2, the “user/supervisor” bit.
- When it is 1, user processes may access the page. When it is 0, only the kernel may access the page (user accesses will page fault).
- Pintos clears this bit in PTEs for kernel virtual memory, to prevent user processes from accessing them.
- Macro:
PTE_A
- Bit 5, the “accessed” bit.
- See section Accessed and Dirty Bits.
- Macro:
PTE_D
- Bit 6, the “dirty” bit.
- See section Accessed and Dirty Bits.
- Macro:
PTE_AVL
- Bits 9…11, available for operating system use.
- Pintos, as provided, does not use them and sets them to 0.
- Macro:
PTE_ADDR
- Bits 12…31, the top 20 bits of the physical address of a frame. The low 12 bits of the frame’s address are always 0.
- Other bits are either reserved or uninteresting in a Pintos context and should be set to 0.
Header threads/pte.h
defines three functions for working with page table entries:
- Function:
uint32_t pte_create_kernel (uint32_t *page, bool writable)
- Returns a page table entry that points to
page
, which should be a kernel virtual address. - The PTE’s present bit will be set. It will be marked for kernel-only access.
- If
writable
is true, the PTE will also be marked read/write; otherwise, it will be read-only.
- Returns a page table entry that points to
- Function:
uint32_t pte_create_user (uint32_t *page, bool writable)
- Returns a page table entry that points to
page
, which should be the kernel virtual address of a frame in the user pool. - The PTE’s present bit will be set and it will be marked to allow user-mode access.
- If writable is true, the PTE will also be marked read/write; otherwise, it will be read-only.
- Returns a page table entry that points to
- Function:
void *pte_get_page (uint32_t pte)
- Returns the kernel
virtual
address for the frame thatpte
points to. - The
pte
may be present or not-present; if it is not-present then the pointer returned is only meaningful if the address bits in the PTE actually represent a physical address.
- Returns the kernel
Page Directory Entry Format
Page directory entries have the same format as PTEs, except that the physical address points to a page table page instead of a frame. Header threads/pte.h
defines two functions for working with page directory entries:
- Function:
uint32_t pde_create (uint32_t *pt)
- Returns a page directory that points to
pt
, which should be the kernel virtual address of a page table page. - The PDE’s present bit will be set, it will be marked to allow user-mode access, and it will be marked read/write.
- Returns a page directory that points to
- Function:
uint32_t *pde_get_pt (uint32_t pde)
- Returns the kernel virtual address for the page table page that
pde
, which must be marked present, points to.
- Returns the kernel virtual address for the page table page that