aboutsummaryrefslogtreecommitdiffstats
path: root/target-arm/helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-arm/helper.c')
-rw-r--r--target-arm/helper.c189
1 files changed, 149 insertions, 40 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 85753e4..a225224 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -91,12 +91,17 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_VFP3);
set_feature(env, ARM_FEATURE_NEON);
+ set_feature(env, ARM_FEATURE_THUMB2EE);
env->vfp.xregs[ARM_VFP_FPSID] = 0x410330c0;
env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222;
env->vfp.xregs[ARM_VFP_MVFR1] = 0x00011100;
memcpy(env->cp15.c0_c1, cortexa8_cp15_c0_c1, 8 * sizeof(uint32_t));
memcpy(env->cp15.c0_c2, cortexa8_cp15_c0_c2, 8 * sizeof(uint32_t));
- env->cp15.c0_cachetype = 0x1dd20d2;
+ env->cp15.c0_cachetype = 0x82048004;
+ env->cp15.c0_clid = (1 << 27) | (2 << 24) | 3;
+ env->cp15.c0_ccsid[0] = 0xe007e01a; /* 16k L1 dcache. */
+ env->cp15.c0_ccsid[1] = 0x2007e01a; /* 16k L1 icache. */
+ env->cp15.c0_ccsid[2] = 0xf0000000; /* No L2 icache. */
break;
case ARM_CPUID_CORTEXM3:
set_feature(env, ARM_FEATURE_V6);
@@ -113,6 +118,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_VFP3);
set_feature(env, ARM_FEATURE_NEON);
+ set_feature(env, ARM_FEATURE_THUMB2EE);
set_feature(env, ARM_FEATURE_DIV);
break;
case ARM_CPUID_TI915T:
@@ -156,6 +162,12 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
void cpu_reset(CPUARMState *env)
{
uint32_t id;
+
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
id = env->cp15.c0_cpuid;
memset(env, 0, offsetof(CPUARMState, breakpoints));
if (id)
@@ -171,11 +183,64 @@ void cpu_reset(CPUARMState *env)
if (IS_M(env))
env->uncached_cpsr &= ~CPSR_I;
env->vfp.xregs[ARM_VFP_FPEXC] = 0;
+ env->cp15.c2_base_mask = 0xffffc000u;
#endif
env->regs[15] = 0;
tlb_flush(env, 1);
}
+static int vfp_gdb_get_reg(CPUState *env, uint8_t *buf, int reg)
+{
+ int nregs;
+
+ /* VFP data registers are always little-endian. */
+ nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16;
+ if (reg < nregs) {
+ stfq_le_p(buf, env->vfp.regs[reg]);
+ return 8;
+ }
+ if (arm_feature(env, ARM_FEATURE_NEON)) {
+ /* Aliases for Q regs. */
+ nregs += 16;
+ if (reg < nregs) {
+ stfq_le_p(buf, env->vfp.regs[(reg - 32) * 2]);
+ stfq_le_p(buf + 8, env->vfp.regs[(reg - 32) * 2 + 1]);
+ return 16;
+ }
+ }
+ switch (reg - nregs) {
+ case 0: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSID]); return 4;
+ case 1: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSCR]); return 4;
+ case 2: stl_p(buf, env->vfp.xregs[ARM_VFP_FPEXC]); return 4;
+ }
+ return 0;
+}
+
+static int vfp_gdb_set_reg(CPUState *env, uint8_t *buf, int reg)
+{
+ int nregs;
+
+ nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16;
+ if (reg < nregs) {
+ env->vfp.regs[reg] = ldfq_le_p(buf);
+ return 8;
+ }
+ if (arm_feature(env, ARM_FEATURE_NEON)) {
+ nregs += 16;
+ if (reg < nregs) {
+ env->vfp.regs[(reg - 32) * 2] = ldfq_le_p(buf);
+ env->vfp.regs[(reg - 32) * 2 + 1] = ldfq_le_p(buf + 8);
+ return 16;
+ }
+ }
+ switch (reg - nregs) {
+ case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); return 4;
+ case 1: env->vfp.xregs[ARM_VFP_FPSCR] = ldl_p(buf); return 4;
+ case 2: env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf); return 4;
+ }
+ return 0;
+}
+
CPUARMState *cpu_arm_init(const char *cpu_model)
{
CPUARMState *env;
@@ -186,8 +251,6 @@ CPUARMState *cpu_arm_init(const char *cpu_model)
if (id == 0)
return NULL;
env = qemu_mallocz(sizeof(CPUARMState));
- if (!env)
- return NULL;
cpu_exec_init(env);
if (!inited) {
inited = 1;
@@ -197,6 +260,17 @@ CPUARMState *cpu_arm_init(const char *cpu_model)
env->cpu_model_str = cpu_model;
env->cp15.c0_cpuid = id;
cpu_reset(env);
+ if (arm_feature(env, ARM_FEATURE_NEON)) {
+ gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg,
+ 51, "arm-neon.xml", 0);
+ } else if (arm_feature(env, ARM_FEATURE_VFP3)) {
+ gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg,
+ 35, "arm-vfp3.xml", 0);
+ } else if (arm_feature(env, ARM_FEATURE_VFP)) {
+ gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg,
+ 19, "arm-vfp.xml", 0);
+ }
+ qemu_init_vcpu(env);
return env;
}
@@ -396,8 +470,6 @@ int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
static void allocate_mmon_state(CPUState *env)
{
env->mmon_entry = malloc(sizeof (mmon_state));
- if (!env->mmon_entry)
- abort();
memset (env->mmon_entry, 0, sizeof (mmon_state));
env->mmon_entry->cpu_env = env;
mmon_head = env->mmon_entry;
@@ -622,7 +694,7 @@ static void do_v7m_exception_exit(CPUARMState *env)
pointer. */
}
-void do_interrupt_v7m(CPUARMState *env)
+static void do_interrupt_v7m(CPUARMState *env)
{
uint32_t xpsr = xpsr_read(env);
uint32_t lr;
@@ -846,17 +918,34 @@ static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
return PAGE_READ | PAGE_WRITE;
case 3:
return PAGE_READ | PAGE_WRITE;
- case 4: case 7: /* Reserved. */
+ case 4: /* Reserved. */
return 0;
case 5:
return is_user ? 0 : prot_ro;
case 6:
return prot_ro;
+ case 7:
+ if (!arm_feature (env, ARM_FEATURE_V7))
+ return 0;
+ return prot_ro;
default:
abort();
}
}
+static uint32_t get_level1_table_address(CPUState *env, uint32_t address)
+{
+ uint32_t table;
+
+ if (address & env->cp15.c2_mask)
+ table = env->cp15.c2_base1 & 0xffffc000;
+ else
+ table = env->cp15.c2_base0 & env->cp15.c2_base_mask;
+
+ table |= (address >> 18) & 0x3ffc;
+ return table;
+}
+
static int get_phys_addr_v5(CPUState *env, uint32_t address, int access_type,
int is_user, uint32_t *phys_ptr, int *prot)
{
@@ -870,11 +959,7 @@ static int get_phys_addr_v5(CPUState *env, uint32_t address, int access_type,
/* Pagetable walk. */
/* Lookup l1 descriptor. */
- if (address & env->cp15.c2_mask)
- table = env->cp15.c2_base1;
- else
- table = env->cp15.c2_base0;
- table = (table & 0xffffc000) | ((address >> 18) & 0x3ffc);
+ table = get_level1_table_address(env, address);
desc = ldl_phys(table);
type = (desc & 3);
domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
@@ -962,11 +1047,7 @@ static int get_phys_addr_v6(CPUState *env, uint32_t address, int access_type,
/* Pagetable walk. */
/* Lookup l1 descriptor. */
- if (address & env->cp15.c2_mask)
- table = env->cp15.c2_base1;
- else
- table = env->cp15.c2_base0;
- table = (table & 0xffffc000) | ((address >> 18) & 0x3ffc);
+ table = get_level1_table_address(env, address);
desc = ldl_phys(table);
type = (desc & 3);
if (type == 0) {
@@ -1026,6 +1107,12 @@ static int get_phys_addr_v6(CPUState *env, uint32_t address, int access_type,
if (xn && access_type == 2)
goto do_fault;
+ /* The simplified model uses AP[0] as an access control bit. */
+ if ((env->cp15.c1_sys & (1 << 29)) && (ap & 1) == 0) {
+ /* Access flag fault. */
+ code = (code == 15) ? 6 : 3;
+ goto do_fault;
+ }
*prot = check_ap(env, ap, domain, access_type, is_user);
if (!*prot) {
/* Access permission fault. */
@@ -1250,15 +1337,16 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
crm = insn & 0xf;
switch ((insn >> 16) & 0xf) {
case 0:
- if (((insn >> 21) & 7) == 2) {
- /* ??? Select cache level. Ignore. */
- return;
- }
/* ID codes. */
if (arm_feature(env, ARM_FEATURE_XSCALE))
break;
if (arm_feature(env, ARM_FEATURE_OMAPCP))
break;
+ if (arm_feature(env, ARM_FEATURE_V7)
+ && op1 == 2 && crm == 0 && op2 == 0) {
+ env->cp15.c0_cssel = val & 0xf;
+ break;
+ }
goto bad_reg;
case 1: /* System configuration. */
if (arm_feature(env, ARM_FEATURE_OMAPCP))
@@ -1281,9 +1369,11 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 2:
if (arm_feature(env, ARM_FEATURE_XSCALE))
goto bad_reg;
- env->cp15.c1_coproc = val;
- /* ??? Is this safe when called from within a TB? */
- tb_flush(env);
+ if (env->cp15.c1_coproc != val) {
+ env->cp15.c1_coproc = val;
+ /* ??? Is this safe when called from within a TB? */
+ tb_flush(env);
+ }
break;
default:
goto bad_reg;
@@ -1310,7 +1400,10 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
env->cp15.c2_base1 = val;
break;
case 2:
+ val &= 7;
+ env->cp15.c2_control = val;
env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> val);
+ env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> val);
break;
default:
goto bad_reg;
@@ -1572,9 +1665,22 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
goto bad_reg;
if (crm != 0)
goto bad_reg;
- if (arm_feature(env, ARM_FEATURE_XSCALE))
+ if (!arm_feature(env, ARM_FEATURE_V7))
+ return 0;
+
+ switch (op2) {
+ case 0:
+ return env->cp15.c0_ccsid[env->cp15.c0_cssel];
+ case 1:
+ return env->cp15.c0_clid;
+ case 7:
+ return 0;
+ }
+ goto bad_reg;
+ case 2:
+ if (op2 != 0 || crm != 0)
goto bad_reg;
- return 0;
+ return env->cp15.c0_cssel;
default:
goto bad_reg;
}
@@ -1598,7 +1704,7 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
case ARM_CPUID_ARM11MPCORE:
return 1;
case ARM_CPUID_CORTEXA8:
- return 0;
+ return 2;
default:
goto bad_reg;
}
@@ -1628,17 +1734,7 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
case 1:
return env->cp15.c2_base1;
case 2:
- {
- int n;
- uint32_t mask;
- n = 0;
- mask = env->cp15.c2_mask;
- while (mask) {
- n++;
- mask <<= 1;
- }
- return n;
- }
+ return env->cp15.c2_control;
default:
goto bad_reg;
}
@@ -2250,10 +2346,13 @@ void HELPER(vfp_set_fpscr)(CPUState *env, uint32_t val)
}
set_float_rounding_mode(i, &env->vfp.fp_status);
}
+ if (changed & (1 << 24))
+ set_flush_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status);
+ if (changed & (1 << 25))
+ set_default_nan_mode((val & (1 << 25)) != 0, &env->vfp.fp_status);
i = vfp_exceptbits_to_host((val >> 8) & 0x1f);
set_float_exception_flags(i, &env->vfp.fp_status);
- /* XXX: FZ and DN are not implemented. */
}
#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p))
@@ -2458,7 +2557,7 @@ ftype VFP_HELPER(name##to, p)(ftype x, uint32_t shift, CPUState *env) \
ftype tmp; \
tmp = sign##int32_to_##ftype ((itype)vfp_##p##toi(x), \
&env->vfp.fp_status); \
- return ftype##_scalbn(tmp, shift, &env->vfp.fp_status); \
+ return ftype##_scalbn(tmp, -(int)shift, &env->vfp.fp_status); \
} \
ftype VFP_HELPER(to##name, p)(ftype x, uint32_t shift, CPUState *env) \
{ \
@@ -2560,3 +2659,13 @@ void HELPER(traceBB64)(uint64_t bb_num, uint64_t tb)
#endif
#endif /* CONFIG_TRACE */
+
+void HELPER(set_teecr)(CPUState *env, uint32_t val)
+{
+ val &= 1;
+ if (env->teecr != val) {
+ env->teecr = val;
+ tb_flush(env);
+ }
+}
+