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