--- trunk/src/cpus/cpu_dyntrans.c 2007/10/08 16:19:11 18 +++ trunk/src/cpus/cpu_dyntrans.c 2007/10/08 16:22:11 40 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,181 +25,309 @@ * SUCH DAMAGE. * * - * $Id: cpu_dyntrans.c,v 1.27 2005/10/27 14:01:13 debug Exp $ + * $Id: cpu_dyntrans.c,v 1.147 2007/04/19 15:18:16 debug Exp $ * * Common dyntrans routines. Included from cpu_*.c. */ -#ifdef DYNTRANS_CPU_RUN_INSTR +#ifndef STATIC_STUFF +#define STATIC_STUFF +/* + * gather_statistics(): + */ static void gather_statistics(struct cpu *cpu) { + char ch, buf[60]; + struct DYNTRANS_IC *ic = cpu->cd.DYNTRANS_ARCH.next_ic; + int i = 0; uint64_t a; int low_pc = ((size_t)cpu->cd.DYNTRANS_ARCH.next_ic - (size_t) cpu->cd.DYNTRANS_ARCH.cur_ic_page) / sizeof(struct DYNTRANS_IC); - if (low_pc < 0 || low_pc >= DYNTRANS_IC_ENTRIES_PER_PAGE) - return; -#if 1 - /* Use the physical address: */ - cpu->cd.DYNTRANS_ARCH.cur_physpage = (void *) - cpu->cd.DYNTRANS_ARCH.cur_ic_page; - a = cpu->cd.DYNTRANS_ARCH.cur_physpage->physaddr; -#else - /* Use the PC (virtual address): */ - a = cpu->pc; -#endif + if (cpu->machine->statistics_file == NULL) { + fatal("statistics gathering with no filename set is" + " meaningless\n"); + return; + } - a &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) << - DYNTRANS_INSTR_ALIGNMENT_SHIFT); - a += low_pc << DYNTRANS_INSTR_ALIGNMENT_SHIFT; + buf[0] = '\0'; - /* - * TODO: Everything below this line should be cleaned up :-) - */ -a &= 0x03ffffff; -{ - static long long *array = NULL; - static char *array_16kpage_in_use = NULL; - static int n = 0; - a >>= DYNTRANS_INSTR_ALIGNMENT_SHIFT; - if (array == NULL) - array = zeroed_alloc(sizeof(long long) * 16384*1024); - if (array_16kpage_in_use == NULL) - array_16kpage_in_use = zeroed_alloc(sizeof(char) * 1024); - a &= (16384*1024-1); - array[a] ++; - array_16kpage_in_use[a / 16384] = 1; - n++; - if ((n & 0x3fffffff) == 0) { - FILE *f = fopen("statistics.out", "w"); - int i, j; - printf("Saving statistics... "); fflush(stdout); - for (i=0; i<1024; i++) - if (array_16kpage_in_use[i]) { - for (j=0; j<16384; j++) - if (array[i*16384 + j] > 0) - fprintf(f, "%lli\t0x%016llx\n", - (long long)array[i*16384+j], - (long long)((i*16384+j) << - DYNTRANS_INSTR_ALIGNMENT_SHIFT)); - } - fclose(f); - printf("n=0x%08x\n", n); + while ((ch = cpu->machine->statistics_fields[i]) != '\0') { + if (i != 0) + strlcat(buf, " ", sizeof(buf)); + + switch (ch) { + case 'i': + snprintf(buf + strlen(buf), sizeof(buf), + "%p", (void *)ic->f); + break; + case 'p': + /* Physical program counter address: */ + /* (low_pc must be within the page!) */ + if (low_pc < 0 || + low_pc >= DYNTRANS_IC_ENTRIES_PER_PAGE) + strlcat(buf, "-", sizeof(buf)); + cpu->cd.DYNTRANS_ARCH.cur_physpage = (void *) + cpu->cd.DYNTRANS_ARCH.cur_ic_page; + a = cpu->cd.DYNTRANS_ARCH.cur_physpage->physaddr; + a &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) << + DYNTRANS_INSTR_ALIGNMENT_SHIFT); + a += low_pc << DYNTRANS_INSTR_ALIGNMENT_SHIFT; + if (cpu->is_32bit) + snprintf(buf + strlen(buf), sizeof(buf), + "0x%016"PRIx32, (uint32_t)a); + else + snprintf(buf + strlen(buf), sizeof(buf), + "0x%016"PRIx64, (uint64_t)a); + break; + case 'v': + /* Virtual program counter address: */ + /* (low_pc inside the page, or in a delay slot) */ + if (low_pc < 0 || + low_pc >= DYNTRANS_IC_ENTRIES_PER_PAGE + 2) + strlcat(buf, "-", sizeof(buf)); + a = cpu->pc; + a &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) << + DYNTRANS_INSTR_ALIGNMENT_SHIFT); + a += low_pc << DYNTRANS_INSTR_ALIGNMENT_SHIFT; + if (cpu->is_32bit) + snprintf(buf + strlen(buf), sizeof(buf), + "0x%016"PRIx32, (uint32_t)a); + else + snprintf(buf + strlen(buf), sizeof(buf), + "0x%016"PRIx64, (uint64_t)a); + break; + } + i++; } -} + + fprintf(cpu->machine->statistics_file, "%s\n", buf); } #define S gather_statistics(cpu) + #ifdef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH -#define I ic = cpu->cd.DYNTRANS_ARCH.next_ic; ic->f(cpu, ic); -#else -#define I ic = cpu->cd.DYNTRANS_ARCH.next_ic ++; ic->f(cpu, ic); +#define I ic = cpu->cd.DYNTRANS_ARCH.next_ic; \ + cpu->cd.DYNTRANS_ARCH.next_ic += ic->arg[0]; \ + ic->f(cpu, ic); +#else + +/* The normal instruction execution core: */ +#define I ic = cpu->cd.DYNTRANS_ARCH.next_ic ++; ic->f(cpu, ic); + +/* For heavy debugging: */ +/* #define I ic = cpu->cd.DYNTRANS_ARCH.next_ic ++; \ + { \ + int low_pc = ((size_t)cpu->cd.DYNTRANS_ARCH.next_ic - \ + (size_t)cpu->cd.DYNTRANS_ARCH.cur_ic_page) / \ + sizeof(struct DYNTRANS_IC); \ + printf("cur_ic_page=%p ic=%p (low_pc=0x%x)\n", \ + cpu->cd.DYNTRANS_ARCH.cur_ic_page, \ + ic, low_pc << DYNTRANS_INSTR_ALIGNMENT_SHIFT); \ + } \ + ic->f(cpu, ic); */ + +/* static long long nr_of_I_calls = 0; */ + +/* Temporary hack for finding NULL bugs: */ +/* #define I ic = cpu->cd.DYNTRANS_ARCH.next_ic ++; \ + nr_of_I_calls ++; \ + if (ic->f == NULL) { \ + int low_pc = ((size_t)cpu->cd.DYNTRANS_ARCH.next_ic - \ + (size_t)cpu->cd.DYNTRANS_ARCH.cur_ic_page) / \ + sizeof(struct DYNTRANS_IC); \ + cpu->pc &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) << \ + DYNTRANS_INSTR_ALIGNMENT_SHIFT); \ + cpu->pc += (low_pc << DYNTRANS_INSTR_ALIGNMENT_SHIFT);\ + printf("Crash at %016"PRIx64"\n", cpu->pc); \ + printf("nr of I calls: %lli\n", nr_of_I_calls); \ + printf("Next ic = %p\n", cpu->cd. \ + DYNTRANS_ARCH.next_ic); \ + printf("cur ic page = %p\n", cpu->cd. \ + DYNTRANS_ARCH.cur_ic_page); \ + cpu->running = 0; \ + return 0; \ + } \ + ic->f(cpu, ic); */ + +/* Temporary hack for MIPS, to hunt for 32-bit/64-bit sign-extension bugs: */ +/* #define I { int k; for (k=1; k<=31; k++) \ + cpu->cd.mips.gpr[k] = (int32_t)cpu->cd.mips.gpr[k];\ + if (cpu->cd.mips.gpr[0] != 0) { \ + fatal("NOOOOOO\n"); exit(1); \ + } \ + ic = cpu->cd.DYNTRANS_ARCH.next_ic ++; ic->f(cpu, ic); } +*/ #endif +#endif /* STATIC STUFF */ + +#ifdef DYNTRANS_RUN_INSTR /* - * XXX_cpu_run_instr(): + * XXX_run_instr(): * * Execute one or more instructions on a specific CPU, using dyntrans. + * (For dualmode archs, this function is included twice.) * * Return value is the number of instructions executed during this call, * 0 if no instructions were executed. */ -int DYNTRANS_CPU_RUN_INSTR(struct emul *emul, struct cpu *cpu) +int DYNTRANS_RUN_INSTR(struct cpu *cpu) { -#ifdef MODE32 - uint32_t cached_pc; -#else - uint64_t cached_pc; -#endif + MODE_uint_t cached_pc; int low_pc, n_instrs; + /* Ugly... fix this some day. */ #ifdef DYNTRANS_DUALMODE_32 - if (cpu->is_32bit) - DYNTRANS_PC_TO_POINTERS32(cpu); - else +#ifdef MODE32 + DYNTRANS_PC_TO_POINTERS32(cpu); +#else + DYNTRANS_PC_TO_POINTERS(cpu); #endif +#else DYNTRANS_PC_TO_POINTERS(cpu); +#endif /* * Interrupt assertion? (This is _below_ the initial PC to pointer * conversion; if the conversion caused an exception of some kind * then interrupts are probably disabled, and the exception will get * priority over device interrupts.) + * + * TODO: Turn this into a family-specific function somewhere... */ #ifdef DYNTRANS_ARM if (cpu->cd.arm.irq_asserted && !(cpu->cd.arm.cpsr & ARM_FLAG_I)) arm_exception(cpu, ARM_EXCEPTION_IRQ); #endif +#ifdef DYNTRANS_MIPS + { + int enabled, mask; + int status = cpu->cd.mips.coproc[0]->reg[COP0_STATUS]; + if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { + /* R3000: */ + enabled = status & MIPS_SR_INT_IE; + } else { + /* R4000 and others: */ + enabled = (status & STATUS_IE) + && !(status & STATUS_EXL) && !(status & STATUS_ERL); + /* Special case for R5900/C790/TX79: */ + if (cpu->cd.mips.cpu_type.rev == MIPS_R5900 && + !(status & R5900_STATUS_EIE)) + enabled = 0; + } + mask = status & cpu->cd.mips.coproc[0]->reg[COP0_CAUSE] + & STATUS_IM_MASK; + + if (enabled && mask) + mips_cpu_exception(cpu, EXCEPTION_INT, 0, 0, 0, 0, 0,0); + } +#endif +#ifdef DYNTRANS_PPC + if (cpu->cd.ppc.dec_intr_pending && cpu->cd.ppc.msr & PPC_MSR_EE) { + if (!(cpu->cd.ppc.cpu_type.flags & PPC_NO_DEC)) + ppc_exception(cpu, PPC_EXCEPTION_DEC); + cpu->cd.ppc.dec_intr_pending = 0; + } + if (cpu->cd.ppc.irq_asserted && cpu->cd.ppc.msr & PPC_MSR_EE) + ppc_exception(cpu, PPC_EXCEPTION_EI); +#endif +#ifdef DYNTRANS_SH + if (cpu->cd.sh.int_to_assert > 0 && !(cpu->cd.sh.sr & SH_SR_BL) + && ((cpu->cd.sh.sr & SH_SR_IMASK) >> SH_SR_IMASK_SHIFT) + < cpu->cd.sh.int_level) + sh_exception(cpu, 0, cpu->cd.sh.int_to_assert, 0); +#endif cached_pc = cpu->pc; cpu->n_translated_instrs = 0; - cpu->running_translated = 1; cpu->cd.DYNTRANS_ARCH.cur_physpage = (void *) cpu->cd.DYNTRANS_ARCH.cur_ic_page; - if (single_step || cpu->machine->instruction_trace) { + if (single_step || cpu->machine->instruction_trace + || cpu->machine->register_dump) { /* * Single-step: */ - struct DYNTRANS_IC *ic = cpu->cd.DYNTRANS_ARCH.next_ic -#ifndef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH - ++ -#endif - ; + struct DYNTRANS_IC *ic = cpu->cd.DYNTRANS_ARCH.next_ic; + if (cpu->machine->register_dump) { + debug("\n"); + cpu_register_dump(cpu->machine, cpu, 1, 0x1); + } if (cpu->machine->instruction_trace) { -#ifdef DYNTRANS_X86 - unsigned char instr[17]; - cpu->cd.x86.cursegment = X86_S_CS; - cpu->cd.x86.seg_override = 0; -#else -#ifdef DYNTRANS_M68K - unsigned char instr[16]; /* TODO: 16? */ -#else - unsigned char instr[4]; /* General case... */ -#endif -#endif + /* TODO/Note: This must be large enough to hold + any instruction for any ISA: */ + unsigned char instr[1 << + DYNTRANS_INSTR_ALIGNMENT_SHIFT]; if (!cpu->memory_rw(cpu, cpu->mem, cached_pc, &instr[0], sizeof(instr), MEM_READ, CACHE_INSTRUCTION)) { - fatal("XXX_cpu_run_instr(): could not read " + fatal("XXX_run_instr(): could not read " "the instruction\n"); - } else - cpu_disassemble_instr(cpu->machine, cpu, - instr, 1, 0, 0); - } - - /* When single-stepping, multiple instruction calls cannot - be combined into one. This clears all translations: */ - if (cpu->cd.DYNTRANS_ARCH.cur_physpage->flags & COMBINATIONS) { - int i; - for (i=0; icd.DYNTRANS_ARCH.cur_physpage->ics[i].f = -#ifdef DYNTRANS_DUALMODE_32 - cpu->is_32bit? - instr32(to_be_translated) : + } else { +#ifdef DYNTRANS_DELAYSLOT + int len = #endif - instr(to_be_translated); - fatal("[ Note: The translation of physical page 0x%llx" - " contained combinations of instructions; these " - "are now flushed because we are single-stepping." - " ]\n", (long long)cpu->cd.DYNTRANS_ARCH. - cur_physpage->physaddr); - cpu->cd.DYNTRANS_ARCH.cur_physpage->flags &= - ~(COMBINATIONS | TRANSLATIONS); + cpu_disassemble_instr( + cpu->machine, cpu, instr, 1, 0); +#ifdef DYNTRANS_DELAYSLOT + /* Show the instruction in the delay slot, + if any: */ + if (cpu->instruction_has_delayslot == NULL) + fatal("WARNING: ihd func not yet" + " implemented?\n"); + else if (cpu->instruction_has_delayslot(cpu, + instr)) { + int saved_delayslot = cpu->delay_slot; + cpu->memory_rw(cpu, cpu->mem, cached_pc + + len, &instr[0], + sizeof(instr), MEM_READ, + CACHE_INSTRUCTION); + cpu->delay_slot = DELAYED; + cpu->pc += len; + cpu_disassemble_instr(cpu->machine, + cpu, instr, 1, 0); + cpu->delay_slot = saved_delayslot; + cpu->pc -= len; + } +#endif + } } - if (show_opcode_statistics) + if (cpu->machine->statistics_enabled) S; /* Execute just one instruction: */ - ic->f(cpu, ic); + I; + n_instrs = 1; - } else if (show_opcode_statistics) { + } else if (cpu->machine->cycle_accurate) { + /* Executing multiple instructions, and call devices' + tick functions: */ + n_instrs = 0; + for (;;) { + struct DYNTRANS_IC *ic; +/* TODO: continue here */ +int64_t cycles = cpu->cd.avr.extra_cycles; + I; + n_instrs += 1; +cycles = cpu->cd.avr.extra_cycles - cycles + 1; +/* The instruction took 'cycles' cycles. */ +/* printf("A\n"); */ +while (cycles-- > 0) + cpu->machine->tick_func[1](cpu, cpu->machine->tick_extra[1]); +/* printf("B\n"); */ + + if (n_instrs + cpu->n_translated_instrs >= + N_SAFE_DYNTRANS_LIMIT) + break; + } + } else if (cpu->machine->statistics_enabled) { /* Gather statistics while executing multiple instructions: */ n_instrs = 0; for (;;) { @@ -212,8 +340,8 @@ n_instrs += 24; - if (!cpu->running_translated || - n_instrs + cpu->n_translated_instrs >= 16384) + if (n_instrs + cpu->n_translated_instrs >= + N_SAFE_DYNTRANS_LIMIT) break; } } else { @@ -230,51 +358,91 @@ I; I; I; I; I; I; I; I; I; I; - n_instrs += 60; + I; I; I; I; I; I; I; I; I; I; + I; I; I; I; I; I; I; I; I; I; + I; I; I; I; I; I; I; I; I; I; + I; I; I; I; I; I; I; I; I; I; + I; I; I; I; I; I; I; I; I; I; + + I; I; I; I; I; I; I; I; I; I; - if (!cpu->running_translated || - n_instrs + cpu->n_translated_instrs >= 16384) + cpu->n_translated_instrs += 120; + if (cpu->n_translated_instrs >= N_SAFE_DYNTRANS_LIMIT) break; } } + n_instrs += cpu->n_translated_instrs; - /* - * Update the program counter and return the correct number of - * executed instructions: - */ + /* Synchronize the program counter: */ low_pc = ((size_t)cpu->cd.DYNTRANS_ARCH.next_ic - (size_t) cpu->cd.DYNTRANS_ARCH.cur_ic_page) / sizeof(struct DYNTRANS_IC); - if (low_pc >= 0 && low_pc < DYNTRANS_IC_ENTRIES_PER_PAGE) { -#ifdef DYNTRANS_ARM - cpu->cd.arm.r[ARM_PC] &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1)<<2); - cpu->cd.arm.r[ARM_PC] += (low_pc << 2); - cpu->pc = cpu->cd.arm.r[ARM_PC]; -#else cpu->pc &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) << DYNTRANS_INSTR_ALIGNMENT_SHIFT); cpu->pc += (low_pc << DYNTRANS_INSTR_ALIGNMENT_SHIFT); -#endif } else if (low_pc == DYNTRANS_IC_ENTRIES_PER_PAGE) { /* Switch to next page: */ -#ifdef DYNTRANS_ARM - cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2); - cpu->cd.arm.r[ARM_PC] += (ARM_IC_ENTRIES_PER_PAGE << 2); - cpu->pc = cpu->cd.arm.r[ARM_PC]; -#else cpu->pc &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) << DYNTRANS_INSTR_ALIGNMENT_SHIFT); cpu->pc += (DYNTRANS_IC_ENTRIES_PER_PAGE << DYNTRANS_INSTR_ALIGNMENT_SHIFT); + } else if (low_pc == DYNTRANS_IC_ENTRIES_PER_PAGE + 1) { + /* Switch to next page and skip an instruction which was + already executed (in a delay slot): */ + cpu->pc &= ~((DYNTRANS_IC_ENTRIES_PER_PAGE-1) << + DYNTRANS_INSTR_ALIGNMENT_SHIFT); + cpu->pc += ((DYNTRANS_IC_ENTRIES_PER_PAGE + 1) << + DYNTRANS_INSTR_ALIGNMENT_SHIFT); + } + +#ifdef DYNTRANS_MIPS + /* Update the count register (on everything except EXC3K): */ + if (cpu->cd.mips.cpu_type.exc_model != EXC3K) { + uint32_t old = cpu->cd.mips.coproc[0]->reg[COP0_COUNT]; + int32_t diff1 = cpu->cd.mips.coproc[0]->reg[COP0_COMPARE] - old; + int32_t diff2; + cpu->cd.mips.coproc[0]->reg[COP0_COUNT] = + (int32_t) (old + n_instrs); + diff2 = cpu->cd.mips.coproc[0]->reg[COP0_COMPARE] - + cpu->cd.mips.coproc[0]->reg[COP0_COUNT]; + + if (cpu->cd.mips.compare_register_set) { +#if 1 +/* Not yet. TODO */ + if (cpu->machine->emulated_hz > 0) { + if (cpu->cd.mips.compare_interrupts_pending > 0) + INTERRUPT_ASSERT( + cpu->cd.mips.irq_compare); + } else #endif - } else { - /* debug("debug: Outside a page (This is actually ok)\n"); */ + { + if (diff1 > 0 && diff2 <= 0) + INTERRUPT_ASSERT( + cpu->cd.mips.irq_compare); + } + } } +#endif +#ifdef DYNTRANS_PPC + /* Update the Decrementer and Time base registers: */ + { + uint32_t old = cpu->cd.ppc.spr[SPR_DEC]; + cpu->cd.ppc.spr[SPR_DEC] = (uint32_t) (old - n_instrs); + if ((old >> 31) == 0 && (cpu->cd.ppc.spr[SPR_DEC] >> 31) == 1 + && !(cpu->cd.ppc.cpu_type.flags & PPC_NO_DEC)) + cpu->cd.ppc.dec_intr_pending = 1; + old = cpu->cd.ppc.spr[SPR_TBL]; + cpu->cd.ppc.spr[SPR_TBL] += n_instrs; + if ((old >> 31) == 1 && (cpu->cd.ppc.spr[SPR_TBL] >> 31) == 0) + cpu->cd.ppc.spr[SPR_TBU] ++; + } +#endif - return n_instrs + cpu->n_translated_instrs; + /* Return the nr of instructions executed: */ + return n_instrs; } -#endif /* DYNTRANS_CPU_RUN_INSTR */ +#endif /* DYNTRANS_RUN_INSTR */ @@ -292,11 +460,11 @@ char *symbol; uint64_t ot; int x, print_dots = 1, n_args_to_print = -#ifdef DYNTRANS_ALPHA +#if defined(DYNTRANS_ALPHA) || defined(DYNTRANS_SPARC) 6 #else -#ifdef DYNTRANS_SH - 8 +#if defined(DYNTRANS_SH) || defined(DYNTRANS_M88K) + 8 /* Both for 32-bit and 64-bit SuperH, and M88K */ #else 4 /* Default value for most archs */ #endif @@ -320,12 +488,7 @@ * than were passed in register. */ for (x=0; xcd.DYNTRANS_ARCH. + int64_t d = cpu->cd.DYNTRANS_ARCH. #ifdef DYNTRANS_ALPHA r[ALPHA_A0 #endif @@ -337,32 +500,24 @@ they go downwards, ie. 22,23 and so on */ r[24 #endif -#ifdef DYNTRANS_HPPA - r[0 /* TODO */ -#endif -#ifdef DYNTRANS_I960 - r[0 /* TODO */ -#endif -#ifdef DYNTRANS_IA64 - r[0 /* TODO */ -#endif -#ifdef DYNTRANS_M68K - d[0 /* TODO */ -#endif #ifdef DYNTRANS_MIPS gpr[MIPS_GPR_A0 #endif +#ifdef DYNTRANS_M88K + r[2 /* r2..r9 */ +#endif #ifdef DYNTRANS_PPC gpr[3 #endif #ifdef DYNTRANS_SH - r[2 + r[4 /* NetBSD seems to use 4? But 2 seems + to be used by other code? TODO */ #endif #ifdef DYNTRANS_SPARC - r_i[0 + r[8 /* o0..o5 */ #endif + x]; -#endif + symbol = get_symbol_name(&cpu->machine->symbol_context, d, &ot); if (d > -256 && d < 256) @@ -374,9 +529,9 @@ fatal("&%s", symbol); else { if (cpu->is_32bit) - fatal("0x%x", (int)d); + fatal("0x%"PRIx32, (uint32_t)d); else - fatal("0x%llx", (long long)d); + fatal("0x%"PRIx64, (uint64_t)d); } if (x < n_args_to_print - 1) @@ -391,13 +546,6 @@ #ifdef DYNTRANS_TC_ALLOCATE_DEFAULT_PAGE -/* forward declaration of to_be_translated and end_of_page: */ -static void instr(to_be_translated)(struct cpu *, struct DYNTRANS_IC *); -static void instr(end_of_page)(struct cpu *,struct DYNTRANS_IC *); -#ifdef DYNTRANS_DUALMODE_32 -static void instr32(to_be_translated)(struct cpu *, struct DYNTRANS_IC *); -static void instr32(end_of_page)(struct cpu *,struct DYNTRANS_IC *); -#endif /* * XXX_tc_allocate_default_page(): * @@ -408,32 +556,20 @@ uint64_t physaddr) { struct DYNTRANS_TC_PHYSPAGE *ppp; - int i; - /* Create the physpage header: */ ppp = (struct DYNTRANS_TC_PHYSPAGE *)(cpu->translation_cache + cpu->translation_cache_cur_ofs); - ppp->next_ofs = 0; - ppp->physaddr = physaddr; - /* TODO: Is this faster than copying an entire template page? */ - for (i=0; iics[i].f = -#ifdef DYNTRANS_DUALMODE_32 - cpu->is_32bit? instr32(to_be_translated) : -#endif - instr(to_be_translated); + /* Copy the entire template page first: */ + memcpy(ppp, cpu->cd.DYNTRANS_ARCH.physpage_template, sizeof( + struct DYNTRANS_TC_PHYSPAGE)); - ppp->ics[DYNTRANS_IC_ENTRIES_PER_PAGE].f = -#ifdef DYNTRANS_DUALMODE_32 - cpu->is_32bit? instr32(end_of_page) : -#endif - instr(end_of_page); + ppp->physaddr = physaddr & ~(DYNTRANS_PAGESIZE - 1); cpu->translation_cache_cur_ofs += sizeof(struct DYNTRANS_TC_PHYSPAGE); cpu->translation_cache_cur_ofs --; - cpu->translation_cache_cur_ofs |= 63; + cpu->translation_cache_cur_ofs |= 127; cpu->translation_cache_cur_ofs ++; } #endif /* DYNTRANS_TC_ALLOCATE_DEFAULT_PAGE */ @@ -453,37 +589,31 @@ #else uint64_t #endif - cached_pc, physaddr; + cached_pc = cpu->pc, physaddr = 0; uint32_t physpage_ofs; int ok, pagenr, table_index; uint32_t *physpage_entryp; struct DYNTRANS_TC_PHYSPAGE *ppp; #ifdef MODE32 - int index; - cached_pc = cpu->pc; - index = DYNTRANS_ADDR_TO_PAGENR(cached_pc); + int index = DYNTRANS_ADDR_TO_PAGENR(cached_pc); #else -#ifdef DYNTRANS_ALPHA - uint32_t a, b; - int kernel = 0; - struct alpha_vph_page *vph_p; - cached_pc = cpu->pc; - a = (cached_pc >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1); - b = (cached_pc >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1); - if ((cached_pc >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) { - vph_p = cpu->cd.alpha.vph_table0_kernel[a]; - kernel = 1; - } else - vph_p = cpu->cd.alpha.vph_table0[a]; -#else -#ifdef DYNTRANS_IA64 - fatal("IA64 todo\n"); -#else - fatal("Neither alpha, ia64, nor 32-bit? 3\n"); - exit(1); -#endif -#endif + const uint32_t mask1 = (1 << DYNTRANS_L1N) - 1; + const uint32_t mask2 = (1 << DYNTRANS_L2N) - 1; + const uint32_t mask3 = (1 << DYNTRANS_L3N) - 1; + uint32_t x1, x2, x3; + struct DYNTRANS_L2_64_TABLE *l2; + struct DYNTRANS_L3_64_TABLE *l3; + + x1 = (cached_pc >> (64-DYNTRANS_L1N)) & mask1; + x2 = (cached_pc >> (64-DYNTRANS_L1N-DYNTRANS_L2N)) & mask2; + x3 = (cached_pc >> (64-DYNTRANS_L1N-DYNTRANS_L2N-DYNTRANS_L3N)) & mask3; + /* fatal("X3: cached_pc=%016"PRIx64" x1=%x x2=%x x3=%x\n", + (uint64_t)cached_pc, (int)x1, (int)x2, (int)x3); */ + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + /* fatal(" l2 = %p\n", l2); */ + l3 = l2->l3[x2]; + /* fatal(" l3 = %p\n", l3); */ #endif /* Virtual to physical address translation: */ @@ -494,74 +624,101 @@ ok = 1; } #else -#ifdef DYNTRANS_ALPHA - if (vph_p->host_load[b] != NULL) { - physaddr = vph_p->phys_addr[b]; + if (l3->host_load[x3] != NULL) { + physaddr = l3->phys_addr[x3]; ok = 1; } -#else -#ifdef DYNTRANS_IA64 - fatal("IA64 todo\n"); -#else - fatal("Neither alpha, ia64, nor 32-bit? 4\n"); - exit(1); -#endif -#endif #endif if (!ok) { uint64_t paddr; - if (cpu->translate_address != NULL) - ok = cpu->translate_address(cpu, cached_pc, - &paddr, FLAG_INSTR); - else { + if (cpu->translate_v2p != NULL) { + uint64_t vaddr = +#if defined(MODE32) && defined(DYNTRANS_MIPS) + /* 32-bit MIPS is _sign_ extend, not zero. */ + (int32_t) +#endif + cached_pc; + ok = cpu->translate_v2p( + cpu, vaddr, &paddr, FLAG_INSTR); + } else { paddr = cached_pc; ok = 1; } if (!ok) { -/* - fatal("TODO: instruction vaddr=>paddr translation" - " failed. vaddr=0x%llx\n", (long long)cached_pc); -fatal("!! cpu->pc=0x%llx arm_pc=0x%x\n", (long long)cpu->pc, -cpu->cd.arm.r[ARM_PC]); -*/ - ok = cpu->translate_address(cpu, cpu->pc, &paddr, - FLAG_INSTR); -/* -printf("EXCEPTION HANDLER: vaddr = 0x%x ==> paddr = 0x%x\n", - (int)cpu->pc, (int)paddr); -fatal("!? cpu->pc=0x%llx arm_pc=0x%x\n", (long long)cpu->pc, -cpu->cd.arm.r[ARM_PC]); -*/ + /* + * The PC is now set to the exception handler. + * Try to find the paddr in the translation arrays, + * or if that fails, call translate_v2p for the + * exception handler. + */ + /* fatal("TODO: instruction vaddr=>paddr translation " + "failed. vaddr=0x%"PRIx64"\n", (uint64_t)cached_pc); + fatal("!! cpu->pc=0x%"PRIx64"\n", (uint64_t)cpu->pc); */ + + /* If there was an exception, the PC has changed. + Update cached_pc: */ + cached_pc = cpu->pc; + +#ifdef MODE32 + index = DYNTRANS_ADDR_TO_PAGENR(cached_pc); + if (cpu->cd.DYNTRANS_ARCH.host_load[index] != NULL) { + paddr = cpu->cd.DYNTRANS_ARCH.phys_addr[index]; + ok = 1; + } +#else + x1 = (cached_pc >> (64-DYNTRANS_L1N)) & mask1; + x2 = (cached_pc >> (64-DYNTRANS_L1N-DYNTRANS_L2N)) + & mask2; + x3 = (cached_pc >> (64-DYNTRANS_L1N-DYNTRANS_L2N + - DYNTRANS_L3N)) & mask3; + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + l3 = l2->l3[x2]; + if (l3->host_load[x3] != NULL) { + paddr = l3->phys_addr[x3]; + ok = 1; + } +#endif + + if (!ok) { + ok = cpu->translate_v2p(cpu, cpu->pc, &paddr, + FLAG_INSTR); + } + + /* printf("EXCEPTION HANDLER: vaddr = 0x%x ==> " + "paddr = 0x%x\n", (int)cpu->pc, (int)paddr); + fatal("!? cpu->pc=0x%"PRIx64"\n", (uint64_t)cpu->pc); */ + if (!ok) { fatal("FATAL: could not find physical" " address of the exception handler?"); exit(1); } } - cached_pc = cpu->pc; -#ifdef MODE32 - index = DYNTRANS_ADDR_TO_PAGENR(cached_pc); -#endif + physaddr = paddr; } + physaddr &= ~(DYNTRANS_PAGESIZE - 1); + #ifdef MODE32 if (cpu->cd.DYNTRANS_ARCH.host_load[index] == NULL) { +#else + if (l3->host_load[x3] == NULL) { +#endif + int q = DYNTRANS_PAGESIZE - 1; unsigned char *host_page = memory_paddr_to_hostaddr(cpu->mem, physaddr, MEM_READ); if (host_page != NULL) { - int q = DYNTRANS_PAGESIZE - 1; - host_page += (physaddr & - ((1 << BITS_PER_MEMBLOCK) - 1) & ~q); cpu->update_translation_table(cpu, cached_pc & ~q, - host_page, TLB_CODE, physaddr & ~q); + host_page, 0, physaddr); } } -#endif - if (cpu->translation_cache_cur_ofs >= DYNTRANS_CACHE_SIZE) { + if (cpu->translation_cache_cur_ofs >= dyntrans_cache_size) { +#ifdef UNSTABLE_DEVEL fatal("[ dyntrans: resetting the translation cache ]\n"); +#endif cpu_create_or_reset_tc(cpu); } @@ -576,20 +733,30 @@ while (physpage_ofs != 0) { ppp = (struct DYNTRANS_TC_PHYSPAGE *)(cpu->translation_cache + physpage_ofs); + /* If we found the page in the cache, then we're done: */ if (ppp->physaddr == physaddr) break; + /* Try the next page in the chain: */ physpage_ofs = ppp->next_ofs; } - /* If the offset is 0 (or ppp is NULL), then we need to create a - new "default" empty translation page. */ + /* + * If the offset is 0, then no translation exists yet for this + * physical address. Let's create a new page, and add it first in + * the chain. + */ + if (physpage_ofs == 0) { + uint32_t previous_first_page_in_chain; - if (ppp == NULL) { - /* fatal("CREATING page %lli (physaddr 0x%llx), table index " - "%i\n", (long long)pagenr, (long long)physaddr, + /* fatal("CREATING page %lli (physaddr 0x%"PRIx64"), table " + "index %i\n", (long long)pagenr, (uint64_t)physaddr, (int)table_index); */ + + previous_first_page_in_chain = *physpage_entryp; + + /* Insert the new page first in the chain: */ *physpage_entryp = physpage_ofs = cpu->translation_cache_cur_ofs; @@ -598,30 +765,39 @@ ppp = (struct DYNTRANS_TC_PHYSPAGE *)(cpu->translation_cache + physpage_ofs); + + /* Point to the other pages in the same chain: */ + ppp->next_ofs = previous_first_page_in_chain; } + /* Here, ppp points to a valid physical page struct. */ + #ifdef MODE32 if (cpu->cd.DYNTRANS_ARCH.host_load[index] != NULL) cpu->cd.DYNTRANS_ARCH.phys_page[index] = ppp; +#else + if (l3->host_load[x3] != NULL) + l3->phys_page[x3] = ppp; #endif -#ifdef DYNTRANS_ALPHA - if (vph_p->host_load[b] != NULL) - vph_p->phys_page[b] = ppp; -#endif - - cpu->invalidate_translation_caches(cpu, physaddr, - JUST_MARK_AS_NON_WRITABLE | INVALIDATE_PADDR); + /* + * If there are no translations yet on this page, then mark it + * as non-writable. If there are already translations, then it + * should already have been marked as non-writable. + */ + if (ppp->translations == 0) { + cpu->invalidate_translation_caches(cpu, physaddr, + JUST_MARK_AS_NON_WRITABLE | INVALIDATE_PADDR); + } -/* cpu->cd.DYNTRANS_ARCH.cur_physpage = ppp; */ cpu->cd.DYNTRANS_ARCH.cur_ic_page = &ppp->ics[0]; cpu->cd.DYNTRANS_ARCH.next_ic = cpu->cd.DYNTRANS_ARCH.cur_ic_page + DYNTRANS_PC_TO_IC_ENTRY(cached_pc); - /* printf("cached_pc=0x%016llx pagenr=%lli table_index=%lli, " - "physpage_ofs=0x%016llx\n", (long long)cached_pc, (long long)pagenr, - (long long)table_index, (long long)physpage_ofs); */ + /* printf("cached_pc=0x%016"PRIx64" pagenr=%lli table_index=%lli, " + "physpage_ofs=0x%016"PRIx64"\n", (uint64_t)cached_pc, (long long) + pagenr, (long long)table_index, (uint64_t)physpage_ofs); */ } @@ -645,43 +821,31 @@ #else uint64_t #endif - cached_pc; + cached_pc = cpu->pc; struct DYNTRANS_TC_PHYSPAGE *ppp; #ifdef MODE32 int index; - cached_pc = cpu->pc; index = DYNTRANS_ADDR_TO_PAGENR(cached_pc); ppp = cpu->cd.DYNTRANS_ARCH.phys_page[index]; if (ppp != NULL) goto have_it; #else -#ifdef DYNTRANS_ALPHA - uint32_t a, b; - int kernel = 0; - struct alpha_vph_page *vph_p; - cached_pc = cpu->pc; - a = (cached_pc >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1); - b = (cached_pc >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1); - if ((cached_pc >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) { - vph_p = cpu->cd.alpha.vph_table0_kernel[a]; - kernel = 1; - } else - vph_p = cpu->cd.alpha.vph_table0[a]; - if (vph_p != cpu->cd.alpha.vph_default_page) { - ppp = vph_p->phys_page[b]; - if (ppp != NULL) - goto have_it; - } -#else -#ifdef DYNTRANS_IA64 - fatal("IA64 todo\n"); -#else - fatal("Neither alpha, ia64, nor 32-bit? 1\n"); - { char *p = (char *) 0; *p = 0; } - exit(1); -#endif -#endif + const uint32_t mask1 = (1 << DYNTRANS_L1N) - 1; + const uint32_t mask2 = (1 << DYNTRANS_L2N) - 1; + const uint32_t mask3 = (1 << DYNTRANS_L3N) - 1; + uint32_t x1, x2, x3; + struct DYNTRANS_L2_64_TABLE *l2; + struct DYNTRANS_L3_64_TABLE *l3; + + x1 = (cached_pc >> (64-DYNTRANS_L1N)) & mask1; + x2 = (cached_pc >> (64-DYNTRANS_L1N-DYNTRANS_L2N)) & mask2; + x3 = (cached_pc >> (64-DYNTRANS_L1N-DYNTRANS_L2N-DYNTRANS_L3N)) & mask3; + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + l3 = l2->l3[x2]; + ppp = l3->phys_page[x3]; + if (ppp != NULL) + goto have_it; #endif DYNTRANS_PC_TO_POINTERS_GENERIC(cpu); @@ -689,19 +853,117 @@ /* Quick return path: */ have_it: -/* cpu->cd.DYNTRANS_ARCH.cur_physpage = ppp; */ cpu->cd.DYNTRANS_ARCH.cur_ic_page = &ppp->ics[0]; cpu->cd.DYNTRANS_ARCH.next_ic = cpu->cd.DYNTRANS_ARCH.cur_ic_page + DYNTRANS_PC_TO_IC_ENTRY(cached_pc); - /* printf("cached_pc=0x%016llx pagenr=%lli table_index=%lli, " - "physpage_ofs=0x%016llx\n", (long long)cached_pc, (long long)pagenr, - (long long)table_index, (long long)physpage_ofs); */ + /* printf("cached_pc=0x%016"PRIx64" pagenr=%lli table_index=%lli, " + "physpage_ofs=0x%016"PRIx64"\n", (uint64_t)cached_pc, (long long) + pagenr, (long long)table_index, (uint64_t)physpage_ofs); */ } #endif /* DYNTRANS_PC_TO_POINTERS_FUNC */ +#ifdef DYNTRANS_INIT_TABLES + +/* forward declaration of to_be_translated and end_of_page: */ +static void instr(to_be_translated)(struct cpu *, struct DYNTRANS_IC *); +static void instr(end_of_page)(struct cpu *,struct DYNTRANS_IC *); +#ifdef DYNTRANS_DUALMODE_32 +static void instr32(to_be_translated)(struct cpu *, struct DYNTRANS_IC *); +static void instr32(end_of_page)(struct cpu *,struct DYNTRANS_IC *); +#endif + +#ifdef DYNTRANS_DELAYSLOT +static void instr(end_of_page2)(struct cpu *,struct DYNTRANS_IC *); +#ifdef DYNTRANS_DUALMODE_32 +static void instr32(end_of_page2)(struct cpu *,struct DYNTRANS_IC *); +#endif +#endif + +/* + * XXX_init_tables(): + * + * Initializes the default translation page (for newly allocated pages), and + * for 64-bit emulation it also initializes 64-bit dummy tables and pointers. + */ +void DYNTRANS_INIT_TABLES(struct cpu *cpu) +{ +#ifndef MODE32 + struct DYNTRANS_L2_64_TABLE *dummy_l2; + struct DYNTRANS_L3_64_TABLE *dummy_l3; + int x1, x2; +#endif + int i; + struct DYNTRANS_TC_PHYSPAGE *ppp = malloc(sizeof( + struct DYNTRANS_TC_PHYSPAGE)); + + if (ppp == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + ppp->next_ofs = 0; + ppp->translations = 0; + /* ppp->physaddr is filled in by the page allocator */ + + for (i=0; iics[i].f = +#ifdef DYNTRANS_DUALMODE_32 + cpu->is_32bit? instr32(to_be_translated) : +#endif + instr(to_be_translated); +#ifdef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH + ppp->ics[i].arg[0] = 0; +#endif + } + + /* End-of-page: */ + ppp->ics[DYNTRANS_IC_ENTRIES_PER_PAGE + 0].f = +#ifdef DYNTRANS_DUALMODE_32 + cpu->is_32bit? instr32(end_of_page) : +#endif + instr(end_of_page); + +#ifdef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH + ppp->ics[DYNTRANS_IC_ENTRIES_PER_PAGE + 0].arg[0] = 0; +#endif + + /* End-of-page-2, for delay-slot architectures: */ +#ifdef DYNTRANS_DELAYSLOT + ppp->ics[DYNTRANS_IC_ENTRIES_PER_PAGE + 1].f = +#ifdef DYNTRANS_DUALMODE_32 + cpu->is_32bit? instr32(end_of_page2) : +#endif + instr(end_of_page2); +#endif + + cpu->cd.DYNTRANS_ARCH.physpage_template = ppp; + + + /* Prepare 64-bit virtual address translation tables: */ +#ifndef MODE32 + if (cpu->is_32bit) + return; + + dummy_l2 = zeroed_alloc(sizeof(struct DYNTRANS_L2_64_TABLE)); + dummy_l3 = zeroed_alloc(sizeof(struct DYNTRANS_L3_64_TABLE)); + + cpu->cd.DYNTRANS_ARCH.l2_64_dummy = dummy_l2; + cpu->cd.DYNTRANS_ARCH.l3_64_dummy = dummy_l3; + + for (x1 = 0; x1 < (1 << DYNTRANS_L1N); x1 ++) + cpu->cd.DYNTRANS_ARCH.l1_64[x1] = dummy_l2; + + for (x2 = 0; x2 < (1 << DYNTRANS_L2N); x2 ++) + dummy_l2->l3[x2] = dummy_l3; +#endif +} +#endif /* DYNTRANS_INIT_TABLES */ + + + #ifdef DYNTRANS_INVAL_ENTRY /* * XXX_invalidate_tlb_entry(): @@ -724,7 +986,7 @@ uint32_t index = DYNTRANS_ADDR_TO_PAGENR(vaddr_page); #ifdef DYNTRANS_ARM - cpu->cd.DYNTRANS_ARCH.is_userpage[index >> 3] &= ~(1 << (index & 7)); + cpu->cd.DYNTRANS_ARCH.is_userpage[index >> 5] &= ~(1 << (index & 31)); #endif if (flags & JUST_MARK_AS_NON_WRITABLE) { @@ -732,62 +994,129 @@ (int)vaddr_page); */ cpu->cd.DYNTRANS_ARCH.host_store[index] = NULL; } else { + int tlbi = cpu->cd.DYNTRANS_ARCH.vaddr_to_tlbindex[index]; cpu->cd.DYNTRANS_ARCH.host_load[index] = NULL; cpu->cd.DYNTRANS_ARCH.host_store[index] = NULL; cpu->cd.DYNTRANS_ARCH.phys_addr[index] = 0; cpu->cd.DYNTRANS_ARCH.phys_page[index] = NULL; + if (tlbi > 0) + cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[tlbi-1].valid = 0; cpu->cd.DYNTRANS_ARCH.vaddr_to_tlbindex[index] = 0; } #else - /* 2-level: */ -#ifdef DYNTRANS_ALPHA - struct alpha_vph_page *vph_p; - uint32_t a, b; - int kernel = 0; - - a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1); - b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1); - if ((vaddr_page >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) { - vph_p = cpu->cd.alpha.vph_table0_kernel[a]; - kernel = 1; - } else - vph_p = cpu->cd.alpha.vph_table0[a]; + const uint32_t mask1 = (1 << DYNTRANS_L1N) - 1; + const uint32_t mask2 = (1 << DYNTRANS_L2N) - 1; + const uint32_t mask3 = (1 << DYNTRANS_L3N) - 1; + uint32_t x1, x2, x3; + struct DYNTRANS_L2_64_TABLE *l2; + struct DYNTRANS_L3_64_TABLE *l3; + + x1 = (vaddr_page >> (64-DYNTRANS_L1N)) & mask1; + x2 = (vaddr_page >> (64-DYNTRANS_L1N-DYNTRANS_L2N)) & mask2; + x3 = (vaddr_page >> (64-DYNTRANS_L1N-DYNTRANS_L2N-DYNTRANS_L3N))& mask3; - if (vph_p == cpu->cd.alpha.vph_default_page) { - fatal("alpha_invalidate_tlb_entry(): huh? Problem 1.\n"); - exit(1); - } + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + if (l2 == cpu->cd.DYNTRANS_ARCH.l2_64_dummy) + return; + + l3 = l2->l3[x2]; + if (l3 == cpu->cd.DYNTRANS_ARCH.l3_64_dummy) + return; if (flags & JUST_MARK_AS_NON_WRITABLE) { - vph_p->host_store[b] = NULL; + l3->host_store[x3] = NULL; return; } - vph_p->host_load[b] = NULL; - vph_p->host_store[b] = NULL; - vph_p->phys_addr[b] = 0; - vph_p->phys_page[b] = NULL; - vph_p->refcount --; - if (vph_p->refcount < 0) { - fatal("alpha_invalidate_tlb_entry(): huh? Problem 2.\n"); + +#ifdef BUGHUNT + +{ + /* Consistency check, for debugging: */ + int x1, x1b; // x2, x3; + struct DYNTRANS_L2_64_TABLE *l2; + //struct DYNTRANS_L3_64_TABLE *l3; + + for (x1 = 0; x1 <= mask1; x1 ++) { + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + if (l2 == cpu->cd.DYNTRANS_ARCH.l2_64_dummy) + continue; + /* Make sure that this l2 isn't used more than 1 time! */ + for (x1b = 0; x1b <= mask1; x1b ++) + if (x1 != x1b && + l2 == cpu->cd.DYNTRANS_ARCH.l1_64[x1b]) { + fatal("L2 reuse: %p\n", l2); + exit(1); + } + } +} + +/* Count how many pages are actually in use: */ +{ + int n=0, i; + for (i=0; i<=mask3; i++) + if (l3->vaddr_to_tlbindex[i]) + n++; + if (n != l3->refcount) { + printf("Z: %i in use, but refcount = %i!\n", n, l3->refcount); + exit(1); + } + + n = 0; + for (i=0; i<=mask3; i++) + if (l3->host_load[i] != NULL) + n++; + if (n != l3->refcount) { + printf("ZHL: %i in use, but refcount = %i!\n", n, l3->refcount); exit(1); } - if (vph_p->refcount == 0) { - vph_p->next = cpu->cd.alpha.vph_next_free_page; - cpu->cd.alpha.vph_next_free_page = vph_p; - if (kernel) - cpu->cd.alpha.vph_table0_kernel[a] = - cpu->cd.alpha.vph_default_page; - else - cpu->cd.alpha.vph_table0[a] = - cpu->cd.alpha.vph_default_page; +} +#endif + + l3->host_load[x3] = NULL; + l3->host_store[x3] = NULL; + l3->phys_addr[x3] = 0; + l3->phys_page[x3] = NULL; + if (l3->vaddr_to_tlbindex[x3] != 0) { + cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[ + l3->vaddr_to_tlbindex[x3] - 1].valid = 0; + l3->refcount --; + } + l3->vaddr_to_tlbindex[x3] = 0; + + if (l3->refcount < 0) { + fatal("xxx_invalidate_tlb_entry(): huh? Refcount bug.\n"); + exit(1); + } + + if (l3->refcount == 0) { + l3->next = cpu->cd.DYNTRANS_ARCH.next_free_l3; + cpu->cd.DYNTRANS_ARCH.next_free_l3 = l3; + l2->l3[x2] = cpu->cd.DYNTRANS_ARCH.l3_64_dummy; + +#ifdef BUGHUNT +/* Make sure that we're placing a CLEAN page on the + freelist: */ +{ + int i; + for (i=0; i<=mask3; i++) + if (l3->host_load[i] != NULL) { + fatal("TRYING TO RETURN A NON-CLEAN L3 PAGE!\n"); + exit(1); + } +} +#endif + l2->refcount --; + if (l2->refcount < 0) { + fatal("xxx_invalidate_tlb_entry(): Refcount bug L2.\n"); + exit(1); + } + if (l2->refcount == 0) { + l2->next = cpu->cd.DYNTRANS_ARCH.next_free_l2; + cpu->cd.DYNTRANS_ARCH.next_free_l2 = l2; + cpu->cd.DYNTRANS_ARCH.l1_64[x1] = + cpu->cd.DYNTRANS_ARCH.l2_64_dummy; + } } -#else /* !DYNTRANS_ALPHA */ -#ifdef DYNTRANS_IA64 - fatal("IA64: blah blah TODO\n"); -#else - fatal("Not yet for non-1-level, non-Alpha, non-ia64\n"); -#endif /* !DYNTRANS_IA64 */ -#endif /* !DYNTRANS_ALPHA */ #endif } #endif @@ -803,6 +1132,10 @@ * flags should be one of * INVALIDATE_PADDR INVALIDATE_VADDR or INVALIDATE_ALL * + * In addition, for INVALIDATE_ALL, INVALIDATE_VADDR_UPPER4 may be set and + * bit 31..28 of addr are used to select the virtual addresses to invalidate. + * (This is useful for PowerPC emulation, when segment registers are updated.) + * * In the case when all translations are invalidated, paddr doesn't need * to be supplied. * @@ -810,7 +1143,7 @@ * the quick translation array, not from the linear * vph_tlb_entry[] array. Hopefully this is enough anyway. */ -void DYNTRANS_INVALIDATE_TC(struct cpu *cpu, uint64_t paddr, int flags) +void DYNTRANS_INVALIDATE_TC(struct cpu *cpu, uint64_t addr, int flags) { int r; #ifdef MODE32 @@ -818,19 +1151,57 @@ #else uint64_t #endif - addr_page = paddr & ~(DYNTRANS_PAGESIZE - 1); + addr_page = addr & ~(DYNTRANS_PAGESIZE - 1); + + /* fatal("invalidate(): "); */ - /* Quick case for virtual addresses: see note above. */ + /* Quick case for _one_ virtual addresses: see note above. */ if (flags & INVALIDATE_VADDR) { + /* fatal("vaddr 0x%08x\n", (int)addr_page); */ DYNTRANS_INVALIDATE_TLB_ENTRY(cpu, addr_page, flags); return; } + /* Invalidate everything: */ +#ifdef DYNTRANS_PPC + if (flags & INVALIDATE_ALL && flags & INVALIDATE_VADDR_UPPER4) { + /* fatal("all, upper4 (PowerPC segment)\n"); */ + for (r=0; rcd.DYNTRANS_ARCH.vph_tlb_entry[r].valid && + (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page + & 0xf0000000) == addr_page) { + DYNTRANS_INVALIDATE_TLB_ENTRY(cpu, cpu->cd. + DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page, + 0); + cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid=0; + } + } + return; + } +#endif + if (flags & INVALIDATE_ALL) { + /* fatal("all\n"); */ + for (r=0; rcd.DYNTRANS_ARCH.vph_tlb_entry[r].valid) { + DYNTRANS_INVALIDATE_TLB_ENTRY(cpu, cpu->cd. + DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page, + 0); + cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid=0; + } + } + return; + } + + /* Invalidate a physical page: */ + + if (!(flags & INVALIDATE_PADDR)) + fatal("HUH? Invalidate: Not vaddr, all, or paddr?\n"); + + /* fatal("addr 0x%08x\n", (int)addr_page); */ + for (r=0; rcd.DYNTRANS_ARCH.vph_tlb_entry[r].valid && ( - (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].paddr_page == - addr_page && flags & INVALIDATE_PADDR) || - flags & INVALIDATE_ALL) ) { + if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid && addr_page + == cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].paddr_page) { DYNTRANS_INVALIDATE_TLB_ENTRY(cpu, cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page, flags); @@ -875,23 +1246,17 @@ struct DYNTRANS_TC_PHYSPAGE *ppp, *prev_ppp; pagenr = DYNTRANS_ADDR_TO_PAGENR(addr); - -#ifdef MODE32 - /* If this page isn't marked as having any translations, - then return immediately. */ - if (!(cpu->cd.DYNTRANS_ARCH.phystranslation[pagenr >> 5] - & 1 << (pagenr & 31))) - return; - /* Remove the mark: */ - cpu->cd.DYNTRANS_ARCH.phystranslation[pagenr >> 5] &= - ~ (1 << (pagenr & 31)); -#endif - table_index = PAGENR_TO_TABLE_INDEX(pagenr); physpage_entryp = &(((uint32_t *)cpu-> translation_cache)[table_index]); physpage_ofs = *physpage_entryp; + + /* Return immediately if there is no code translation + for this page. */ + if (physpage_ofs == 0) + return; + prev_ppp = ppp = NULL; /* Traverse the physical page chain: */ @@ -899,18 +1264,22 @@ prev_ppp = ppp; ppp = (struct DYNTRANS_TC_PHYSPAGE *) (cpu->translation_cache + physpage_ofs); + /* If we found the page in the cache, then we're done: */ if (ppp->physaddr == addr) break; + /* Try the next page in the chain: */ physpage_ofs = ppp->next_ofs; } + /* If there is no translation, there is no need to go + on and try to remove it from the vph_tlb_entry array: */ if (physpage_ofs == 0) - ppp = NULL; + return; -#if 1 +#if 0 /* * "Bypass" the page, removing it from the code cache. * @@ -932,23 +1301,34 @@ * it might be faster since we don't risk wasting cache * memory as quickly (which would force unnecessary Restarts). */ - if (ppp != NULL) { - /* TODO: Is this faster than copying an entire - template page? */ - int i; - for (i=0; iics[i].f = + if (ppp != NULL && ppp->translations != 0) { + uint32_t x = ppp->translations; /* TODO: + urk Should be same type as ppp->translations */ + int i, j, n, m; + n = 8 * sizeof(x); + m = DYNTRANS_IC_ENTRIES_PER_PAGE / n; + + for (i=0; iics[i*m + j].f = #ifdef DYNTRANS_DUALMODE_32 - cpu->is_32bit? instr32(to_be_translated) : + cpu->is_32bit? + instr32(to_be_translated) : #endif - instr(to_be_translated); + instr(to_be_translated); + } + + x >>= 1; + } + + ppp->translations = 0; } #endif } - /* Invalidate entries (NOTE: only code entries) in the VPH table: */ - for (r = DYNTRANS_MAX_VPH_TLB_ENTRIES/2; - r < DYNTRANS_MAX_VPH_TLB_ENTRIES; r ++) { + /* Invalidate entries in the VPH table: */ + for (r = 0; r < DYNTRANS_MAX_VPH_TLB_ENTRIES; r ++) { if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid) { vaddr_page = cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r] .vaddr_page & ~(DYNTRANS_PAGESIZE-1); @@ -962,37 +1342,22 @@ uint32_t index = DYNTRANS_ADDR_TO_PAGENR(vaddr_page); cpu->cd.DYNTRANS_ARCH.phys_page[index] = NULL; - /* Remove the mark: */ - index = DYNTRANS_ADDR_TO_PAGENR(paddr_page); - cpu->cd.DYNTRANS_ARCH.phystranslation[ - index >> 5] &= ~ (1 << (index & 31)); #else - /* 2-level: */ -#ifdef DYNTRANS_ALPHA - struct alpha_vph_page *vph_p; - uint32_t a, b; - int kernel = 0; - - a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) - & (ALPHA_LEVEL0 - 1); - b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) - & (ALPHA_LEVEL1 - 1); - if ((vaddr_page >> ALPHA_TOPSHIFT) == - ALPHA_TOP_KERNEL) { - vph_p = cpu->cd.alpha. - vph_table0_kernel[a]; - kernel = 1; - } else - vph_p = cpu->cd.alpha.vph_table0[a]; - vph_p->phys_page[b] = NULL; -#else /* !DYNTRANS_ALPHA */ -#ifdef DYNTRANS_IA64 - fatal("IA64: blah yo yo TODO\n"); -#else - fatal("Not yet for non-1-level, non-Alpha, " - "non-ia64\n"); -#endif /* !DYNTRANS_IA64 */ -#endif /* !DYNTRANS_ALPHA */ + const uint32_t mask1 = (1 << DYNTRANS_L1N) - 1; + const uint32_t mask2 = (1 << DYNTRANS_L2N) - 1; + const uint32_t mask3 = (1 << DYNTRANS_L3N) - 1; + uint32_t x1, x2, x3; + struct DYNTRANS_L2_64_TABLE *l2; + struct DYNTRANS_L3_64_TABLE *l3; + + x1 = (vaddr_page >> (64-DYNTRANS_L1N)) & mask1; + x2 = (vaddr_page >> (64-DYNTRANS_L1N - + DYNTRANS_L2N)) & mask2; + x3 = (vaddr_page >> (64-DYNTRANS_L1N - + DYNTRANS_L2N - DYNTRANS_L3N)) & mask3; + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + l3 = l2->l3[x2]; + l3->phys_page[x3] = NULL; #endif } } @@ -1011,17 +1376,8 @@ void DYNTRANS_UPDATE_TRANSLATION_TABLE(struct cpu *cpu, uint64_t vaddr_page, unsigned char *host_page, int writeflag, uint64_t paddr_page) { - int64_t lowest, highest = -1; - int found, r, lowest_index, start, end, useraccess = 0; + int found, r, useraccess = 0; -#ifdef DYNTRANS_ALPHA - uint32_t a, b; - struct alpha_vph_page *vph_p; - int kernel = 0; - /* fatal("update_translation_table(): v=0x%llx, h=%p w=%i" - " p=0x%llx\n", (long long)vaddr_page, host_page, writeflag, - (long long)paddr_page); */ -#else #ifdef MODE32 uint32_t index; vaddr_page &= 0xffffffffULL; @@ -1030,66 +1386,62 @@ " p=0x%x\n", (int)vaddr_page, host_page, writeflag, (int)paddr_page); */ #else /* !MODE32 */ -#ifdef DYNTRANS_IA64 - fatal("IA64 update todo\n"); -#else - fatal("Neither 32-bit, IA64, nor Alpha? 2\n"); - exit(1); -#endif -#endif + const uint32_t mask1 = (1 << DYNTRANS_L1N) - 1; + const uint32_t mask2 = (1 << DYNTRANS_L2N) - 1; + const uint32_t mask3 = (1 << DYNTRANS_L3N) - 1; + uint32_t x1, x2, x3; + struct DYNTRANS_L2_64_TABLE *l2; + struct DYNTRANS_L3_64_TABLE *l3; + + /* fatal("update_translation_table(): v=0x%016"PRIx64", h=%p w=%i" + " p=0x%016"PRIx64"\n", (uint64_t)vaddr_page, host_page, writeflag, + (uint64_t)paddr_page); */ #endif + assert((vaddr_page & (DYNTRANS_PAGESIZE-1)) == 0); + assert((paddr_page & (DYNTRANS_PAGESIZE-1)) == 0); + if (writeflag & MEMORY_USER_ACCESS) { writeflag &= ~MEMORY_USER_ACCESS; useraccess = 1; } - start = 0; end = DYNTRANS_MAX_VPH_TLB_ENTRIES / 2; -#if 1 - /* Half of the TLB used for data, half for code: */ - if (writeflag & TLB_CODE) { - writeflag &= ~TLB_CODE; - start = end; end = DYNTRANS_MAX_VPH_TLB_ENTRIES; - } -#else - /* Data and code entries are mixed. */ - end = DYNTRANS_MAX_VPH_TLB_ENTRIES; -#endif - /* Scan the current TLB entries: */ - found = -1; lowest_index = start; - lowest = cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[0].timestamp; #ifdef MODE32 - /* NOTE: vaddr_to_tlbindex is one more than the index, so that - 0 becomes -1, which means a miss. */ - found = cpu->cd.DYNTRANS_ARCH.vaddr_to_tlbindex[ + /* + * NOTE 1: vaddr_to_tlbindex is one more than the index, so that + * 0 becomes -1, which means a miss. + * + * NOTE 2: When a miss occurs, instead of scanning the entire tlb + * for the entry with the lowest time stamp, just choosing + * one at random will work as well. + */ + found = (int)cpu->cd.DYNTRANS_ARCH.vaddr_to_tlbindex[ DYNTRANS_ADDR_TO_PAGENR(vaddr_page)] - 1; - if (found < 0) - lowest_index = (random() % (end-start)) + start; - if (0) -#endif - - for (r=start; rcd.DYNTRANS_ARCH.vph_tlb_entry[r].timestamp < lowest) { - lowest = cpu->cd.DYNTRANS_ARCH. - vph_tlb_entry[r].timestamp; - lowest_index = r; - } - if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].timestamp > highest) - highest = cpu->cd.DYNTRANS_ARCH. - vph_tlb_entry[r].timestamp; - if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid && - cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page == - vaddr_page) { - found = r; - break; - } +#else + x1 = (vaddr_page >> (64-DYNTRANS_L1N)) & mask1; + x2 = (vaddr_page >> (64-DYNTRANS_L1N-DYNTRANS_L2N)) & mask2; + x3 = (vaddr_page >> (64-DYNTRANS_L1N-DYNTRANS_L2N-DYNTRANS_L3N)) + & mask3; + + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + if (l2 == cpu->cd.DYNTRANS_ARCH.l2_64_dummy) + found = -1; + else { + l3 = l2->l3[x2]; + if (l3 == cpu->cd.DYNTRANS_ARCH.l3_64_dummy) + found = -1; + else + found = (int)l3->vaddr_to_tlbindex[x3] - 1; } +#endif if (found < 0) { - /* Create the new TLB entry, overwriting the oldest one: */ - r = lowest_index; + /* Create the new TLB entry, overwriting a "random" entry: */ + static unsigned int x = 0; + r = (x++) % DYNTRANS_MAX_VPH_TLB_ENTRIES; + if (cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid) { /* This one has to be invalidated first: */ DYNTRANS_INVALIDATE_TLB_ENTRY(cpu, @@ -1101,46 +1453,10 @@ cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].host_page = host_page; cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].paddr_page = paddr_page; cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].vaddr_page = vaddr_page; - cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].writeflag = writeflag; - cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].timestamp = highest + 1; + cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].writeflag = + writeflag & MEM_WRITE; /* Add the new translation to the table: */ -#ifdef DYNTRANS_ALPHA - a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1); - b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1); - if ((vaddr_page >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) { - vph_p = cpu->cd.alpha.vph_table0_kernel[a]; - kernel = 1; - } else - vph_p = cpu->cd.alpha.vph_table0[a]; - if (vph_p == cpu->cd.alpha.vph_default_page) { - if (cpu->cd.alpha.vph_next_free_page != NULL) { - if (kernel) - vph_p = cpu->cd.alpha.vph_table0_kernel - [a] = cpu->cd.alpha. - vph_next_free_page; - else - vph_p = cpu->cd.alpha.vph_table0[a] = - cpu->cd.alpha.vph_next_free_page; - cpu->cd.alpha.vph_next_free_page = vph_p->next; - } else { - if (kernel) - vph_p = cpu->cd.alpha.vph_table0_kernel - [a] = malloc(sizeof(struct - alpha_vph_page)); - else - vph_p = cpu->cd.alpha.vph_table0[a] = - malloc(sizeof(struct - alpha_vph_page)); - memset(vph_p, 0, sizeof(struct alpha_vph_page)); - } - } - vph_p->refcount ++; - vph_p->host_load[b] = host_page; - vph_p->host_store[b] = writeflag? host_page : NULL; - vph_p->phys_addr[b] = paddr_page; - vph_p->phys_page[b] = NULL; -#else #ifdef MODE32 index = DYNTRANS_ADDR_TO_PAGENR(vaddr_page); cpu->cd.DYNTRANS_ARCH.host_load[index] = host_page; @@ -1151,59 +1467,112 @@ cpu->cd.DYNTRANS_ARCH.vaddr_to_tlbindex[index] = r + 1; #ifdef DYNTRANS_ARM if (useraccess) - cpu->cd.DYNTRANS_ARCH.is_userpage[index >> 3] - |= 1 << (index & 7); + cpu->cd.DYNTRANS_ARCH.is_userpage[index >> 5] + |= 1 << (index & 31); #endif -#endif /* 32 */ -#endif /* !ALPHA */ +#else /* !MODE32 */ + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + if (l2 == cpu->cd.DYNTRANS_ARCH.l2_64_dummy) { + if (cpu->cd.DYNTRANS_ARCH.next_free_l2 != NULL) { + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1] = + cpu->cd.DYNTRANS_ARCH.next_free_l2; + cpu->cd.DYNTRANS_ARCH.next_free_l2 = l2->next; + } else { + int i; + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1] = + malloc(sizeof(struct DYNTRANS_L2_64_TABLE)); + l2->refcount = 0; + for (i=0; i<(1 << DYNTRANS_L2N); i++) + l2->l3[i] = cpu->cd.DYNTRANS_ARCH. + l3_64_dummy; + } + if (l2->refcount != 0) { + fatal("Huh? l2 Refcount problem.\n"); + exit(1); + } + } + if (l2 == cpu->cd.DYNTRANS_ARCH.l2_64_dummy) { + fatal("INTERNAL ERROR L2 reuse\n"); + exit(1); + } + l3 = l2->l3[x2]; + if (l3 == cpu->cd.DYNTRANS_ARCH.l3_64_dummy) { + if (cpu->cd.DYNTRANS_ARCH.next_free_l3 != NULL) { + l3 = l2->l3[x2] = + cpu->cd.DYNTRANS_ARCH.next_free_l3; + cpu->cd.DYNTRANS_ARCH.next_free_l3 = l3->next; + } else { + l3 = l2->l3[x2] = zeroed_alloc(sizeof( + struct DYNTRANS_L3_64_TABLE)); + } + if (l3->refcount != 0) { + fatal("Huh? l3 Refcount problem.\n"); + exit(1); + } + l2->refcount ++; + } + if (l3 == cpu->cd.DYNTRANS_ARCH.l3_64_dummy) { + fatal("INTERNAL ERROR L3 reuse\n"); + exit(1); + } + + l3->host_load[x3] = host_page; + l3->host_store[x3] = writeflag? host_page : NULL; + l3->phys_addr[x3] = paddr_page; + l3->phys_page[x3] = NULL; + l3->vaddr_to_tlbindex[x3] = r + 1; + l3->refcount ++; + +#ifdef BUGHUNT +/* Count how many pages are actually in use: */ +{ + int n=0, i; + for (i=0; i<=mask3; i++) + if (l3->vaddr_to_tlbindex[i]) + n++; + if (n != l3->refcount) { + printf("X: %i in use, but refcount = %i!\n", n, l3->refcount); + exit(1); + } + + n = 0; + for (i=0; i<=mask3; i++) + if (l3->host_load[i] != NULL) + n++; + if (n != l3->refcount) { + printf("XHL: %i in use, but refcount = %i!\n", n, l3->refcount); + exit(1); + } +} +#endif + +#endif /* !MODE32 */ } else { /* * The translation was already in the TLB. * Writeflag = 0: Do nothing. * Writeflag = 1: Make sure the page is writable. - * Writeflag = -1: Downgrade to readonly. + * Writeflag = MEM_DOWNGRADE: Downgrade to readonly. */ r = found; - cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].timestamp = highest + 1; - if (writeflag == 1) + if (writeflag & MEM_WRITE) cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].writeflag = 1; - if (writeflag == -1) + if (writeflag & MEM_DOWNGRADE) cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].writeflag = 0; -#ifdef DYNTRANS_ALPHA - a = (vaddr_page >> ALPHA_LEVEL0_SHIFT) & (ALPHA_LEVEL0 - 1); - b = (vaddr_page >> ALPHA_LEVEL1_SHIFT) & (ALPHA_LEVEL1 - 1); - if ((vaddr_page >> ALPHA_TOPSHIFT) == ALPHA_TOP_KERNEL) { - vph_p = cpu->cd.alpha.vph_table0_kernel[a]; - kernel = 1; - } else - vph_p = cpu->cd.alpha.vph_table0[a]; - vph_p->phys_page[b] = NULL; - if (vph_p->phys_addr[b] == paddr_page) { - if (writeflag == 1) - vph_p->host_store[b] = host_page; - if (writeflag == -1) - vph_p->host_store[b] = NULL; - } else { - /* Change the entire physical/host mapping: */ - vph_p->host_load[b] = host_page; - vph_p->host_store[b] = writeflag? host_page : NULL; - vph_p->phys_addr[b] = paddr_page; - } -#else #ifdef MODE32 index = DYNTRANS_ADDR_TO_PAGENR(vaddr_page); cpu->cd.DYNTRANS_ARCH.phys_page[index] = NULL; #ifdef DYNTRANS_ARM - cpu->cd.DYNTRANS_ARCH.is_userpage[index >> 3]&=~(1<<(index&7)); + cpu->cd.DYNTRANS_ARCH.is_userpage[index>>5] &= ~(1<<(index&31)); if (useraccess) - cpu->cd.DYNTRANS_ARCH.is_userpage[index >> 3] - |= 1 << (index & 7); + cpu->cd.DYNTRANS_ARCH.is_userpage[index >> 5] + |= 1 << (index & 31); #endif if (cpu->cd.DYNTRANS_ARCH.phys_addr[index] == paddr_page) { - if (writeflag == 1) + if (writeflag & MEM_WRITE) cpu->cd.DYNTRANS_ARCH.host_store[index] = host_page; - if (writeflag == -1) + if (writeflag & MEM_DOWNGRADE) cpu->cd.DYNTRANS_ARCH.host_store[index] = NULL; } else { /* Change the entire physical/host mapping: */ @@ -1212,8 +1581,53 @@ writeflag? host_page : NULL; cpu->cd.DYNTRANS_ARCH.phys_addr[index] = paddr_page; } -#endif /* 32 */ -#endif /* !ALPHA */ +#else /* !MODE32 */ + x1 = (vaddr_page >> (64-DYNTRANS_L1N)) & mask1; + x2 = (vaddr_page >> (64-DYNTRANS_L1N-DYNTRANS_L2N)) & mask2; + x3 = (vaddr_page >> (64-DYNTRANS_L1N-DYNTRANS_L2N-DYNTRANS_L3N)) + & mask3; + l2 = cpu->cd.DYNTRANS_ARCH.l1_64[x1]; + l3 = l2->l3[x2]; + if (l3->phys_addr[x3] == paddr_page) { + if (writeflag & MEM_WRITE) + l3->host_store[x3] = host_page; + if (writeflag & MEM_DOWNGRADE) + l3->host_store[x3] = NULL; + } else { + /* Change the entire physical/host mapping: */ +printf("HOST LOAD 2 set to %p\n", host_page); + l3->host_load[x3] = host_page; + l3->host_store[x3] = writeflag? host_page : NULL; + l3->phys_addr[x3] = paddr_page; + } + +#ifdef BUGHUNT +/* Count how many pages are actually in use: */ +{ + int n=0, i; + for (i=0; i<=mask3; i++) + if (l3->vaddr_to_tlbindex[i]) + n++; + if (n != l3->refcount) { + printf("Y: %i in use, but refcount = %i!\n", n, l3->refcount); + exit(1); + } + + n = 0; + for (i=0; i<=mask3; i++) + if (l3->host_load[i] != NULL) + n++; + if (n != l3->refcount) { + printf("YHL: %i in use, but refcount = %i!\n", n, l3->refcount); + printf("Entry r = %i\n", r); + printf("Valid = %i\n", +cpu->cd.DYNTRANS_ARCH.vph_tlb_entry[r].valid); + exit(1); + } +} +#endif + +#endif /* !MODE32 */ } } #endif /* DYNTRANS_UPDATE_TRANSLATION_TABLE */ @@ -1227,29 +1641,27 @@ * Check for breakpoints. */ if (!single_step_breakpoint) { -#ifdef MODE32 - uint32_t curpc = cpu->pc; -#else - uint64_t curpc = cpu->pc; -#endif + MODE_uint_t curpc = cpu->pc; int i; for (i=0; imachine->n_breakpoints; i++) - if (curpc == -#ifdef MODE32 - (uint32_t) -#endif + if (curpc == (MODE_uint_t) cpu->machine->breakpoint_addr[i]) { if (!cpu->machine->instruction_trace) { int old_quiet_mode = quiet_mode; quiet_mode = 0; - DISASSEMBLE(cpu, ib, 1, 0, 0); + DISASSEMBLE(cpu, ib, 1, 0); quiet_mode = old_quiet_mode; } - fatal("BREAKPOINT: pc = 0x%llx\n(The " + fatal("BREAKPOINT: pc = 0x%"PRIx64"\n(The " "instruction has not yet executed.)\n", - (long long)cpu->pc); + (uint64_t)cpu->pc); +#ifdef DYNTRANS_DELAYSLOT + if (cpu->delay_slot != NOT_DELAYED) + fatal("ERROR! Breakpoint in a delay" + " slot! Not yet supported.\n"); +#endif single_step_breakpoint = 1; - single_step = 1; + single_step = ENTER_SINGLE_STEPPING; goto stop_running_translated; } } @@ -1261,53 +1673,96 @@ #ifdef DYNTRANS_TO_BE_TRANSLATED_TAIL /* - * If we end up here, then an instruction was translated. - * Mark the page as containing a translation. - * - * (Special case for 32-bit mode: set the corresponding bit in the - * phystranslation[] array.) + * If we end up here, then an instruction was translated. Let's mark + * the page as containing a translation at this part of the page. */ -#ifdef MODE32 - if (!(cpu->cd.DYNTRANS_ARCH.cur_physpage->flags & TRANSLATIONS)) { - uint32_t index = DYNTRANS_ADDR_TO_PAGENR(addr); - cpu->cd.DYNTRANS_ARCH.phystranslation[index >> 5] |= - (1 << (index & 31)); - } -#endif - cpu->cd.DYNTRANS_ARCH.cur_physpage->flags |= TRANSLATIONS; + /* Make sure cur_physpage is in synch: */ + cpu->cd.DYNTRANS_ARCH.cur_physpage = (void *) + cpu->cd.DYNTRANS_ARCH.cur_ic_page; + + { + int x = addr & (DYNTRANS_PAGESIZE - 1); + int addr_per_translation_range = DYNTRANS_PAGESIZE / (8 * + sizeof(cpu->cd.DYNTRANS_ARCH.cur_physpage->translations)); + x /= addr_per_translation_range; + + cpu->cd.DYNTRANS_ARCH.cur_physpage->translations |= (1 << x); + } /* * Now it is time to check for combinations of instructions that can * be converted into a single function call. * * Note: Single-stepping or instruction tracing doesn't work with - * instruction combination. + * instruction combination. For architectures with delay slots, + * we also ignore combinations if the delay slot is across a page + * boundary. */ - if (!single_step && !cpu->machine->instruction_trace) { - if (cpu->combination_check != NULL && - cpu->machine->speed_tricks) - cpu->combination_check(cpu, ic, - addr & (DYNTRANS_PAGESIZE - 1)); - cpu->combination_check = NULL; + if (!single_step && !cpu->machine->instruction_trace +#ifdef DYNTRANS_DELAYSLOT + && !in_crosspage_delayslot +#endif + && cpu->cd.DYNTRANS_ARCH.combination_check != NULL + && cpu->machine->allow_instruction_combinations) { + cpu->cd.DYNTRANS_ARCH.combination_check(cpu, ic, + addr & (DYNTRANS_PAGESIZE - 1)); + } + + cpu->cd.DYNTRANS_ARCH.combination_check = NULL; + + /* An additional check, to catch some bugs: */ + if (ic->f == ( +#ifdef DYNTRANS_DUALMODE_32 + cpu->is_32bit? instr32(to_be_translated) : +#endif + instr(to_be_translated))) { + fatal("INTERNAL ERROR: ic->f not set!\n"); + goto bad; + } + if (ic->f == NULL) { + fatal("INTERNAL ERROR: ic->f == NULL!\n"); + goto bad; } /* ... and finally execute the translated instruction: */ - if (single_step_breakpoint) { + if ((single_step_breakpoint && cpu->delay_slot == NOT_DELAYED) +#ifdef DYNTRANS_DELAYSLOT + || in_crosspage_delayslot +#endif + ) { /* * Special case when single-stepping: Execute the translated * instruction, but then replace it with a "to be translated" * directly afterwards. */ single_step_breakpoint = 0; +#ifdef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH + cpu->cd.DYNTRANS_ARCH.next_ic = ic + ic->arg[0]; +#endif ic->f(cpu, ic); ic->f = #ifdef DYNTRANS_DUALMODE_32 cpu->is_32bit? instr32(to_be_translated) : #endif instr(to_be_translated); - } else +#ifdef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH + ic->arg[0] = 0; +#endif + } else { +#ifdef DYNTRANS_VARIABLE_INSTRUCTION_LENGTH + cpu->cd.DYNTRANS_ARCH.next_ic = ic + ic->arg[0]; + + /* Additional check, for variable length ISAs: */ + if (ic->arg[0] == 0) { + fatal("INTERNAL ERROR: instr len = 0!\n"); + goto bad; + } +#endif + + /* Finally finally :-), execute the instruction: */ ic->f(cpu, ic); + } return; @@ -1321,24 +1776,27 @@ if (cpu->machine->instruction_trace) #ifdef MODE32 - fatal(" at 0x%x\n", (int)cpu->pc); + fatal(" at 0x%"PRIx32"\n", (uint32_t)cpu->pc); #else - fatal(" at 0x%llx\n", (long long)cpu->pc); + fatal(" at 0x%"PRIx64"\n", (uint64_t)cpu->pc); #endif else { fatal(":\n"); - DISASSEMBLE(cpu, ib, 1, 0, 0); + DISASSEMBLE(cpu, ib, 1, 0); } cpu->running = 0; - cpu->dead = 1; + + /* Note: Single-stepping can jump here. */ stop_running_translated: + debugger_n_steps_left_before_interaction = 0; - cpu->running_translated = 0; + ic = cpu->cd.DYNTRANS_ARCH.next_ic = ¬hing_call; cpu->cd.DYNTRANS_ARCH.next_ic ++; /* Execute the "nothing" instruction: */ ic->f(cpu, ic); + #endif /* DYNTRANS_TO_BE_TRANSLATED_TAIL */