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.33 2006/06/22 13:30:38 debug Exp $ |
* $Id: cpu_mips_coproc.c,v 1.62 2007/02/03 16:18:56 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 |
551 |
INVALIDATE_VADDR); |
INVALIDATE_VADDR); |
552 |
} |
} |
553 |
} else { |
} else { |
554 |
/* TODO: Implement support for other. */ |
int non4kpages = 0; |
555 |
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; |
556 |
|
|
557 |
|
if (cpu->is_32bit) { |
558 |
|
topbit = 0x80000000; |
559 |
|
fillmask = 0xffffffff00000000ULL; |
560 |
|
} else if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
561 |
|
topbit <<= 43; |
562 |
|
fillmask <<= 4; |
563 |
|
} else { |
564 |
|
topbit <<= 39; |
565 |
|
} |
566 |
|
|
567 |
|
for (i=0; i<ntlbs; i++) { |
568 |
|
if (tlb[i].mask != 0 && tlb[i].mask != 0x1800) { |
569 |
|
non4kpages = 1; |
570 |
|
continue; |
571 |
|
} |
572 |
|
|
573 |
|
if ((tlb[i].hi & ENTRYHI_ASID) == asid && |
574 |
|
!(tlb[i].hi & TLB_G)) { |
575 |
|
uint64_t vaddr0, vaddr1; |
576 |
|
vaddr0 = cp->tlbs[i].hi & ~fillmask; |
577 |
|
if (vaddr0 & topbit) |
578 |
|
vaddr0 |= fillmask; |
579 |
|
vaddr1 = vaddr0 | 0x1000; /* TODO: mask */ |
580 |
|
|
581 |
|
if (tlb[i].lo0 & ENTRYLO_V) |
582 |
|
cpu->invalidate_translation_caches(cpu, |
583 |
|
vaddr0, INVALIDATE_VADDR); |
584 |
|
if (tlb[i].lo1 & ENTRYLO_V) |
585 |
|
cpu->invalidate_translation_caches(cpu, |
586 |
|
vaddr1, INVALIDATE_VADDR); |
587 |
|
} |
588 |
|
} |
589 |
|
|
590 |
|
if (non4kpages) { |
591 |
|
cpu->invalidate_translation_caches(cpu, |
592 |
|
0, INVALIDATE_ALL); |
593 |
|
} |
594 |
} |
} |
595 |
} |
} |
596 |
|
|
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) { |
if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { |
617 |
#if 0 |
/* TODO: Increase count in a more meaningful way! */ |
618 |
/* |
cp->reg[COP0_COUNT] = (int32_t) (cp->reg[COP0_COUNT] + 1); |
|
* This speeds up delay-loops that just read the count |
|
|
* register until it has reached a certain value. (Only for |
|
|
* R4000 etc.) |
|
|
* |
|
|
* TODO: Maybe this should be optional? |
|
|
*/ |
|
|
if (cpu->cd.mips.cpu_type.exc_model != EXC3K) { |
|
|
int increase = 500; |
|
|
int32_t x = cp->reg[COP0_COUNT]; |
|
|
int32_t y = cp->reg[COP0_COMPARE]; |
|
|
int32_t diff = x - y; |
|
|
if (diff < 0 && diff + increase >= 0 |
|
|
&& cpu->cd.mips.compare_register_set) { |
|
|
mips_cpu_interrupt(cpu, 7); |
|
|
cpu->cd.mips.compare_register_set = 0; |
|
|
} |
|
|
cp->reg[COP0_COUNT] = (int64_t) |
|
|
(int32_t)(cp->reg[COP0_COUNT] + increase); |
|
|
} |
|
|
#endif |
|
619 |
unimpl = 0; |
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; |
775 |
unimpl = 0; |
unimpl = 0; |
776 |
break; |
break; |
777 |
case COP0_COMPARE: |
case COP0_COMPARE: |
778 |
/* Clear the timer interrupt bit (bit 7): */ |
if (cpu->machine->emulated_hz > 0) { |
779 |
cpu->cd.mips.compare_register_set = 1; |
int32_t compare_diff = tmp - |
780 |
mips_cpu_interrupt_ack(cpu, 7); |
cp->reg[COP0_COMPARE]; |
781 |
|
double hz; |
782 |
|
|
783 |
|
if (compare_diff < 0) |
784 |
|
hz = tmp - cp->reg[COP0_COUNT]; |
785 |
|
|
786 |
|
if (compare_diff == 0) |
787 |
|
hz = 0; |
788 |
|
else |
789 |
|
hz = (double)cpu->machine->emulated_hz |
790 |
|
/ (double)compare_diff; |
791 |
|
/* |
792 |
|
* TODO: DON'T HARDCODE THIS! |
793 |
|
*/ |
794 |
|
hz = 100.0; |
795 |
|
|
796 |
|
/* Initialize or re-set the periodic timer: */ |
797 |
|
if (hz > 0) { |
798 |
|
if (cpu->cd.mips.timer == NULL) |
799 |
|
cpu->cd.mips.timer = timer_add( |
800 |
|
hz, mips_timer_tick, cpu); |
801 |
|
else |
802 |
|
timer_update_frequency( |
803 |
|
cpu->cd.mips.timer, hz); |
804 |
|
} |
805 |
|
} |
806 |
|
|
807 |
|
/* Ack the periodic timer, if it was asserted: */ |
808 |
|
if (cp->reg[COP0_CAUSE] & 0x8000 && |
809 |
|
cpu->cd.mips.compare_interrupts_pending > 0) |
810 |
|
cpu->cd.mips.compare_interrupts_pending --; |
811 |
|
|
812 |
|
/* Clear the timer interrupt assertion (bit 7): */ |
813 |
|
cp->reg[COP0_CAUSE] &= ~0x8000; |
814 |
|
|
815 |
if (tmp != (uint64_t)(int64_t)(int32_t)tmp) |
if (tmp != (uint64_t)(int64_t)(int32_t)tmp) |
816 |
fatal("WARNING: trying to write a 64-bit value" |
fatal("WARNING: trying to write a 64-bit value" |
817 |
" to the COMPARE register!\n"); |
" to the COMPARE register!\n"); |
818 |
|
|
819 |
tmp = (int64_t)(int32_t)tmp; |
tmp = (int64_t)(int32_t)tmp; |
820 |
|
cpu->cd.mips.compare_register_set = 1; |
821 |
unimpl = 0; |
unimpl = 0; |
822 |
break; |
break; |
823 |
case COP0_ENTRYHI: |
case COP0_ENTRYHI: |
900 |
case COP0_STATUS: |
case COP0_STATUS: |
901 |
oldmode = cp->reg[COP0_STATUS]; |
oldmode = cp->reg[COP0_STATUS]; |
902 |
tmp &= ~(1 << 21); /* bit 21 is read-only */ |
tmp &= ~(1 << 21); /* bit 21 is read-only */ |
903 |
|
|
904 |
/* |
/* |
905 |
* TODO: Perhaps this can be solved some other |
* When isolating caches, invalidate all translations. |
906 |
* way, like in the old bintrans system? |
* During the isolation, a special hack in memory_rw.c |
907 |
|
* prevents translation tables from being updated, so |
908 |
|
* the translation caches don't have to be invalidated |
909 |
|
* when switching back to normal mode. |
910 |
*/ |
*/ |
911 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU3K && |
if (cpu->cd.mips.cpu_type.mmu_model == MMU3K && |
912 |
(oldmode & MIPS1_ISOL_CACHES) != |
(oldmode & MIPS1_ISOL_CACHES) != |
913 |
(tmp & MIPS1_ISOL_CACHES)) { |
(tmp & MIPS1_ISOL_CACHES)) { |
914 |
cpu->invalidate_translation_caches( |
/* Invalidate everything if we are switching |
915 |
cpu, 0, INVALIDATE_ALL); |
to isolated mode: */ |
916 |
|
if (tmp & MIPS1_ISOL_CACHES) { |
917 |
/* Perhaps add some kind of INVALIDATE_ |
cpu->invalidate_translation_caches( |
918 |
ALL_PADDR_WHICH_HAS_A_CORRESPONDING_ |
cpu, 0, INVALIDATE_ALL); |
919 |
VADDR of some kind? :-) */ |
} |
|
cpu_create_or_reset_tc(cpu); |
|
920 |
} |
} |
921 |
unimpl = 0; |
unimpl = 0; |
922 |
break; |
break; |
1262 |
|
|
1263 |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
1264 |
if ((function & 0x03e00000) == 0x01000000) { |
if ((function & 0x03e00000) == 0x01000000) { |
1265 |
int nd, tf, imm, cond_true; |
int nd, tf, imm; |
1266 |
char *instr_mnem; |
char *instr_mnem; |
1267 |
|
|
1268 |
/* cc are bits 20..18: */ |
/* cc are bits 20..18: */ |
1285 |
if (unassemble_only) |
if (unassemble_only) |
1286 |
return 1; |
return 1; |
1287 |
|
|
1288 |
if (cpu->delay_slot) { |
fatal("INTERNAL ERROR: MIPS coprocessor branches should not" |
1289 |
fatal("%s: jump inside a jump's delay slot, " |
" be implemented in cpu_mips_coproc.c, but in" |
1290 |
"or similar. TODO\n", instr_mnem); |
" cpu_mips_instr.c!\n"); |
1291 |
cpu->running = 0; |
exit(1); |
|
return 1; |
|
|
} |
|
|
|
|
|
/* Both the FCCR and FCSR contain condition code bits... */ |
|
|
if (cc == 0) |
|
|
cond_true = (cp->fcr[MIPS_FPU_FCSR] >> |
|
|
MIPS_FCSR_FCC0_SHIFT) & 1; |
|
|
else |
|
|
cond_true = (cp->fcr[MIPS_FPU_FCSR] >> |
|
|
(MIPS_FCSR_FCC1_SHIFT + cc-1)) & 1; |
|
|
|
|
|
if (!tf) |
|
|
cond_true = !cond_true; |
|
|
|
|
|
if (cond_true) { |
|
|
cpu->delay_slot = TO_BE_DELAYED; |
|
|
cpu->delay_jmpaddr = cpu->pc + (imm << 2); |
|
|
} else { |
|
|
/* "likely": */ |
|
|
if (nd) { |
|
|
/* nullify the delay slot */ |
|
|
cpu->cd.mips.nullify_next = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
return 1; |
|
1292 |
} |
} |
1293 |
|
|
1294 |
/* add.fmt: Floating-point add */ |
/* add.fmt: Floating-point add */ |
1502 |
R2K3K_INDEX_SHIFT; |
R2K3K_INDEX_SHIFT; |
1503 |
if (i >= cp->nr_of_tlbs) { |
if (i >= cp->nr_of_tlbs) { |
1504 |
/* TODO: exception? */ |
/* TODO: exception? */ |
1505 |
fatal("warning: tlbr from index %i (too " |
fatal("[ warning: tlbr from index %i (too " |
1506 |
"high)\n", i); |
"high) ]\n", i); |
1507 |
return; |
return; |
1508 |
} |
} |
1509 |
|
|
1510 |
/* |
cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; |
1511 |
* TODO: Hm. Earlier I had an & ~0x3f on the high |
cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0; |
|
* assignment and an & ~0xff on the lo0 assignment. |
|
|
* I wonder why. |
|
|
*/ |
|
|
|
|
|
cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; /* & ~0x3f; */ |
|
|
cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0;/* & ~0xff; */ |
|
1512 |
} else { |
} else { |
1513 |
/* R4000: */ |
/* R4000: */ |
1514 |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
1605 |
void coproc_tlbwri(struct cpu *cpu, int randomflag) |
void coproc_tlbwri(struct cpu *cpu, int randomflag) |
1606 |
{ |
{ |
1607 |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
1608 |
int index, g_bit, old_asid = -1; |
int index, g_bit; |
1609 |
uint64_t oldvaddr; |
uint64_t oldvaddr; |
1610 |
|
|
1611 |
if (randomflag) { |
if (randomflag) { |
1612 |
if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { |
if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { |
1613 |
cp->reg[COP0_RANDOM] = |
index = ((cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) |
1614 |
((random() % (cp->nr_of_tlbs - 8)) + 8) |
>> R2K3K_RANDOM_SHIFT) - 1; |
1615 |
<< R2K3K_RANDOM_SHIFT; |
/* R3000 always has 8 wired entries: */ |
1616 |
index = (cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) |
if (index < 8) |
1617 |
>> R2K3K_RANDOM_SHIFT; |
index = cp->nr_of_tlbs - 1; |
1618 |
|
cp->reg[COP0_RANDOM] = index << R2K3K_RANDOM_SHIFT; |
1619 |
} else { |
} else { |
1620 |
cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() |
cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() |
1621 |
% (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); |
% (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); |
1662 |
/* |
/* |
1663 |
* Any virtual address translation for the old TLB entry must be |
* Any virtual address translation for the old TLB entry must be |
1664 |
* invalidated first: |
* invalidated first: |
1665 |
|
* |
1666 |
|
* (Only Valid entries need to be invalidated, and only those that |
1667 |
|
* are either Global, or have the same ASID as the new entry will |
1668 |
|
* have. No other address translations should be active anyway.) |
1669 |
*/ |
*/ |
1670 |
|
|
1671 |
switch (cpu->cd.mips.cpu_type.mmu_model) { |
switch (cpu->cd.mips.cpu_type.mmu_model) { |
1672 |
|
|
1673 |
case MMU3K: |
case MMU3K: |
1674 |
oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; |
oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; |
1675 |
oldvaddr &= 0xffffffffULL; |
oldvaddr = (int32_t) oldvaddr; |
1676 |
if (oldvaddr & 0x80000000ULL) |
|
1677 |
oldvaddr |= 0xffffffff00000000ULL; |
if (cp->tlbs[index].lo0 & R2K3K_ENTRYLO_V && |
1678 |
old_asid = (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) |
(cp->tlbs[index].lo0 & R2K3K_ENTRYLO_G || |
1679 |
>> R2K3K_ENTRYHI_ASID_SHIFT; |
(cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) == |
1680 |
|
(cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) |
1681 |
|
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1682 |
|
INVALIDATE_VADDR); |
1683 |
|
|
|
cpu->invalidate_translation_caches(cpu, oldvaddr, |
|
|
INVALIDATE_VADDR); |
|
1684 |
break; |
break; |
1685 |
|
|
1686 |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1687 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
oldvaddr = cp->tlbs[index].hi & |
1688 |
|
(ENTRYHI_VPN2_MASK_R10K | ENTRYHI_R_MASK); |
1689 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1690 |
if (oldvaddr & 0x80000000000ULL) |
if (oldvaddr & 0x80000000000ULL) |
1691 |
oldvaddr |= 0xfffff00000000000ULL; |
oldvaddr |= 0x3ffff00000000000ULL; |
1692 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1693 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1694 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1695 |
oldvaddr = (int32_t)oldvaddr; |
oldvaddr = (int32_t)oldvaddr; |
1696 |
} else { |
} else { |
1697 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1698 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
oldvaddr = cp->tlbs[index].hi & |
1699 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); |
1700 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1701 |
if (oldvaddr & 0x8000000000ULL) |
if (oldvaddr & 0x8000000000ULL) |
1702 |
oldvaddr |= 0xffffff0000000000ULL; |
oldvaddr |= 0x3fffff0000000000ULL; |
1703 |
} |
} |
1704 |
|
|
|
#if 1 |
|
|
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
|
|
#else |
|
1705 |
/* |
/* |
1706 |
* TODO: non-4KB page sizes! |
* TODO: non-4KB page sizes! |
1707 |
*/ |
*/ |
1708 |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
if (cp->tlbs[index].lo0 & ENTRYLO_V) |
1709 |
INVALIDATE_VADDR); |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1710 |
cpu->invalidate_translation_caches(cpu, oldvaddr | 0x1000, |
INVALIDATE_VADDR); |
1711 |
INVALIDATE_VADDR); |
if (cp->tlbs[index].lo1 & ENTRYLO_V) |
1712 |
#endif |
cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, |
1713 |
|
INVALIDATE_VADDR); |
1714 |
} |
} |
1715 |
|
|
1716 |
|
#if 0 |
1717 |
/* |
/* |
1718 |
* Check for duplicate entries. (There should not be two mappings |
* Check for duplicate entries. (There should not be two mappings |
1719 |
* from one virtual address to physical addresses.) |
* from one virtual address to physical addresses.) |
1728 |
int i; |
int i; |
1729 |
unsigned int asid; |
unsigned int asid; |
1730 |
|
|
1731 |
vaddr1 = cp->reg[COP0_ENTRYHI] & ENTRYHI_VPN2_MASK_R10K; |
vaddr1 = cp->reg[COP0_ENTRYHI] & |
1732 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1733 |
asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; |
asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; |
1734 |
/* Since this is just a warning, it's probably not necessary |
/* Since this is just a warning, it's probably not necessary |
1735 |
to use R4000 masks etc. */ |
to use R4000 masks etc. */ |
1742 |
(cp->tlbs[i].hi & ENTRYHI_ASID) != asid) |
(cp->tlbs[i].hi & ENTRYHI_ASID) != asid) |
1743 |
continue; |
continue; |
1744 |
|
|
1745 |
vaddr2 = cp->tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; |
vaddr2 = cp->tlbs[i].hi & |
1746 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1747 |
if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & |
if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & |
1748 |
ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) |
ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) |
1749 |
fatal("\n[ WARNING! tlbw%s to index 0x%02x " |
fatal("\n[ WARNING! tlbw%s to index 0x%02x " |
1753 |
(long long)vaddr1, asid, i); |
(long long)vaddr1, asid, i); |
1754 |
} |
} |
1755 |
} |
} |
1756 |
|
#endif |
1757 |
|
|
1758 |
/* Write the new entry: */ |
/* Write the new entry: */ |
1759 |
|
|
1777 |
INVALIDATE_PADDR); |
INVALIDATE_PADDR); |
1778 |
} |
} |
1779 |
|
|
1780 |
|
/* Set new last_written_tlb_index hint: */ |
1781 |
|
cpu->cd.mips.last_written_tlb_index = index; |
1782 |
|
|
1783 |
|
if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { |
1784 |
|
fatal("Wow! Interesting case; tlbw* while caches" |
1785 |
|
" are isolated. TODO\n"); |
1786 |
|
/* Don't update the translation table in this |
1787 |
|
case... */ |
1788 |
|
exit(1); |
1789 |
|
} |
1790 |
|
|
1791 |
/* If we have a memblock (host page) for the physical |
/* If we have a memblock (host page) for the physical |
1792 |
page, then add a translation for it immediately: */ |
page, then add a translation for it immediately: */ |
1793 |
if (memblock != NULL && |
if (memblock != NULL && |
1794 |
cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) { |
cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) |
|
memblock += (paddr & ((1 << BITS_PER_PAGETABLE) - 1)); |
|
1795 |
cpu->update_translation_table(cpu, vaddr, memblock, |
cpu->update_translation_table(cpu, vaddr, memblock, |
1796 |
wf, paddr); |
wf, paddr); |
|
} |
|
1797 |
} else { |
} else { |
1798 |
/* R4000: */ |
/* R4000 etc.: */ |
1799 |
g_bit = (cp->reg[COP0_ENTRYLO0] & |
unsigned char *memblock = NULL; |
1800 |
cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; |
int pfn_shift = 12, vpn_shift = 12; |
1801 |
|
int wf0, wf1, mask; |
1802 |
|
uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; |
1803 |
|
|
1804 |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
1805 |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
1806 |
cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; |
cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; |
1807 |
cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; |
cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; |
1808 |
|
|
1809 |
|
wf0 = cp->tlbs[index].lo0 & ENTRYLO_D; |
1810 |
|
wf1 = cp->tlbs[index].lo1 & ENTRYLO_D; |
1811 |
|
|
1812 |
|
mask = cp->reg[COP0_PAGEMASK]; |
1813 |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
1814 |
/* NOTE: The VR4131 (and possibly others) don't have |
pfn_shift = 10; |
1815 |
a Global bit in entryhi */ |
mask |= 0x07ff; |
|
cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; |
|
1816 |
} else { |
} else { |
1817 |
cp->tlbs[index].lo0 &= ~ENTRYLO_G; |
mask |= 0x1fff; |
1818 |
cp->tlbs[index].lo1 &= ~ENTRYLO_G; |
} |
1819 |
|
switch (mask) { |
1820 |
cp->tlbs[index].hi &= ~TLB_G; |
case 0x00007ff: |
1821 |
if (g_bit) |
if (cp->tlbs[index].lo0 & ENTRYLO_V || |
1822 |
cp->tlbs[index].hi |= TLB_G; |
cp->tlbs[index].lo1 & ENTRYLO_V) { |
1823 |
|
fatal("1KB pages don't work with dyntrans.\n"); |
1824 |
|
exit(1); |
1825 |
|
} |
1826 |
|
vpn_shift = 10; |
1827 |
|
break; |
1828 |
|
case 0x0001fff: break; |
1829 |
|
case 0x0007fff: vpn_shift = 14; break; |
1830 |
|
case 0x001ffff: vpn_shift = 16; break; |
1831 |
|
case 0x007ffff: vpn_shift = 18; break; |
1832 |
|
case 0x01fffff: vpn_shift = 20; break; |
1833 |
|
case 0x07fffff: vpn_shift = 22; break; |
1834 |
|
case 0x1ffffff: vpn_shift = 24; break; |
1835 |
|
case 0x7ffffff: vpn_shift = 26; break; |
1836 |
|
default:fatal("Unimplemented MASK = 0x%016x\n", mask); |
1837 |
|
exit(1); |
1838 |
} |
} |
1839 |
|
|
1840 |
#if 1 |
paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
1841 |
cpu_create_or_reset_tc(cpu); |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
1842 |
#else |
paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) |
1843 |
/* Invalidate any code translations, if we are writing |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
1844 |
Dirty pages to the TLB: */ |
|
1845 |
if (cp->reg[COP0_PAGEMASK] != 0) |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1846 |
printf("MASK = %08"PRIx32"\n", (uint32_t)cp->reg[COP0_PAGEMASK]); |
vaddr0 = cp->tlbs[index].hi & |
1847 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
|
// 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); |
|
|
|
|
|
|
|
|
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
|
|
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
|
1848 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1849 |
if (oldvaddr & 0x80000000000ULL) |
if (vaddr0 & 0x80000000000ULL) |
1850 |
oldvaddr |= 0xfffff00000000000ULL; |
vaddr0 |= 0x3ffff00000000000ULL; |
1851 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1852 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1853 |
oldvaddr = (int32_t)oldvaddr; |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1854 |
|
vaddr0 = (int32_t)vaddr0; |
1855 |
} else { |
} else { |
1856 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1857 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
vaddr0 = cp->tlbs[index].hi & |
1858 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); |
1859 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1860 |
if (oldvaddr & 0x8000000000ULL) |
if (vaddr0 & 0x8000000000ULL) |
1861 |
oldvaddr |= 0xffffff0000000000ULL; |
vaddr0 |= 0x3fffff0000000000ULL; |
1862 |
} |
} |
1863 |
|
|
1864 |
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); |
|
1865 |
|
|
1866 |
cpu->invalidate_translation_caches(cpu, oldvaddr, INVALIDATE_VADDR); |
g_bit = (cp->reg[COP0_ENTRYLO0] & |
1867 |
cpu->invalidate_translation_caches(cpu, oldvaddr | 0x1000, INVALIDATE_VADDR); |
cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; |
1868 |
|
|
1869 |
|
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
1870 |
|
/* NOTE: The VR4131 (and possibly others) don't have |
1871 |
|
a Global bit in entryhi */ |
1872 |
|
cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; |
1873 |
|
} else { |
1874 |
|
cp->tlbs[index].lo0 &= ~ENTRYLO_G; |
1875 |
|
cp->tlbs[index].lo1 &= ~ENTRYLO_G; |
1876 |
|
|
1877 |
#endif |
cp->tlbs[index].hi &= ~TLB_G; |
1878 |
} |
if (g_bit) |
1879 |
} |
cp->tlbs[index].hi |= TLB_G; |
1880 |
|
} |
1881 |
|
|
1882 |
|
/* |
1883 |
|
* Invalidate any code translations, if we are writing Dirty |
1884 |
|
* pages to the TLB: (TODO: 4KB hardcoded... ugly) |
1885 |
|
*/ |
1886 |
|
for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { |
1887 |
|
if (wf0) |
1888 |
|
cpu->invalidate_code_translation(cpu, |
1889 |
|
paddr0 + ptmp, INVALIDATE_PADDR); |
1890 |
|
if (wf1) |
1891 |
|
cpu->invalidate_code_translation(cpu, |
1892 |
|
paddr1 + ptmp, INVALIDATE_PADDR); |
1893 |
|
} |
1894 |
|
|
1895 |
|
/* |
1896 |
|
* If we have a memblock (host page) for the physical page, |
1897 |
|
* then add a translation for it immediately, to save some |
1898 |
|
* time. (It would otherwise be added later on anyway, |
1899 |
|
* because of a translation miss.) |
1900 |
|
* |
1901 |
|
* NOTE/TODO: This is only for 4KB pages so far. It would |
1902 |
|
* be too expensive to add e.g. 16MB pages like |
1903 |
|
* this. |
1904 |
|
*/ |
1905 |
|
memblock = memory_paddr_to_hostaddr(cpu->mem, paddr0, 0); |
1906 |
|
if (memblock != NULL && cp->reg[COP0_ENTRYLO0] & ENTRYLO_V) |
1907 |
|
cpu->update_translation_table(cpu, vaddr0, memblock, |
1908 |
|
wf0, paddr0); |
1909 |
|
memblock = memory_paddr_to_hostaddr(cpu->mem, paddr1, 0); |
1910 |
|
if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) |
1911 |
|
cpu->update_translation_table(cpu, vaddr1, memblock, |
1912 |
|
wf1, paddr1); |
1913 |
|
|
1914 |
/* |
/* Set new last_written_tlb_index hint: */ |
1915 |
* coproc_rfe(): |
cpu->cd.mips.last_written_tlb_index = index; |
1916 |
* |
} |
|
* 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); |
|
1917 |
} |
} |
1918 |
|
|
1919 |
|
|
2135 |
|
|
2136 |
/* TLB operations and other things: */ |
/* TLB operations and other things: */ |
2137 |
if (cp->coproc_nr == 0) { |
if (cp->coproc_nr == 0) { |
2138 |
|
if (!unassemble_only) { |
2139 |
|
fatal("FATAL INTERNAL ERROR: Should be implemented" |
2140 |
|
" with dyntrans instead.\n"); |
2141 |
|
exit(1); |
2142 |
|
} |
2143 |
|
|
2144 |
op = (function) & 0xff; |
op = (function) & 0xff; |
2145 |
switch (co_bit) { |
switch (co_bit) { |
2146 |
case 1: |
case 1: |
2147 |
switch (op) { |
switch (op) { |
2148 |
case COP0_TLBR: /* Read indexed TLB entry */ |
case COP0_TLBR: /* Read indexed TLB entry */ |
2149 |
if (unassemble_only) { |
debug("tlbr\n"); |
|
debug("tlbr\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 1); |
|
2150 |
return; |
return; |
2151 |
case COP0_TLBWI: /* Write indexed */ |
case COP0_TLBWI: /* Write indexed */ |
2152 |
case COP0_TLBWR: /* Write random */ |
case COP0_TLBWR: /* Write random */ |
2153 |
if (unassemble_only) { |
if (op == COP0_TLBWI) |
2154 |
if (op == COP0_TLBWI) |
debug("tlbwi"); |
2155 |
debug("tlbwi"); |
else |
2156 |
else |
debug("tlbwr"); |
2157 |
debug("tlbwr"); |
if (!running) { |
2158 |
if (!running) { |
debug("\n"); |
|
debug("\n"); |
|
|
return; |
|
|
} |
|
|
debug("\tindex=%08llx", |
|
|
(long long)cp->reg[COP0_INDEX]); |
|
|
debug(", random=%08llx", |
|
|
(long long)cp->reg[COP0_RANDOM]); |
|
|
debug(", mask=%016llx", |
|
|
(long long)cp->reg[COP0_PAGEMASK]); |
|
|
debug(", hi=%016llx", |
|
|
(long long)cp->reg[COP0_ENTRYHI]); |
|
|
debug(", lo0=%016llx", |
|
|
(long long)cp->reg[COP0_ENTRYLO0]); |
|
|
debug(", lo1=%016llx\n", |
|
|
(long long)cp->reg[COP0_ENTRYLO1]); |
|
2159 |
return; |
return; |
2160 |
} |
} |
2161 |
coproc_tlbwri(cpu, op == COP0_TLBWR); |
debug("\tindex=%08llx", |
2162 |
|
(long long)cp->reg[COP0_INDEX]); |
2163 |
|
debug(", random=%08llx", |
2164 |
|
(long long)cp->reg[COP0_RANDOM]); |
2165 |
|
debug(", mask=%016llx", |
2166 |
|
(long long)cp->reg[COP0_PAGEMASK]); |
2167 |
|
debug(", hi=%016llx", |
2168 |
|
(long long)cp->reg[COP0_ENTRYHI]); |
2169 |
|
debug(", lo0=%016llx", |
2170 |
|
(long long)cp->reg[COP0_ENTRYLO0]); |
2171 |
|
debug(", lo1=%016llx\n", |
2172 |
|
(long long)cp->reg[COP0_ENTRYLO1]); |
2173 |
return; |
return; |
2174 |
case COP0_TLBP: /* Probe TLB for |
case COP0_TLBP: /* Probe TLB for |
2175 |
matching entry */ |
matching entry */ |
2176 |
if (unassemble_only) { |
debug("tlbp\n"); |
|
debug("tlbp\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 0); |
|
2177 |
return; |
return; |
2178 |
case COP0_RFE: /* R2000/R3000 only: |
case COP0_RFE: /* R2000/R3000 only: |
2179 |
Return from Exception */ |
Return from Exception */ |
2180 |
if (unassemble_only) { |
debug("rfe\n"); |
|
debug("rfe\n"); |
|
|
return; |
|
|
} |
|
|
coproc_rfe(cpu); |
|
2181 |
return; |
return; |
2182 |
case COP0_ERET: /* R4000: Return from exception */ |
case COP0_ERET: /* R4000: Return from exception */ |
2183 |
if (unassemble_only) { |
debug("eret\n"); |
|
debug("eret\n"); |
|
|
return; |
|
|
} |
|
|
coproc_eret(cpu); |
|
2184 |
return; |
return; |
2185 |
case COP0_DERET: |
case COP0_DERET: |
2186 |
if (unassemble_only) { |
debug("deret\n"); |
2187 |
debug("deret\n"); |
return; |
2188 |
return; |
case COP0_WAIT: |
2189 |
|
{ |
2190 |
|
int code = (function >> 6) & 0x7ffff; |
2191 |
|
debug("wait"); |
2192 |
|
if (code > 0) |
2193 |
|
debug("\t0x%x", code); |
2194 |
|
debug("\n"); |
2195 |
} |
} |
|
/* |
|
|
* According to the MIPS64 manual, deret |
|
|
* loads PC from the DEPC cop0 register, and |
|
|
* jumps there immediately. No delay slot. |
|
|
* |
|
|
* TODO: This instruction is only available |
|
|
* if the processor is in debug mode. (What |
|
|
* does that mean?) TODO: This instruction |
|
|
* is undefined in a delay slot. |
|
|
*/ |
|
|
cpu->pc = cp->reg[COP0_DEPC]; |
|
|
cpu->delay_slot = 0; |
|
|
cp->reg[COP0_STATUS] &= ~STATUS_EXL; |
|
2196 |
return; |
return; |
2197 |
case COP0_STANDBY: |
case COP0_STANDBY: |
2198 |
if (unassemble_only) { |
debug("standby\n"); |
|
debug("standby\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2199 |
return; |
return; |
2200 |
case COP0_SUSPEND: |
case COP0_SUSPEND: |
2201 |
if (unassemble_only) { |
debug("suspend\n"); |
|
debug("suspend\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2202 |
return; |
return; |
2203 |
case COP0_HIBERNATE: |
case COP0_HIBERNATE: |
2204 |
if (unassemble_only) { |
debug("hibernate\n"); |
|
debug("hibernate\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2205 |
return; |
return; |
2206 |
default: |
default: |
2207 |
; |
; |
2221 |
return; |
return; |
2222 |
} |
} |
2223 |
|
|
|
/* TODO: RM5200 idle (?) */ |
|
|
if ((cp->coproc_nr==0 || cp->coproc_nr==3) && function == 0x02000020) { |
|
|
if (unassemble_only) { |
|
|
debug("idle(?)\n"); /* TODO */ |
|
|
return; |
|
|
} |
|
|
|
|
|
/* Idle? TODO */ |
|
|
return; |
|
|
} |
|
|
|
|
2224 |
if (unassemble_only) { |
if (unassemble_only) { |
2225 |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
2226 |
return; |
return; |