25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_mips_coproc.c,v 1.18 2005/05/07 02:13:22 debug Exp $ |
* $Id: cpu_mips_coproc.c,v 1.23 2005/06/26 11:36:27 debug Exp $ |
29 |
* |
* |
30 |
* Emulation of MIPS coprocessors. |
* Emulation of MIPS coprocessors. |
31 |
*/ |
*/ |
88 |
#else |
#else |
89 |
const int m16 = 0; |
const int m16 = 0; |
90 |
#endif |
#endif |
91 |
int IB, DB, SB, IC, DC, SC; |
int cpu_type, IB, DB, SB, IC, DC, SC; |
92 |
|
|
93 |
/* Default values: */ |
/* Default values: */ |
94 |
c->reg[COP0_CONFIG] = |
c->reg[COP0_CONFIG] = |
111 |
(TODO) */ |
(TODO) */ |
112 |
; |
; |
113 |
|
|
114 |
switch (cpu->cd.mips.cpu_type.rev) { |
cpu_type = cpu->cd.mips.cpu_type.rev & 0xff; |
115 |
|
|
116 |
|
/* AU1x00 are treated as 4Kc (MIPS32 cores): */ |
117 |
|
if ((cpu->cd.mips.cpu_type.rev & 0xffff) == 0x0301) |
118 |
|
cpu_type = MIPS_4Kc; |
119 |
|
|
120 |
|
switch (cpu_type) { |
121 |
case MIPS_R4000: /* according to the R4000 manual */ |
case MIPS_R4000: /* according to the R4000 manual */ |
122 |
case MIPS_R4600: |
case MIPS_R4600: |
123 |
IB = cpu->machine->cache_picache_linesize - 4; |
IB = cpu->machine->cache_picache_linesize - 4; |
304 |
break; |
break; |
305 |
case MIPS_4Kc: |
case MIPS_4Kc: |
306 |
case MIPS_5Kc: |
case MIPS_5Kc: |
307 |
/* According to the MIPS64 5K User's Manual: */ |
/* According to the MIPS64 (5K) User's Manual: */ |
|
/* TODO: How good does this work with 4K? */ |
|
308 |
c->reg[COP0_CONFIG] = |
c->reg[COP0_CONFIG] = |
309 |
( (uint32_t)1 << 31)/* Config 1 present bit */ |
( (uint32_t)1 << 31)/* Config 1 present bit */ |
310 |
| ( 0 << 20) /* ISD: instruction scheduling |
| ( 0 << 20) /* ISD: instruction scheduling |
311 |
disable (=1) */ |
disable (=1) */ |
312 |
| ( 0 << 17) /* DID: dual issue disable */ |
| ( 0 << 17) /* DID: dual issue disable */ |
313 |
| ( 0 << 16) /* BM: burst mode */ |
| ( 0 << 16) /* BM: burst mode */ |
314 |
| ((cpu->byte_order==EMUL_BIG_ENDIAN? 1 : 0) << 15) |
| ((cpu->byte_order == EMUL_BIG_ENDIAN? 1 : 0) << 15) |
315 |
/* endian mode */ |
/* endian mode */ |
316 |
| ((cpu->cd.mips.cpu_type.rev==MIPS_5Kc?2:1) << 13) |
| ((cpu_type == MIPS_5Kc? 2 : 0) << 13) |
317 |
/* 1=32-bit only, 2=32/64 */ |
/* 0=MIPS32, 1=64S, 2=64 */ |
318 |
| ( 0 << 10) /* Architecture revision */ |
| ( 0 << 10) /* Architecture revision */ |
319 |
| ( 1 << 7) /* MMU type: 1=TLB, 3=FMT */ |
| ( 1 << 7) /* MMU type: 1=TLB, 3=FMT */ |
320 |
| ( 2 << 0) /* kseg0 cache coherency algorithm */ |
| ( 2 << 0) /* kseg0 cache coherency algorithm */ |
321 |
; |
; |
322 |
/* TODO: Config select 1: caches and such */ |
/* Config select 1: caches etc. TODO: Associativity? */ |
323 |
|
IB = cpu->machine->cache_picache_linesize - 1; |
324 |
|
IB = IB < 0? 0 : (IB > 7? 7 : IB); |
325 |
|
DB = cpu->machine->cache_pdcache_linesize - 1; |
326 |
|
DB = DB < 0? 0 : (DB > 7? 7 : DB); |
327 |
|
IC = cpu->machine->cache_picache - |
328 |
|
cpu->machine->cache_picache_linesize - 7; |
329 |
|
DC = cpu->machine->cache_pdcache - |
330 |
|
cpu->machine->cache_pdcache_linesize - 7; |
331 |
|
cpu->cd.mips.cop0_config_select1 = |
332 |
|
((cpu->cd.mips.cpu_type.nr_of_tlb_entries - 1) << 25) |
333 |
|
| (IC << 22) /* IS: I-cache sets per way */ |
334 |
|
| (IB << 19) /* IL: I-cache line-size */ |
335 |
|
| (1 << 16) /* IA: I-cache assoc. (ways-1) */ |
336 |
|
| (DC << 13) /* DS: D-cache sets per way */ |
337 |
|
| (DB << 10) /* DL: D-cache line-size */ |
338 |
|
| (1 << 7) /* DA: D-cache assoc. (ways-1) */ |
339 |
|
| (16 * 0) /* Existance of PerformanceCounters */ |
340 |
|
| ( 8 * 0) /* Existance of Watch Registers */ |
341 |
|
| ( 4 * m16) /* Existance of MIPS16 */ |
342 |
|
| ( 2 * 0) /* Existance of EJTAG */ |
343 |
|
| ( 1 * 1) /* Existance of FPU */ |
344 |
|
; |
345 |
break; |
break; |
346 |
default: |
default: |
347 |
; |
; |
532 |
} |
} |
533 |
|
|
534 |
|
|
535 |
|
#ifdef BINTRANS |
536 |
/* |
/* |
537 |
* old_update_translation_table(): |
* old_update_translation_table(): |
538 |
*/ |
*/ |
539 |
static void old_update_translation_table(struct cpu *cpu, uint64_t vaddr_page, |
static void old_update_translation_table(struct cpu *cpu, uint64_t vaddr_page, |
540 |
unsigned char *host_page, int writeflag, uint64_t paddr_page) |
unsigned char *host_page, int writeflag, uint64_t paddr_page) |
541 |
{ |
{ |
|
#ifdef BINTRANS |
|
542 |
int a, b; |
int a, b; |
543 |
struct vth32_table *tbl1; |
struct vth32_table *tbl1; |
544 |
void *p; |
void *p_r, *p_w; |
545 |
uint32_t p_paddr; |
uint32_t p_paddr; |
546 |
|
|
547 |
/* This table stuff only works for 32-bit mode: */ |
/* This table stuff only works for 32-bit mode: */ |
582 |
exit(1); |
exit(1); |
583 |
} |
} |
584 |
} |
} |
585 |
p = tbl1->haddr_entry[b]; |
p_r = tbl1->haddr_entry[b*2]; |
586 |
|
p_w = tbl1->haddr_entry[b*2+1]; |
587 |
p_paddr = tbl1->paddr_entry[b]; |
p_paddr = tbl1->paddr_entry[b]; |
588 |
/* printf(" p = %p\n", p); */ |
/* printf(" p_r=%p p_w=%p\n", p_r, p_w); */ |
589 |
if (p == NULL && p_paddr == 0 && |
if (p_r == NULL && p_paddr == 0 && |
590 |
(host_page!=NULL || paddr_page!=0)) { |
(host_page != NULL || paddr_page != 0)) { |
591 |
tbl1->refcount ++; |
tbl1->refcount ++; |
592 |
/* printf("ADDING %08x -> %p wf=%i (refcount is " |
/* printf("ADDING %08x -> %p wf=%i (refcount is " |
593 |
"now %i)\n", (int)vaddr_page, host_page, |
"now %i)\n", (int)vaddr_page, host_page, |
595 |
} |
} |
596 |
if (writeflag == -1) { |
if (writeflag == -1) { |
597 |
/* Forced downgrade to read-only: */ |
/* Forced downgrade to read-only: */ |
598 |
tbl1->haddr_entry[b] = (void *) |
tbl1->haddr_entry[b*2 + 1] = NULL; |
599 |
((size_t)tbl1->haddr_entry[b] & ~1); |
} else if (writeflag==0 && p_w != NULL && host_page != NULL) { |
|
} else if (writeflag==0 && (size_t)p&1 && host_page != NULL) { |
|
600 |
/* Don't degrade a page from writable to readonly. */ |
/* Don't degrade a page from writable to readonly. */ |
601 |
} else { |
} else { |
602 |
if (host_page != NULL) |
if (host_page != NULL) { |
603 |
tbl1->haddr_entry[b] = (void *) |
tbl1->haddr_entry[b*2] = host_page; |
604 |
((size_t)host_page + (writeflag?1:0)); |
if (writeflag) |
605 |
else |
tbl1->haddr_entry[b*2+1] = host_page; |
606 |
tbl1->haddr_entry[b] = NULL; |
else |
607 |
|
tbl1->haddr_entry[b*2+1] = NULL; |
608 |
|
} else { |
609 |
|
tbl1->haddr_entry[b*2] = NULL; |
610 |
|
tbl1->haddr_entry[b*2+1] = NULL; |
611 |
|
} |
612 |
tbl1->paddr_entry[b] = paddr_page; |
tbl1->paddr_entry[b] = paddr_page; |
613 |
} |
} |
614 |
tbl1->bintrans_chunks[b] = NULL; |
tbl1->bintrans_chunks[b] = NULL; |
|
#endif |
|
615 |
} |
} |
616 |
|
#endif |
617 |
|
|
618 |
|
|
619 |
/* |
/* |
649 |
{ |
{ |
650 |
int a, b; |
int a, b; |
651 |
struct vth32_table *tbl1; |
struct vth32_table *tbl1; |
652 |
void *p; |
void *p_r, *p_w; |
653 |
uint32_t p_paddr; |
uint32_t p_paddr; |
654 |
|
|
655 |
if (!cpu->machine->old_bintrans_enable) { |
if (!cpu->machine->old_bintrans_enable) { |
679 |
|
|
680 |
tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; |
tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; |
681 |
/* printf("tbl1 = %p\n", tbl1); */ |
/* printf("tbl1 = %p\n", tbl1); */ |
682 |
p = tbl1->haddr_entry[b]; |
p_r = tbl1->haddr_entry[b*2]; |
683 |
|
p_w = tbl1->haddr_entry[b*2+1]; |
684 |
p_paddr = tbl1->paddr_entry[b]; |
p_paddr = tbl1->paddr_entry[b]; |
685 |
tbl1->bintrans_chunks[b] = NULL; |
tbl1->bintrans_chunks[b] = NULL; |
686 |
/* printf(" p = %p\n", p); */ |
/* printf("B: p_r=%p p_w=%p\n", p_r,p_w); */ |
687 |
if (p != NULL || p_paddr != 0) { |
if (p_r != NULL || p_paddr != 0) { |
688 |
/* printf("Found a mapping, " |
/* printf("Found a mapping, " |
689 |
"vaddr = %08x, a = %03x, b = %03x\n", (int)vaddr,a, b); */ |
"vaddr = %08x, a = %03x, b = %03x\n", (int)vaddr,a, b); */ |
690 |
tbl1->haddr_entry[b] = NULL; |
tbl1->haddr_entry[b*2] = NULL; |
691 |
|
tbl1->haddr_entry[b*2+1] = NULL; |
692 |
tbl1->paddr_entry[b] = 0; |
tbl1->paddr_entry[b] = 0; |
693 |
tbl1->refcount --; |
tbl1->refcount --; |
694 |
if (tbl1->refcount == 0) { |
if (tbl1->refcount == 0) { |
719 |
tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; |
tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; |
720 |
if (tbl1 != cpu->cd.mips.vaddr_to_hostaddr_nulltable) { |
if (tbl1 != cpu->cd.mips.vaddr_to_hostaddr_nulltable) { |
721 |
for (b=0; b<0x400; b++) { |
for (b=0; b<0x400; b++) { |
722 |
tbl1->haddr_entry[b] = NULL; |
tbl1->haddr_entry[b*2] = NULL; |
723 |
|
tbl1->haddr_entry[b*2+1] = NULL; |
724 |
tbl1->paddr_entry[b] = 0; |
tbl1->paddr_entry[b] = 0; |
725 |
tbl1->bintrans_chunks[b] = NULL; |
tbl1->bintrans_chunks[b] = NULL; |
726 |
} |
} |
975 |
* Read a value from a MIPS coprocessor register. |
* Read a value from a MIPS coprocessor register. |
976 |
*/ |
*/ |
977 |
void coproc_register_read(struct cpu *cpu, |
void coproc_register_read(struct cpu *cpu, |
978 |
struct mips_coproc *cp, int reg_nr, uint64_t *ptr) |
struct mips_coproc *cp, int reg_nr, uint64_t *ptr, int select) |
979 |
{ |
{ |
980 |
int unimpl = 1; |
int unimpl = 1; |
981 |
|
|
1017 |
if (cp->coproc_nr==0 && reg_nr==COP0_CAUSE) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_CAUSE) unimpl = 0; |
1018 |
if (cp->coproc_nr==0 && reg_nr==COP0_EPC) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_EPC) unimpl = 0; |
1019 |
if (cp->coproc_nr==0 && reg_nr==COP0_PRID) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_PRID) unimpl = 0; |
1020 |
if (cp->coproc_nr==0 && reg_nr==COP0_CONFIG) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_CONFIG) { |
1021 |
|
if (select > 0) { |
1022 |
|
switch (select) { |
1023 |
|
case 1: *ptr = cpu->cd.mips.cop0_config_select1; |
1024 |
|
break; |
1025 |
|
default:fatal("coproc_register_read(): unimplemented" |
1026 |
|
" config register select %i\n", select); |
1027 |
|
exit(1); |
1028 |
|
} |
1029 |
|
return; |
1030 |
|
} |
1031 |
|
unimpl = 0; |
1032 |
|
} |
1033 |
if (cp->coproc_nr==0 && reg_nr==COP0_LLADDR) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_LLADDR) unimpl = 0; |
1034 |
if (cp->coproc_nr==0 && reg_nr==COP0_WATCHLO) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_WATCHLO) unimpl = 0; |
1035 |
if (cp->coproc_nr==0 && reg_nr==COP0_WATCHHI) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_WATCHHI) unimpl = 0; |
1069 |
* Write a value to a MIPS coprocessor register. |
* Write a value to a MIPS coprocessor register. |
1070 |
*/ |
*/ |
1071 |
void coproc_register_write(struct cpu *cpu, |
void coproc_register_write(struct cpu *cpu, |
1072 |
struct mips_coproc *cp, int reg_nr, uint64_t *ptr, int flag64) |
struct mips_coproc *cp, int reg_nr, uint64_t *ptr, int flag64, |
1073 |
|
int select) |
1074 |
{ |
{ |
1075 |
int unimpl = 1; |
int unimpl = 1; |
1076 |
int readonly = 0; |
int readonly = 0; |
1236 |
readonly = 1; |
readonly = 1; |
1237 |
break; |
break; |
1238 |
case COP0_CONFIG: |
case COP0_CONFIG: |
1239 |
|
if (select > 0) { |
1240 |
|
switch (select) { |
1241 |
|
case 1: cpu->cd.mips.cop0_config_select1 = tmp; |
1242 |
|
break; |
1243 |
|
default:fatal("coproc_register_write(): unimpl" |
1244 |
|
"emented config register select " |
1245 |
|
"%i\n", select); |
1246 |
|
exit(1); |
1247 |
|
} |
1248 |
|
return; |
1249 |
|
} |
1250 |
|
|
1251 |
/* fatal("COP0_CONFIG: modifying K0 bits: " |
/* fatal("COP0_CONFIG: modifying K0 bits: " |
1252 |
"0x%08x => ", cp->reg[reg_nr]); */ |
"0x%08x => ", cp->reg[reg_nr]); */ |
1253 |
tmp = *ptr; |
tmp = *ptr; |
2575 |
if (cpnr < 2 && (((function & 0x03e007f8) == (COPz_MFCz << 21)) |
if (cpnr < 2 && (((function & 0x03e007f8) == (COPz_MFCz << 21)) |
2576 |
|| ((function & 0x03e007f8) == (COPz_DMFCz << 21)))) { |
|| ((function & 0x03e007f8) == (COPz_DMFCz << 21)))) { |
2577 |
if (unassemble_only) { |
if (unassemble_only) { |
2578 |
debug("%s%i\t%s,%s\n", |
debug("%s%i\t%s,%s", |
2579 |
copz==COPz_DMFCz? "dmfc" : "mfc", cpnr, |
copz==COPz_DMFCz? "dmfc" : "mfc", cpnr, |
2580 |
regnames[rt], cop0_names[rd]); |
regnames[rt], cop0_names[rd]); |
2581 |
|
if (function & 7) |
2582 |
|
debug(",%i", (int)(function & 7)); |
2583 |
|
debug("\n"); |
2584 |
return; |
return; |
2585 |
} |
} |
2586 |
coproc_register_read(cpu, cpu->cd.mips.coproc[cpnr], |
coproc_register_read(cpu, cpu->cd.mips.coproc[cpnr], |
2587 |
rd, &tmpvalue); |
rd, &tmpvalue, function & 7); |
2588 |
cpu->cd.mips.gpr[rt] = tmpvalue; |
cpu->cd.mips.gpr[rt] = tmpvalue; |
2589 |
if (copz == COPz_MFCz) { |
if (copz == COPz_MFCz) { |
2590 |
/* Sign-extend: */ |
/* Sign-extend: */ |
2598 |
if (cpnr < 2 && (((function & 0x03e007f8) == (COPz_MTCz << 21)) |
if (cpnr < 2 && (((function & 0x03e007f8) == (COPz_MTCz << 21)) |
2599 |
|| ((function & 0x03e007f8) == (COPz_DMTCz << 21)))) { |
|| ((function & 0x03e007f8) == (COPz_DMTCz << 21)))) { |
2600 |
if (unassemble_only) { |
if (unassemble_only) { |
2601 |
debug("%s%i\t%s,%s\n", |
debug("%s%i\t%s,%s", |
2602 |
copz==COPz_DMTCz? "dmtc" : "mtc", cpnr, |
copz==COPz_DMTCz? "dmtc" : "mtc", cpnr, |
2603 |
regnames[rt], cop0_names[rd]); |
regnames[rt], cop0_names[rd]); |
2604 |
|
if (function & 7) |
2605 |
|
debug(",%i", (int)(function & 7)); |
2606 |
|
debug("\n"); |
2607 |
return; |
return; |
2608 |
} |
} |
2609 |
tmpvalue = cpu->cd.mips.gpr[rt]; |
tmpvalue = cpu->cd.mips.gpr[rt]; |
2614 |
tmpvalue |= 0xffffffff00000000ULL; |
tmpvalue |= 0xffffffff00000000ULL; |
2615 |
} |
} |
2616 |
coproc_register_write(cpu, cpu->cd.mips.coproc[cpnr], rd, |
coproc_register_write(cpu, cpu->cd.mips.coproc[cpnr], rd, |
2617 |
&tmpvalue, copz == COPz_DMTCz); |
&tmpvalue, copz == COPz_DMTCz, function & 7); |
2618 |
return; |
return; |
2619 |
} |
} |
2620 |
|
|