2fa549cd9424c73a0abc133b506aabf0cc13fe20
[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   pagedir_activate (pd);
81 }
82
83 uint32_t *
84 pagedir_create (void) 
85 {
86   uint32_t *pd = palloc_get (0);
87   memcpy (pd, base_page_dir, PGSIZE);
88   return pd;
89 }
90
91 void
92 pagedir_destroy (uint32_t *pd) 
93 {
94   void *kpage, *upage;
95
96   for (kpage = pagedir_first (pd, &upage); kpage != NULL;
97        kpage = pagedir_next (pd, &upage)) 
98     palloc_free (kpage);
99   palloc_free (pd);
100 }
101
102 static uint32_t *
103 lookup_page (uint32_t *pagedir, void *upage, bool create)
104 {
105   uint32_t *pagetab;
106   uint32_t *pde;
107
108   ASSERT (pagedir != NULL);
109   ASSERT (pg_ofs (upage) == 0);
110   ASSERT (upage < PHYS_BASE);
111
112   /* Check for a page table for UPAGE.
113      If one is missing, create one if requested. */
114   pde = pagedir + pd_no (upage);
115   if (*pde == 0) 
116     {
117       if (create)
118         {
119           pagetab = palloc_get (PAL_ZERO);
120           if (pagetab == NULL) 
121             return NULL; 
122       
123           *pde = make_pde (pagetab);
124         }
125       else
126         return NULL;
127     }
128
129   /* Return the page table entry. */
130   pagetab = pde_get_pagetab (*pde);
131   return &pagetab[pt_no (upage)];
132 }
133
134 bool
135 pagedir_set_page (uint32_t *pagedir, void *upage, void *kpage,
136                   bool writable) 
137 {
138   uint32_t *pte;
139
140   ASSERT (pg_ofs (kpage) == 0);
141
142   pte = lookup_page (pagedir, upage, true);
143   if (pte != NULL) 
144     {
145       *pte = make_pte (kpage, writable);
146       return true;
147     }
148   else
149     return false;
150 }
151
152 void *
153 pagedir_get_page (uint32_t *pagedir, void *upage) 
154 {
155   uint32_t *pte = lookup_page (pagedir, upage, false);
156   return pte != NULL && *pte != 0 ? pte_get_page (*pte) : NULL;
157 }
158
159 void
160 pagedir_clear_page (uint32_t *pagedir, void *upage)
161 {
162   uint32_t *pte = lookup_page (pagedir, upage, false);
163   if (pte != NULL)
164     *pte = 0;
165 }
166
167 static uint32_t *
168 scan_pt (uint32_t *pt, unsigned pde_idx, unsigned pte_idx, void **upage) 
169 {
170   for (; pte_idx < PGSIZE / sizeof *pt; pte_idx++) 
171     {
172       uint32_t pte = pt[pte_idx];
173
174       if (pte != 0) 
175         {
176           void *kpage = pte_get_page (pte);
177           if (kpage != NULL) 
178             {
179               *upage = (void *) ((pde_idx << PDSHIFT) | (pte_idx << PTSHIFT));
180               return kpage;
181             }
182         }
183     }
184   
185   return NULL;
186 }
187
188 static void *
189 scan_pd (uint32_t *pd, unsigned pde_idx, void **upage) 
190 {
191   for (; pde_idx < pd_no (PHYS_BASE); pde_idx++) 
192     {
193       uint32_t pde = pd[pde_idx];
194
195       if (pde != 0) 
196         {
197           void *kpage = scan_pt (pde_get_pagetab (pde), pde_idx, 0, upage);
198           if (kpage != NULL)
199             return kpage;
200         }
201     }
202   
203   return NULL;
204 }
205
206 void *
207 pagedir_first (uint32_t *pagedir, void **upage) 
208 {
209   return scan_pd (pagedir, 0, upage);
210 }
211
212 void *
213 pagedir_next (uint32_t *pd, void **upage) 
214 {
215   unsigned pde_idx, pte_idx;
216   void *kpage;
217
218   pde_idx = pd_no (*upage);
219   pte_idx = pt_no (*upage);
220   kpage = scan_pt (pde_get_pagetab (pd[pde_idx]),
221                    pde_idx, pte_idx + 1, upage);
222   if (kpage == NULL)
223     kpage = scan_pd (pd, pde_idx + 1, upage);
224   return kpage;
225 }
226
227 void
228 pagedir_activate (uint32_t *pagedir) 
229 {
230   asm volatile ("movl %0,%%cr3" :: "r" (vtop (pagedir)));
231 }