Merge branch 'sparc-dev' of ssh://waterman@scm.millennium.berkeley.edu/project/cs...
[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(kpage_alloc(&new_table))
64                         return NULL;
65                 memset(page2kva(new_table),0,PGSIZE);
66
67                 l2_tables_per_page = PGSIZE/(sizeof(pte_t)*NL2ENTRIES);
68                 l1x_start = L1X(va)/l2_tables_per_page*l2_tables_per_page;
69
70                 for(i = 0; i < l2_tables_per_page; i++)
71                 {
72                         if(l1pt[l1x_start+i] != 0)
73                                 continue;
74
75                         page_incref(new_table);
76                         pa = page2pa(new_table) + i*sizeof(pte_t)*NL2ENTRIES;
77                         l1pt[l1x_start+i] = PTD(pa);
78                 }
79
80                 l1pte = &l1pt[L1X(va)];
81         }
82
83         l2pt = (pte_t*)KADDR(PTD_ADDR(*l1pte));
84         l2pte = &l2pt[L2X(va)];
85         if(*l2pte & PTE_PTE)
86                 return l2pte;
87         if(!(*l2pte & PTE_PTD))
88         {
89                 int i, l2x_start, l3_tables_per_page;
90                 physaddr_t pa;
91
92                 if(!create)
93                         return NULL;
94
95                 if(kpage_alloc(&new_table))
96                         return NULL;
97                 memset(page2kva(new_table),0,PGSIZE);
98
99                 l3_tables_per_page = PGSIZE/(sizeof(pte_t)*NL3ENTRIES);
100                 l2x_start = L2X(va)/l3_tables_per_page*l3_tables_per_page;
101
102                 for(i = 0; i < l3_tables_per_page; i++)
103                 {
104                         if(l2pt[l2x_start+i] != 0)
105                                 continue;
106
107                         page_incref(new_table);
108                         pa = page2pa(new_table) + i*sizeof(pte_t)*NL3ENTRIES;
109                         l2pt[l2x_start+i] = PTD(pa);
110                 }
111
112                 l2pte = &l2pt[L2X(va)];
113         }
114
115         l3pt = (pte_t*)KADDR(PTD_ADDR(*l2pte));
116         l3pte = &l3pt[L3X(va)];
117         return l3pte;
118 }
119
120 /* TODO: this is probably wrong, since it only returns the pte as if it were the
121  * perms. */
122 int get_va_perms(pde_t *pgdir, const void *SNT va)
123 {
124         pte_t* pte = pgdir_walk(pgdir, va, 0);
125         return pte == NULL ? 0 : (*pte & (PTE_ACC | PTE_PTE));
126 }
127
128 void *get_free_va_range(pde_t *pgdir, uintptr_t addr, size_t len)
129 {
130         // SARAH TODO
131         assert(0);
132 }
133
134
135
136
137 void
138 page_check(void)
139 {
140 /*
141         page_t *pp, *pp0, *pp1, *pp2;
142         page_list_t fl;
143         pte_t *ptep;
144
145         // should be able to allocate three pages
146         pp0 = pp1 = pp2 = 0;
147         assert(page_alloc(&pp0) == 0);
148         assert(page_alloc(&pp1) == 0);
149         assert(page_alloc(&pp2) == 0);
150
151         assert(pp0);
152         assert(pp1 && pp1 != pp0);
153         assert(pp2 && pp2 != pp1 && pp2 != pp0);
154
155         // temporarily steal the rest of the free pages
156         fl = page_free_list;
157         LIST_INIT(&page_free_list);
158
159         // should be no free memory
160         assert(page_alloc(&pp) == -ENOMEM);
161
162         // Fill pp1 with bogus data and check for invalid tlb entries
163         memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
164
165         // there is no page allocated at address 0
166         assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
167
168         // there is no free memory, so we can't allocate a page table 
169         assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
170
171         // free pp0 and try again: pp0 should be used for page table
172         page_free(pp0);
173         assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
174         tlb_invalidate(boot_pgdir, 0x0);
175         // DEP Should have shot down invalid TLB entry - let's check
176         {
177           int *x = 0x0;
178           assert(*x == 0xFFFFFFFF);
179         }
180         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
181         assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
182         assert(pp1->page_ref == 1);
183         assert(pp0->page_ref == 1);
184
185         // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
186         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
187         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
188         assert(pp2->page_ref == 1);
189
190         // Make sure that pgdir_walk returns a pointer to the pte and
191         // not the table or some other garbage
192         {
193           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(PGSIZE)]));
194           assert(pgdir_walk(boot_pgdir, (void *)PGSIZE, 0) == &p[PTX(PGSIZE)]);
195         }
196
197         // should be no free memory
198         assert(page_alloc(&pp) == -ENOMEM);
199
200         // should be able to map pp2 at PGSIZE because it's already there
201         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0);
202         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
203         assert(pp2->page_ref == 1);
204
205         // Make sure that we actually changed the permission on pp2 when we re-mapped it
206         {
207           pte_t *p = pgdir_walk(boot_pgdir, (void*)PGSIZE, 0);
208           assert(((*p) & PTE_U) == PTE_U);
209         }
210
211         // pp2 should NOT be on the free list
212         // could happen in ref counts are handled sloppily in page_insert
213         assert(page_alloc(&pp) == -ENOMEM);
214
215         // should not be able to map at PTSIZE because need free page for page table
216         assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
217
218         // insert pp1 at PGSIZE (replacing pp2)
219         assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
220
221         // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
222         assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
223         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
224         // ... and ref counts should reflect this
225         assert(pp1->page_ref == 2);
226         assert(pp2->page_ref == 0);
227
228         // pp2 should be returned by page_alloc
229         assert(page_alloc(&pp) == 0 && pp == pp2);
230
231         // unmapping pp1 at 0 should keep pp1 at PGSIZE
232         page_remove(boot_pgdir, 0x0);
233         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
234         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
235         assert(pp1->page_ref == 1);
236         assert(pp2->page_ref == 0);
237
238         // unmapping pp1 at PGSIZE should free it
239         page_remove(boot_pgdir, (void*) PGSIZE);
240         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
241         assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
242         assert(pp1->page_ref == 0);
243         assert(pp2->page_ref == 0);
244
245         // so it should be returned by page_alloc
246         assert(page_alloc(&pp) == 0 && pp == pp1);
247
248         // should be no free memory
249         assert(page_alloc(&pp) == -ENOMEM);
250
251         // forcibly take pp0 back
252         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
253         boot_pgdir[0] = 0;
254         assert(pp0->page_ref == 1);
255         pp0->page_ref = 0;
256
257         // Catch invalid pointer addition in pgdir_walk - i.e. pgdir + PDX(va)
258         {
259           // Give back pp0 for a bit
260           page_free(pp0);
261
262           void * va = (void *)((PGSIZE * NPDENTRIES) + PGSIZE);
263           pte_t *p2 = pgdir_walk(boot_pgdir, va, 1);
264           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(va)]));
265           assert(p2 == &p[PTX(va)]);
266
267           // Clean up again
268           boot_pgdir[PDX(va)] = 0;
269           pp0->page_ref = 0;
270         }
271
272         // give free list back
273         page_free_list = fl;
274
275         // free the pages we took
276         page_free(pp0);
277         page_free(pp1);
278         page_free(pp2);
279
280         cprintf("page_check() succeeded!\n");
281 */
282 }