Add memory clobbers to several asm statements,
[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/pte.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 & PTE_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 & PTE_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, bool writable)
100 {
101   uint32_t *pte;
102
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);
108
109   pte = lookup_page (pd, upage, true);
110
111   if (pte != NULL) 
112     {
113       ASSERT ((*pte & PTE_P) == 0);
114       *pte = pte_create_user (kpage, writable);
115       return true;
116     }
117   else
118     return false;
119 }
120
121 /* Looks up the physical address that corresponds to user virtual
122    address UADDR in PD.  Returns the kernel virtual address
123    corresponding to that physical address, or a null pointer if
124    UADDR is unmapped. */
125 void *
126 pagedir_get_page (uint32_t *pd, const void *uaddr) 
127 {
128   uint32_t *pte;
129
130   ASSERT (is_user_vaddr (uaddr));
131   
132   pte = lookup_page (pd, uaddr, false);
133   if (pte != NULL && (*pte & PTE_P) != 0)
134     return pte_get_page (*pte) + pg_ofs (uaddr);
135   else
136     return NULL;
137 }
138
139 /* Marks user virtual page UPAGE "not present" in page
140    directory PD.  Later accesses to the page will fault.  Other
141    bits in the page table entry are preserved.
142    UPAGE need not be mapped. */
143 void
144 pagedir_clear_page (uint32_t *pd, void *upage) 
145 {
146   uint32_t *pte;
147
148   ASSERT (pg_ofs (upage) == 0);
149   ASSERT (is_user_vaddr (upage));
150
151   pte = lookup_page (pd, upage, false);
152   if (pte != NULL && (*pte & PTE_P) != 0)
153     {
154       *pte &= ~PTE_P;
155       invalidate_pagedir (pd);
156     }
157 }
158
159 /* Returns true if the PTE for virtual page VPAGE in PD is dirty,
160    that is, if the page has been modified since the PTE was
161    installed.
162    Returns false if PD contains no PTE for VPAGE. */
163 bool
164 pagedir_is_dirty (uint32_t *pd, const void *vpage) 
165 {
166   uint32_t *pte = lookup_page (pd, vpage, false);
167   return pte != NULL && (*pte & PTE_D) != 0;
168 }
169
170 /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
171    in PD. */
172 void
173 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) 
174 {
175   uint32_t *pte = lookup_page (pd, vpage, false);
176   if (pte != NULL) 
177     {
178       if (dirty)
179         *pte |= PTE_D;
180       else 
181         {
182           *pte &= ~(uint32_t) PTE_D;
183           invalidate_pagedir (pd);
184         }
185     }
186 }
187
188 /* Returns true if the PTE for virtual page VPAGE in PD has been
189    accessed recently, that is, between the time the PTE was
190    installed and the last time it was cleared.  Returns false if
191    PD contains no PTE for VPAGE. */
192 bool
193 pagedir_is_accessed (uint32_t *pd, const void *vpage) 
194 {
195   uint32_t *pte = lookup_page (pd, vpage, false);
196   return pte != NULL && (*pte & PTE_A) != 0;
197 }
198
199 /* Sets the accessed bit to ACCESSED in the PTE for virtual page
200    VPAGE in PD. */
201 void
202 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) 
203 {
204   uint32_t *pte = lookup_page (pd, vpage, false);
205   if (pte != NULL) 
206     {
207       if (accessed)
208         *pte |= PTE_A;
209       else 
210         {
211           *pte &= ~(uint32_t) PTE_A; 
212           invalidate_pagedir (pd);
213         }
214     }
215 }
216
217 /* Loads page directory PD into the CPU's page directory base
218    register. */
219 void
220 pagedir_activate (uint32_t *pd) 
221 {
222   if (pd == NULL)
223     pd = base_page_dir;
224
225   /* Store the physical address of the page directory into CR3
226      aka PDBR (page directory base register).  This activates our
227      new page tables immediately.  See [IA32-v2a] "MOV--Move
228      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
229      Address of the Page Directory". */
230   asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory");
231 }
232
233 /* Returns the currently active page directory. */
234 static uint32_t *
235 active_pd (void) 
236 {
237   /* Copy CR3, the page directory base register (PDBR), into
238      `pd'.
239      See [IA32-v2a] "MOV--Move to/from Control Registers" and
240      [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
241   uintptr_t pd;
242   asm volatile ("movl %%cr3, %0" : "=r" (pd));
243   return ptov (pd);
244 }
245
246 /* Seom page table changes can cause the CPU's translation
247    lookaside buffer (TLB) to become out-of-sync with the page
248    table.  When this happens, we have to "invalidate" the TLB by
249    re-activating it.
250
251    This function invalidates the TLB if PD is the active page
252    directory.  (If PD is not active then its entries are not in
253    the TLB, so there is no need to invalidate anything.) */
254 static void
255 invalidate_pagedir (uint32_t *pd) 
256 {
257   if (active_pd () == pd) 
258     {
259       /* Re-activating PD clears the TLB.  See [IA32-v3a] 3.12
260          "Translation Lookaside Buffers (TLBs)". */
261       pagedir_activate (pd);
262     } 
263 }