1 |
/* |
/* |
2 |
* Copyright (C) 2003-2006 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_mips_coproc.c,v 1.53 2006/08/11 17:43:30 debug Exp $ |
* $Id: cpu_mips_coproc.c,v 1.64 2007/04/28 09:19:51 debug Exp $ |
29 |
* |
* |
30 |
* Emulation of MIPS coprocessors. |
* Emulation of MIPS coprocessors. |
31 |
*/ |
*/ |
45 |
#include "mips_cpu_types.h" |
#include "mips_cpu_types.h" |
46 |
#include "misc.h" |
#include "misc.h" |
47 |
#include "opcodes_mips.h" |
#include "opcodes_mips.h" |
48 |
|
#include "timer.h" |
49 |
|
|
50 |
|
|
51 |
#ifndef ENABLE_MIPS |
#ifndef ENABLE_MIPS |
439 |
|
|
440 |
|
|
441 |
/* |
/* |
442 |
|
* mips_timer_tick(): |
443 |
|
*/ |
444 |
|
static void mips_timer_tick(struct timer *timer, void *extra) |
445 |
|
{ |
446 |
|
struct cpu *cpu = (struct cpu *) extra; |
447 |
|
|
448 |
|
cpu->cd.mips.compare_interrupts_pending ++; |
449 |
|
|
450 |
|
if ((int32_t) (cpu->cd.mips.coproc[0]->reg[COP0_COUNT] - |
451 |
|
cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]) < 0) { |
452 |
|
cpu->cd.mips.coproc[0]->reg[COP0_COUNT] = |
453 |
|
cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]; |
454 |
|
} |
455 |
|
} |
456 |
|
|
457 |
|
|
458 |
|
/* |
459 |
* mips_coproc_tlb_set_entry(): |
* mips_coproc_tlb_set_entry(): |
460 |
* |
* |
461 |
* Used by machine setup code, if a specific machine emulation starts up |
* Used by machine setup code, if a specific machine emulation starts up |
535 |
* |
* |
536 |
* Note: In the R3000 case, the asid argument is shifted 6 bits. |
* Note: In the R3000 case, the asid argument is shifted 6 bits. |
537 |
*/ |
*/ |
538 |
static void invalidate_asid(struct cpu *cpu, int asid) |
static void invalidate_asid(struct cpu *cpu, unsigned int asid) |
539 |
{ |
{ |
540 |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
541 |
int i, ntlbs = cp->nr_of_tlbs; |
unsigned int i, ntlbs = cp->nr_of_tlbs; |
542 |
struct mips_tlb *tlb = cp->tlbs; |
struct mips_tlb *tlb = cp->tlbs; |
543 |
|
|
544 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { |
if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { |
613 |
if (cp->coproc_nr==0 && reg_nr==COP0_PAGEMASK) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_PAGEMASK) unimpl = 0; |
614 |
if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; |
615 |
if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; |
616 |
if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { |
617 |
|
/* TODO: Increase count in a more meaningful way! */ |
618 |
|
cp->reg[COP0_COUNT] = (int32_t) (cp->reg[COP0_COUNT] + 1); |
619 |
|
unimpl = 0; |
620 |
|
} |
621 |
if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; |
622 |
if (cp->coproc_nr==0 && reg_nr==COP0_COMPARE) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_COMPARE) unimpl = 0; |
623 |
if (cp->coproc_nr==0 && reg_nr==COP0_STATUS) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_STATUS) unimpl = 0; |
683 |
int readonly = 0; |
int readonly = 0; |
684 |
uint64_t tmp = *ptr; |
uint64_t tmp = *ptr; |
685 |
uint64_t tmp2 = 0, old; |
uint64_t tmp2 = 0, old; |
686 |
int inval = 0, old_asid, oldmode; |
int inval = 0; |
687 |
|
unsigned int old_asid; |
688 |
|
uint64_t oldmode; |
689 |
|
|
690 |
switch (cp->coproc_nr) { |
switch (cp->coproc_nr) { |
691 |
case 0: |
case 0: |
777 |
unimpl = 0; |
unimpl = 0; |
778 |
break; |
break; |
779 |
case COP0_COMPARE: |
case COP0_COMPARE: |
780 |
/* Clear the timer interrupt bit (bit 7): */ |
if (cpu->machine->emulated_hz > 0) { |
781 |
cpu->cd.mips.compare_register_set = 1; |
int32_t compare_diff = tmp - |
782 |
mips_cpu_interrupt_ack(cpu, 7); |
cp->reg[COP0_COMPARE]; |
783 |
|
double hz; |
784 |
|
|
785 |
|
if (compare_diff < 0) |
786 |
|
hz = tmp - cp->reg[COP0_COUNT]; |
787 |
|
|
788 |
|
if (compare_diff == 0) |
789 |
|
hz = 0; |
790 |
|
else |
791 |
|
hz = (double)cpu->machine->emulated_hz |
792 |
|
/ (double)compare_diff; |
793 |
|
/* |
794 |
|
* TODO: DON'T HARDCODE THIS! |
795 |
|
*/ |
796 |
|
hz = 100.0; |
797 |
|
|
798 |
|
/* Initialize or re-set the periodic timer: */ |
799 |
|
if (hz > 0) { |
800 |
|
if (cpu->cd.mips.timer == NULL) |
801 |
|
cpu->cd.mips.timer = timer_add( |
802 |
|
hz, mips_timer_tick, cpu); |
803 |
|
else |
804 |
|
timer_update_frequency( |
805 |
|
cpu->cd.mips.timer, hz); |
806 |
|
} |
807 |
|
} |
808 |
|
|
809 |
|
/* Ack the periodic timer, if it was asserted: */ |
810 |
|
if (cp->reg[COP0_CAUSE] & 0x8000 && |
811 |
|
cpu->cd.mips.compare_interrupts_pending > 0) |
812 |
|
cpu->cd.mips.compare_interrupts_pending --; |
813 |
|
|
814 |
|
/* Clear the timer interrupt assertion (bit 7): */ |
815 |
|
cp->reg[COP0_CAUSE] &= ~0x8000; |
816 |
|
|
817 |
if (tmp != (uint64_t)(int64_t)(int32_t)tmp) |
if (tmp != (uint64_t)(int64_t)(int32_t)tmp) |
818 |
fatal("WARNING: trying to write a 64-bit value" |
fatal("WARNING: trying to write a 64-bit value" |
819 |
" to the COMPARE register!\n"); |
" to the COMPARE register!\n"); |
820 |
|
|
821 |
tmp = (int64_t)(int32_t)tmp; |
tmp = (int64_t)(int32_t)tmp; |
822 |
|
cpu->cd.mips.compare_register_set = 1; |
823 |
unimpl = 0; |
unimpl = 0; |
824 |
break; |
break; |
825 |
case COP0_ENTRYHI: |
case COP0_ENTRYHI: |
1682 |
(cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) |
(cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) |
1683 |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1684 |
INVALIDATE_VADDR); |
INVALIDATE_VADDR); |
1685 |
|
|
1686 |
break; |
break; |
1687 |
|
|
1688 |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1689 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
oldvaddr = cp->tlbs[index].hi & |
1690 |
|
(ENTRYHI_VPN2_MASK_R10K | ENTRYHI_R_MASK); |
1691 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1692 |
if (oldvaddr & 0x80000000000ULL) |
if (oldvaddr & 0x80000000000ULL) |
1693 |
oldvaddr |= 0xfffff00000000000ULL; |
oldvaddr |= 0x3ffff00000000000ULL; |
1694 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1695 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1696 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1697 |
oldvaddr = (int32_t)oldvaddr; |
oldvaddr = (int32_t)oldvaddr; |
1698 |
} else { |
} else { |
1699 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1700 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
oldvaddr = cp->tlbs[index].hi & |
1701 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); |
1702 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1703 |
if (oldvaddr & 0x8000000000ULL) |
if (oldvaddr & 0x8000000000ULL) |
1704 |
oldvaddr |= 0xffffff0000000000ULL; |
oldvaddr |= 0x3fffff0000000000ULL; |
1705 |
} |
} |
1706 |
|
|
|
#if 0 |
|
|
/* TODO: FIX THIS! It shouldn't be needed! */ |
|
|
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
|
|
#else |
|
1707 |
/* |
/* |
1708 |
* TODO: non-4KB page sizes! |
* TODO: non-4KB page sizes! |
1709 |
*/ |
*/ |
1713 |
if (cp->tlbs[index].lo1 & ENTRYLO_V) |
if (cp->tlbs[index].lo1 & ENTRYLO_V) |
1714 |
cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, |
cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, |
1715 |
INVALIDATE_VADDR); |
INVALIDATE_VADDR); |
|
#endif |
|
1716 |
} |
} |
1717 |
|
|
1718 |
#if 0 |
#if 0 |
1730 |
int i; |
int i; |
1731 |
unsigned int asid; |
unsigned int asid; |
1732 |
|
|
1733 |
vaddr1 = cp->reg[COP0_ENTRYHI] & ENTRYHI_VPN2_MASK_R10K; |
vaddr1 = cp->reg[COP0_ENTRYHI] & |
1734 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1735 |
asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; |
asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; |
1736 |
/* Since this is just a warning, it's probably not necessary |
/* Since this is just a warning, it's probably not necessary |
1737 |
to use R4000 masks etc. */ |
to use R4000 masks etc. */ |
1744 |
(cp->tlbs[i].hi & ENTRYHI_ASID) != asid) |
(cp->tlbs[i].hi & ENTRYHI_ASID) != asid) |
1745 |
continue; |
continue; |
1746 |
|
|
1747 |
vaddr2 = cp->tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; |
vaddr2 = cp->tlbs[i].hi & |
1748 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1749 |
if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & |
if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & |
1750 |
ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) |
ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) |
1751 |
fatal("\n[ WARNING! tlbw%s to index 0x%02x " |
fatal("\n[ WARNING! tlbw%s to index 0x%02x " |
1779 |
INVALIDATE_PADDR); |
INVALIDATE_PADDR); |
1780 |
} |
} |
1781 |
|
|
1782 |
|
/* Set new last_written_tlb_index hint: */ |
1783 |
|
cpu->cd.mips.last_written_tlb_index = index; |
1784 |
|
|
1785 |
if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { |
if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { |
1786 |
fatal("Wow! Interesting case; tlbw* while caches" |
fatal("Wow! Interesting case; tlbw* while caches" |
1787 |
" are isolated. TODO\n"); |
" are isolated. TODO\n"); |
1802 |
int pfn_shift = 12, vpn_shift = 12; |
int pfn_shift = 12, vpn_shift = 12; |
1803 |
int wf0, wf1, mask; |
int wf0, wf1, mask; |
1804 |
uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; |
uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; |
1805 |
|
uint64_t psize; |
1806 |
|
|
1807 |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
1808 |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
1841 |
} |
} |
1842 |
|
|
1843 |
paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
1844 |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
>> ENTRYLO_PFN_SHIFT) << pfn_shift |
1845 |
|
>> vpn_shift << vpn_shift; |
1846 |
paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) |
paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) |
1847 |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
>> ENTRYLO_PFN_SHIFT) << pfn_shift |
1848 |
|
>> vpn_shift << vpn_shift; |
1849 |
|
|
1850 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1851 |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
vaddr0 = cp->tlbs[index].hi & |
1852 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1853 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1854 |
if (vaddr0 & 0x80000000000ULL) |
if (vaddr0 & 0x80000000000ULL) |
1855 |
vaddr0 |= 0xfffff00000000000ULL; |
vaddr0 |= 0x3ffff00000000000ULL; |
1856 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1857 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1858 |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1859 |
vaddr0 = (int32_t)vaddr0; |
vaddr0 = (int32_t)vaddr0; |
1860 |
} else { |
} else { |
1861 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1862 |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
vaddr0 = cp->tlbs[index].hi & |
1863 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); |
1864 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1865 |
if (vaddr0 & 0x8000000000ULL) |
if (vaddr0 & 0x8000000000ULL) |
1866 |
vaddr0 |= 0xffffff0000000000ULL; |
vaddr0 |= 0x3fffff0000000000ULL; |
1867 |
} |
} |
1868 |
|
|
1869 |
vaddr1 = vaddr0 | (1 << vpn_shift); |
vaddr1 = vaddr0 | (1 << vpn_shift); |
1888 |
* Invalidate any code translations, if we are writing Dirty |
* Invalidate any code translations, if we are writing Dirty |
1889 |
* pages to the TLB: (TODO: 4KB hardcoded... ugly) |
* pages to the TLB: (TODO: 4KB hardcoded... ugly) |
1890 |
*/ |
*/ |
1891 |
for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { |
psize = 1 << pfn_shift; |
1892 |
|
for (ptmp = 0; ptmp < psize; ptmp += 0x1000) { |
1893 |
if (wf0) |
if (wf0) |
1894 |
cpu->invalidate_code_translation(cpu, |
cpu->invalidate_code_translation(cpu, |
1895 |
paddr0 + ptmp, INVALIDATE_PADDR); |
paddr0 + ptmp, INVALIDATE_PADDR); |
1916 |
if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) |
if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) |
1917 |
cpu->update_translation_table(cpu, vaddr1, memblock, |
cpu->update_translation_table(cpu, vaddr1, memblock, |
1918 |
wf1, paddr1); |
wf1, paddr1); |
1919 |
|
|
1920 |
|
/* Set new last_written_tlb_index hint: */ |
1921 |
|
cpu->cd.mips.last_written_tlb_index = index; |
1922 |
} |
} |
1923 |
} |
} |
1924 |
|
|