Fixed user_mem_check()
[akaros.git] / kern / arch / sparc / pmap.c
1 #ifdef __DEPUTY__
2 #pragma nodeputy
3 #endif
4
5 #include <arch/mmu.h>
6 #include <ros/memlayout.h>
7 #include <pmap.h>
8 #include <string.h>
9 #include <kmalloc.h>
10
11 physaddr_t boot_cr3;
12 pde_t* boot_pgdir;
13 page_t* pages;
14 page_list_t page_free_list;
15
16 void
17 vm_init(void)
18 {
19         // we already set up our page tables before jumping
20         // into the kernel, so there's not much going on here
21
22         extern pde_t l1_page_table[NL1ENTRIES];
23         boot_pgdir = l1_page_table;
24         boot_cr3 = PADDR(boot_pgdir);
25
26         size_t env_array_size = ROUNDUP(NENV*sizeof(env_t), PGSIZE);
27         envs = /*(env_t *)*/boot_calloc(env_array_size, PGSIZE);
28         //memset(envs, 0, env_array_size);
29 }
30
31 error_t
32 pagetable_remove(pde_t* l1pt, void* va)
33 {
34         panic("pagetable_remove doesn't work yet... -asw");
35         return 0;
36 }
37
38 pte_t*
39 pgdir_walk(pde_t* l1pt, const void*SNT va, int create)
40 {
41         pte_t *l1pte, *l2pt, *l2pte, *l3pt, *l3pte;
42         page_t* new_table;
43
44         l1pte = &l1pt[L1X(va)];
45         if(*l1pte & PTE_PTE)
46                 return l1pte;
47         if(!(*l1pte & PTE_PTD))
48         {
49                 int i, l1x_start, l2_tables_per_page;
50                 physaddr_t pa;
51
52                 if(!create)
53                         return NULL;
54
55                 // create a new L2 PT.  we actually allocated way more
56                 // space than needed, so also use it for the adjacent
57                 // l2_tables_per_page-1 pages (if they're unmapped)
58
59                 if(page_alloc(&new_table))
60                         return NULL;
61                 page_setref(new_table,1);
62                 memset(page2kva(new_table),0,PGSIZE);
63
64                 l2_tables_per_page = PGSIZE/(sizeof(pte_t)*NL2ENTRIES);
65                 l1x_start = L1X(va)/l2_tables_per_page*l2_tables_per_page;
66
67                 for(i = 0; i < l2_tables_per_page; i++)
68                 {
69                         if(l1pt[l1x_start+i] != 0)
70                                 continue;
71
72                         page_incref(new_table);
73                         pa = page2pa(new_table) + i*sizeof(pte_t)*NL2ENTRIES;
74                         l1pt[l1x_start+i] = PTD(pa);
75                 }
76
77                 l1pte = &l1pt[L1X(va)];
78         }
79
80         l2pt = (pte_t*)KADDR(PTD_ADDR(*l1pte));
81         l2pte = &l2pt[L2X(va)];
82         if(*l2pte & PTE_PTE)
83                 return l2pte;
84         if(!(*l2pte & PTE_PTD))
85         {
86                 int i, l2x_start, l3_tables_per_page;
87                 physaddr_t pa;
88
89                 if(!create)
90                         return NULL;
91
92                 if(page_alloc(&new_table))
93                         return NULL;
94                 page_setref(new_table,1);
95                 memset(page2kva(new_table),0,PGSIZE);
96
97                 l3_tables_per_page = PGSIZE/(sizeof(pte_t)*NL3ENTRIES);
98                 l2x_start = L2X(va)/l3_tables_per_page*l3_tables_per_page;
99
100                 for(i = 0; i < l3_tables_per_page; i++)
101                 {
102                         if(l2pt[l2x_start+i] != 0)
103                                 continue;
104
105                         page_incref(new_table);
106                         pa = page2pa(new_table) + i*sizeof(pte_t)*NL3ENTRIES;
107                         l2pt[l2x_start+i] = PTD(pa);
108                 }
109
110                 l2pte = &l2pt[L2X(va)];
111         }
112
113         l3pt = (pte_t*)KADDR(PTD_ADDR(*l2pte));
114         l3pte = &l3pt[L3X(va)];
115         return l3pte;
116 }
117
118 /* TODO: this is probably wrong, since it only returns the pte as if it were the
119  * perms. */
120 int get_va_perms(pde_t *pgdir, const void *SNT va)
121 {
122         return (int)pgdir_walk(pgdir, va, 0);
123 }
124
125 void
126 page_check(void)
127 {
128 /*
129         page_t *pp, *pp0, *pp1, *pp2;
130         page_list_t fl;
131         pte_t *ptep;
132
133         // should be able to allocate three pages
134         pp0 = pp1 = pp2 = 0;
135         assert(page_alloc(&pp0) == 0);
136         assert(page_alloc(&pp1) == 0);
137         assert(page_alloc(&pp2) == 0);
138
139         assert(pp0);
140         assert(pp1 && pp1 != pp0);
141         assert(pp2 && pp2 != pp1 && pp2 != pp0);
142
143         // temporarily steal the rest of the free pages
144         fl = page_free_list;
145         LIST_INIT(&page_free_list);
146
147         // should be no free memory
148         assert(page_alloc(&pp) == -ENOMEM);
149
150         // Fill pp1 with bogus data and check for invalid tlb entries
151         memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
152
153         // there is no page allocated at address 0
154         assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
155
156         // there is no free memory, so we can't allocate a page table 
157         assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
158
159         // free pp0 and try again: pp0 should be used for page table
160         page_free(pp0);
161         assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
162         tlb_invalidate(boot_pgdir, 0x0);
163         // DEP Should have shot down invalid TLB entry - let's check
164         {
165           int *x = 0x0;
166           assert(*x == 0xFFFFFFFF);
167         }
168         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
169         assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
170         assert(pp1->page_ref == 1);
171         assert(pp0->page_ref == 1);
172
173         // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
174         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
175         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
176         assert(pp2->page_ref == 1);
177
178         // Make sure that pgdir_walk returns a pointer to the pte and
179         // not the table or some other garbage
180         {
181           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(PGSIZE)]));
182           assert(pgdir_walk(boot_pgdir, (void *)PGSIZE, 0) == &p[PTX(PGSIZE)]);
183         }
184
185         // should be no free memory
186         assert(page_alloc(&pp) == -ENOMEM);
187
188         // should be able to map pp2 at PGSIZE because it's already there
189         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0);
190         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
191         assert(pp2->page_ref == 1);
192
193         // Make sure that we actually changed the permission on pp2 when we re-mapped it
194         {
195           pte_t *p = pgdir_walk(boot_pgdir, (void*)PGSIZE, 0);
196           assert(((*p) & PTE_U) == PTE_U);
197         }
198
199         // pp2 should NOT be on the free list
200         // could happen in ref counts are handled sloppily in page_insert
201         assert(page_alloc(&pp) == -ENOMEM);
202
203         // should not be able to map at PTSIZE because need free page for page table
204         assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
205
206         // insert pp1 at PGSIZE (replacing pp2)
207         assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
208
209         // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
210         assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
211         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
212         // ... and ref counts should reflect this
213         assert(pp1->page_ref == 2);
214         assert(pp2->page_ref == 0);
215
216         // pp2 should be returned by page_alloc
217         assert(page_alloc(&pp) == 0 && pp == pp2);
218
219         // unmapping pp1 at 0 should keep pp1 at PGSIZE
220         page_remove(boot_pgdir, 0x0);
221         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
222         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
223         assert(pp1->page_ref == 1);
224         assert(pp2->page_ref == 0);
225
226         // unmapping pp1 at PGSIZE should free it
227         page_remove(boot_pgdir, (void*) PGSIZE);
228         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
229         assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
230         assert(pp1->page_ref == 0);
231         assert(pp2->page_ref == 0);
232
233         // so it should be returned by page_alloc
234         assert(page_alloc(&pp) == 0 && pp == pp1);
235
236         // should be no free memory
237         assert(page_alloc(&pp) == -ENOMEM);
238
239         // forcibly take pp0 back
240         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
241         boot_pgdir[0] = 0;
242         assert(pp0->page_ref == 1);
243         pp0->page_ref = 0;
244
245         // Catch invalid pointer addition in pgdir_walk - i.e. pgdir + PDX(va)
246         {
247           // Give back pp0 for a bit
248           page_free(pp0);
249
250           void * va = (void *)((PGSIZE * NPDENTRIES) + PGSIZE);
251           pte_t *p2 = pgdir_walk(boot_pgdir, va, 1);
252           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(va)]));
253           assert(p2 == &p[PTX(va)]);
254
255           // Clean up again
256           boot_pgdir[PDX(va)] = 0;
257           pp0->page_ref = 0;
258         }
259
260         // give free list back
261         page_free_list = fl;
262
263         // free the pages we took
264         page_free(pp0);
265         page_free(pp1);
266         page_free(pp2);
267
268         cprintf("page_check() succeeded!\n");
269 */
270 }