Improve wording.
[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 in page directory PD from user virtual page
89    UPAGE to the physical frame identified by kernel virtual
90    address KPAGE.
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
97    failed. */
98 bool
99 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
100                   bool writable) 
101 {
102   uint32_t *pte;
103
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);
109
110   pte = lookup_page (pd, upage, true);
111
112   if (pte != NULL) 
113     {
114       ASSERT ((*pte & PG_P) == 0);
115       *pte = pte_create_user (kpage, writable);
116       return true;
117     }
118   else
119     return false;
120 }
121
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. */
126 void *
127 pagedir_get_page (uint32_t *pd, const void *uaddr) 
128 {
129   uint32_t *pte;
130
131   ASSERT (is_user_vaddr (uaddr));
132   
133   pte = lookup_page (pd, uaddr, false);
134   if (pte != NULL && (*pte & PG_P) != 0)
135     return pte_get_page (*pte) + pg_ofs (uaddr);
136   else
137     return NULL;
138 }
139
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. */
144 void
145 pagedir_clear_page (uint32_t *pd, void *upage) 
146 {
147   uint32_t *pte;
148
149   ASSERT (pg_ofs (upage) == 0);
150   ASSERT (is_user_vaddr (upage));
151
152   pte = lookup_page (pd, upage, false);
153   if (pte != NULL && (*pte & PG_P) != 0)
154     {
155       *pte &= ~PG_P;
156       invalidate_pagedir (pd);
157     }
158 }
159
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
162    installed.
163    Returns false if PD contains no PTE for VPAGE. */
164 bool
165 pagedir_is_dirty (uint32_t *pd, const void *vpage) 
166 {
167   uint32_t *pte = lookup_page (pd, vpage, false);
168   return pte != NULL && (*pte & PG_D) != 0;
169 }
170
171 /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
172    in PD. */
173 void
174 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) 
175 {
176   uint32_t *pte = lookup_page (pd, vpage, false);
177   if (pte != NULL) 
178     {
179       if (dirty)
180         *pte |= PG_D;
181       else 
182         {
183           *pte &= ~(uint32_t) PG_D;
184           invalidate_pagedir (pd);
185         }
186     }
187 }
188
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. */
193 bool
194 pagedir_is_accessed (uint32_t *pd, const void *vpage) 
195 {
196   uint32_t *pte = lookup_page (pd, vpage, false);
197   return pte != NULL && (*pte & PG_A) != 0;
198 }
199
200 /* Sets the accessed bit to ACCESSED in the PTE for virtual page
201    VPAGE in PD. */
202 void
203 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) 
204 {
205   uint32_t *pte = lookup_page (pd, vpage, false);
206   if (pte != NULL) 
207     {
208       if (accessed)
209         *pte |= PG_A;
210       else 
211         {
212           *pte &= ~(uint32_t) PG_A; 
213           invalidate_pagedir (pd);
214         }
215     }
216 }
217
218 /* Loads page directory PD into the CPU's page directory base
219    register. */
220 void
221 pagedir_activate (uint32_t *pd) 
222 {
223   if (pd == NULL)
224     pd = base_page_dir;
225
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)));
232 }
233
234 /* Returns the currently active page directory. */
235 static uint32_t *
236 active_pd (void) 
237 {
238   /* Copy CR3, the page directory base register (PDBR), into
239      `pd'.
240      See [IA32-v2a] "MOV--Move to/from Control Registers" and
241      [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
242   uintptr_t pd;
243   asm volatile ("movl %%cr3, %0" : "=r" (pd));
244   return ptov (pd);
245 }
246
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
250    re-activating it.
251
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.) */
255 static void
256 invalidate_pagedir (uint32_t *pd) 
257 {
258   if (active_pd () == pd) 
259     {
260       /* Re-activating PD clears the TLB.  See [IA32-v3a] 3.12
261          "Translation Lookaside Buffers (TLBs)". */
262       pagedir_activate (pd);
263     } 
264 }