1 #include "userprog/pagedir.h"
5 #include "threads/init.h"
6 #include "threads/mmu.h"
7 #include "threads/palloc.h"
9 static uint32_t *active_pd (void);
10 static void invalidate_pagedir (uint32_t *);
12 /* Creates a new page directory that has mappings for kernel
13 virtual addresses, but none for user virtual addresses.
14 Returns the new page directory, or a null pointer if memory
19 uint32_t *pd = palloc_get_page (0);
21 memcpy (pd, base_page_dir, PGSIZE);
25 /* Destroys page directory PD, freeing all the pages it
28 pagedir_destroy (uint32_t *pd)
35 ASSERT (pd != base_page_dir);
36 for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
39 uint32_t *pt = pde_get_pt (*pde);
42 for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
44 palloc_free_page (pte_get_page (*pte));
45 palloc_free_page (pt);
47 palloc_free_page (pd);
50 /* Returns the address of the page table entry for virtual
51 address VADDR in page directory PD.
52 If PD does not have a page table for VADDR, behavior depends
53 on CREATE. If CREATE is true, then a new page table is
54 created and a pointer into it is returned. Otherwise, a null
55 pointer is returned. */
57 lookup_page (uint32_t *pd, const void *vaddr, bool create)
63 /* Shouldn't create new kernel virtual mappings. */
64 ASSERT (!create || is_user_vaddr (vaddr));
66 /* Check for a page table for VADDR.
67 If one is missing, create one if requested. */
68 pde = pd + pd_no (vaddr);
73 pt = palloc_get_page (PAL_ZERO);
77 *pde = pde_create (pt);
83 /* Return the page table entry. */
84 pt = pde_get_pt (*pde);
85 return &pt[pt_no (vaddr)];
88 /* Adds a mapping in page directory PD from user virtual page
89 UPAGE to the physical frame identified by kernel virtual
91 UPAGE must not already be mapped.
92 KPAGE should probably be a page obtained from the user pool
93 with palloc_get_page().
94 If WRITABLE is true, the new page is read/write;
95 otherwise it is read-only.
96 Returns true if successful, false if memory allocation
99 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
104 ASSERT (pg_ofs (upage) == 0);
105 ASSERT (pg_ofs (kpage) == 0);
106 ASSERT (is_user_vaddr (upage));
107 ASSERT (vtop (kpage) >> PTSHIFT < ram_pages);
108 ASSERT (pd != base_page_dir);
110 pte = lookup_page (pd, upage, true);
114 ASSERT ((*pte & PG_P) == 0);
115 *pte = pte_create_user (kpage, writable);
122 /* Looks up the physical address that corresponds to user virtual
123 address UADDR in PD. Returns the kernel virtual address
124 corresponding to that physical address, or a null pointer if
125 UADDR is unmapped. */
127 pagedir_get_page (uint32_t *pd, const void *uaddr)
131 ASSERT (is_user_vaddr (uaddr));
133 pte = lookup_page (pd, uaddr, false);
134 if (pte != NULL && (*pte & PG_P) != 0)
135 return pte_get_page (*pte) + pg_ofs (uaddr);
140 /* Marks user virtual page UPAGE "not present" in page
141 directory PD. Later accesses to the page will fault. Other
142 bits in the page table entry are preserved.
143 UPAGE need not be mapped. */
145 pagedir_clear_page (uint32_t *pd, void *upage)
149 ASSERT (pg_ofs (upage) == 0);
150 ASSERT (is_user_vaddr (upage));
152 pte = lookup_page (pd, upage, false);
153 if (pte != NULL && (*pte & PG_P) != 0)
156 invalidate_pagedir (pd);
160 /* Returns true if the PTE for virtual page VPAGE in PD is dirty,
161 that is, if the page has been modified since the PTE was
163 Returns false if PD contains no PTE for VPAGE. */
165 pagedir_is_dirty (uint32_t *pd, const void *vpage)
167 uint32_t *pte = lookup_page (pd, vpage, false);
168 return pte != NULL && (*pte & PG_D) != 0;
171 /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
174 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty)
176 uint32_t *pte = lookup_page (pd, vpage, false);
183 *pte &= ~(uint32_t) PG_D;
184 invalidate_pagedir (pd);
189 /* Returns true if the PTE for virtual page VPAGE in PD has been
190 accessed recently, that is, between the time the PTE was
191 installed and the last time it was cleared. Returns false if
192 PD contains no PTE for VPAGE. */
194 pagedir_is_accessed (uint32_t *pd, const void *vpage)
196 uint32_t *pte = lookup_page (pd, vpage, false);
197 return pte != NULL && (*pte & PG_A) != 0;
200 /* Sets the accessed bit to ACCESSED in the PTE for virtual page
203 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed)
205 uint32_t *pte = lookup_page (pd, vpage, false);
212 *pte &= ~(uint32_t) PG_A;
213 invalidate_pagedir (pd);
218 /* Loads page directory PD into the CPU's page directory base
221 pagedir_activate (uint32_t *pd)
226 /* Store the physical address of the page directory into CR3
227 aka PDBR (page directory base register). This activates our
228 new page tables immediately. See [IA32-v2a] "MOV--Move
229 to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
230 Address of the Page Directory". */
231 asm volatile ("movl %0, %%cr3" :: "r" (vtop (pd)));
234 /* Returns the currently active page directory. */
238 /* Copy CR3, the page directory base register (PDBR), into
240 See [IA32-v2a] "MOV--Move to/from Control Registers" and
241 [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
243 asm volatile ("movl %%cr3, %0" : "=r" (pd));
247 /* Seom page table changes can cause the CPU's translation
248 lookaside buffer (TLB) to become out-of-sync with the page
249 table. When this happens, we have to "invalidate" the TLB by
252 This function invalidates the TLB if PD is the active page
253 directory. (If PD is not active then its entries are not in
254 the TLB, so there is no need to invalidate anything.) */
256 invalidate_pagedir (uint32_t *pd)
258 if (active_pd () == pd)
260 /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12
261 "Translation Lookaside Buffers (TLBs)". */
262 pagedir_activate (pd);