--- trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:20:10 26 +++ trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:20:26 28 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_mips_coproc.c,v 1.37 2006/06/25 02:46:07 debug Exp $ + * $Id: cpu_mips_coproc.c,v 1.49 2006/07/21 20:39:40 debug Exp $ * * Emulation of MIPS coprocessors. */ @@ -533,8 +533,43 @@ INVALIDATE_VADDR); } } else { - /* TODO: Implement support for other. */ - cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); + int non4kpages = 0; + uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; + + if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + topbit <<= 43; + fillmask <<= 4; + } else { + topbit <<= 39; + } + + for (i=0; itlbs[i].hi & ~fillmask; + if (vaddr0 & topbit) + vaddr0 |= fillmask; + vaddr1 = vaddr0 | 0x1000; /* TODO: mask */ + + if (tlb[i].lo0 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, + vaddr0, INVALIDATE_VADDR); + if (tlb[i].lo1 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, + vaddr1, INVALIDATE_VADDR); + } + } + + if (non4kpages) { + cpu->invalidate_translation_caches(cpu, + 0, INVALIDATE_ALL); + } } } @@ -821,15 +856,6 @@ cpu->invalidate_translation_caches( cpu, 0, INVALIDATE_ALL); } - -#if 1 - /* - * NOTE: This is not needed for NetBSD, but - * Ultrix and Linux still needs this. They - * shouldn't, though. Something else is buggy. - */ - cpu_create_or_reset_tc(cpu); -#endif } unimpl = 0; break; @@ -1550,16 +1576,17 @@ void coproc_tlbwri(struct cpu *cpu, int randomflag) { struct mips_coproc *cp = cpu->cd.mips.coproc[0]; - int index, g_bit, old_asid = -1; + int index, g_bit; uint64_t oldvaddr; if (randomflag) { if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { - cp->reg[COP0_RANDOM] = - ((random() % (cp->nr_of_tlbs - 8)) + 8) - << R2K3K_RANDOM_SHIFT; - index = (cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) - >> R2K3K_RANDOM_SHIFT; + index = ((cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) + >> R2K3K_RANDOM_SHIFT) - 1; + /* R3000 always has 8 wired entries: */ + if (index < 8) + index = cp->nr_of_tlbs - 1; + cp->reg[COP0_RANDOM] = index << R2K3K_RANDOM_SHIFT; } else { cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() % (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); @@ -1606,20 +1633,24 @@ /* * Any virtual address translation for the old TLB entry must be * invalidated first: + * + * (Only Valid entries need to be invalidated, and only those that + * are either Global, or have the same ASID as the new entry will + * have. No other address translations should be active anyway.) */ switch (cpu->cd.mips.cpu_type.mmu_model) { case MMU3K: oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; - oldvaddr &= 0xffffffffULL; - if (oldvaddr & 0x80000000ULL) - oldvaddr |= 0xffffffff00000000ULL; - old_asid = (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) - >> R2K3K_ENTRYHI_ASID_SHIFT; + oldvaddr = (int32_t) oldvaddr; - cpu->invalidate_translation_caches(cpu, oldvaddr, - INVALIDATE_VADDR); + if (cp->tlbs[index].lo0 & R2K3K_ENTRYLO_V && + (cp->tlbs[index].lo0 & R2K3K_ENTRYLO_G || + (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) == + (cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) + cpu->invalidate_translation_caches(cpu, oldvaddr, + INVALIDATE_VADDR); break; default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { @@ -1639,20 +1670,23 @@ oldvaddr |= 0xffffff0000000000ULL; } -#if 1 +#if 0 + /* TODO: FIX THIS! It shouldn't be needed! */ cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); #else /* * TODO: non-4KB page sizes! */ - cpu->invalidate_translation_caches(cpu, oldvaddr, - INVALIDATE_VADDR); - cpu->invalidate_translation_caches(cpu, oldvaddr | 0x1000, - INVALIDATE_VADDR); + if (cp->tlbs[index].lo0 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, oldvaddr, + INVALIDATE_VADDR); + if (cp->tlbs[index].lo1 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, + INVALIDATE_VADDR); #endif } - +#if 0 /* * Check for duplicate entries. (There should not be two mappings * from one virtual address to physical addresses.) @@ -1690,7 +1724,7 @@ (long long)vaddr1, asid, i); } } - +#endif /* Write the new entry: */ @@ -1714,93 +1748,135 @@ INVALIDATE_PADDR); } + if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { + fatal("Wow! Interesting case; tlbw* while caches" + " are isolated. TODO\n"); + /* Don't update the translation table in this + case... */ + exit(1); + } + /* If we have a memblock (host page) for the physical page, then add a translation for it immediately: */ if (memblock != NULL && - cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) { - memblock += (paddr & ((1 << BITS_PER_PAGETABLE) - 1)); + cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) cpu->update_translation_table(cpu, vaddr, memblock, wf, paddr); - } } else { - /* R4000: */ - g_bit = (cp->reg[COP0_ENTRYLO0] & - cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; + /* R4000 etc.: */ + unsigned char *memblock = NULL; + int pfn_shift = 12, vpn_shift = 12; + int wf0, wf1, mask; + uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; + cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; + wf0 = cp->tlbs[index].lo0 & ENTRYLO_D; + wf1 = cp->tlbs[index].lo1 & ENTRYLO_D; + + mask = cp->reg[COP0_PAGEMASK]; if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { - /* NOTE: The VR4131 (and possibly others) don't have - a Global bit in entryhi */ - cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; + pfn_shift = 10; + mask |= 0x07ff; } else { - cp->tlbs[index].lo0 &= ~ENTRYLO_G; - cp->tlbs[index].lo1 &= ~ENTRYLO_G; - - cp->tlbs[index].hi &= ~TLB_G; - if (g_bit) - cp->tlbs[index].hi |= TLB_G; + mask |= 0x1fff; } - - /* Invalidate any code translations, if we are writing - Dirty pages to the TLB: */ - if (cp->reg[COP0_PAGEMASK] != 0 && - cp->reg[COP0_PAGEMASK] != 0x1800) { - printf("TODO: MASK = %08"PRIx32"\n", - (uint32_t)cp->reg[COP0_PAGEMASK]); + switch (mask) { + case 0x00007ff: + if (cp->tlbs[index].lo0 & ENTRYLO_V || + cp->tlbs[index].lo1 & ENTRYLO_V) { + fatal("1KB pages don't work with dyntrans.\n"); + exit(1); + } + vpn_shift = 10; + break; + case 0x0001fff: break; + case 0x0007fff: vpn_shift = 14; break; + case 0x001ffff: vpn_shift = 16; break; + case 0x007ffff: vpn_shift = 18; break; + case 0x01fffff: vpn_shift = 20; break; + case 0x07fffff: vpn_shift = 22; break; + case 0x1ffffff: vpn_shift = 24; break; + case 0x7ffffff: vpn_shift = 26; break; + default:fatal("Unimplemented MASK = 0x%016x\n", mask); exit(1); } - if (cp->tlbs[index].lo0 & ENTRYLO_D) - cpu->invalidate_code_translation(cpu, - ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) - >> ENTRYLO_PFN_SHIFT) << 12, - INVALIDATE_PADDR); - if (cp->tlbs[index].lo1 & ENTRYLO_D) - cpu->invalidate_code_translation(cpu, - ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) - >> ENTRYLO_PFN_SHIFT) << 12, - INVALIDATE_PADDR); + paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) + >> ENTRYLO_PFN_SHIFT) << pfn_shift; + paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) + >> ENTRYLO_PFN_SHIFT) << pfn_shift; -#if 1 - if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { - oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; + if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; /* 44 addressable bits: */ - if (oldvaddr & 0x80000000000ULL) - oldvaddr |= 0xfffff00000000000ULL; + if (vaddr0 & 0x80000000000ULL) + vaddr0 |= 0xfffff00000000000ULL; } else if (cpu->is_32bit) { /* MIPS32 etc.: */ - oldvaddr = (int32_t)oldvaddr; + vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + vaddr0 = (int32_t)vaddr0; } else { /* Assume MMU4K */ - oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; /* 40 addressable bits: */ - if (oldvaddr & 0x8000000000ULL) - oldvaddr |= 0xffffff0000000000ULL; + if (vaddr0 & 0x8000000000ULL) + vaddr0 |= 0xffffff0000000000ULL; } -cpu->invalidate_translation_caches(cpu, ((cp->tlbs[index].lo0 & -ENTRYLO_PFN_MASK) >> ENTRYLO_PFN_SHIFT) << 12, INVALIDATE_PADDR); -cpu->invalidate_translation_caches(cpu, ((cp->tlbs[index].lo1 & -ENTRYLO_PFN_MASK) >> ENTRYLO_PFN_SHIFT) << 12, INVALIDATE_PADDR); + vaddr1 = vaddr0 | (1 << vpn_shift); -#endif - } -} + g_bit = (cp->reg[COP0_ENTRYLO0] & + cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; + if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { + /* NOTE: The VR4131 (and possibly others) don't have + a Global bit in entryhi */ + cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; + } else { + cp->tlbs[index].lo0 &= ~ENTRYLO_G; + cp->tlbs[index].lo1 &= ~ENTRYLO_G; -/* - * coproc_rfe(): - * - * Return from exception. (R3000 etc.) - */ -void coproc_rfe(struct cpu *cpu) -{ - cpu->cd.mips.coproc[0]->reg[COP0_STATUS] = - (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & ~0x3f) | - ((cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & 0x3c) >> 2); + cp->tlbs[index].hi &= ~TLB_G; + if (g_bit) + cp->tlbs[index].hi |= TLB_G; + } + + /* + * Invalidate any code translations, if we are writing Dirty + * pages to the TLB: (TODO: 4KB hardcoded... ugly) + */ + for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { + if (wf0) + cpu->invalidate_code_translation(cpu, + paddr0 + ptmp, INVALIDATE_PADDR); + if (wf1) + cpu->invalidate_code_translation(cpu, + paddr1 + ptmp, INVALIDATE_PADDR); + } + + /* + * If we have a memblock (host page) for the physical page, + * then add a translation for it immediately, to save some + * time. (It would otherwise be added later on anyway, + * because of a translation miss.) + * + * NOTE/TODO: This is only for 4KB pages so far. It would + * be too expensive to add e.g. 16MB pages like + * this. + */ + memblock = memory_paddr_to_hostaddr(cpu->mem, paddr0, 0); + if (memblock != NULL && cp->reg[COP0_ENTRYLO0] & ENTRYLO_V) + cpu->update_translation_table(cpu, vaddr0, memblock, + wf0, paddr0); + memblock = memory_paddr_to_hostaddr(cpu->mem, paddr1, 0); + if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) + cpu->update_translation_table(cpu, vaddr1, memblock, + wf1, paddr1); + } } @@ -2074,15 +2150,17 @@ debug("rfe\n"); return; } - coproc_rfe(cpu); - return; + fatal("Internal error (rfe): Should be " + "implemented in dyntrans instead.\n"); + exit(1); case COP0_ERET: /* R4000: Return from exception */ if (unassemble_only) { debug("eret\n"); return; } - coproc_eret(cpu); - return; + fatal("Internal error (eret): Should be " + "implemented in dyntrans instead.\n"); + exit(1); case COP0_DERET: if (unassemble_only) { debug("deret\n");