18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci** Tablewalk MMU emulator
48c2ecf20Sopenharmony_ci**
58c2ecf20Sopenharmony_ci** by Toshiyasu Morita
68c2ecf20Sopenharmony_ci**
78c2ecf20Sopenharmony_ci** Started 1/16/98 @ 2:22 am
88c2ecf20Sopenharmony_ci*/
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/mman.h>
128c2ecf20Sopenharmony_ci#include <linux/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/memblock.h>
178c2ecf20Sopenharmony_ci#include <linux/bitops.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/setup.h>
228c2ecf20Sopenharmony_ci#include <asm/traps.h>
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci#include <asm/page.h>
258c2ecf20Sopenharmony_ci#include <asm/sun3mmu.h>
268c2ecf20Sopenharmony_ci#include <asm/segment.h>
278c2ecf20Sopenharmony_ci#include <asm/oplib.h>
288c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
298c2ecf20Sopenharmony_ci#include <asm/dvma.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#undef DEBUG_MMU_EMU
338c2ecf20Sopenharmony_ci#define DEBUG_PROM_MAPS
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci** Defines
378c2ecf20Sopenharmony_ci*/
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define CONTEXTS_NUM		8
408c2ecf20Sopenharmony_ci#define SEGMAPS_PER_CONTEXT_NUM 2048
418c2ecf20Sopenharmony_ci#define PAGES_PER_SEGMENT	16
428c2ecf20Sopenharmony_ci#define PMEGS_NUM		256
438c2ecf20Sopenharmony_ci#define PMEG_MASK		0xFF
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci** Globals
478c2ecf20Sopenharmony_ci*/
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciunsigned long m68k_vmalloc_end;
508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(m68k_vmalloc_end);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciunsigned long pmeg_vaddr[PMEGS_NUM];
538c2ecf20Sopenharmony_ciunsigned char pmeg_alloc[PMEGS_NUM];
548c2ecf20Sopenharmony_ciunsigned char pmeg_ctx[PMEGS_NUM];
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* pointers to the mm structs for each task in each
578c2ecf20Sopenharmony_ci   context. 0xffffffff is a marker for kernel context */
588c2ecf20Sopenharmony_cistatic struct mm_struct *ctx_alloc[CONTEXTS_NUM] = {
598c2ecf20Sopenharmony_ci    [0] = (struct mm_struct *)0xffffffff
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* has this context been mmdrop'd? */
638c2ecf20Sopenharmony_cistatic unsigned char ctx_avail = CONTEXTS_NUM-1;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* array of pages to be marked off for the rom when we do mem_init later */
668c2ecf20Sopenharmony_ci/* 256 pages lets the rom take up to 2mb of physical ram..  I really
678c2ecf20Sopenharmony_ci   hope it never wants mote than that. */
688c2ecf20Sopenharmony_ciunsigned long rom_pages[256];
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Print a PTE value in symbolic form. For debugging. */
718c2ecf20Sopenharmony_civoid print_pte (pte_t pte)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci#if 0
748c2ecf20Sopenharmony_ci	/* Verbose version. */
758c2ecf20Sopenharmony_ci	unsigned long val = pte_val (pte);
768c2ecf20Sopenharmony_ci	pr_cont(" pte=%lx [addr=%lx",
778c2ecf20Sopenharmony_ci		val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT);
788c2ecf20Sopenharmony_ci	if (val & SUN3_PAGE_VALID)	pr_cont(" valid");
798c2ecf20Sopenharmony_ci	if (val & SUN3_PAGE_WRITEABLE)	pr_cont(" write");
808c2ecf20Sopenharmony_ci	if (val & SUN3_PAGE_SYSTEM)	pr_cont(" sys");
818c2ecf20Sopenharmony_ci	if (val & SUN3_PAGE_NOCACHE)	pr_cont(" nocache");
828c2ecf20Sopenharmony_ci	if (val & SUN3_PAGE_ACCESSED)	pr_cont(" accessed");
838c2ecf20Sopenharmony_ci	if (val & SUN3_PAGE_MODIFIED)	pr_cont(" modified");
848c2ecf20Sopenharmony_ci	switch (val & SUN3_PAGE_TYPE_MASK) {
858c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_MEMORY: pr_cont(" memory"); break;
868c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_IO:     pr_cont(" io");     break;
878c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_VME16:  pr_cont(" vme16");  break;
888c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_VME32:  pr_cont(" vme32");  break;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	pr_cont("]\n");
918c2ecf20Sopenharmony_ci#else
928c2ecf20Sopenharmony_ci	/* Terse version. More likely to fit on a line. */
938c2ecf20Sopenharmony_ci	unsigned long val = pte_val (pte);
948c2ecf20Sopenharmony_ci	char flags[7], *type;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	flags[0] = (val & SUN3_PAGE_VALID)     ? 'v' : '-';
978c2ecf20Sopenharmony_ci	flags[1] = (val & SUN3_PAGE_WRITEABLE) ? 'w' : '-';
988c2ecf20Sopenharmony_ci	flags[2] = (val & SUN3_PAGE_SYSTEM)    ? 's' : '-';
998c2ecf20Sopenharmony_ci	flags[3] = (val & SUN3_PAGE_NOCACHE)   ? 'x' : '-';
1008c2ecf20Sopenharmony_ci	flags[4] = (val & SUN3_PAGE_ACCESSED)  ? 'a' : '-';
1018c2ecf20Sopenharmony_ci	flags[5] = (val & SUN3_PAGE_MODIFIED)  ? 'm' : '-';
1028c2ecf20Sopenharmony_ci	flags[6] = '\0';
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	switch (val & SUN3_PAGE_TYPE_MASK) {
1058c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_MEMORY: type = "memory"; break;
1068c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_IO:     type = "io"    ; break;
1078c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_VME16:  type = "vme16" ; break;
1088c2ecf20Sopenharmony_ci		case SUN3_PAGE_TYPE_VME32:  type = "vme32" ; break;
1098c2ecf20Sopenharmony_ci		default: type = "unknown?"; break;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	pr_cont(" pte=%08lx [%07lx %s %s]\n",
1138c2ecf20Sopenharmony_ci		val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type);
1148c2ecf20Sopenharmony_ci#endif
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* Print the PTE value for a given virtual address. For debugging. */
1188c2ecf20Sopenharmony_civoid print_pte_vaddr (unsigned long vaddr)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	pr_cont(" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
1218c2ecf20Sopenharmony_ci	print_pte (__pte (sun3_get_pte (vaddr)));
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * Initialise the MMU emulator.
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_civoid __init mmu_emu_init(unsigned long bootmem_end)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	unsigned long seg, num;
1308c2ecf20Sopenharmony_ci	int i,j;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	memset(rom_pages, 0, sizeof(rom_pages));
1338c2ecf20Sopenharmony_ci	memset(pmeg_vaddr, 0, sizeof(pmeg_vaddr));
1348c2ecf20Sopenharmony_ci	memset(pmeg_alloc, 0, sizeof(pmeg_alloc));
1358c2ecf20Sopenharmony_ci	memset(pmeg_ctx, 0, sizeof(pmeg_ctx));
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* pmeg align the end of bootmem, adding another pmeg,
1388c2ecf20Sopenharmony_ci	 * later bootmem allocations will likely need it */
1398c2ecf20Sopenharmony_ci	bootmem_end = (bootmem_end + (2 * SUN3_PMEG_SIZE)) & ~SUN3_PMEG_MASK;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* mark all of the pmegs used thus far as reserved */
1428c2ecf20Sopenharmony_ci	for (i=0; i < __pa(bootmem_end) / SUN3_PMEG_SIZE ; ++i)
1438c2ecf20Sopenharmony_ci		pmeg_alloc[i] = 2;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* I'm thinking that most of the top pmeg's are going to be
1478c2ecf20Sopenharmony_ci	   used for something, and we probably shouldn't risk it */
1488c2ecf20Sopenharmony_ci	for(num = 0xf0; num <= 0xff; num++)
1498c2ecf20Sopenharmony_ci		pmeg_alloc[num] = 2;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* liberate all existing mappings in the rest of kernel space */
1528c2ecf20Sopenharmony_ci	for(seg = bootmem_end; seg < 0x0f800000; seg += SUN3_PMEG_SIZE) {
1538c2ecf20Sopenharmony_ci		i = sun3_get_segmap(seg);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		if(!pmeg_alloc[i]) {
1568c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU_EMU
1578c2ecf20Sopenharmony_ci			pr_info("freed:");
1588c2ecf20Sopenharmony_ci			print_pte_vaddr (seg);
1598c2ecf20Sopenharmony_ci#endif
1608c2ecf20Sopenharmony_ci			sun3_put_segmap(seg, SUN3_INVALID_PMEG);
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	j = 0;
1658c2ecf20Sopenharmony_ci	for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) {
1668c2ecf20Sopenharmony_ci		if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) {
1678c2ecf20Sopenharmony_ci#ifdef DEBUG_PROM_MAPS
1688c2ecf20Sopenharmony_ci			for(i = 0; i < 16; i++) {
1698c2ecf20Sopenharmony_ci				pr_info("mapped:");
1708c2ecf20Sopenharmony_ci				print_pte_vaddr (seg + (i*PAGE_SIZE));
1718c2ecf20Sopenharmony_ci				break;
1728c2ecf20Sopenharmony_ci			}
1738c2ecf20Sopenharmony_ci#endif
1748c2ecf20Sopenharmony_ci			// the lowest mapping here is the end of our
1758c2ecf20Sopenharmony_ci			// vmalloc region
1768c2ecf20Sopenharmony_ci			if (!m68k_vmalloc_end)
1778c2ecf20Sopenharmony_ci				m68k_vmalloc_end = seg;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci			// mark the segmap alloc'd, and reserve any
1808c2ecf20Sopenharmony_ci			// of the first 0xbff pages the hardware is
1818c2ecf20Sopenharmony_ci			// already using...  does any sun3 support > 24mb?
1828c2ecf20Sopenharmony_ci			pmeg_alloc[sun3_get_segmap(seg)] = 2;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	dvma_init();
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* blank everything below the kernel, and we've got the base
1908c2ecf20Sopenharmony_ci	   mapping to start all the contexts off with... */
1918c2ecf20Sopenharmony_ci	for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE)
1928c2ecf20Sopenharmony_ci		sun3_put_segmap(seg, SUN3_INVALID_PMEG);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	set_fs(MAKE_MM_SEG(3));
1958c2ecf20Sopenharmony_ci	for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) {
1968c2ecf20Sopenharmony_ci		i = sun3_get_segmap(seg);
1978c2ecf20Sopenharmony_ci		for(j = 1; j < CONTEXTS_NUM; j++)
1988c2ecf20Sopenharmony_ci			(*(romvec->pv_setctxt))(j, (void *)seg, i);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	set_fs(KERNEL_DS);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* erase the mappings for a dead context.  Uses the pg_dir for hints
2058c2ecf20Sopenharmony_ci   as the pmeg tables proved somewhat unreliable, and unmapping all of
2068c2ecf20Sopenharmony_ci   TASK_SIZE was much slower and no more stable. */
2078c2ecf20Sopenharmony_ci/* todo: find a better way to keep track of the pmegs used by a
2088c2ecf20Sopenharmony_ci   context for when they're cleared */
2098c2ecf20Sopenharmony_civoid clear_context(unsigned long context)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci     unsigned char oldctx;
2128c2ecf20Sopenharmony_ci     unsigned long i;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci     if(context) {
2158c2ecf20Sopenharmony_ci	     if(!ctx_alloc[context])
2168c2ecf20Sopenharmony_ci		     panic("clear_context: context not allocated\n");
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	     ctx_alloc[context]->context = SUN3_INVALID_CONTEXT;
2198c2ecf20Sopenharmony_ci	     ctx_alloc[context] = (struct mm_struct *)0;
2208c2ecf20Sopenharmony_ci	     ctx_avail++;
2218c2ecf20Sopenharmony_ci     }
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci     oldctx = sun3_get_context();
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci     sun3_put_context(context);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci     for(i = 0; i < SUN3_INVALID_PMEG; i++) {
2288c2ecf20Sopenharmony_ci	     if((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) {
2298c2ecf20Sopenharmony_ci		     sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG);
2308c2ecf20Sopenharmony_ci		     pmeg_ctx[i] = 0;
2318c2ecf20Sopenharmony_ci		     pmeg_alloc[i] = 0;
2328c2ecf20Sopenharmony_ci		     pmeg_vaddr[i] = 0;
2338c2ecf20Sopenharmony_ci	     }
2348c2ecf20Sopenharmony_ci     }
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci     sun3_put_context(oldctx);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/* gets an empty context.  if full, kills the next context listed to
2408c2ecf20Sopenharmony_ci   die first */
2418c2ecf20Sopenharmony_ci/* This context invalidation scheme is, well, totally arbitrary, I'm
2428c2ecf20Sopenharmony_ci   sure it could be much more intelligent...  but it gets the job done
2438c2ecf20Sopenharmony_ci   for now without much overhead in making it's decision. */
2448c2ecf20Sopenharmony_ci/* todo: come up with optimized scheme for flushing contexts */
2458c2ecf20Sopenharmony_ciunsigned long get_free_context(struct mm_struct *mm)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	unsigned long new = 1;
2488c2ecf20Sopenharmony_ci	static unsigned char next_to_die = 1;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if(!ctx_avail) {
2518c2ecf20Sopenharmony_ci		/* kill someone to get our context */
2528c2ecf20Sopenharmony_ci		new = next_to_die;
2538c2ecf20Sopenharmony_ci		clear_context(new);
2548c2ecf20Sopenharmony_ci		next_to_die = (next_to_die + 1) & 0x7;
2558c2ecf20Sopenharmony_ci		if(!next_to_die)
2568c2ecf20Sopenharmony_ci			next_to_die++;
2578c2ecf20Sopenharmony_ci	} else {
2588c2ecf20Sopenharmony_ci		while(new < CONTEXTS_NUM) {
2598c2ecf20Sopenharmony_ci			if(ctx_alloc[new])
2608c2ecf20Sopenharmony_ci				new++;
2618c2ecf20Sopenharmony_ci			else
2628c2ecf20Sopenharmony_ci				break;
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci		// check to make sure one was really free...
2658c2ecf20Sopenharmony_ci		if(new == CONTEXTS_NUM)
2668c2ecf20Sopenharmony_ci			panic("get_free_context: failed to find free context");
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ctx_alloc[new] = mm;
2708c2ecf20Sopenharmony_ci	ctx_avail--;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return new;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/*
2768c2ecf20Sopenharmony_ci * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in
2778c2ecf20Sopenharmony_ci * `context'. Maintain internal PMEG management structures. This doesn't
2788c2ecf20Sopenharmony_ci * actually map the physical address, but does clear the old mappings.
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_ci//todo: better allocation scheme? but is extra complexity worthwhile?
2818c2ecf20Sopenharmony_ci//todo: only clear old entries if necessary? how to tell?
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ciinline void mmu_emu_map_pmeg (int context, int vaddr)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	static unsigned char curr_pmeg = 128;
2868c2ecf20Sopenharmony_ci	int i;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Round address to PMEG boundary. */
2898c2ecf20Sopenharmony_ci	vaddr &= ~SUN3_PMEG_MASK;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* Find a spare one. */
2928c2ecf20Sopenharmony_ci	while (pmeg_alloc[curr_pmeg] == 2)
2938c2ecf20Sopenharmony_ci		++curr_pmeg;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU_EMU
2978c2ecf20Sopenharmony_ci	pr_info("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n",
2988c2ecf20Sopenharmony_ci		curr_pmeg, context, vaddr);
2998c2ecf20Sopenharmony_ci#endif
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Invalidate old mapping for the pmeg, if any */
3028c2ecf20Sopenharmony_ci	if (pmeg_alloc[curr_pmeg] == 1) {
3038c2ecf20Sopenharmony_ci		sun3_put_context(pmeg_ctx[curr_pmeg]);
3048c2ecf20Sopenharmony_ci		sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG);
3058c2ecf20Sopenharmony_ci		sun3_put_context(context);
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* Update PMEG management structures. */
3098c2ecf20Sopenharmony_ci	// don't take pmeg's away from the kernel...
3108c2ecf20Sopenharmony_ci	if(vaddr >= PAGE_OFFSET) {
3118c2ecf20Sopenharmony_ci		/* map kernel pmegs into all contexts */
3128c2ecf20Sopenharmony_ci		unsigned char i;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		for(i = 0; i < CONTEXTS_NUM; i++) {
3158c2ecf20Sopenharmony_ci			sun3_put_context(i);
3168c2ecf20Sopenharmony_ci			sun3_put_segmap (vaddr, curr_pmeg);
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci		sun3_put_context(context);
3198c2ecf20Sopenharmony_ci		pmeg_alloc[curr_pmeg] = 2;
3208c2ecf20Sopenharmony_ci		pmeg_ctx[curr_pmeg] = 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci	else {
3248c2ecf20Sopenharmony_ci		pmeg_alloc[curr_pmeg] = 1;
3258c2ecf20Sopenharmony_ci		pmeg_ctx[curr_pmeg] = context;
3268c2ecf20Sopenharmony_ci		sun3_put_segmap (vaddr, curr_pmeg);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	pmeg_vaddr[curr_pmeg] = vaddr;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Set hardware mapping and clear the old PTE entries. */
3328c2ecf20Sopenharmony_ci	for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE)
3338c2ecf20Sopenharmony_ci		sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Consider a different one next time. */
3368c2ecf20Sopenharmony_ci	++curr_pmeg;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci/*
3408c2ecf20Sopenharmony_ci * Handle a pagefault at virtual address `vaddr'; check if there should be a
3418c2ecf20Sopenharmony_ci * page there (specifically, whether the software pagetables indicate that
3428c2ecf20Sopenharmony_ci * there is). This is necessary due to the limited size of the second-level
3438c2ecf20Sopenharmony_ci * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a
3448c2ecf20Sopenharmony_ci * mapping present, we select a `spare' PMEG and use it to create a mapping.
3458c2ecf20Sopenharmony_ci * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero
3468c2ecf20Sopenharmony_ci * if we successfully handled the fault.
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_ci//todo: should we bump minor pagefault counter? if so, here or in caller?
3498c2ecf20Sopenharmony_ci//todo: possibly inline this into bus_error030 in <asm/buserror.h> ?
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci// kernel_fault is set when a kernel page couldn't be demand mapped,
3528c2ecf20Sopenharmony_ci// and forces another try using the kernel page table.  basically a
3538c2ecf20Sopenharmony_ci// hack so that vmalloc would work correctly.
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciint mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	unsigned long segment, offset;
3588c2ecf20Sopenharmony_ci	unsigned char context;
3598c2ecf20Sopenharmony_ci	pte_t *pte;
3608c2ecf20Sopenharmony_ci	pgd_t * crp;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if(current->mm == NULL) {
3638c2ecf20Sopenharmony_ci		crp = swapper_pg_dir;
3648c2ecf20Sopenharmony_ci		context = 0;
3658c2ecf20Sopenharmony_ci	} else {
3668c2ecf20Sopenharmony_ci		context = current->mm->context;
3678c2ecf20Sopenharmony_ci		if(kernel_fault)
3688c2ecf20Sopenharmony_ci			crp = swapper_pg_dir;
3698c2ecf20Sopenharmony_ci		else
3708c2ecf20Sopenharmony_ci			crp = current->mm->pgd;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU_EMU
3748c2ecf20Sopenharmony_ci	pr_info("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n",
3758c2ecf20Sopenharmony_ci		vaddr, read_flag ? "read" : "write", crp);
3768c2ecf20Sopenharmony_ci#endif
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF;
3798c2ecf20Sopenharmony_ci	offset  = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU_EMU
3828c2ecf20Sopenharmony_ci	pr_info("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment,
3838c2ecf20Sopenharmony_ci		offset);
3848c2ecf20Sopenharmony_ci#endif
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	pte = (pte_t *) pgd_val (*(crp + segment));
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci//todo: next line should check for valid pmd properly.
3898c2ecf20Sopenharmony_ci	if (!pte) {
3908c2ecf20Sopenharmony_ci//                pr_info("mmu_emu_handle_fault: invalid pmd\n");
3918c2ecf20Sopenharmony_ci                return 0;
3928c2ecf20Sopenharmony_ci        }
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	pte = (pte_t *) __va ((unsigned long)(pte + offset));
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* Make sure this is a valid page */
3978c2ecf20Sopenharmony_ci	if (!(pte_val (*pte) & SUN3_PAGE_VALID))
3988c2ecf20Sopenharmony_ci		return 0;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* Make sure there's a pmeg allocated for the page */
4018c2ecf20Sopenharmony_ci	if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
4028c2ecf20Sopenharmony_ci		mmu_emu_map_pmeg (context, vaddr);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* Write the pte value to hardware MMU */
4058c2ecf20Sopenharmony_ci	sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte));
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* Update software copy of the pte value */
4088c2ecf20Sopenharmony_ci// I'm not sure this is necessary. If this is required, we ought to simply
4098c2ecf20Sopenharmony_ci// copy this out when we reuse the PMEG or at some other convenient time.
4108c2ecf20Sopenharmony_ci// Doing it here is fairly meaningless, anyway, as we only know about the
4118c2ecf20Sopenharmony_ci// first access to a given page. --m
4128c2ecf20Sopenharmony_ci	if (!read_flag) {
4138c2ecf20Sopenharmony_ci		if (pte_val (*pte) & SUN3_PAGE_WRITEABLE)
4148c2ecf20Sopenharmony_ci			pte_val (*pte) |= (SUN3_PAGE_ACCESSED
4158c2ecf20Sopenharmony_ci					   | SUN3_PAGE_MODIFIED);
4168c2ecf20Sopenharmony_ci		else
4178c2ecf20Sopenharmony_ci			return 0;	/* Write-protect error. */
4188c2ecf20Sopenharmony_ci	} else
4198c2ecf20Sopenharmony_ci		pte_val (*pte) |= SUN3_PAGE_ACCESSED;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU_EMU
4228c2ecf20Sopenharmony_ci	pr_info("seg:%ld crp:%p ->", get_fs().seg, crp);
4238c2ecf20Sopenharmony_ci	print_pte_vaddr (vaddr);
4248c2ecf20Sopenharmony_ci	pr_cont("\n");
4258c2ecf20Sopenharmony_ci#endif
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return 1;
4288c2ecf20Sopenharmony_ci}
429