Make userspace actually work.
[pintos-anon] / src / threads / paging.c
1 #include "paging.h"
2 #include <stdbool.h>
3 #include <stddef.h>
4 #include "init.h"
5 #include "lib.h"
6 #include "mmu.h"
7 #include "palloc.h"
8
9 static uint32_t *base_page_dir;
10
11 static uint32_t make_pde (uint32_t *pt) {
12   ASSERT (pg_ofs (pt) == 0);
13   return vtop (pt) | PG_U | PG_P | PG_W;
14 }
15
16 static uint32_t make_kernel_pte (uint32_t *page, bool writable) {
17   ASSERT (pg_ofs (page) == 0);
18   return vtop (page) | PG_P | (writable ? PG_W : 0);
19 }
20
21 static uint32_t make_user_pte (uint32_t *page, bool writable) {
22   return make_kernel_pte (page, writable) | PG_U;
23 }
24
25 static uint32_t *
26 pde_get_pt (uint32_t pde) 
27 {
28   ASSERT (pde & PG_P);
29
30   return ptov (pde & ~PGMASK);
31 }
32
33 static void *
34 pte_get_page (uint32_t pte) 
35 {
36   ASSERT (pte & PG_P);
37   
38   return ptov (pte & ~PGMASK);
39 }
40
41 /* Populates the base page directory and page table with the
42    kernel virtual mapping, and then sets up the CPU to use the
43    new page directory.
44
45    At the time this function is called, the active page table
46    only maps the first 4 MB of RAM, so it should not try to use
47    extravagant amounts of memory.  Fortunately, there is no need
48    to do so. */
49 void
50 paging_init (void)
51 {
52   uint32_t *pd, *pt;
53   size_t page;
54
55   pd = base_page_dir = palloc_get (PAL_ASSERT | PAL_ZERO);
56   pt = NULL;
57   for (page = 0; page < ram_pages; page++) 
58     {
59       uintptr_t paddr = page * PGSIZE;
60       void *vaddr = ptov (paddr);
61       size_t pde_idx = pd_no (vaddr);
62       size_t pte_idx = pt_no (vaddr);
63
64       if (pd[pde_idx] == 0)
65         {
66           pt = palloc_get (PAL_ASSERT | PAL_ZERO);
67           pd[pde_idx] = make_pde (pt);
68         }
69
70       pt[pte_idx] = make_kernel_pte (vaddr, true);
71     }
72
73   pagedir_activate (pd);
74 }
75
76 uint32_t *
77 pagedir_create (void) 
78 {
79   uint32_t *pd = palloc_get (0);
80   memcpy (pd, base_page_dir, PGSIZE);
81   return pd;
82 }
83
84 void
85 pagedir_destroy (uint32_t *pd) 
86 {
87   void *kpage, *upage;
88
89   for (kpage = pagedir_first (pd, &upage); kpage != NULL;
90        kpage = pagedir_next (pd, &upage)) 
91     palloc_free (kpage);
92   palloc_free (pd);
93 }
94
95 static uint32_t *
96 lookup_page (uint32_t *pd, void *upage, bool create)
97 {
98   uint32_t *pt;
99   uint32_t *pde;
100
101   ASSERT (pd != NULL);
102   ASSERT (pg_ofs (upage) == 0);
103   ASSERT (upage < PHYS_BASE);
104
105   /* Check for a page table for UPAGE.
106      If one is missing, create one if requested. */
107   pde = pd + pd_no (upage);
108   if (*pde == 0) 
109     {
110       if (create)
111         {
112           pt = palloc_get (PAL_ZERO);
113           if (pt == NULL) 
114             return NULL; 
115       
116           *pde = make_pde (pt);
117         }
118       else
119         return NULL;
120     }
121
122   /* Return the page table entry. */
123   pt = pde_get_pt (*pde);
124   return &pt[pt_no (upage)];
125 }
126
127 bool
128 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
129                   bool writable) 
130 {
131   uint32_t *pte;
132
133   ASSERT (pg_ofs (kpage) == 0);
134
135   pte = lookup_page (pd, upage, true);
136   if (pte != NULL) 
137     {
138       *pte = make_user_pte (kpage, writable);
139       return true;
140     }
141   else
142     return false;
143 }
144
145 void *
146 pagedir_get_page (uint32_t *pd, void *upage) 
147 {
148   uint32_t *pte = lookup_page (pd, upage, false);
149   return pte != NULL && *pte != 0 ? pte_get_page (*pte) : NULL;
150 }
151
152 void
153 pagedir_clear_page (uint32_t *pd, void *upage)
154 {
155   uint32_t *pte = lookup_page (pd, upage, false);
156   if (pte != NULL)
157     *pte = 0;
158 }
159
160 static uint32_t *
161 scan_pt (uint32_t *pt, unsigned pde_idx, unsigned pte_idx, void **upage) 
162 {
163   for (; pte_idx < PGSIZE / sizeof *pt; pte_idx++) 
164     {
165       uint32_t pte = pt[pte_idx];
166
167       if (pte != 0) 
168         {
169           void *kpage = pte_get_page (pte);
170           if (kpage != NULL) 
171             {
172               *upage = (void *) ((pde_idx << PDSHIFT) | (pte_idx << PTSHIFT));
173               return kpage;
174             }
175         }
176     }
177   
178   return NULL;
179 }
180
181 static void *
182 scan_pd (uint32_t *pd, unsigned pde_idx, void **upage) 
183 {
184   for (; pde_idx < pd_no (PHYS_BASE); pde_idx++) 
185     {
186       uint32_t pde = pd[pde_idx];
187
188       if (pde != 0) 
189         {
190           void *kpage = scan_pt (pde_get_pt (pde), pde_idx, 0, upage);
191           if (kpage != NULL)
192             return kpage;
193         }
194     }
195   
196   return NULL;
197 }
198
199 void *
200 pagedir_first (uint32_t *pd, void **upage) 
201 {
202   return scan_pd (pd, 0, upage);
203 }
204
205 void *
206 pagedir_next (uint32_t *pd, void **upage) 
207 {
208   unsigned pde_idx, pte_idx;
209   void *kpage;
210
211   pde_idx = pd_no (*upage);
212   pte_idx = pt_no (*upage);
213   kpage = scan_pt (pde_get_pt (pd[pde_idx]),
214                    pde_idx, pte_idx + 1, upage);
215   if (kpage == NULL)
216     kpage = scan_pd (pd, pde_idx + 1, upage);
217   return kpage;
218 }
219
220 void
221 pagedir_activate (uint32_t *pd) 
222 {
223   asm volatile ("movl %0,%%cr3" :: "r" (vtop (pd)));
224 }