Proc data structure management, env gutting
[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
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         pte_t* pte = pgdir_walk(pgdir, va, 0);
123         return pte == NULL ? 0 : (*pte & (PTE_ACC | PTE_PTE));
124 }
125
126 void *get_free_va_range(pde_t *pgdir, uintptr_t addr, size_t len)
127 {
128         // SARAH TODO
129         assert(0);
130 }
131
132
133
134
135 void
136 page_check(void)
137 {
138 /*
139         page_t *pp, *pp0, *pp1, *pp2;
140         page_list_t fl;
141         pte_t *ptep;
142
143         // should be able to allocate three pages
144         pp0 = pp1 = pp2 = 0;
145         assert(page_alloc(&pp0) == 0);
146         assert(page_alloc(&pp1) == 0);
147         assert(page_alloc(&pp2) == 0);
148
149         assert(pp0);
150         assert(pp1 && pp1 != pp0);
151         assert(pp2 && pp2 != pp1 && pp2 != pp0);
152
153         // temporarily steal the rest of the free pages
154         fl = page_free_list;
155         LIST_INIT(&page_free_list);
156
157         // should be no free memory
158         assert(page_alloc(&pp) == -ENOMEM);
159
160         // Fill pp1 with bogus data and check for invalid tlb entries
161         memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
162
163         // there is no page allocated at address 0
164         assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
165
166         // there is no free memory, so we can't allocate a page table 
167         assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
168
169         // free pp0 and try again: pp0 should be used for page table
170         page_free(pp0);
171         assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
172         tlb_invalidate(boot_pgdir, 0x0);
173         // DEP Should have shot down invalid TLB entry - let's check
174         {
175           int *x = 0x0;
176           assert(*x == 0xFFFFFFFF);
177         }
178         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
179         assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
180         assert(pp1->page_ref == 1);
181         assert(pp0->page_ref == 1);
182
183         // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
184         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
185         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
186         assert(pp2->page_ref == 1);
187
188         // Make sure that pgdir_walk returns a pointer to the pte and
189         // not the table or some other garbage
190         {
191           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(PGSIZE)]));
192           assert(pgdir_walk(boot_pgdir, (void *)PGSIZE, 0) == &p[PTX(PGSIZE)]);
193         }
194
195         // should be no free memory
196         assert(page_alloc(&pp) == -ENOMEM);
197
198         // should be able to map pp2 at PGSIZE because it's already there
199         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0);
200         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
201         assert(pp2->page_ref == 1);
202
203         // Make sure that we actually changed the permission on pp2 when we re-mapped it
204         {
205           pte_t *p = pgdir_walk(boot_pgdir, (void*)PGSIZE, 0);
206           assert(((*p) & PTE_U) == PTE_U);
207         }
208
209         // pp2 should NOT be on the free list
210         // could happen in ref counts are handled sloppily in page_insert
211         assert(page_alloc(&pp) == -ENOMEM);
212
213         // should not be able to map at PTSIZE because need free page for page table
214         assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
215
216         // insert pp1 at PGSIZE (replacing pp2)
217         assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
218
219         // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
220         assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
221         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
222         // ... and ref counts should reflect this
223         assert(pp1->page_ref == 2);
224         assert(pp2->page_ref == 0);
225
226         // pp2 should be returned by page_alloc
227         assert(page_alloc(&pp) == 0 && pp == pp2);
228
229         // unmapping pp1 at 0 should keep pp1 at PGSIZE
230         page_remove(boot_pgdir, 0x0);
231         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
232         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
233         assert(pp1->page_ref == 1);
234         assert(pp2->page_ref == 0);
235
236         // unmapping pp1 at PGSIZE should free it
237         page_remove(boot_pgdir, (void*) PGSIZE);
238         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
239         assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
240         assert(pp1->page_ref == 0);
241         assert(pp2->page_ref == 0);
242
243         // so it should be returned by page_alloc
244         assert(page_alloc(&pp) == 0 && pp == pp1);
245
246         // should be no free memory
247         assert(page_alloc(&pp) == -ENOMEM);
248
249         // forcibly take pp0 back
250         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
251         boot_pgdir[0] = 0;
252         assert(pp0->page_ref == 1);
253         pp0->page_ref = 0;
254
255         // Catch invalid pointer addition in pgdir_walk - i.e. pgdir + PDX(va)
256         {
257           // Give back pp0 for a bit
258           page_free(pp0);
259
260           void * va = (void *)((PGSIZE * NPDENTRIES) + PGSIZE);
261           pte_t *p2 = pgdir_walk(boot_pgdir, va, 1);
262           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(va)]));
263           assert(p2 == &p[PTX(va)]);
264
265           // Clean up again
266           boot_pgdir[PDX(va)] = 0;
267           pp0->page_ref = 0;
268         }
269
270         // give free list back
271         page_free_list = fl;
272
273         // free the pages we took
274         page_free(pp0);
275         page_free(pp1);
276         page_free(pp2);
277
278         cprintf("page_check() succeeded!\n");
279 */
280 }