Update Intel architecture guide references to latest.
[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    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
96    failed. */
97 bool
98 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
99                   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 & PG_P) == 0);
114       *pte = pte_create_user (kpage, writable);
115       return true;
116     }
117   else
118     return false;
119 }
120
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
123    present. */
124 void *
125 pagedir_get_page (uint32_t *pd, const void *uaddr) 
126 {
127   uint32_t *pte;
128
129   ASSERT (is_user_vaddr (uaddr));
130   
131   pte = lookup_page (pd, uaddr, false);
132   if (pte != NULL && (*pte & PG_P) != 0)
133     return pte_get_page (*pte) + pg_ofs (uaddr);
134   else
135     return NULL;
136 }
137
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. */
142 void
143 pagedir_clear_page (uint32_t *pd, void *upage) 
144 {
145   uint32_t *pte;
146
147   ASSERT (pg_ofs (upage) == 0);
148   ASSERT (is_user_vaddr (upage));
149
150   pte = lookup_page (pd, upage, false);
151   if (pte != NULL && (*pte & PG_P) != 0)
152     {
153       *pte &= ~PG_P;
154       invalidate_pagedir (pd);
155     }
156 }
157
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
160    installed.
161    Returns false if PD contains no PTE for VPAGE. */
162 bool
163 pagedir_is_dirty (uint32_t *pd, const void *vpage) 
164 {
165   uint32_t *pte = lookup_page (pd, vpage, false);
166   return pte != NULL && (*pte & PG_D) != 0;
167 }
168
169 /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
170    in PD. */
171 void
172 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) 
173 {
174   uint32_t *pte = lookup_page (pd, vpage, false);
175   if (pte != NULL) 
176     {
177       if (dirty)
178         *pte |= PG_D;
179       else 
180         {
181           *pte &= ~(uint32_t) PG_D;
182           invalidate_pagedir (pd);
183         }
184     }
185 }
186
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. */
191 bool
192 pagedir_is_accessed (uint32_t *pd, const void *vpage) 
193 {
194   uint32_t *pte = lookup_page (pd, vpage, false);
195   return pte != NULL && (*pte & PG_A) != 0;
196 }
197
198 /* Sets the accessed bit to ACCESSED in the PTE for virtual page
199    VPAGE in PD. */
200 void
201 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) 
202 {
203   uint32_t *pte = lookup_page (pd, vpage, false);
204   if (pte != NULL) 
205     {
206       if (accessed)
207         *pte |= PG_A;
208       else 
209         {
210           *pte &= ~(uint32_t) PG_A; 
211           invalidate_pagedir (pd);
212         }
213     }
214 }
215
216 /* Loads page directory PD into the CPU's page directory base
217    register. */
218 void
219 pagedir_activate (uint32_t *pd) 
220 {
221   if (pd == NULL)
222     pd = base_page_dir;
223
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)));
230 }
231
232 /* Returns the currently active page directory. */
233 static uint32_t *
234 active_pd (void) 
235 {
236   /* Copy CR3, the page directory base register (PDBR), into
237      `pd'.
238      See [IA32-v2a] "MOV--Move to/from Control Registers" and
239      [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
240   uintptr_t pd;
241   asm volatile ("movl %%cr3, %0" : "=r" (pd));
242   return ptov (pd);
243 }
244
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
248    re-activating it.
249
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.) */
253 static void
254 invalidate_pagedir (uint32_t *pd) 
255 {
256   if (active_pd () == pd) 
257     {
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);
262     } 
263 }