Added page-coloring support to SPARC port
[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 <pmap.h>
8 #include <string.h>
9 #include <kmalloc.h>
10
11 physaddr_t boot_cr3;
12 pde_t* boot_pgdir;
13 page_t* pages;
14 page_list_t page_free_list;
15
16 void
17 vm_init(void)
18 {
19         // we already set up our page tables before jumping
20         // into the kernel, so there's not much going on here
21
22         extern pde_t l1_page_table[NL1ENTRIES];
23         boot_pgdir = l1_page_table;
24         boot_cr3 = PADDR(boot_pgdir);
25
26         size_t env_array_size = ROUNDUP(NENV*sizeof(env_t), PGSIZE);
27         envs = (env_t *)boot_alloc(env_array_size, PGSIZE);
28         memset(envs, 0, env_array_size);
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 void
118 page_check(void)
119 {
120 /*
121         page_t *pp, *pp0, *pp1, *pp2;
122         page_list_t fl;
123         pte_t *ptep;
124
125         // should be able to allocate three pages
126         pp0 = pp1 = pp2 = 0;
127         assert(page_alloc(&pp0) == 0);
128         assert(page_alloc(&pp1) == 0);
129         assert(page_alloc(&pp2) == 0);
130
131         assert(pp0);
132         assert(pp1 && pp1 != pp0);
133         assert(pp2 && pp2 != pp1 && pp2 != pp0);
134
135         // temporarily steal the rest of the free pages
136         fl = page_free_list;
137         LIST_INIT(&page_free_list);
138
139         // should be no free memory
140         assert(page_alloc(&pp) == -ENOMEM);
141
142         // Fill pp1 with bogus data and check for invalid tlb entries
143         memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
144
145         // there is no page allocated at address 0
146         assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
147
148         // there is no free memory, so we can't allocate a page table 
149         assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
150
151         // free pp0 and try again: pp0 should be used for page table
152         page_free(pp0);
153         assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
154         tlb_invalidate(boot_pgdir, 0x0);
155         // DEP Should have shot down invalid TLB entry - let's check
156         {
157           int *x = 0x0;
158           assert(*x == 0xFFFFFFFF);
159         }
160         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
161         assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
162         assert(pp1->page_ref == 1);
163         assert(pp0->page_ref == 1);
164
165         // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
166         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
167         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
168         assert(pp2->page_ref == 1);
169
170         // Make sure that pgdir_walk returns a pointer to the pte and
171         // not the table or some other garbage
172         {
173           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(PGSIZE)]));
174           assert(pgdir_walk(boot_pgdir, (void *)PGSIZE, 0) == &p[PTX(PGSIZE)]);
175         }
176
177         // should be no free memory
178         assert(page_alloc(&pp) == -ENOMEM);
179
180         // should be able to map pp2 at PGSIZE because it's already there
181         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0);
182         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
183         assert(pp2->page_ref == 1);
184
185         // Make sure that we actually changed the permission on pp2 when we re-mapped it
186         {
187           pte_t *p = pgdir_walk(boot_pgdir, (void*)PGSIZE, 0);
188           assert(((*p) & PTE_U) == PTE_U);
189         }
190
191         // pp2 should NOT be on the free list
192         // could happen in ref counts are handled sloppily in page_insert
193         assert(page_alloc(&pp) == -ENOMEM);
194
195         // should not be able to map at PTSIZE because need free page for page table
196         assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
197
198         // insert pp1 at PGSIZE (replacing pp2)
199         assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
200
201         // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
202         assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
203         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
204         // ... and ref counts should reflect this
205         assert(pp1->page_ref == 2);
206         assert(pp2->page_ref == 0);
207
208         // pp2 should be returned by page_alloc
209         assert(page_alloc(&pp) == 0 && pp == pp2);
210
211         // unmapping pp1 at 0 should keep pp1 at PGSIZE
212         page_remove(boot_pgdir, 0x0);
213         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
214         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
215         assert(pp1->page_ref == 1);
216         assert(pp2->page_ref == 0);
217
218         // unmapping pp1 at PGSIZE should free it
219         page_remove(boot_pgdir, (void*) PGSIZE);
220         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
221         assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
222         assert(pp1->page_ref == 0);
223         assert(pp2->page_ref == 0);
224
225         // so it should be returned by page_alloc
226         assert(page_alloc(&pp) == 0 && pp == pp1);
227
228         // should be no free memory
229         assert(page_alloc(&pp) == -ENOMEM);
230
231         // forcibly take pp0 back
232         assert(PTD_ADDR(boot_pgdir[0]) == page2pa(pp0));
233         boot_pgdir[0] = 0;
234         assert(pp0->page_ref == 1);
235         pp0->page_ref = 0;
236
237         // Catch invalid pointer addition in pgdir_walk - i.e. pgdir + PDX(va)
238         {
239           // Give back pp0 for a bit
240           page_free(pp0);
241
242           void * va = (void *)((PGSIZE * NPDENTRIES) + PGSIZE);
243           pte_t *p2 = pgdir_walk(boot_pgdir, va, 1);
244           pte_t *p = KADDR(PTD_ADDR(boot_pgdir[PDX(va)]));
245           assert(p2 == &p[PTX(va)]);
246
247           // Clean up again
248           boot_pgdir[PDX(va)] = 0;
249           pp0->page_ref = 0;
250         }
251
252         // give free list back
253         page_free_list = fl;
254
255         // free the pages we took
256         page_free(pp0);
257         page_free(pp1);
258         page_free(pp2);
259
260         cprintf("page_check() succeeded!\n");
261 */
262 }