Improve mmu.h.
[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 (pg_ofs (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 (pg_ofs (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 (pde & ~PGMASK);
38 }
39
40 static void *
41 pte_get_page (uint32_t pte) 
42 {
43   ASSERT (pte & PG_P);
44   
45   return ptov (pte & ~PGMASK);
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 * PGSIZE;
67       void *vaddr = ptov (paddr);
68       size_t pde_idx = pd_no (vaddr);
69       size_t pte_idx = pt_no (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, PGSIZE);
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 (pg_ofs (upage) == 0);
111   ASSERT (upage < PHYS_BASE);
112
113   /* Check for a page table for UPAGE.
114      If one is missing, create one if requested. */
115   pde = pagedir + pd_no (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[pt_no (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 (pg_ofs (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 < PGSIZE / 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) | (pte_idx << PTSHIFT));
181               return kpage;
182             }
183         }
184     }
185   
186   return NULL;
187 }
188
189 static void *
190 scan_pd (uint32_t *pd, unsigned pde_idx, void **upage) 
191 {
192   for (; pde_idx < pd_no (PHYS_BASE); pde_idx++) 
193     {
194       uint32_t pde = pd[pde_idx];
195
196       if (pde != 0) 
197         {
198           void *kpage = scan_pt (pde_get_pagetab (pde), pde_idx, 0, upage);
199           if (kpage != NULL)
200             return kpage;
201         }
202     }
203   
204   return NULL;
205 }
206
207 void *
208 pagedir_first (uint32_t *pagedir, void **upage) 
209 {
210   return scan_pd (pagedir, 0, upage);
211 }
212
213 void *
214 pagedir_next (uint32_t *pd, void **upage) 
215 {
216   unsigned pde_idx, pte_idx;
217   void *kpage;
218
219   pde_idx = pd_no (*upage);
220   pte_idx = pt_no (*upage);
221   kpage = scan_pt (pde_get_pagetab (pd[pde_idx]),
222                    pde_idx, pte_idx + 1, upage);
223   if (kpage == NULL)
224     kpage = scan_pd (pd, pde_idx + 1, upage);
225   return kpage;
226 }
227
228 void pagedir_activate (uint32_t *pagedir);