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