Comments.
[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
125           ? (uint8_t *) pte_get_page (*pte) + pg_ofs (uaddr)
126           : NULL);
127 }
128
129 /* Clears any mapping for user virtual address UPAGE in page
130    directory PD.
131    UPAGE need not already be mapped. */
132 void
133 pagedir_clear_page (uint32_t *pd, void *upage) 
134 {
135   uint32_t *pte = lookup_page (pd, upage, false);
136   if (pte != NULL && *pte != 0)
137     {
138       *pte = 0;
139
140       if (active_pd () == pd) 
141         {
142           /* We cleared a page-table entry in the active page
143              table, so we have to invalidate the TLB.  See
144              [IA32-v3], section 3.11. */
145           pagedir_activate (pd);
146         }
147     }
148
149 }
150
151 /* Returns true if the PTE for user virtual page UPAGE in PD is
152    dirty, that is, if the page has been modified since the PTE
153    was installed.
154    Returns false if PD contains no PDE for UPAGE. */
155 bool
156 pagedir_test_dirty (uint32_t *pd, const void *upage) 
157 {
158   uint32_t *pte = lookup_page (pd, (void *) upage, false);
159   return pte != NULL && (*pte & PG_D) != 0;
160 }
161
162 /* Returns true if the PTE for user virtual page UPAGE in PD has
163    been accessed recently, that is, between the time the PTE was
164    installed and the last time it was cleared.
165    Returns false if PD contains no PDE for UPAGE. */
166 bool
167 pagedir_test_accessed (uint32_t *pd, const void *upage) 
168 {
169   uint32_t *pte = lookup_page (pd, (void *) upage, false);
170   return pte != NULL && (*pte & PG_A) != 0;
171 }
172
173 /* Resets the accessed bit in the PTE for user virtual page UPAGE
174    in PD. */
175 void
176 pagedir_clear_accessed (uint32_t *pd, const void *upage) 
177 {
178   uint32_t *pte = lookup_page (pd, (void *) upage, false);
179   if (pte != NULL) 
180     *pte &= ~(uint32_t) PG_A;
181 }
182
183 /* Loads page directory PD into the CPU's page directory base
184    register. */
185 void
186 pagedir_activate (uint32_t *pd) 
187 {
188   if (pd == NULL)
189     pd = base_page_dir;
190
191   /* Store the physical address of the page directory into CR3
192      aka PDBR (page directory base register).  This activates our
193      new page tables immediately.  See [IA32-v2a] "MOV--Move
194      to/from Control Registers" and [IA32-v3] 3.7.5. */
195   asm volatile ("movl %0,%%cr3" :: "r" (vtop (pd)));
196 }
197
198 /* Returns the currently active page directory. */
199 static uint32_t *
200 active_pd (void) 
201 {
202   uint32_t *pd;
203
204   /* Copy CR3, the page directory base register (PDBR), into `pd'
205      for us to exmaine.  See [IA32-v2a] "MOV--Move to/from
206      Control Registers" and [IA32-v3] 3.7.5. */
207   asm ("movl %%cr3,%0" : "=r" (pd));
208   return pd;
209 }