4604ca2ddf94b24e97f31fbd5d553b06ab3ff7c1
[pintos-anon] / src / userprog / pagedir.c
1 #include "userprog/pagedir.h"
2 #include <stdbool.h>
3 #include <stddef.h>
4 #include <string.h>
5 #include "threads/init.h"
6 #include "threads/mmu.h"
7 #include "threads/palloc.h"
8
9 static uint32_t *active_pd (void);
10 static void invalidate_pagedir (uint32_t *);
11
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
15    allocation fails. */
16 uint32_t *
17 pagedir_create (void) 
18 {
19   uint32_t *pd = palloc_get_page (0);
20   if (pd != NULL)
21     memcpy (pd, base_page_dir, PGSIZE);
22   return pd;
23 }
24
25 /* Destroys page directory PD, freeing all the pages it
26    references. */
27 void
28 pagedir_destroy (uint32_t *pd) 
29 {
30   uint32_t *pde;
31
32   if (pd == NULL)
33     return;
34
35   ASSERT (pd != base_page_dir);
36   for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
37     if (*pde & PG_P) 
38       {
39         uint32_t *pt = pde_get_pt (*pde);
40         uint32_t *pte;
41         
42         for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
43           if (*pte & PG_P) 
44             palloc_free_page (pte_get_page (*pte));
45         palloc_free_page (pt);
46       }
47   palloc_free_page (pd);
48 }
49
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. */
56 static uint32_t *
57 lookup_page (uint32_t *pd, const void *vaddr, bool create)
58 {
59   uint32_t *pt, *pde;
60
61   ASSERT (pd != NULL);
62
63   /* Shouldn't create new kernel virtual mappings. */
64   ASSERT (!create || is_user_vaddr (vaddr));
65
66   /* Check for a page table for VADDR.
67      If one is missing, create one if requested. */
68   pde = pd + pd_no (vaddr);
69   if (*pde == 0) 
70     {
71       if (create)
72         {
73           pt = palloc_get_page (PAL_ZERO);
74           if (pt == NULL) 
75             return NULL; 
76       
77           *pde = pde_create (pt);
78         }
79       else
80         return NULL;
81     }
82
83   /* Return the page table entry. */
84   pt = pde_get_pt (*pde);
85   return &pt[pt_no (vaddr)];
86 }
87
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
94    failed. */
95 bool
96 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
97                   bool writable) 
98 {
99   uint32_t *pte;
100
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);
106
107   pte = lookup_page (pd, upage, true);
108
109   if (pte != NULL) 
110     {
111       ASSERT ((*pte & PG_P) == 0);
112       *pte = pte_create_user (kpage, writable);
113       return true;
114     }
115   else
116     return false;
117 }
118
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
121    present. */
122 void *
123 pagedir_get_page (uint32_t *pd, const void *uaddr) 
124 {
125   uint32_t *pte;
126
127   ASSERT (is_user_vaddr (uaddr));
128   
129   pte = lookup_page (pd, uaddr, false);
130   if (pte != NULL && (*pte & PG_P) != 0)
131     return pte_get_page (*pte) + pg_ofs (uaddr);
132   else
133     return NULL;
134 }
135
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. */
140 void
141 pagedir_clear_page (uint32_t *pd, void *upage) 
142 {
143   uint32_t *pte;
144
145   ASSERT (pg_ofs (upage) == 0);
146   ASSERT (is_user_vaddr (upage));
147
148   pte = lookup_page (pd, upage, false);
149   if (pte != NULL && (*pte & PG_P) != 0)
150     {
151       *pte &= ~PG_P;
152       invalidate_pagedir (pd);
153     }
154 }
155
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
158    installed.
159    Returns false if PD contains no PTE for VPAGE. */
160 bool
161 pagedir_is_dirty (uint32_t *pd, const void *vpage) 
162 {
163   uint32_t *pte = lookup_page (pd, vpage, false);
164   return pte != NULL && (*pte & PG_D) != 0;
165 }
166
167 /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
168    in PD. */
169 void
170 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) 
171 {
172   uint32_t *pte = lookup_page (pd, vpage, false);
173   if (pte != NULL) 
174     {
175       if (dirty)
176         *pte |= PG_D;
177       else 
178         {
179           *pte &= ~(uint32_t) PG_D;
180           invalidate_pagedir (pd);
181         }
182     }
183 }
184
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. */
189 bool
190 pagedir_is_accessed (uint32_t *pd, const void *vpage) 
191 {
192   uint32_t *pte = lookup_page (pd, vpage, false);
193   return pte != NULL && (*pte & PG_A) != 0;
194 }
195
196 /* Sets the accessed bit to ACCESSED in the PTE for virtual page
197    VPAGE in PD. */
198 void
199 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) 
200 {
201   uint32_t *pte = lookup_page (pd, vpage, false);
202   if (pte != NULL) 
203     {
204       if (accessed)
205         *pte |= PG_A;
206       else 
207         {
208           *pte &= ~(uint32_t) PG_A; 
209           invalidate_pagedir (pd);
210         }
211     }
212 }
213
214 /* Loads page directory PD into the CPU's page directory base
215    register. */
216 void
217 pagedir_activate (uint32_t *pd) 
218 {
219   if (pd == NULL)
220     pd = base_page_dir;
221
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)));
227 }
228
229 /* Returns the currently active page directory. */
230 static uint32_t *
231 active_pd (void) 
232 {
233   /* Copy CR3, the page directory base register (PDBR), into
234      `pd'.
235      See [IA32-v2a] "MOV--Move to/from Control Registers" and
236      [IA32-v3] 3.7.5. */
237   uintptr_t pd;
238   asm volatile ("mov %0, %%cr3" : "=r" (pd));
239   return ptov (pd);
240 }
241
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
245    re-activating it.
246
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.) */
250 static void
251 invalidate_pagedir (uint32_t *pd) 
252 {
253   if (active_pd () == pd) 
254     {
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);
259     } 
260 }