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