diff --git a/target-arm/cpu.h b/target-arm/cpu.h index c4d742f084..26b4981fdd 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -149,6 +149,10 @@ typedef struct CPUARMState { uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ uint32_t c15_i_min; /* Minimum D-cache dirty line index. */ uint32_t c15_threadid; /* TI debugger thread-ID. */ + uint32_t c15_config_base_address; /* SCU base address. */ + uint32_t c15_diagnostic; /* diagnostic register */ + uint32_t c15_power_diagnostic; + uint32_t c15_power_control; /* power control */ } cp15; struct { @@ -448,7 +452,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, #define cpu_signal_handler cpu_arm_signal_handler #define cpu_list arm_cpu_list -#define CPU_SAVE_VERSION 4 +#define CPU_SAVE_VERSION 5 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel diff --git a/target-arm/helper.c b/target-arm/helper.c index 65f4fbf98e..fa42c64d3a 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -463,6 +463,26 @@ void cpu_arm_close(CPUARMState *env) g_free(env); } +static int bad_mode_switch(CPUState *env, int mode) +{ + /* Return true if it is not valid for us to switch to + * this CPU mode (ie all the UNPREDICTABLE cases in + * the ARM ARM CPSRWriteByInstr pseudocode). + */ + switch (mode) { + case ARM_CPU_MODE_USR: + case ARM_CPU_MODE_SYS: + case ARM_CPU_MODE_SVC: + case ARM_CPU_MODE_ABT: + case ARM_CPU_MODE_UND: + case ARM_CPU_MODE_IRQ: + case ARM_CPU_MODE_FIQ: + return 0; + default: + return 1; + } +} + uint32_t cpsr_read(CPUARMState *env) { int ZF; @@ -499,7 +519,15 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) } if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { - switch_mode(env, val & CPSR_M); + if (bad_mode_switch(env, val & CPSR_M)) { + /* Attempt to switch to an invalid mode: this is UNPREDICTABLE. + * We choose to ignore the attempt and leave the CPSR M field + * untouched. + */ + mask &= ~CPSR_M; + } else { + switch_mode(env, val & CPSR_M); + } } mask &= ~CACHED_CPSR_BITS; env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); @@ -642,7 +670,7 @@ uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode) extern int semihosting_enabled; /* Map CPU modes onto saved register banks. */ -static inline int bank_number (int mode) +static inline int bank_number(CPUState *env, int mode) { switch (mode) { case ARM_CPU_MODE_USR: @@ -659,7 +687,7 @@ static inline int bank_number (int mode) case ARM_CPU_MODE_FIQ: return 5; } - cpu_abort(cpu_single_env, "Bad mode %x\n", mode); + cpu_abort(env, "Bad mode %x\n", mode); return -1; } @@ -680,12 +708,12 @@ void switch_mode(CPUState *env, int mode) memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); } - i = bank_number(old_mode); + i = bank_number(env, old_mode); env->banked_r13[i] = env->regs[13]; env->banked_r14[i] = env->regs[14]; env->banked_spsr[i] = env->spsr; - i = bank_number(mode); + i = bank_number(env, mode); env->regs[13] = env->banked_r13[i]; env->regs[14] = env->banked_r14[i]; env->spsr = env->banked_spsr[i]; @@ -1768,6 +1796,20 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val) goto bad_reg; } } + if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) { + switch (crm) { + case 0: + if ((op1 == 0) && (op2 == 0)) { + env->cp15.c15_power_control = val; + } else if ((op1 == 0) && (op2 == 1)) { + env->cp15.c15_diagnostic = val; + } else if ((op1 == 0) && (op2 == 2)) { + env->cp15.c15_power_diagnostic = val; + } + default: + break; + } + } break; } return; @@ -2111,6 +2153,40 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn) * 0x200 << ($rn & 0xfff), when MMU is off. */ goto bad_reg; } + if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) { + switch (crm) { + case 0: + if ((op1 == 4) && (op2 == 0)) { + /* The config_base_address should hold the value of + * the peripheral base. ARM should get this from a CPU + * object property, but that support isn't available in + * December 2011. Default to 0 for now and board models + * that care can set it by a private hook */ + return env->cp15.c15_config_base_address; + } else if ((op1 == 0) && (op2 == 0)) { + /* power_control should be set to maximum latency. Again, + default to 0 and set by private hook */ + return env->cp15.c15_power_control; + } else if ((op1 == 0) && (op2 == 1)) { + return env->cp15.c15_diagnostic; + } else if ((op1 == 0) && (op2 == 2)) { + return env->cp15.c15_power_diagnostic; + } + break; + case 1: /* NEON Busy */ + return 0; + case 5: /* tlb lockdown */ + case 6: + case 7: + if ((op1 == 5) && (op2 == 2)) { + return 0; + } + break; + default: + break; + } + goto bad_reg; + } return 0; } bad_reg: @@ -2125,7 +2201,7 @@ void HELPER(set_r13_banked)(CPUState *env, uint32_t mode, uint32_t val) if ((env->uncached_cpsr & CPSR_M) == mode) { env->regs[13] = val; } else { - env->banked_r13[bank_number(mode)] = val; + env->banked_r13[bank_number(env, mode)] = val; } } @@ -2134,7 +2210,7 @@ uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode) if ((env->uncached_cpsr & CPSR_M) == mode) { return env->regs[13]; } else { - return env->banked_r13[bank_number(mode)]; + return env->banked_r13[bank_number(env, mode)]; } } diff --git a/target-arm/machine.c b/target-arm/machine.c index aaee9b9c11..89847759b7 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -56,6 +56,9 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_be32(f, env->cp15.c13_tls2); qemu_put_be32(f, env->cp15.c13_tls3); qemu_put_be32(f, env->cp15.c15_cpar); + qemu_put_be32(f, env->cp15.c15_power_control); + qemu_put_be32(f, env->cp15.c15_diagnostic); + qemu_put_be32(f, env->cp15.c15_power_diagnostic); qemu_put_be32(f, env->features); @@ -170,6 +173,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) env->cp15.c13_tls2 = qemu_get_be32(f); env->cp15.c13_tls3 = qemu_get_be32(f); env->cp15.c15_cpar = qemu_get_be32(f); + env->cp15.c15_power_control = qemu_get_be32(f); + env->cp15.c15_diagnostic = qemu_get_be32(f); + env->cp15.c15_power_diagnostic = qemu_get_be32(f); env->features = qemu_get_be32(f);