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