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