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 from user virtual page UPAGE to kernel virtual
89 address KPAGE in page directory PD.
90 UPAGE must not already be mapped.
91 KPAGE should probably be a page obtained from the user pool
92 with palloc_get_page().
93 If WRITABLE is true, the new page is read/write;
94 otherwise it is read-only.
95 Returns true if successful, false if memory allocation
98 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
103 ASSERT (pg_ofs (upage) == 0);
104 ASSERT (pg_ofs (kpage) == 0);
105 ASSERT (is_user_vaddr (upage));
106 ASSERT (vtop (kpage) >> PTSHIFT < ram_pages);
107 ASSERT (pd != base_page_dir);
109 pte = lookup_page (pd, upage, true);
113 ASSERT ((*pte & PG_P) == 0);
114 *pte = pte_create_user (kpage, writable);
121 /* Returns the kernel virtual address that user virtual address
122 UADDR is mapped to in PD, or a null pointer if UADDR is not
125 pagedir_get_page (uint32_t *pd, const void *uaddr)
129 ASSERT (is_user_vaddr (uaddr));
131 pte = lookup_page (pd, uaddr, false);
132 if (pte != NULL && (*pte & PG_P) != 0)
133 return pte_get_page (*pte) + pg_ofs (uaddr);
138 /* Marks user virtual page UPAGE "not present" in page
139 directory PD. Later accesses to the page will fault. Other
140 bits in the page table entry are preserved.
141 UPAGE need not be mapped. */
143 pagedir_clear_page (uint32_t *pd, void *upage)
147 ASSERT (pg_ofs (upage) == 0);
148 ASSERT (is_user_vaddr (upage));
150 pte = lookup_page (pd, upage, false);
151 if (pte != NULL && (*pte & PG_P) != 0)
154 invalidate_pagedir (pd);
158 /* Returns true if the PTE for virtual page VPAGE in PD is dirty,
159 that is, if the page has been modified since the PTE was
161 Returns false if PD contains no PTE for VPAGE. */
163 pagedir_is_dirty (uint32_t *pd, const void *vpage)
165 uint32_t *pte = lookup_page (pd, vpage, false);
166 return pte != NULL && (*pte & PG_D) != 0;
169 /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
172 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty)
174 uint32_t *pte = lookup_page (pd, vpage, false);
181 *pte &= ~(uint32_t) PG_D;
182 invalidate_pagedir (pd);
187 /* Returns true if the PTE for virtual page VPAGE in PD has been
188 accessed recently, that is, between the time the PTE was
189 installed and the last time it was cleared. Returns false if
190 PD contains no PTE for VPAGE. */
192 pagedir_is_accessed (uint32_t *pd, const void *vpage)
194 uint32_t *pte = lookup_page (pd, vpage, false);
195 return pte != NULL && (*pte & PG_A) != 0;
198 /* Sets the accessed bit to ACCESSED in the PTE for virtual page
201 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed)
203 uint32_t *pte = lookup_page (pd, vpage, false);
210 *pte &= ~(uint32_t) PG_A;
211 invalidate_pagedir (pd);
216 /* Loads page directory PD into the CPU's page directory base
219 pagedir_activate (uint32_t *pd)
224 /* Store the physical address of the page directory into CR3
225 aka PDBR (page directory base register). This activates our
226 new page tables immediately. See [IA32-v2a] "MOV--Move
227 to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
228 Address of the Page Directory". */
229 asm volatile ("movl %0, %%cr3" :: "r" (vtop (pd)));
232 /* Returns the currently active page directory. */
236 /* Copy CR3, the page directory base register (PDBR), into
238 See [IA32-v2a] "MOV--Move to/from Control Registers" and
239 [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
241 asm volatile ("movl %%cr3, %0" : "=r" (pd));
245 /* Seom page table changes can cause the CPU's translation
246 lookaside buffer (TLB) to become out-of-sync with the page
247 table. When this happens, we have to "invalidate" the TLB by
250 This function invalidates the TLB if PD is the active page
251 directory. (If PD is not active then its entries are not in
252 the TLB, so there is no need to invalidate anything.) */
254 invalidate_pagedir (uint32_t *pd)
256 if (active_pd () == pd)
258 /* We cleared a page-table entry in the active page table,
259 so we have to invalidate the TLB. See [IA32-v3a] 3.12
260 "Translation Lookaside Buffers (TLBs)". */
261 pagedir_activate (pd);