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