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