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 If WRITABLE is true, the new page is read/write;
92 otherwise it is read-only.
93 Returns true if successful, false if memory allocation
96 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
101 ASSERT (pg_ofs (upage) == 0);
102 ASSERT (pg_ofs (kpage) == 0);
103 ASSERT (is_user_vaddr (upage));
104 ASSERT (vtop (kpage) >> PTSHIFT < ram_pages);
105 ASSERT (pd != base_page_dir);
107 pte = lookup_page (pd, upage, true);
111 ASSERT ((*pte & PG_P) == 0);
112 *pte = pte_create_user (kpage, writable);
119 /* Returns the kernel virtual address that user virtual address
120 UADDR is mapped to in PD, or a null pointer if UADDR is not
123 pagedir_get_page (uint32_t *pd, const void *uaddr)
127 ASSERT (is_user_vaddr (uaddr));
129 pte = lookup_page (pd, uaddr, false);
130 if (pte != NULL && (*pte & PG_P) != 0)
131 return pte_get_page (*pte) + pg_ofs (uaddr);
136 /* Marks user virtual page UPAGE "not present" in page
137 directory PD. Later accesses to the page will fault. Other
138 bits in the page table entry are preserved.
139 UPAGE need not be mapped. */
141 pagedir_clear_page (uint32_t *pd, void *upage)
145 ASSERT (pg_ofs (upage) == 0);
146 ASSERT (is_user_vaddr (upage));
148 pte = lookup_page (pd, upage, false);
149 if (pte != NULL && (*pte & PG_P) != 0)
152 invalidate_pagedir (pd);
156 /* Returns true if the PTE for virtual page VPAGE in PD is dirty,
157 that is, if the page has been modified since the PTE was
159 Returns false if PD contains no PTE for VPAGE. */
161 pagedir_is_dirty (uint32_t *pd, const void *vpage)
163 uint32_t *pte = lookup_page (pd, vpage, false);
164 return pte != NULL && (*pte & PG_D) != 0;
167 /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
170 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty)
172 uint32_t *pte = lookup_page (pd, vpage, false);
179 *pte &= ~(uint32_t) PG_D;
180 invalidate_pagedir (pd);
185 /* Returns true if the PTE for virtual page VPAGE in PD has been
186 accessed recently, that is, between the time the PTE was
187 installed and the last time it was cleared. Returns false if
188 PD contains no PTE for VPAGE. */
190 pagedir_is_accessed (uint32_t *pd, const void *vpage)
192 uint32_t *pte = lookup_page (pd, vpage, false);
193 return pte != NULL && (*pte & PG_A) != 0;
196 /* Sets the accessed bit to ACCESSED in the PTE for virtual page
199 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed)
201 uint32_t *pte = lookup_page (pd, vpage, false);
208 *pte &= ~(uint32_t) PG_A;
209 invalidate_pagedir (pd);
214 /* Loads page directory PD into the CPU's page directory base
217 pagedir_activate (uint32_t *pd)
222 /* Store the physical address of the page directory into CR3
223 aka PDBR (page directory base register). This activates our
224 new page tables immediately. See [IA32-v2a] "MOV--Move
225 to/from Control Registers" and [IA32-v3] 3.7.5. */
226 asm volatile ("mov %%cr3, %0" :: "r" (vtop (pd)));
229 /* Returns the currently active page directory. */
233 /* Copy CR3, the page directory base register (PDBR), into
235 See [IA32-v2a] "MOV--Move to/from Control Registers" and
238 asm volatile ("mov %0, %%cr3" : "=r" (pd));
242 /* Seom page table changes can cause the CPU's translation
243 lookaside buffer (TLB) to become out-of-sync with the page
244 table. When this happens, we have to "invalidate" the TLB by
247 This function invalidates the TLB if PD is the active page
248 directory. (If PD is not active then its entries are not in
249 the TLB, so there is no need to invalidate anything.) */
251 invalidate_pagedir (uint32_t *pd)
253 if (active_pd () == pd)
255 /* We cleared a page-table entry in the active page
256 table, so we have to invalidate the TLB. See
257 [IA32-v3], section 3.11. */
258 pagedir_activate (pd);