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