1 /*
2 ** Tablewalk MMU emulator
3 **
4 ** by Toshiyasu Morita
5 **
6 ** Started 1/16/98 @ 2:22 am
7 */
8
9 #include <linux/mman.h>
10 #include <linux/mm.h>
11 #include <linux/kernel.h>
12 #include <linux/ptrace.h>
13 #include <linux/delay.h>
14 #include <linux/bootmem.h>
15
16 #include <asm/setup.h>
17 #include <asm/traps.h>
18 #include <asm/system.h>
19 #include <asm/uaccess.h>
20 #include <asm/page.h>
21 #include <asm/pgtable.h>
22 #include <asm/sun3mmu.h>
23 #include <asm/segment.h>
24 #include <asm/bitops.h>
25 #include <asm/oplib.h>
26 #include <asm/mmu_context.h>
27 #include <asm/dvma.h>
28
29 extern void prom_reboot (char *) __attribute__ ((__noreturn__));
30
31 #undef DEBUG_MMU_EMU
32
33 /*
34 ** Defines
35 */
36
37 #define CONTEXTS_NUM 8
38 #define SEGMAPS_PER_CONTEXT_NUM 2048
39 #define PAGES_PER_SEGMENT 16
40 #define PMEGS_NUM 256
41 #define PMEG_MASK 0xFF
42
43 /*
44 ** Globals
45 */
46
47 unsigned long vmalloc_end = 0;
48 unsigned long pmeg_vaddr[PMEGS_NUM];
49 unsigned char pmeg_alloc[PMEGS_NUM];
50 unsigned char pmeg_ctx[PMEGS_NUM];
51
52 /* pointers to the mm structs for each task in each
53 context. 0xffffffff is a marker for kernel context */
54 struct mm_struct *ctx_alloc[CONTEXTS_NUM] = {0xffffffff, 0, 0, 0, 0, 0, 0, 0};
55 /* has this context been mmdrop'd? */
56 static unsigned char ctx_avail = CONTEXTS_NUM-1;
57
58 /* array of pages to be marked off for the rom when we do mem_init later */
59 /* 256 pages lets the rom take up to 2mb of physical ram.. I really
60 hope it never wants mote than that. */
61 unsigned long rom_pages[256];
62
63 /* Print a PTE value in symbolic form. For debugging. */
64 void print_pte (pte_t pte)
65 {
66 #if 0
67 /* Verbose version. */
68 unsigned long val = pte_val (pte);
69 printk (" pte=%lx [addr=%lx",
70 val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT);
71 if (val & SUN3_PAGE_VALID) printk (" valid");
72 if (val & SUN3_PAGE_WRITEABLE) printk (" write");
73 if (val & SUN3_PAGE_SYSTEM) printk (" sys");
74 if (val & SUN3_PAGE_NOCACHE) printk (" nocache");
75 if (val & SUN3_PAGE_ACCESSED) printk (" accessed");
76 if (val & SUN3_PAGE_MODIFIED) printk (" modified");
77 switch (val & SUN3_PAGE_TYPE_MASK) {
78 case SUN3_PAGE_TYPE_MEMORY: printk (" memory"); break;
79 case SUN3_PAGE_TYPE_IO: printk (" io"); break;
80 case SUN3_PAGE_TYPE_VME16: printk (" vme16"); break;
81 case SUN3_PAGE_TYPE_VME32: printk (" vme32"); break;
82 }
83 printk ("]\n");
84 #else
85 /* Terse version. More likely to fit on a line. */
86 unsigned long val = pte_val (pte);
87 char flags[7], *type;
88
89 flags[0] = (val & SUN3_PAGE_VALID) ? 'v' : '-';
90 flags[1] = (val & SUN3_PAGE_WRITEABLE) ? 'w' : '-';
91 flags[2] = (val & SUN3_PAGE_SYSTEM) ? 's' : '-';
92 flags[3] = (val & SUN3_PAGE_NOCACHE) ? 'x' : '-';
93 flags[4] = (val & SUN3_PAGE_ACCESSED) ? 'a' : '-';
94 flags[5] = (val & SUN3_PAGE_MODIFIED) ? 'm' : '-';
95 flags[6] = '\0';
96
97 switch (val & SUN3_PAGE_TYPE_MASK) {
98 case SUN3_PAGE_TYPE_MEMORY: type = "memory"; break;
99 case SUN3_PAGE_TYPE_IO: type = "io" ; break;
100 case SUN3_PAGE_TYPE_VME16: type = "vme16" ; break;
101 case SUN3_PAGE_TYPE_VME32: type = "vme32" ; break;
102 default: type = "unknown?"; break;
103 }
104
105 printk (" pte=%08lx [%07lx %s %s]\n",
106 val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type);
107 #endif
108 }
109
110 /* Print the PTE value for a given virtual address. For debugging. */
111 void print_pte_vaddr (unsigned long vaddr)
112 {
113 printk (" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
114 print_pte (__pte (sun3_get_pte (vaddr)));
115 }
116
117 /*
118 * Initialise the MMU emulator.
119 */
120 void mmu_emu_init(unsigned long bootmem_end)
121 {
122 unsigned long seg, num;
123 int i,j;
124
125 memset(rom_pages, 0, sizeof(rom_pages));
126 memset(pmeg_vaddr, 0, sizeof(pmeg_vaddr));
127 memset(pmeg_alloc, 0, sizeof(pmeg_alloc));
128 memset(pmeg_ctx, 0, sizeof(pmeg_ctx));
129
130 /* pmeg align the end of bootmem, adding another pmeg,
131 * later bootmem allocations will likely need it */
132 bootmem_end = (bootmem_end + (2 * SUN3_PMEG_SIZE)) & ~SUN3_PMEG_MASK;
133
134 /* mark all of the pmegs used thus far as reserved */
135 for (i=0; i < __pa(bootmem_end) / SUN3_PMEG_SIZE ; ++i)
136 pmeg_alloc[i] = 2;
137
138
139 /* I'm thinking that most of the top pmeg's are going to be
140 used for something, and we probably shouldn't risk it */
141 for(num = 0xf0; num <= 0xff; num++)
142 pmeg_alloc[num] = 2;
143
144 /* liberate all existing mappings in the rest of kernel space */
145 for(seg = bootmem_end; seg < 0x0f800000; seg += SUN3_PMEG_SIZE) {
146 i = sun3_get_segmap(seg);
147
148 if(!pmeg_alloc[i]) {
149 #ifdef DEBUG_MMU_EMU
150 printk("freed: ");
151 print_pte_vaddr (seg);
152 #endif
153 sun3_put_segmap(seg, SUN3_INVALID_PMEG);
154 }
155 }
156
157 j = 0;
158 for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) {
159 if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) {
160 #ifdef DEBUG_MMU_EMU
161 printk ("mapped:");
162 print_pte_vaddr (seg);
163 #endif
164 // the lowest mapping here is the end of our
165 // vmalloc region
166 if(!vmalloc_end)
167 vmalloc_end = seg;
168
169 // mark the segmap alloc'd, and reserve any
170 // of the first 0xbff pages the hardware is
171 // already using... does any sun3 support > 24mb?
172 pmeg_alloc[sun3_get_segmap(seg)] = 2;
173 }
174 }
175
176
177 sun3_dvma_init();
178
179
180 /* blank everything below the kernel, and we've got the base
181 mapping to start all the contexts off with... */
182 for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE)
183 sun3_put_segmap(seg, SUN3_INVALID_PMEG);
184
185 set_fs(MAKE_MM_SEG(3));
186 for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) {
187 i = sun3_get_segmap(seg);
188 for(j = 1; j < CONTEXTS_NUM; j++)
189 (*(romvec->pv_setctxt))(j, (void *)seg, i);
190 }
191 set_fs(KERNEL_DS);
192
193 }
194
195 /* erase the mappings for a dead context. Uses the pg_dir for hints
196 as the pmeg tables proved somewhat unreliable, and unmapping all of
197 TASK_SIZE was much slower and no more stable. */
198 /* todo: find a better way to keep track of the pmegs used by a
199 context for when they're cleared */
200 void clear_context(unsigned long context)
201 {
202 unsigned char oldctx;
203 unsigned long i;
204
205 if(context) {
206 if(!ctx_alloc[context])
207 panic("clear_context: context not allocated\n");
208
209 ctx_alloc[context]->context = SUN3_INVALID_CONTEXT;
210 ctx_alloc[context] = (struct mm_struct *)0;
211 ctx_avail++;
212 }
213
214 oldctx = sun3_get_context();
215
216 sun3_put_context(context);
217
218 for(i = 0; i < SUN3_INVALID_PMEG; i++) {
219 if((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) {
220 sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG);
221 pmeg_ctx[i] = 0;
222 pmeg_alloc[i] = 0;
223 pmeg_vaddr[i] = 0;
224 }
225 }
226
227 sun3_put_context(oldctx);
228 }
229
230 /* gets an empty context. if full, kills the next context listed to
231 die first */
232 /* This context invalidation scheme is, well, totally arbitrary, I'm
233 sure it could be much more intellegent... but it gets the job done
234 for now without much overhead in making it's decision. */
235 /* todo: come up with optimized scheme for flushing contexts */
236 unsigned long get_free_context(struct mm_struct *mm)
237 {
238 unsigned long new = 1;
239 static unsigned char next_to_die = 1;
240
241 if(!ctx_avail) {
242 /* kill someone to get our context */
243 new = next_to_die;
244 clear_context(new);
245 next_to_die = (next_to_die + 1) & 0x7;
246 if(!next_to_die)
247 next_to_die++;
248 } else {
249 while(new < CONTEXTS_NUM) {
250 if(ctx_alloc[new])
251 new++;
252 else
253 break;
254 }
255 // check to make sure one was really free...
256 if(new == CONTEXTS_NUM)
257 panic("get_free_context: failed to find free context");
258 }
259
260 ctx_alloc[new] = mm;
261 ctx_avail--;
262
263 return new;
264 }
265
266 /*
267 * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in
268 * `context'. Maintain internal PMEG management structures. This doesn't
269 * actually map the physical address, but does clear the old mappings.
270 */
271 //todo: better allocation scheme? but is extra complexity worthwhile?
272 //todo: only clear old entries if necessary? how to tell?
273
274 static inline void mmu_emu_map_pmeg (int context, int vaddr)
275 {
276 static unsigned char curr_pmeg = 128;
277 int i;
278
279 /* Round address to PMEG boundary. */
280 vaddr &= ~SUN3_PMEG_MASK;
281
282 /* Find a spare one. */
283 while (pmeg_alloc[curr_pmeg] == 2)
284 ++curr_pmeg;
285
286
287 #ifdef DEBUG_MMU_EMU
288 printk("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n",
289 curr_pmeg, context, vaddr);
290 #endif
291
292 /* Invalidate old mapping for the pmeg, if any */
293 if (pmeg_alloc[curr_pmeg] == 1) {
294 sun3_put_context(pmeg_ctx[curr_pmeg]);
295 sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG);
296 sun3_put_context(context);
297 }
298
299 /* Update PMEG management structures. */
300 // don't take pmeg's away from the kernel...
301 if(vaddr >= PAGE_OFFSET) {
302 /* map kernel pmegs into all contexts */
303 unsigned char i;
304
305 for(i = 0; i < CONTEXTS_NUM; i++) {
306 sun3_put_context(i);
307 sun3_put_segmap (vaddr, curr_pmeg);
308 }
309 sun3_put_context(context);
310 pmeg_alloc[curr_pmeg] = 2;
311 pmeg_ctx[curr_pmeg] = 0;
312
313 }
314 else {
315 pmeg_alloc[curr_pmeg] = 1;
316 pmeg_ctx[curr_pmeg] = context;
317 sun3_put_segmap (vaddr, curr_pmeg);
318
319 }
320 pmeg_vaddr[curr_pmeg] = vaddr;
321
322 /* Set hardware mapping and clear the old PTE entries. */
323 for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE)
324 sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM);
325
326 /* Consider a different one next time. */
327 ++curr_pmeg;
328 }
329
330 /*
331 * Handle a pagefault at virtual address `vaddr'; check if there should be a
332 * page there (specifically, whether the software pagetables indicate that
333 * there is). This is necessary due to the limited size of the second-level
334 * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a
335 * mapping present, we select a `spare' PMEG and use it to create a mapping.
336 * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero
337 * if we successfully handled the fault.
338 */
339 //todo: should we bump minor pagefault counter? if so, here or in caller?
340 //todo: possibly inline this into bus_error030 in <asm/buserror.h> ?
341
342 // kernel_fault is set when a kernel page couldn't be demand mapped,
343 // and forces another try using the kernel page table. basically a
344 // hack so that vmalloc would work correctly.
345
346 int mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault)
347 {
348 unsigned long segment, offset;
349 unsigned char context;
350 pte_t *pte;
351 pgd_t * crp;
352
353 if(current->mm == NULL) {
354 crp = swapper_pg_dir;
355 context = 0;
356 } else {
357 context = current->mm->context;
358 if(kernel_fault)
359 crp = swapper_pg_dir;
360 else
361 crp = current->mm->pgd;
362 }
363
364 #ifdef DEBUG_MMU_EMU
365 printk ("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n",
366 vaddr, read_flag ? "read" : "write", crp);
367 #endif
368
369 segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF;
370 offset = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF;
371
372 #ifdef DEBUG_MMU_EMU
373 printk ("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment, offset);
374 #endif
375
376 pte = (pte_t *) pgd_val (*(crp + segment));
377
378 //todo: next line should check for valid pmd properly.
379 if (!pte) {
380 // printk ("mmu_emu_handle_fault: invalid pmd\n");
381 return 0;
382 }
383
384 pte = (pte_t *) __va ((unsigned long)(pte + offset));
385
386 /* Make sure this is a valid page */
387 if (!(pte_val (*pte) & SUN3_PAGE_VALID))
388 return 0;
389
390 /* Make sure there's a pmeg allocated for the page */
391 if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
392 mmu_emu_map_pmeg (context, vaddr);
393
394 /* Write the pte value to hardware MMU */
395 sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte));
396
397 /* Update software copy of the pte value */
398 // I'm not sure this is necessary. If this is required, we ought to simply
399 // copy this out when we reuse the PMEG or at some other convenient time.
400 // Doing it here is fairly meaningless, anyway, as we only know about the
401 // first access to a given page. --m
402 if (!read_flag) {
403 if (pte_val (*pte) & SUN3_PAGE_WRITEABLE)
404 pte_val (*pte) |= (SUN3_PAGE_ACCESSED
405 | SUN3_PAGE_MODIFIED);
406 else
407 return 0; /* Write-protect error. */
408 } else
409 pte_val (*pte) |= SUN3_PAGE_ACCESSED;
410
411 #ifdef DEBUG_MMU_EMU
412 printk ("seg:%d crp:%p ->", get_fs().seg, crp);
413 print_pte_vaddr (vaddr);
414 printk ("\n");
415 #endif
416
417 return 1;
418 }
419
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.