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