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.49 2006/07/21 20:39:40 debug Exp $ |
* $Id: cpu_mips_coproc.c,v 1.69 2007/06/15 18:07:08 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" |
|
|
|
|
#ifndef ENABLE_MIPS |
|
|
|
|
|
|
|
|
struct mips_coproc *mips_coproc_new(struct cpu *cpu, int coproc_nr) |
|
|
{ return NULL; } |
|
|
|
|
|
void mips_coproc_tlb_set_entry(struct cpu *cpu, int entrynr, int size, |
|
|
uint64_t vaddr, uint64_t paddr0, uint64_t paddr1, |
|
|
int valid0, int valid1, int dirty0, int dirty1, int global, int asid, |
|
|
int cachealgo0, int cachealgo1) { } |
|
|
|
|
|
|
|
|
#else /* ENABLE_MIPS */ |
|
49 |
|
|
50 |
|
|
51 |
extern volatile int single_step; |
extern volatile int single_step; |
84 |
; |
; |
85 |
/* Config select 1: caches etc. TODO: Don't use |
/* Config select 1: caches etc. TODO: Don't use |
86 |
cpu->machine for this stuff! */ |
cpu->machine for this stuff! */ |
87 |
IB = cpu->machine->cache_picache_linesize - 1; |
IB = cpu->cd.mips.cache_picache_linesize - 1; |
88 |
IB = IB < 0? 0 : (IB > 7? 7 : IB); |
IB = IB < 0? 0 : (IB > 7? 7 : IB); |
89 |
DB = cpu->machine->cache_pdcache_linesize - 1; |
DB = cpu->cd.mips.cache_pdcache_linesize - 1; |
90 |
DB = DB < 0? 0 : (DB > 7? 7 : DB); |
DB = DB < 0? 0 : (DB > 7? 7 : DB); |
91 |
IC = cpu->machine->cache_picache - |
IC = cpu->cd.mips.cache_picache - |
92 |
cpu->machine->cache_picache_linesize - 7; |
cpu->cd.mips.cache_picache_linesize - 7; |
93 |
DC = cpu->machine->cache_pdcache - |
DC = cpu->cd.mips.cache_pdcache - |
94 |
cpu->machine->cache_pdcache_linesize - 7; |
cpu->cd.mips.cache_pdcache_linesize - 7; |
95 |
IA = cpu->cd.mips.cpu_type.piways - 1; |
IA = cpu->cd.mips.cpu_type.piways - 1; |
96 |
DA = cpu->cd.mips.cpu_type.pdways - 1; |
DA = cpu->cd.mips.cpu_type.pdways - 1; |
97 |
cpu->cd.mips.cop0_config_select1 = |
cpu->cd.mips.cop0_config_select1 = |
119 |
break; |
break; |
120 |
case MIPS_R4000: /* according to the R4000 manual */ |
case MIPS_R4000: /* according to the R4000 manual */ |
121 |
case MIPS_R4600: |
case MIPS_R4600: |
122 |
IB = cpu->machine->cache_picache_linesize - 4; |
IB = cpu->cd.mips.cache_picache_linesize - 4; |
123 |
IB = IB < 0? 0 : (IB > 1? 1 : IB); |
IB = IB < 0? 0 : (IB > 1? 1 : IB); |
124 |
DB = cpu->machine->cache_pdcache_linesize - 4; |
DB = cpu->cd.mips.cache_pdcache_linesize - 4; |
125 |
DB = DB < 0? 0 : (DB > 1? 1 : DB); |
DB = DB < 0? 0 : (DB > 1? 1 : DB); |
126 |
SB = cpu->machine->cache_secondary_linesize - 4; |
SB = cpu->cd.mips.cache_secondary_linesize - 4; |
127 |
SB = SB < 0? 0 : (SB > 3? 3 : SB); |
SB = SB < 0? 0 : (SB > 3? 3 : SB); |
128 |
IC = cpu->machine->cache_picache - 12; |
IC = cpu->cd.mips.cache_picache - 12; |
129 |
IC = IC < 0? 0 : (IC > 7? 7 : IC); |
IC = IC < 0? 0 : (IC > 7? 7 : IC); |
130 |
DC = cpu->machine->cache_pdcache - 12; |
DC = cpu->cd.mips.cache_pdcache - 12; |
131 |
DC = DC < 0? 0 : (DC > 7? 7 : DC); |
DC = DC < 0? 0 : (DC > 7? 7 : DC); |
132 |
SC = cpu->machine->cache_secondary? 0 : 1; |
SC = cpu->cd.mips.cache_secondary? 0 : 1; |
133 |
c->reg[COP0_CONFIG] = |
c->reg[COP0_CONFIG] = |
134 |
( 0 << 31) /* Master/Checker present bit */ |
( 0 << 31) /* Master/Checker present bit */ |
135 |
| (0x00 << 28) /* EC: system clock divisor, |
| (0x00 << 28) /* EC: system clock divisor, |
161 |
; |
; |
162 |
break; |
break; |
163 |
case MIPS_R4100: /* According to the VR4131 manual: */ |
case MIPS_R4100: /* According to the VR4131 manual: */ |
164 |
IB = cpu->machine->cache_picache_linesize - 4; |
IB = cpu->cd.mips.cache_picache_linesize - 4; |
165 |
IB = IB < 0? 0 : (IB > 1? 1 : IB); |
IB = IB < 0? 0 : (IB > 1? 1 : IB); |
166 |
DB = cpu->machine->cache_pdcache_linesize - 4; |
DB = cpu->cd.mips.cache_pdcache_linesize - 4; |
167 |
DB = DB < 0? 0 : (DB > 1? 1 : DB); |
DB = DB < 0? 0 : (DB > 1? 1 : DB); |
168 |
IC = cpu->machine->cache_picache - 10; |
IC = cpu->cd.mips.cache_picache - 10; |
169 |
IC = IC < 0? 0 : (IC > 7? 7 : IC); |
IC = IC < 0? 0 : (IC > 7? 7 : IC); |
170 |
DC = cpu->machine->cache_pdcache - 10; |
DC = cpu->cd.mips.cache_pdcache - 10; |
171 |
DC = DC < 0? 0 : (DC > 7? 7 : DC); |
DC = DC < 0? 0 : (DC > 7? 7 : DC); |
172 |
c->reg[COP0_CONFIG] = |
c->reg[COP0_CONFIG] = |
173 |
( 0 << 31) /* IS: Instruction Streaming bit */ |
( 0 << 31) /* IS: Instruction Streaming bit */ |
232 |
case MIPS_R10000: |
case MIPS_R10000: |
233 |
case MIPS_R12000: |
case MIPS_R12000: |
234 |
case MIPS_R14000: |
case MIPS_R14000: |
235 |
IC = cpu->machine->cache_picache - 12; |
IC = cpu->cd.mips.cache_picache - 12; |
236 |
IC = IC < 0? 0 : (IC > 7? 7 : IC); |
IC = IC < 0? 0 : (IC > 7? 7 : IC); |
237 |
DC = cpu->machine->cache_pdcache - 12; |
DC = cpu->cd.mips.cache_pdcache - 12; |
238 |
DC = DC < 0? 0 : (DC > 7? 7 : DC); |
DC = DC < 0? 0 : (DC > 7? 7 : DC); |
239 |
SC = cpu->machine->cache_secondary - 19; |
SC = cpu->cd.mips.cache_secondary - 19; |
240 |
SC = SC < 0? 0 : (SC > 7? 7 : SC); |
SC = SC < 0? 0 : (SC > 7? 7 : SC); |
241 |
/* According to the R10000 User's Manual: */ |
/* According to the R10000 User's Manual: */ |
242 |
c->reg[COP0_CONFIG] = |
c->reg[COP0_CONFIG] = |
359 |
{ |
{ |
360 |
struct mips_coproc *c; |
struct mips_coproc *c; |
361 |
|
|
362 |
c = malloc(sizeof(struct mips_coproc)); |
CHECK_ALLOCATION(c = malloc(sizeof(struct mips_coproc))); |
|
if (c == NULL) { |
|
|
fprintf(stderr, "out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
|
|
|
363 |
memset(c, 0, sizeof(struct mips_coproc)); |
memset(c, 0, sizeof(struct mips_coproc)); |
364 |
|
|
365 |
c->coproc_nr = coproc_nr; |
c->coproc_nr = coproc_nr; |
366 |
|
|
367 |
if (coproc_nr == 0) { |
if (coproc_nr == 0) { |
420 |
|
|
421 |
|
|
422 |
/* |
/* |
423 |
|
* mips_timer_tick(): |
424 |
|
*/ |
425 |
|
static void mips_timer_tick(struct timer *timer, void *extra) |
426 |
|
{ |
427 |
|
struct cpu *cpu = (struct cpu *) extra; |
428 |
|
|
429 |
|
cpu->cd.mips.compare_interrupts_pending ++; |
430 |
|
|
431 |
|
if ((int32_t) (cpu->cd.mips.coproc[0]->reg[COP0_COUNT] - |
432 |
|
cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]) < 0) { |
433 |
|
cpu->cd.mips.coproc[0]->reg[COP0_COUNT] = |
434 |
|
cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]; |
435 |
|
} |
436 |
|
} |
437 |
|
|
438 |
|
|
439 |
|
/* |
440 |
* mips_coproc_tlb_set_entry(): |
* mips_coproc_tlb_set_entry(): |
441 |
* |
* |
442 |
* Used by machine setup code, if a specific machine emulation starts up |
* Used by machine setup code, if a specific machine emulation starts up |
516 |
* |
* |
517 |
* Note: In the R3000 case, the asid argument is shifted 6 bits. |
* Note: In the R3000 case, the asid argument is shifted 6 bits. |
518 |
*/ |
*/ |
519 |
static void invalidate_asid(struct cpu *cpu, int asid) |
static void invalidate_asid(struct cpu *cpu, unsigned int asid) |
520 |
{ |
{ |
521 |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
522 |
int i, ntlbs = cp->nr_of_tlbs; |
unsigned int i, ntlbs = cp->nr_of_tlbs; |
523 |
struct mips_tlb *tlb = cp->tlbs; |
struct mips_tlb *tlb = cp->tlbs; |
524 |
|
|
525 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { |
if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { |
535 |
int non4kpages = 0; |
int non4kpages = 0; |
536 |
uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; |
uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; |
537 |
|
|
538 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
if (cpu->is_32bit) { |
539 |
|
topbit = 0x80000000; |
540 |
|
fillmask = 0xffffffff00000000ULL; |
541 |
|
} else if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
542 |
topbit <<= 43; |
topbit <<= 43; |
543 |
fillmask <<= 4; |
fillmask <<= 4; |
544 |
} else { |
} else { |
594 |
if (cp->coproc_nr==0 && reg_nr==COP0_PAGEMASK) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_PAGEMASK) unimpl = 0; |
595 |
if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; |
596 |
if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; |
597 |
if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { |
598 |
|
/* TODO: Increase count in a more meaningful way! */ |
599 |
|
cp->reg[COP0_COUNT] = (int32_t) (cp->reg[COP0_COUNT] + 1); |
600 |
|
unimpl = 0; |
601 |
|
} |
602 |
if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; |
603 |
if (cp->coproc_nr==0 && reg_nr==COP0_COMPARE) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_COMPARE) unimpl = 0; |
604 |
if (cp->coproc_nr==0 && reg_nr==COP0_STATUS) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_STATUS) unimpl = 0; |
664 |
int readonly = 0; |
int readonly = 0; |
665 |
uint64_t tmp = *ptr; |
uint64_t tmp = *ptr; |
666 |
uint64_t tmp2 = 0, old; |
uint64_t tmp2 = 0, old; |
667 |
int inval = 0, old_asid, oldmode; |
int inval = 0; |
668 |
|
unsigned int old_asid; |
669 |
|
uint64_t oldmode; |
670 |
|
|
671 |
switch (cp->coproc_nr) { |
switch (cp->coproc_nr) { |
672 |
case 0: |
case 0: |
758 |
unimpl = 0; |
unimpl = 0; |
759 |
break; |
break; |
760 |
case COP0_COMPARE: |
case COP0_COMPARE: |
761 |
/* Clear the timer interrupt bit (bit 7): */ |
if (cpu->machine->emulated_hz > 0) { |
762 |
cpu->cd.mips.compare_register_set = 1; |
int32_t compare_diff = tmp - |
763 |
mips_cpu_interrupt_ack(cpu, 7); |
cp->reg[COP0_COMPARE]; |
764 |
|
double hz; |
765 |
|
|
766 |
|
if (compare_diff < 0) |
767 |
|
hz = tmp - cp->reg[COP0_COUNT]; |
768 |
|
|
769 |
|
if (compare_diff == 0) |
770 |
|
hz = 0; |
771 |
|
else |
772 |
|
hz = (double)cpu->machine->emulated_hz |
773 |
|
/ (double)compare_diff; |
774 |
|
/* |
775 |
|
* TODO: DON'T HARDCODE THIS! |
776 |
|
*/ |
777 |
|
hz = 100.0; |
778 |
|
|
779 |
|
/* Initialize or re-set the periodic timer: */ |
780 |
|
if (hz > 0) { |
781 |
|
if (cpu->cd.mips.timer == NULL) |
782 |
|
cpu->cd.mips.timer = timer_add( |
783 |
|
hz, mips_timer_tick, cpu); |
784 |
|
else |
785 |
|
timer_update_frequency( |
786 |
|
cpu->cd.mips.timer, hz); |
787 |
|
} |
788 |
|
} |
789 |
|
|
790 |
|
/* Ack the periodic timer, if it was asserted: */ |
791 |
|
if (cp->reg[COP0_CAUSE] & 0x8000 && |
792 |
|
cpu->cd.mips.compare_interrupts_pending > 0) |
793 |
|
cpu->cd.mips.compare_interrupts_pending --; |
794 |
|
|
795 |
|
/* Clear the timer interrupt assertion (bit 7): */ |
796 |
|
cp->reg[COP0_CAUSE] &= ~0x8000; |
797 |
|
|
798 |
if (tmp != (uint64_t)(int64_t)(int32_t)tmp) |
if (tmp != (uint64_t)(int64_t)(int32_t)tmp) |
799 |
fatal("WARNING: trying to write a 64-bit value" |
fatal("WARNING: trying to write a 64-bit value" |
800 |
" to the COMPARE register!\n"); |
" to the COMPARE register!\n"); |
801 |
|
|
802 |
tmp = (int64_t)(int32_t)tmp; |
tmp = (int64_t)(int32_t)tmp; |
803 |
|
cpu->cd.mips.compare_register_set = 1; |
804 |
unimpl = 0; |
unimpl = 0; |
805 |
break; |
break; |
806 |
case COP0_ENTRYHI: |
case COP0_ENTRYHI: |
960 |
cp->coproc_nr, reg_nr, cp->coproc_nr==0? |
cp->coproc_nr, reg_nr, cp->coproc_nr==0? |
961 |
cop0_names[reg_nr] : "?", (long long)tmp); |
cop0_names[reg_nr] : "?", (long long)tmp); |
962 |
|
|
963 |
mips_cpu_exception(cpu, EXCEPTION_CPU, 0, 0, |
/* mips_cpu_exception(cpu, EXCEPTION_CPU, 0, 0, |
964 |
cp->coproc_nr, 0, 0, 0); |
cp->coproc_nr, 0, 0, 0); |
965 |
return; |
return; */ |
966 |
} |
} |
967 |
|
|
968 |
if (readonly) { |
if (readonly) { |
1245 |
|
|
1246 |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
1247 |
if ((function & 0x03e00000) == 0x01000000) { |
if ((function & 0x03e00000) == 0x01000000) { |
1248 |
int nd, tf, imm, cond_true; |
int nd, tf, imm; |
1249 |
char *instr_mnem; |
char *instr_mnem; |
1250 |
|
|
1251 |
/* cc are bits 20..18: */ |
/* cc are bits 20..18: */ |
1268 |
if (unassemble_only) |
if (unassemble_only) |
1269 |
return 1; |
return 1; |
1270 |
|
|
1271 |
if (cpu->delay_slot) { |
fatal("INTERNAL ERROR: MIPS coprocessor branches should not" |
1272 |
fatal("%s: jump inside a jump's delay slot, " |
" be implemented in cpu_mips_coproc.c, but in" |
1273 |
"or similar. TODO\n", instr_mnem); |
" cpu_mips_instr.c!\n"); |
1274 |
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; |
|
1275 |
} |
} |
1276 |
|
|
1277 |
/* add.fmt: Floating-point add */ |
/* add.fmt: Floating-point add */ |
1485 |
R2K3K_INDEX_SHIFT; |
R2K3K_INDEX_SHIFT; |
1486 |
if (i >= cp->nr_of_tlbs) { |
if (i >= cp->nr_of_tlbs) { |
1487 |
/* TODO: exception? */ |
/* TODO: exception? */ |
1488 |
fatal("warning: tlbr from index %i (too " |
fatal("[ warning: tlbr from index %i (too " |
1489 |
"high)\n", i); |
"high) ]\n", i); |
1490 |
return; |
return; |
1491 |
} |
} |
1492 |
|
|
1493 |
/* |
cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; |
1494 |
* 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; */ |
|
1495 |
} else { |
} else { |
1496 |
/* R4000: */ |
/* R4000: */ |
1497 |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
1663 |
(cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) |
(cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) |
1664 |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1665 |
INVALIDATE_VADDR); |
INVALIDATE_VADDR); |
1666 |
|
|
1667 |
break; |
break; |
1668 |
|
|
1669 |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1670 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
oldvaddr = cp->tlbs[index].hi & |
1671 |
|
(ENTRYHI_VPN2_MASK_R10K | ENTRYHI_R_MASK); |
1672 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1673 |
if (oldvaddr & 0x80000000000ULL) |
if (oldvaddr & 0x80000000000ULL) |
1674 |
oldvaddr |= 0xfffff00000000000ULL; |
oldvaddr |= 0x3ffff00000000000ULL; |
1675 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1676 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1677 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1678 |
oldvaddr = (int32_t)oldvaddr; |
oldvaddr = (int32_t)oldvaddr; |
1679 |
} else { |
} else { |
1680 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1681 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
oldvaddr = cp->tlbs[index].hi & |
1682 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); |
1683 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1684 |
if (oldvaddr & 0x8000000000ULL) |
if (oldvaddr & 0x8000000000ULL) |
1685 |
oldvaddr |= 0xffffff0000000000ULL; |
oldvaddr |= 0x3fffff0000000000ULL; |
1686 |
} |
} |
1687 |
|
|
|
#if 0 |
|
|
/* TODO: FIX THIS! It shouldn't be needed! */ |
|
|
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
|
|
#else |
|
1688 |
/* |
/* |
1689 |
* TODO: non-4KB page sizes! |
* TODO: non-4KB page sizes! |
1690 |
*/ |
*/ |
1694 |
if (cp->tlbs[index].lo1 & ENTRYLO_V) |
if (cp->tlbs[index].lo1 & ENTRYLO_V) |
1695 |
cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, |
cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, |
1696 |
INVALIDATE_VADDR); |
INVALIDATE_VADDR); |
|
#endif |
|
1697 |
} |
} |
1698 |
|
|
1699 |
#if 0 |
#if 0 |
1711 |
int i; |
int i; |
1712 |
unsigned int asid; |
unsigned int asid; |
1713 |
|
|
1714 |
vaddr1 = cp->reg[COP0_ENTRYHI] & ENTRYHI_VPN2_MASK_R10K; |
vaddr1 = cp->reg[COP0_ENTRYHI] & |
1715 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1716 |
asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; |
asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; |
1717 |
/* Since this is just a warning, it's probably not necessary |
/* Since this is just a warning, it's probably not necessary |
1718 |
to use R4000 masks etc. */ |
to use R4000 masks etc. */ |
1725 |
(cp->tlbs[i].hi & ENTRYHI_ASID) != asid) |
(cp->tlbs[i].hi & ENTRYHI_ASID) != asid) |
1726 |
continue; |
continue; |
1727 |
|
|
1728 |
vaddr2 = cp->tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; |
vaddr2 = cp->tlbs[i].hi & |
1729 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1730 |
if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & |
if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & |
1731 |
ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) |
ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) |
1732 |
fatal("\n[ WARNING! tlbw%s to index 0x%02x " |
fatal("\n[ WARNING! tlbw%s to index 0x%02x " |
1760 |
INVALIDATE_PADDR); |
INVALIDATE_PADDR); |
1761 |
} |
} |
1762 |
|
|
1763 |
|
/* Set new last_written_tlb_index hint: */ |
1764 |
|
cpu->cd.mips.last_written_tlb_index = index; |
1765 |
|
|
1766 |
if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { |
if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { |
1767 |
fatal("Wow! Interesting case; tlbw* while caches" |
fatal("Wow! Interesting case; tlbw* while caches" |
1768 |
" are isolated. TODO\n"); |
" are isolated. TODO\n"); |
1783 |
int pfn_shift = 12, vpn_shift = 12; |
int pfn_shift = 12, vpn_shift = 12; |
1784 |
int wf0, wf1, mask; |
int wf0, wf1, mask; |
1785 |
uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; |
uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; |
1786 |
|
uint64_t psize; |
1787 |
|
|
1788 |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
1789 |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
1822 |
} |
} |
1823 |
|
|
1824 |
paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
1825 |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
>> ENTRYLO_PFN_SHIFT) << pfn_shift |
1826 |
|
>> vpn_shift << vpn_shift; |
1827 |
paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) |
paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) |
1828 |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
>> ENTRYLO_PFN_SHIFT) << pfn_shift |
1829 |
|
>> vpn_shift << vpn_shift; |
1830 |
|
|
1831 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1832 |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
vaddr0 = cp->tlbs[index].hi & |
1833 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); |
1834 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1835 |
if (vaddr0 & 0x80000000000ULL) |
if (vaddr0 & 0x80000000000ULL) |
1836 |
vaddr0 |= 0xfffff00000000000ULL; |
vaddr0 |= 0x3ffff00000000000ULL; |
1837 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1838 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1839 |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1840 |
vaddr0 = (int32_t)vaddr0; |
vaddr0 = (int32_t)vaddr0; |
1841 |
} else { |
} else { |
1842 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1843 |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
vaddr0 = cp->tlbs[index].hi & |
1844 |
|
(ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); |
1845 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1846 |
if (vaddr0 & 0x8000000000ULL) |
if (vaddr0 & 0x8000000000ULL) |
1847 |
vaddr0 |= 0xffffff0000000000ULL; |
vaddr0 |= 0x3fffff0000000000ULL; |
1848 |
} |
} |
1849 |
|
|
1850 |
vaddr1 = vaddr0 | (1 << vpn_shift); |
vaddr1 = vaddr0 | (1 << vpn_shift); |
1869 |
* Invalidate any code translations, if we are writing Dirty |
* Invalidate any code translations, if we are writing Dirty |
1870 |
* pages to the TLB: (TODO: 4KB hardcoded... ugly) |
* pages to the TLB: (TODO: 4KB hardcoded... ugly) |
1871 |
*/ |
*/ |
1872 |
for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { |
psize = 1 << pfn_shift; |
1873 |
|
for (ptmp = 0; ptmp < psize; ptmp += 0x1000) { |
1874 |
if (wf0) |
if (wf0) |
1875 |
cpu->invalidate_code_translation(cpu, |
cpu->invalidate_code_translation(cpu, |
1876 |
paddr0 + ptmp, INVALIDATE_PADDR); |
paddr0 + ptmp, INVALIDATE_PADDR); |
1897 |
if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) |
if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) |
1898 |
cpu->update_translation_table(cpu, vaddr1, memblock, |
cpu->update_translation_table(cpu, vaddr1, memblock, |
1899 |
wf1, paddr1); |
wf1, paddr1); |
1900 |
|
|
1901 |
|
/* Set new last_written_tlb_index hint: */ |
1902 |
|
cpu->cd.mips.last_written_tlb_index = index; |
1903 |
} |
} |
1904 |
} |
} |
1905 |
|
|
2122 |
|
|
2123 |
/* TLB operations and other things: */ |
/* TLB operations and other things: */ |
2124 |
if (cp->coproc_nr == 0) { |
if (cp->coproc_nr == 0) { |
2125 |
op = (function) & 0xff; |
if (!unassemble_only) { |
2126 |
|
fatal("FATAL INTERNAL ERROR: Should be implemented" |
2127 |
|
" with dyntrans instead.\n"); |
2128 |
|
exit(1); |
2129 |
|
} |
2130 |
|
|
2131 |
switch (co_bit) { |
switch (co_bit) { |
2132 |
|
case 0: |
2133 |
|
if ((function & 0x03e0ffdf) == 0x01606000) { |
2134 |
|
debug("%ci", function & 0x20? 'e' : 'd'); |
2135 |
|
if (rt != MIPS_GPR_ZERO) |
2136 |
|
debug("\t%s", regnames[rt]); |
2137 |
|
debug("\n"); |
2138 |
|
return; |
2139 |
|
} |
2140 |
|
break; |
2141 |
case 1: |
case 1: |
2142 |
|
op = (function) & 0xff; |
2143 |
switch (op) { |
switch (op) { |
2144 |
case COP0_TLBR: /* Read indexed TLB entry */ |
case COP0_TLBR: /* Read indexed TLB entry */ |
2145 |
if (unassemble_only) { |
debug("tlbr\n"); |
|
debug("tlbr\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 1); |
|
2146 |
return; |
return; |
2147 |
case COP0_TLBWI: /* Write indexed */ |
case COP0_TLBWI: /* Write indexed */ |
2148 |
case COP0_TLBWR: /* Write random */ |
case COP0_TLBWR: /* Write random */ |
2149 |
if (unassemble_only) { |
if (op == COP0_TLBWI) |
2150 |
if (op == COP0_TLBWI) |
debug("tlbwi"); |
2151 |
debug("tlbwi"); |
else |
2152 |
else |
debug("tlbwr"); |
2153 |
debug("tlbwr"); |
if (!running) { |
2154 |
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]); |
|
2155 |
return; |
return; |
2156 |
} |
} |
2157 |
coproc_tlbwri(cpu, op == COP0_TLBWR); |
debug("\tindex=%08llx", |
2158 |
|
(long long)cp->reg[COP0_INDEX]); |
2159 |
|
debug(", random=%08llx", |
2160 |
|
(long long)cp->reg[COP0_RANDOM]); |
2161 |
|
debug(", mask=%016llx", |
2162 |
|
(long long)cp->reg[COP0_PAGEMASK]); |
2163 |
|
debug(", hi=%016llx", |
2164 |
|
(long long)cp->reg[COP0_ENTRYHI]); |
2165 |
|
debug(", lo0=%016llx", |
2166 |
|
(long long)cp->reg[COP0_ENTRYLO0]); |
2167 |
|
debug(", lo1=%016llx\n", |
2168 |
|
(long long)cp->reg[COP0_ENTRYLO1]); |
2169 |
return; |
return; |
2170 |
case COP0_TLBP: /* Probe TLB for |
case COP0_TLBP: /* Probe TLB for |
2171 |
matching entry */ |
matching entry */ |
2172 |
if (unassemble_only) { |
debug("tlbp\n"); |
|
debug("tlbp\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 0); |
|
2173 |
return; |
return; |
2174 |
case COP0_RFE: /* R2000/R3000 only: |
case COP0_RFE: /* R2000/R3000 only: |
2175 |
Return from Exception */ |
Return from Exception */ |
2176 |
if (unassemble_only) { |
debug("rfe\n"); |
2177 |
debug("rfe\n"); |
return; |
|
return; |
|
|
} |
|
|
fatal("Internal error (rfe): Should be " |
|
|
"implemented in dyntrans instead.\n"); |
|
|
exit(1); |
|
2178 |
case COP0_ERET: /* R4000: Return from exception */ |
case COP0_ERET: /* R4000: Return from exception */ |
2179 |
if (unassemble_only) { |
debug("eret\n"); |
2180 |
debug("eret\n"); |
return; |
|
return; |
|
|
} |
|
|
fatal("Internal error (eret): Should be " |
|
|
"implemented in dyntrans instead.\n"); |
|
|
exit(1); |
|
2181 |
case COP0_DERET: |
case COP0_DERET: |
2182 |
if (unassemble_only) { |
debug("deret\n"); |
2183 |
debug("deret\n"); |
return; |
2184 |
return; |
case COP0_WAIT: |
2185 |
|
{ |
2186 |
|
int code = (function >> 6) & 0x7ffff; |
2187 |
|
debug("wait"); |
2188 |
|
if (code > 0) |
2189 |
|
debug("\t0x%x", code); |
2190 |
|
debug("\n"); |
2191 |
} |
} |
|
/* |
|
|
* 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; |
|
2192 |
return; |
return; |
2193 |
case COP0_STANDBY: |
case COP0_STANDBY: |
2194 |
if (unassemble_only) { |
debug("standby\n"); |
|
debug("standby\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2195 |
return; |
return; |
2196 |
case COP0_SUSPEND: |
case COP0_SUSPEND: |
2197 |
if (unassemble_only) { |
debug("suspend\n"); |
|
debug("suspend\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2198 |
return; |
return; |
2199 |
case COP0_HIBERNATE: |
case COP0_HIBERNATE: |
2200 |
if (unassemble_only) { |
debug("hibernate\n"); |
|
debug("hibernate\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2201 |
return; |
return; |
2202 |
default: |
default: |
2203 |
; |
; |
2204 |
} |
} |
2205 |
default: |
break; |
|
; |
|
2206 |
} |
} |
2207 |
} |
} |
2208 |
|
|
2216 |
return; |
return; |
2217 |
} |
} |
2218 |
|
|
|
/* 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; |
|
|
} |
|
|
|
|
2219 |
if (unassemble_only) { |
if (unassemble_only) { |
2220 |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
2221 |
return; |
return; |
2228 |
mips_cpu_exception(cpu, EXCEPTION_CPU, 0, 0, cp->coproc_nr, 0, 0, 0); |
mips_cpu_exception(cpu, EXCEPTION_CPU, 0, 0, cp->coproc_nr, 0, 0, 0); |
2229 |
} |
} |
2230 |
|
|
|
#endif /* ENABLE_MIPS */ |
|