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