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