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