7c392e61d5fee657b773b879ca4377bce1d62e32
[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
11 /* Creates a new page directory that has mappings for kernel
12    virtual addresses, but none for user virtual addresses.
13    Returns the new page directory, or a null pointer if memory
14    allocation fails. */
15 uint32_t *
16 pagedir_create (void) 
17 {
18   uint32_t *pd = palloc_get_page (0);
19   memcpy (pd, base_page_dir, PGSIZE);
20   return pd;
21 }
22
23 /* Destroys page directory PD, freeing all the pages it
24    references. */
25 void
26 pagedir_destroy (uint32_t *pd) 
27 {
28   uint32_t *pde;
29
30   if (pd == NULL)
31     return;
32
33   ASSERT (pd != base_page_dir);
34   for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
35     if (*pde & PG_P) 
36       {
37         uint32_t *pt = pde_get_pt (*pde);
38         uint32_t *pte;
39         
40         for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
41           if (*pte & PG_P) 
42             palloc_free_page (pte_get_page (*pte));
43         palloc_free_page (pt);
44       }
45   palloc_free_page (pd);
46 }
47
48 /* Returns the address of the page table entry for user virtual
49    address UADDR in page directory PD.
50    If PD does not have a page table for UADDR, behavior varies
51    based on CREATE:
52    if CREATE is true, then a new page table is created and a
53    pointer into it is returned,
54    otherwise a null pointer is returned.
55    Also returns a null pointer if UADDR is a kernel address. */
56 static uint32_t *
57 lookup_page (uint32_t *pd, void *uaddr, bool create)
58 {
59   uint32_t *pt, *pde;
60
61   ASSERT (pd != NULL);
62
63   /* Make sure it's a user address. */
64   if (uaddr >= PHYS_BASE)
65     return NULL;
66
67   /* Check for a page table for UADDR.
68      If one is missing, create one if requested. */
69   pde = pd + pd_no (uaddr);
70   if (*pde == 0) 
71     {
72       if (create)
73         {
74           pt = palloc_get_page (PAL_ZERO);
75           if (pt == NULL) 
76             return NULL; 
77       
78           *pde = pde_create (pt);
79         }
80       else
81         return NULL;
82     }
83
84   /* Return the page table entry. */
85   pt = pde_get_pt (*pde);
86   return &pt[pt_no (uaddr)];
87 }
88
89 /* Adds a mapping from user virtual address UPAGE to kernel
90    virtual address KPAGE in page directory PD.
91    UPAGE must not already be mapped.
92    If WRITABLE is true, the new page is read/write;
93    otherwise it is read-only.
94    Returns true if successful, false if memory allocation
95    failed. */
96 bool
97 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
98                   bool writable) 
99 {
100   uint32_t *pte;
101
102   ASSERT (pg_ofs (upage) == 0);
103   ASSERT (pg_ofs (kpage) == 0);
104   ASSERT (upage < PHYS_BASE);
105   ASSERT (pagedir_get_page (pd, upage) == NULL);
106
107   pte = lookup_page (pd, upage, true);
108   if (pte != NULL) 
109     {
110       *pte = pte_create_user (kpage, writable);
111       return true;
112     }
113   else
114     return false;
115 }
116
117 /* Returns the kernel virtual address that user virtual address
118    UADDR is mapped to in PD, or a null pointer if there is no
119    mapping. */
120 void *
121 pagedir_get_page (uint32_t *pd, const void *uaddr) 
122 {
123   uint32_t *pte = lookup_page (pd, (void *) uaddr, false);
124   return pte != NULL && *pte != 0 ? pte_get_page (*pte) : NULL;
125 }
126
127 /* Clears any mapping for user virtual address UPAGE in page
128    directory PD.
129    UPAGE need not already be mapped. */
130 void
131 pagedir_clear_page (uint32_t *pd, void *upage) 
132 {
133   uint32_t *pte = lookup_page (pd, upage, false);
134   if (pte != NULL && *pte != 0)
135     {
136       *pte = 0;
137
138       if (active_pd () == pd) 
139         {
140           /* We cleared a page-table entry in the active page
141              table, so we have to invalidate the TLB.  See
142              [IA32-v3], section 3.11. */
143           pagedir_activate (pd);
144         }
145     }
146
147 }
148
149 /* Returns true if the PTE for user virtual page UPAGE in PD is
150    dirty, that is, if the page has been modified since the PTE
151    was installed.
152    Returns false if PD contains no PDE for UPAGE. */
153 bool
154 pagedir_test_dirty (uint32_t *pd, const void *upage) 
155 {
156   uint32_t *pte = lookup_page (pd, (void *) upage, false);
157   return pte != NULL && (*pte & PG_D) != 0;
158 }
159
160 /* Returns true if the PTE for user virtual page UPAGE in PD has
161    been accessed recently, that is, between the time the PTE was
162    installed and the last time it was cleared.
163    Returns false if PD contains no PDE for UPAGE. */
164 bool
165 pagedir_test_accessed (uint32_t *pd, const void *upage) 
166 {
167   uint32_t *pte = lookup_page (pd, (void *) upage, false);
168   return pte != NULL && (*pte & PG_A) != 0;
169 }
170
171 /* Returns true if the PTE for user virtual page UPAGE in PD has
172    been accessed recently, and then resets the accessed bit for
173    that page.
174    Returns false and has no effect if PD contains no PDE for
175    UPAGE. */
176 bool
177 pagedir_test_accessed_and_clear (uint32_t *pd, const void *upage) 
178 {
179   uint32_t *pte = lookup_page (pd, (void *) upage, false);
180   if (pte != NULL) 
181     {
182       bool accessed = (*pte & PG_A) != 0;
183       *pte &= ~(uint32_t) PG_A;
184       return accessed;
185     }
186   else
187     return false;
188 }
189
190 /* Loads page directory PD into the CPU's page directory base
191    register. */
192 void
193 pagedir_activate (uint32_t *pd) 
194 {
195   if (pd == NULL)
196     pd = base_page_dir;
197   asm volatile ("movl %0,%%cr3" :: "r" (vtop (pd)));
198 }
199
200 /* Returns the currently active page directory. */
201 static uint32_t *
202 active_pd (void) 
203 {
204   uint32_t *pd;
205
206   asm ("movl %%cr3,%0" : "=r" (pd));
207   return pd;
208 }