25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $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 $ |
29 |
* |
* |
30 |
* Emulation of MIPS coprocessors. |
* Emulation of MIPS coprocessors. |
31 |
*/ |
*/ |
533 |
INVALIDATE_VADDR); |
INVALIDATE_VADDR); |
534 |
} |
} |
535 |
} else { |
} else { |
536 |
/* TODO: Implement support for other. */ |
int non4kpages = 0; |
537 |
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; |
538 |
|
|
539 |
|
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
540 |
|
topbit <<= 43; |
541 |
|
fillmask <<= 4; |
542 |
|
} else { |
543 |
|
topbit <<= 39; |
544 |
|
} |
545 |
|
|
546 |
|
for (i=0; i<ntlbs; i++) { |
547 |
|
if (tlb[i].mask != 0 && tlb[i].mask != 0x1800) { |
548 |
|
non4kpages = 1; |
549 |
|
continue; |
550 |
|
} |
551 |
|
|
552 |
|
if ((tlb[i].hi & ENTRYHI_ASID) == asid && |
553 |
|
!(tlb[i].hi & TLB_G)) { |
554 |
|
uint64_t vaddr0, vaddr1; |
555 |
|
vaddr0 = cp->tlbs[i].hi & ~fillmask; |
556 |
|
if (vaddr0 & topbit) |
557 |
|
vaddr0 |= fillmask; |
558 |
|
vaddr1 = vaddr0 | 0x1000; /* TODO: mask */ |
559 |
|
|
560 |
|
if (tlb[i].lo0 & ENTRYLO_V) |
561 |
|
cpu->invalidate_translation_caches(cpu, |
562 |
|
vaddr0, INVALIDATE_VADDR); |
563 |
|
if (tlb[i].lo1 & ENTRYLO_V) |
564 |
|
cpu->invalidate_translation_caches(cpu, |
565 |
|
vaddr1, INVALIDATE_VADDR); |
566 |
|
} |
567 |
|
} |
568 |
|
|
569 |
|
if (non4kpages) { |
570 |
|
cpu->invalidate_translation_caches(cpu, |
571 |
|
0, INVALIDATE_ALL); |
572 |
|
} |
573 |
} |
} |
574 |
} |
} |
575 |
|
|
856 |
cpu->invalidate_translation_caches( |
cpu->invalidate_translation_caches( |
857 |
cpu, 0, INVALIDATE_ALL); |
cpu, 0, INVALIDATE_ALL); |
858 |
} |
} |
|
|
|
|
#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 |
|
859 |
} |
} |
860 |
unimpl = 0; |
unimpl = 0; |
861 |
break; |
break; |
1576 |
void coproc_tlbwri(struct cpu *cpu, int randomflag) |
void coproc_tlbwri(struct cpu *cpu, int randomflag) |
1577 |
{ |
{ |
1578 |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
1579 |
int index, g_bit, old_asid = -1; |
int index, g_bit; |
1580 |
uint64_t oldvaddr; |
uint64_t oldvaddr; |
1581 |
|
|
1582 |
if (randomflag) { |
if (randomflag) { |
1583 |
if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { |
if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { |
1584 |
cp->reg[COP0_RANDOM] = |
index = ((cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) |
1585 |
((random() % (cp->nr_of_tlbs - 8)) + 8) |
>> R2K3K_RANDOM_SHIFT) - 1; |
1586 |
<< R2K3K_RANDOM_SHIFT; |
/* R3000 always has 8 wired entries: */ |
1587 |
index = (cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) |
if (index < 8) |
1588 |
>> R2K3K_RANDOM_SHIFT; |
index = cp->nr_of_tlbs - 1; |
1589 |
|
cp->reg[COP0_RANDOM] = index << R2K3K_RANDOM_SHIFT; |
1590 |
} else { |
} else { |
1591 |
cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() |
cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() |
1592 |
% (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); |
% (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); |
1633 |
/* |
/* |
1634 |
* Any virtual address translation for the old TLB entry must be |
* Any virtual address translation for the old TLB entry must be |
1635 |
* invalidated first: |
* invalidated first: |
1636 |
|
* |
1637 |
|
* (Only Valid entries need to be invalidated, and only those that |
1638 |
|
* are either Global, or have the same ASID as the new entry will |
1639 |
|
* have. No other address translations should be active anyway.) |
1640 |
*/ |
*/ |
1641 |
|
|
1642 |
switch (cpu->cd.mips.cpu_type.mmu_model) { |
switch (cpu->cd.mips.cpu_type.mmu_model) { |
1643 |
|
|
1644 |
case MMU3K: |
case MMU3K: |
1645 |
oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; |
oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; |
1646 |
oldvaddr &= 0xffffffffULL; |
oldvaddr = (int32_t) oldvaddr; |
|
if (oldvaddr & 0x80000000ULL) |
|
|
oldvaddr |= 0xffffffff00000000ULL; |
|
|
old_asid = (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) |
|
|
>> R2K3K_ENTRYHI_ASID_SHIFT; |
|
1647 |
|
|
1648 |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
if (cp->tlbs[index].lo0 & R2K3K_ENTRYLO_V && |
1649 |
INVALIDATE_VADDR); |
(cp->tlbs[index].lo0 & R2K3K_ENTRYLO_G || |
1650 |
|
(cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) == |
1651 |
|
(cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) |
1652 |
|
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1653 |
|
INVALIDATE_VADDR); |
1654 |
break; |
break; |
1655 |
|
|
1656 |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1670 |
oldvaddr |= 0xffffff0000000000ULL; |
oldvaddr |= 0xffffff0000000000ULL; |
1671 |
} |
} |
1672 |
|
|
1673 |
#if 1 |
#if 0 |
1674 |
|
/* TODO: FIX THIS! It shouldn't be needed! */ |
1675 |
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
1676 |
#else |
#else |
1677 |
/* |
/* |
1678 |
* TODO: non-4KB page sizes! |
* TODO: non-4KB page sizes! |
1679 |
*/ |
*/ |
1680 |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
if (cp->tlbs[index].lo0 & ENTRYLO_V) |
1681 |
INVALIDATE_VADDR); |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1682 |
cpu->invalidate_translation_caches(cpu, oldvaddr | 0x1000, |
INVALIDATE_VADDR); |
1683 |
INVALIDATE_VADDR); |
if (cp->tlbs[index].lo1 & ENTRYLO_V) |
1684 |
|
cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, |
1685 |
|
INVALIDATE_VADDR); |
1686 |
#endif |
#endif |
1687 |
} |
} |
1688 |
|
|
1689 |
|
#if 0 |
1690 |
/* |
/* |
1691 |
* Check for duplicate entries. (There should not be two mappings |
* Check for duplicate entries. (There should not be two mappings |
1692 |
* from one virtual address to physical addresses.) |
* from one virtual address to physical addresses.) |
1724 |
(long long)vaddr1, asid, i); |
(long long)vaddr1, asid, i); |
1725 |
} |
} |
1726 |
} |
} |
1727 |
|
#endif |
1728 |
|
|
1729 |
/* Write the new entry: */ |
/* Write the new entry: */ |
1730 |
|
|
1748 |
INVALIDATE_PADDR); |
INVALIDATE_PADDR); |
1749 |
} |
} |
1750 |
|
|
1751 |
|
if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { |
1752 |
|
fatal("Wow! Interesting case; tlbw* while caches" |
1753 |
|
" are isolated. TODO\n"); |
1754 |
|
/* Don't update the translation table in this |
1755 |
|
case... */ |
1756 |
|
exit(1); |
1757 |
|
} |
1758 |
|
|
1759 |
/* If we have a memblock (host page) for the physical |
/* If we have a memblock (host page) for the physical |
1760 |
page, then add a translation for it immediately: */ |
page, then add a translation for it immediately: */ |
1761 |
if (memblock != NULL && |
if (memblock != NULL && |
1762 |
cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) { |
cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) |
|
memblock += (paddr & ((1 << BITS_PER_PAGETABLE) - 1)); |
|
1763 |
cpu->update_translation_table(cpu, vaddr, memblock, |
cpu->update_translation_table(cpu, vaddr, memblock, |
1764 |
wf, paddr); |
wf, paddr); |
|
} |
|
1765 |
} else { |
} else { |
1766 |
/* R4000: */ |
/* R4000 etc.: */ |
1767 |
g_bit = (cp->reg[COP0_ENTRYLO0] & |
unsigned char *memblock = NULL; |
1768 |
cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; |
int pfn_shift = 12, vpn_shift = 12; |
1769 |
|
int wf0, wf1, mask; |
1770 |
|
uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; |
1771 |
|
|
1772 |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
1773 |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
1774 |
cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; |
cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; |
1775 |
cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; |
cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; |
1776 |
|
|
1777 |
|
wf0 = cp->tlbs[index].lo0 & ENTRYLO_D; |
1778 |
|
wf1 = cp->tlbs[index].lo1 & ENTRYLO_D; |
1779 |
|
|
1780 |
|
mask = cp->reg[COP0_PAGEMASK]; |
1781 |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
1782 |
/* NOTE: The VR4131 (and possibly others) don't have |
pfn_shift = 10; |
1783 |
a Global bit in entryhi */ |
mask |= 0x07ff; |
|
cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; |
|
1784 |
} else { |
} else { |
1785 |
cp->tlbs[index].lo0 &= ~ENTRYLO_G; |
mask |= 0x1fff; |
|
cp->tlbs[index].lo1 &= ~ENTRYLO_G; |
|
|
|
|
|
cp->tlbs[index].hi &= ~TLB_G; |
|
|
if (g_bit) |
|
|
cp->tlbs[index].hi |= TLB_G; |
|
1786 |
} |
} |
1787 |
|
switch (mask) { |
1788 |
/* Invalidate any code translations, if we are writing |
case 0x00007ff: |
1789 |
Dirty pages to the TLB: */ |
if (cp->tlbs[index].lo0 & ENTRYLO_V || |
1790 |
if (cp->reg[COP0_PAGEMASK] != 0 && |
cp->tlbs[index].lo1 & ENTRYLO_V) { |
1791 |
cp->reg[COP0_PAGEMASK] != 0x1800) { |
fatal("1KB pages don't work with dyntrans.\n"); |
1792 |
printf("TODO: MASK = %08"PRIx32"\n", |
exit(1); |
1793 |
(uint32_t)cp->reg[COP0_PAGEMASK]); |
} |
1794 |
|
vpn_shift = 10; |
1795 |
|
break; |
1796 |
|
case 0x0001fff: break; |
1797 |
|
case 0x0007fff: vpn_shift = 14; break; |
1798 |
|
case 0x001ffff: vpn_shift = 16; break; |
1799 |
|
case 0x007ffff: vpn_shift = 18; break; |
1800 |
|
case 0x01fffff: vpn_shift = 20; break; |
1801 |
|
case 0x07fffff: vpn_shift = 22; break; |
1802 |
|
case 0x1ffffff: vpn_shift = 24; break; |
1803 |
|
case 0x7ffffff: vpn_shift = 26; break; |
1804 |
|
default:fatal("Unimplemented MASK = 0x%016x\n", mask); |
1805 |
exit(1); |
exit(1); |
1806 |
} |
} |
1807 |
|
|
1808 |
if (cp->tlbs[index].lo0 & ENTRYLO_D) |
paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
1809 |
cpu->invalidate_code_translation(cpu, |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
1810 |
((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) |
1811 |
>> ENTRYLO_PFN_SHIFT) << 12, |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
|
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); |
|
1812 |
|
|
1813 |
#if 1 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1814 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
|
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
|
1815 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1816 |
if (oldvaddr & 0x80000000000ULL) |
if (vaddr0 & 0x80000000000ULL) |
1817 |
oldvaddr |= 0xfffff00000000000ULL; |
vaddr0 |= 0xfffff00000000000ULL; |
1818 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1819 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1820 |
oldvaddr = (int32_t)oldvaddr; |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1821 |
|
vaddr0 = (int32_t)vaddr0; |
1822 |
} else { |
} else { |
1823 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1824 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1825 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1826 |
if (oldvaddr & 0x8000000000ULL) |
if (vaddr0 & 0x8000000000ULL) |
1827 |
oldvaddr |= 0xffffff0000000000ULL; |
vaddr0 |= 0xffffff0000000000ULL; |
1828 |
} |
} |
1829 |
|
|
1830 |
cpu->invalidate_translation_caches(cpu, ((cp->tlbs[index].lo0 & |
vaddr1 = vaddr0 | (1 << vpn_shift); |
|
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); |
|
1831 |
|
|
1832 |
#endif |
g_bit = (cp->reg[COP0_ENTRYLO0] & |
1833 |
} |
cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; |
|
} |
|
1834 |
|
|
1835 |
|
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
1836 |
|
/* NOTE: The VR4131 (and possibly others) don't have |
1837 |
|
a Global bit in entryhi */ |
1838 |
|
cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; |
1839 |
|
} else { |
1840 |
|
cp->tlbs[index].lo0 &= ~ENTRYLO_G; |
1841 |
|
cp->tlbs[index].lo1 &= ~ENTRYLO_G; |
1842 |
|
|
1843 |
/* |
cp->tlbs[index].hi &= ~TLB_G; |
1844 |
* coproc_rfe(): |
if (g_bit) |
1845 |
* |
cp->tlbs[index].hi |= TLB_G; |
1846 |
* Return from exception. (R3000 etc.) |
} |
1847 |
*/ |
|
1848 |
void coproc_rfe(struct cpu *cpu) |
/* |
1849 |
{ |
* Invalidate any code translations, if we are writing Dirty |
1850 |
cpu->cd.mips.coproc[0]->reg[COP0_STATUS] = |
* pages to the TLB: (TODO: 4KB hardcoded... ugly) |
1851 |
(cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & ~0x3f) | |
*/ |
1852 |
((cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & 0x3c) >> 2); |
for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { |
1853 |
|
if (wf0) |
1854 |
|
cpu->invalidate_code_translation(cpu, |
1855 |
|
paddr0 + ptmp, INVALIDATE_PADDR); |
1856 |
|
if (wf1) |
1857 |
|
cpu->invalidate_code_translation(cpu, |
1858 |
|
paddr1 + ptmp, INVALIDATE_PADDR); |
1859 |
|
} |
1860 |
|
|
1861 |
|
/* |
1862 |
|
* If we have a memblock (host page) for the physical page, |
1863 |
|
* then add a translation for it immediately, to save some |
1864 |
|
* time. (It would otherwise be added later on anyway, |
1865 |
|
* because of a translation miss.) |
1866 |
|
* |
1867 |
|
* NOTE/TODO: This is only for 4KB pages so far. It would |
1868 |
|
* be too expensive to add e.g. 16MB pages like |
1869 |
|
* this. |
1870 |
|
*/ |
1871 |
|
memblock = memory_paddr_to_hostaddr(cpu->mem, paddr0, 0); |
1872 |
|
if (memblock != NULL && cp->reg[COP0_ENTRYLO0] & ENTRYLO_V) |
1873 |
|
cpu->update_translation_table(cpu, vaddr0, memblock, |
1874 |
|
wf0, paddr0); |
1875 |
|
memblock = memory_paddr_to_hostaddr(cpu->mem, paddr1, 0); |
1876 |
|
if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) |
1877 |
|
cpu->update_translation_table(cpu, vaddr1, memblock, |
1878 |
|
wf1, paddr1); |
1879 |
|
} |
1880 |
} |
} |
1881 |
|
|
1882 |
|
|
2150 |
debug("rfe\n"); |
debug("rfe\n"); |
2151 |
return; |
return; |
2152 |
} |
} |
2153 |
coproc_rfe(cpu); |
fatal("Internal error (rfe): Should be " |
2154 |
return; |
"implemented in dyntrans instead.\n"); |
2155 |
|
exit(1); |
2156 |
case COP0_ERET: /* R4000: Return from exception */ |
case COP0_ERET: /* R4000: Return from exception */ |
2157 |
if (unassemble_only) { |
if (unassemble_only) { |
2158 |
debug("eret\n"); |
debug("eret\n"); |
2159 |
return; |
return; |
2160 |
} |
} |
2161 |
coproc_eret(cpu); |
fatal("Internal error (eret): Should be " |
2162 |
return; |
"implemented in dyntrans instead.\n"); |
2163 |
|
exit(1); |
2164 |
case COP0_DERET: |
case COP0_DERET: |
2165 |
if (unassemble_only) { |
if (unassemble_only) { |
2166 |
debug("deret\n"); |
debug("deret\n"); |