diff options
author | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-11-11 00:04:49 +0000 |
---|---|---|
committer | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-11-11 00:04:49 +0000 |
commit | 9ee6e8bb853bdea7ef6c645a1a07aa55fd206aba (patch) | |
tree | 1cd430d3d9ac641c8550cfd8956dbcce1a4b9121 /target-arm/helper.c | |
parent | ee4e83ed8ddc8dac572a0123398adf78b63014ae (diff) | |
download | qemu-9ee6e8bb853bdea7ef6c645a1a07aa55fd206aba.zip qemu-9ee6e8bb853bdea7ef6c645a1a07aa55fd206aba.tar.gz qemu-9ee6e8bb853bdea7ef6c645a1a07aa55fd206aba.tar.bz2 |
ARMv7 support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3572 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-arm/helper.c')
-rw-r--r-- | target-arm/helper.c | 1104 |
1 files changed, 923 insertions, 181 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c index 06eac66..6975256 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -4,6 +4,25 @@ #include "cpu.h" #include "exec-all.h" +#include "gdbstub.h" + +static uint32_t cortexa8_cp15_c0_c1[8] = +{ 0x1031, 0x11, 0x400, 0, 0x31100003, 0x20000000, 0x01202000, 0x11 }; + +static uint32_t cortexa8_cp15_c0_c2[8] = +{ 0x00101111, 0x12112111, 0x21232031, 0x11112131, 0x00111142, 0, 0, 0 }; + +static uint32_t mpcore_cp15_c0_c1[8] = +{ 0x111, 0x1, 0, 0x2, 0x01100103, 0x10020302, 0x01222000, 0 }; + +static uint32_t mpcore_cp15_c0_c2[8] = +{ 0x00100011, 0x12002111, 0x11221011, 0x01102131, 0x141, 0, 0, 0 }; + +static uint32_t arm1136_cp15_c0_c1[8] = +{ 0x111, 0x1, 0x2, 0x3, 0x01130003, 0x10030302, 0x01222110, 0 }; + +static uint32_t arm1136_cp15_c0_c2[8] = +{ 0x00140011, 0x12002111, 0x11231111, 0x01102131, 0x141, 0, 0, 0 }; static uint32_t cpu_arm_find_by_name(const char *name); @@ -34,6 +53,62 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id) env->cp15.c0_cachetype = 0x1dd20d2; env->cp15.c1_sys = 0x00090078; break; + case ARM_CPUID_ARM1136: + set_feature(env, ARM_FEATURE_V6); + set_feature(env, ARM_FEATURE_VFP); + set_feature(env, ARM_FEATURE_AUXCR); + env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4; + env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111; + env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000; + memcpy(env->cp15.c0_c1, arm1136_cp15_c0_c1, 8 * sizeof(uint32_t)); + memcpy(env->cp15.c0_c1, arm1136_cp15_c0_c2, 8 * sizeof(uint32_t)); + env->cp15.c0_cachetype = 0x1dd20d2; + break; + case ARM_CPUID_ARM11MPCORE: + set_feature(env, ARM_FEATURE_V6); + set_feature(env, ARM_FEATURE_V6K); + set_feature(env, ARM_FEATURE_VFP); + set_feature(env, ARM_FEATURE_AUXCR); + env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4; + env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111; + env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000; + memcpy(env->cp15.c0_c1, mpcore_cp15_c0_c1, 8 * sizeof(uint32_t)); + memcpy(env->cp15.c0_c1, mpcore_cp15_c0_c2, 8 * sizeof(uint32_t)); + env->cp15.c0_cachetype = 0x1dd20d2; + break; + case ARM_CPUID_CORTEXA8: + set_feature(env, ARM_FEATURE_V6); + set_feature(env, ARM_FEATURE_V6K); + set_feature(env, ARM_FEATURE_V7); + set_feature(env, ARM_FEATURE_AUXCR); + set_feature(env, ARM_FEATURE_THUMB2); + set_feature(env, ARM_FEATURE_VFP); + set_feature(env, ARM_FEATURE_VFP3); + set_feature(env, ARM_FEATURE_NEON); + 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_c1, cortexa8_cp15_c0_c2, 8 * sizeof(uint32_t)); + env->cp15.c0_cachetype = 0x1dd20d2; + break; + case ARM_CPUID_CORTEXM3: + set_feature(env, ARM_FEATURE_V6); + set_feature(env, ARM_FEATURE_THUMB2); + set_feature(env, ARM_FEATURE_V7); + set_feature(env, ARM_FEATURE_M); + set_feature(env, ARM_FEATURE_DIV); + break; + case ARM_CPUID_ANY: /* For userspace emulation. */ + set_feature(env, ARM_FEATURE_V6); + set_feature(env, ARM_FEATURE_V6K); + set_feature(env, ARM_FEATURE_V7); + set_feature(env, ARM_FEATURE_THUMB2); + set_feature(env, ARM_FEATURE_VFP); + set_feature(env, ARM_FEATURE_VFP3); + set_feature(env, ARM_FEATURE_NEON); + set_feature(env, ARM_FEATURE_DIV); + break; case ARM_CPUID_TI915T: case ARM_CPUID_TI925T: set_feature(env, ARM_FEATURE_OMAPCP); @@ -85,6 +160,10 @@ void cpu_reset(CPUARMState *env) #else /* SVC mode with interrupts disabled. */ env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; + /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is + clear at reset. */ + if (IS_M(env)) + env->uncached_cpsr &= ~CPSR_I; env->vfp.xregs[ARM_VFP_FPEXC] = 0; #endif env->regs[15] = 0; @@ -117,6 +196,10 @@ static const struct arm_cpu_t arm_cpu_names[] = { { ARM_CPUID_ARM926, "arm926"}, { ARM_CPUID_ARM946, "arm946"}, { ARM_CPUID_ARM1026, "arm1026"}, + { ARM_CPUID_ARM1136, "arm1136"}, + { ARM_CPUID_ARM11MPCORE, "arm11mpcore"}, + { ARM_CPUID_CORTEXM3, "cortex-m3"}, + { ARM_CPUID_CORTEXA8, "cortex-a8"}, { ARM_CPUID_TI925T, "ti925t" }, { ARM_CPUID_PXA250, "pxa250" }, { ARM_CPUID_PXA255, "pxa255" }, @@ -130,6 +213,7 @@ static const struct arm_cpu_t arm_cpu_names[] = { { ARM_CPUID_PXA270_B1, "pxa270-b1" }, { ARM_CPUID_PXA270_C0, "pxa270-c0" }, { ARM_CPUID_PXA270_C5, "pxa270-c5" }, + { ARM_CPUID_ANY, "any"}, { 0, NULL} }; @@ -164,6 +248,30 @@ void cpu_arm_close(CPUARMState *env) free(env); } +/* Polynomial multiplication is like integer multiplcation except the + partial products are XORed, not added. */ +uint32_t helper_neon_mul_p8(uint32_t op1, uint32_t op2) +{ + uint32_t mask; + uint32_t result; + result = 0; + while (op1) { + mask = 0; + if (op1 & 1) + mask |= 0xff; + if (op1 & (1 << 8)) + mask |= (0xff << 8); + if (op1 & (1 << 16)) + mask |= (0xff << 16); + if (op1 & (1 << 24)) + mask |= (0xff << 24); + result ^= op2 & mask; + op1 = (op1 >> 1) & 0x7f7f7f7f; + op2 = (op2 << 1) & 0xfefefefe; + } + return result; +} + #if defined(CONFIG_USER_ONLY) void do_interrupt (CPUState *env) @@ -171,6 +279,16 @@ void do_interrupt (CPUState *env) env->exception_index = -1; } +/* Structure used to record exclusive memory locations. */ +typedef struct mmon_state { + struct mmon_state *next; + CPUARMState *cpu_env; + uint32_t addr; +} mmon_state; + +/* Chain of current locks. */ +static mmon_state* mmon_head = NULL; + int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu) { @@ -184,6 +302,64 @@ int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw, return 1; } +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; +} + +/* Flush any monitor locks for the specified address. */ +static void flush_mmon(uint32_t addr) +{ + mmon_state *mon; + + for (mon = mmon_head; mon; mon = mon->next) + { + if (mon->addr != addr) + continue; + + mon->addr = 0; + break; + } +} + +/* Mark an address for exclusive access. */ +void helper_mark_exclusive(CPUState *env, uint32_t addr) +{ + if (!env->mmon_entry) + allocate_mmon_state(env); + /* Clear any previous locks. */ + flush_mmon(addr); + env->mmon_entry->addr = addr; +} + +/* Test if an exclusive address is still exclusive. Returns zero + if the address is still exclusive. */ +int helper_test_exclusive(CPUState *env, uint32_t addr) +{ + int res; + + if (!env->mmon_entry) + return 1; + if (env->mmon_entry->addr == addr) + res = 0; + else + res = 1; + flush_mmon(addr); + return res; +} + +void helper_clrex(CPUState *env) +{ + if (!(env->mmon_entry && env->mmon_entry->addr)) + return; + flush_mmon(env->mmon_entry->addr); +} + target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { return addr; @@ -215,12 +391,35 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn) return 0; } +/* These should probably raise undefined insn exceptions. */ +void helper_v7m_msr(CPUState *env, int reg, uint32_t val) +{ + cpu_abort(env, "v7m_mrs %d\n", reg); +} + +uint32_t helper_v7m_mrs(CPUState *env, int reg) +{ + cpu_abort(env, "v7m_mrs %d\n", reg); + return 0; +} + void switch_mode(CPUState *env, int mode) { if (mode != ARM_CPU_MODE_USR) cpu_abort(env, "Tried to switch out of user mode\n"); } +void helper_set_r13_banked(CPUState *env, int mode, uint32_t val) +{ + cpu_abort(env, "banked r13 write\n"); +} + +uint32_t helper_get_r13_banked(CPUState *env, int mode) +{ + cpu_abort(env, "banked r13 read\n"); + return 0; +} + #else extern int semihosting_enabled; @@ -275,6 +474,129 @@ void switch_mode(CPUState *env, int mode) env->spsr = env->banked_spsr[i]; } +static void v7m_push(CPUARMState *env, uint32_t val) +{ + env->regs[13] -= 4; + stl_phys(env->regs[13], val); +} + +static uint32_t v7m_pop(CPUARMState *env) +{ + uint32_t val; + val = ldl_phys(env->regs[13]); + env->regs[13] += 4; + return val; +} + +/* Switch to V7M main or process stack pointer. */ +static void switch_v7m_sp(CPUARMState *env, int process) +{ + uint32_t tmp; + if (env->v7m.current_sp != process) { + tmp = env->v7m.other_sp; + env->v7m.other_sp = env->regs[13]; + env->regs[13] = tmp; + env->v7m.current_sp = process; + } +} + +static void do_v7m_exception_exit(CPUARMState *env) +{ + uint32_t type; + uint32_t xpsr; + + type = env->regs[15]; + if (env->v7m.exception != 0) + armv7m_nvic_complete_irq(env->v7m.nvic, env->v7m.exception); + + /* Switch to the target stack. */ + switch_v7m_sp(env, (type & 4) != 0); + /* Pop registers. */ + env->regs[0] = v7m_pop(env); + env->regs[1] = v7m_pop(env); + env->regs[2] = v7m_pop(env); + env->regs[3] = v7m_pop(env); + env->regs[12] = v7m_pop(env); + env->regs[14] = v7m_pop(env); + env->regs[15] = v7m_pop(env); + xpsr = v7m_pop(env); + xpsr_write(env, xpsr, 0xfffffdff); + /* Undo stack alignment. */ + if (xpsr & 0x200) + env->regs[13] |= 4; + /* ??? The exception return type specifies Thread/Handler mode. However + this is also implied by the xPSR value. Not sure what to do + if there is a mismatch. */ + /* ??? Likewise for mismatches between the CONTROL register and the stack + pointer. */ +} + +void do_interrupt_v7m(CPUARMState *env) +{ + uint32_t xpsr = xpsr_read(env); + uint32_t lr; + uint32_t addr; + + lr = 0xfffffff1; + if (env->v7m.current_sp) + lr |= 4; + if (env->v7m.exception == 0) + lr |= 8; + + /* For exceptions we just mark as pending on the NVIC, and let that + handle it. */ + /* TODO: Need to escalate if the current priority is higher than the + one we're raising. */ + switch (env->exception_index) { + case EXCP_UDEF: + armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_USAGE); + return; + case EXCP_SWI: + env->regs[15] += 2; + armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_SVC); + return; + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_MEM); + return; + case EXCP_BKPT: + armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_DEBUG); + return; + case EXCP_IRQ: + env->v7m.exception = armv7m_nvic_acknowledge_irq(env->v7m.nvic); + break; + case EXCP_EXCEPTION_EXIT: + do_v7m_exception_exit(env); + return; + default: + cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index); + return; /* Never happens. Keep compiler happy. */ + } + + /* Align stack pointer. */ + /* ??? Should only do this if Configuration Control Register + STACKALIGN bit is set. */ + if (env->regs[13] & 4) { + env->regs[13] += 4; + xpsr |= 0x200; + } + /* Switch to the hander mode. */ + v7m_push(env, xpsr); + v7m_push(env, env->regs[15]); + v7m_push(env, env->regs[14]); + v7m_push(env, env->regs[12]); + v7m_push(env, env->regs[3]); + v7m_push(env, env->regs[2]); + v7m_push(env, env->regs[1]); + v7m_push(env, env->regs[0]); + switch_v7m_sp(env, 0); + env->uncached_cpsr &= ~CPSR_IT; + env->regs[14] = lr; + addr = ldl_phys(env->v7m.vecbase + env->v7m.exception * 4); + env->regs[15] = addr & 0xfffffffe; + env->thumb = addr & 1; +} + /* Handle a CPU exception. */ void do_interrupt(CPUARMState *env) { @@ -283,6 +605,10 @@ void do_interrupt(CPUARMState *env) int new_mode; uint32_t offset; + if (IS_M(env)) { + do_interrupt_v7m(env); + return; + } /* TODO: Vectored interrupt controller. */ switch (env->exception_index) { case EXCP_UDEF: @@ -317,8 +643,19 @@ void do_interrupt(CPUARMState *env) /* The PC already points to the next instructon. */ offset = 0; break; - case EXCP_PREFETCH_ABORT: case EXCP_BKPT: + /* See if this is a semihosting syscall. */ + if (env->thumb) { + mask = lduw_code(env->regs[15]) & 0xff; + if (mask == 0xab + && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { + env->regs[15] += 2; + env->regs[0] = do_arm_semihosting(env); + return; + } + } + /* Fall through to prefetch abort. */ + case EXCP_PREFETCH_ABORT: new_mode = ARM_CPU_MODE_ABT; addr = 0x0c; mask = CPSR_A | CPSR_I; @@ -354,6 +691,8 @@ void do_interrupt(CPUARMState *env) } switch_mode (env, new_mode); env->spsr = cpsr_read(env); + /* Clear IT bits. */ + env->condexec_bits = 0; /* Switch to the new mode, and switch to Arm mode. */ /* ??? Thumb interrupt handlers not implemented. */ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; @@ -370,9 +709,16 @@ void do_interrupt(CPUARMState *env) static inline int check_ap(CPUState *env, int ap, int domain, int access_type, int is_user) { + int prot_ro; + if (domain == 3) return PAGE_READ | PAGE_WRITE; + if (access_type == 1) + prot_ro = 0; + else + prot_ro = PAGE_READ; + switch (ap) { case 0: if (access_type == 1) @@ -389,18 +735,24 @@ static inline int check_ap(CPUState *env, int ap, int domain, int access_type, return is_user ? 0 : PAGE_READ | PAGE_WRITE; case 2: if (is_user) - return (access_type == 1) ? 0 : PAGE_READ; + return prot_ro; else return PAGE_READ | PAGE_WRITE; case 3: return PAGE_READ | PAGE_WRITE; + case 4: case 7: /* Reserved. */ + return 0; + case 5: + return is_user ? 0 : prot_ro; + case 6: + return prot_ro; default: abort(); } } -static int get_phys_addr(CPUState *env, uint32_t address, int access_type, - int is_user, uint32_t *phys_ptr, int *prot) +static int get_phys_addr_v5(CPUState *env, uint32_t address, int access_type, + int is_user, uint32_t *phys_ptr, int *prot) { int code; uint32_t table; @@ -410,145 +762,259 @@ static int get_phys_addr(CPUState *env, uint32_t address, int access_type, int domain; uint32_t phys_addr; - /* Fast Context Switch Extension. */ - if (address < 0x02000000) - address += env->cp15.c13_fcse; - - if ((env->cp15.c1_sys & 1) == 0) { - /* MMU/MPU disabled. */ - *phys_ptr = address; - *prot = PAGE_READ | PAGE_WRITE; - } else if (arm_feature(env, ARM_FEATURE_MPU)) { - int n; - uint32_t mask; - uint32_t base; - - *phys_ptr = address; - for (n = 7; n >= 0; n--) { - base = env->cp15.c6_region[n]; - if ((base & 1) == 0) - continue; - mask = 1 << ((base >> 1) & 0x1f); - /* Keep this shift separate from the above to avoid an - (undefined) << 32. */ - mask = (mask << 1) - 1; - if (((base ^ address) & ~mask) == 0) - break; - } - if (n < 0) - return 2; - - if (access_type == 2) { - mask = env->cp15.c5_insn; - } else { - mask = env->cp15.c5_data; - } - mask = (mask >> (n * 4)) & 0xf; - switch (mask) { - case 0: - return 1; - case 1: - if (is_user) - return 1; - *prot = PAGE_READ | PAGE_WRITE; - break; - case 2: - *prot = PAGE_READ; - if (!is_user) - *prot |= PAGE_WRITE; + /* 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); + desc = ldl_phys(table); + type = (desc & 3); + domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3; + if (type == 0) { + /* Secton translation fault. */ + code = 5; + goto do_fault; + } + if (domain == 0 || domain == 2) { + if (type == 2) + code = 9; /* Section domain fault. */ + else + code = 11; /* Page domain fault. */ + goto do_fault; + } + if (type == 2) { + /* 1Mb section. */ + phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); + ap = (desc >> 10) & 3; + code = 13; + } else { + /* Lookup l2 entry. */ + if (type == 1) { + /* Coarse pagetable. */ + table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); + } else { + /* Fine pagetable. */ + table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); + } + desc = ldl_phys(table); + switch (desc & 3) { + case 0: /* Page translation fault. */ + code = 7; + goto do_fault; + case 1: /* 64k page. */ + phys_addr = (desc & 0xffff0000) | (address & 0xffff); + ap = (desc >> (4 + ((address >> 13) & 6))) & 3; break; - case 3: - *prot = PAGE_READ | PAGE_WRITE; + case 2: /* 4k page. */ + phys_addr = (desc & 0xfffff000) | (address & 0xfff); + ap = (desc >> (4 + ((address >> 13) & 6))) & 3; break; - case 5: - if (is_user) - return 1; - *prot = PAGE_READ; - break; - case 6: - *prot = PAGE_READ; + case 3: /* 1k page. */ + if (type == 1) { + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + phys_addr = (desc & 0xfffff000) | (address & 0xfff); + } else { + /* Page translation fault. */ + code = 7; + goto do_fault; + } + } else { + phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); + } + ap = (desc >> 4) & 3; break; default: - /* Bad permission. */ - return 1; + /* Never happens, but compiler isn't smart enough to tell. */ + abort(); } + code = 15; + } + *prot = check_ap(env, ap, domain, access_type, is_user); + if (!*prot) { + /* Access permission fault. */ + goto do_fault; + } + *phys_ptr = phys_addr; + return 0; +do_fault: + return code | (domain << 4); +} + +static int get_phys_addr_v6(CPUState *env, uint32_t address, int access_type, + int is_user, uint32_t *phys_ptr, int *prot) +{ + int code; + uint32_t table; + uint32_t desc; + uint32_t xn; + int type; + int ap; + int domain; + uint32_t phys_addr; + + /* 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); + desc = ldl_phys(table); + type = (desc & 3); + if (type == 0) { + /* Secton translation fault. */ + code = 5; + domain = 0; + goto do_fault; + } else if (type == 2 && (desc & (1 << 18))) { + /* Supersection. */ + domain = 0; } else { - /* Pagetable walk. */ - /* Lookup l1 descriptor. */ - table = (env->cp15.c2_base & 0xffffc000) | ((address >> 18) & 0x3ffc); - desc = ldl_phys(table); - type = (desc & 3); - domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3; - if (type == 0) { - /* Secton translation fault. */ - code = 5; - goto do_fault; - } - if (domain == 0 || domain == 2) { - if (type == 2) - code = 9; /* Section domain fault. */ - else - code = 11; /* Page domain fault. */ - goto do_fault; - } - if (type == 2) { - /* 1Mb section. */ - phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); - ap = (desc >> 10) & 3; - code = 13; + /* Section or page. */ + domain = (desc >> 4) & 0x1e; + } + domain = (env->cp15.c3 >> domain) & 3; + if (domain == 0 || domain == 2) { + if (type == 2) + code = 9; /* Section domain fault. */ + else + code = 11; /* Page domain fault. */ + goto do_fault; + } + if (type == 2) { + if (desc & (1 << 18)) { + /* Supersection. */ + phys_addr = (desc & 0xff000000) | (address & 0x00ffffff); } else { - /* Lookup l2 entry. */ - if (type == 1) { - /* Coarse pagetable. */ - table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); - } else { - /* Fine pagetable. */ - table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); - } - desc = ldl_phys(table); - switch (desc & 3) { - case 0: /* Page translation fault. */ - code = 7; - goto do_fault; - case 1: /* 64k page. */ - phys_addr = (desc & 0xffff0000) | (address & 0xffff); - ap = (desc >> (4 + ((address >> 13) & 6))) & 3; - break; - case 2: /* 4k page. */ - phys_addr = (desc & 0xfffff000) | (address & 0xfff); - ap = (desc >> (4 + ((address >> 13) & 6))) & 3; - break; - case 3: /* 1k page. */ - if (type == 1) { - if (arm_feature(env, ARM_FEATURE_XSCALE)) - phys_addr = (desc & 0xfffff000) | (address & 0xfff); - else { - /* Page translation fault. */ - code = 7; - goto do_fault; - } - } else - phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); - ap = (desc >> 4) & 3; - break; - default: - /* Never happens, but compiler isn't smart enough to tell. */ - abort(); - } - code = 15; + /* Section. */ + phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); } - *prot = check_ap(env, ap, domain, access_type, is_user); - if (!*prot) { - /* Access permission fault. */ + ap = ((desc >> 10) & 3) | ((desc >> 13) & 4); + xn = desc & (1 << 4); + code = 13; + } else { + /* Lookup l2 entry. */ + table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); + desc = ldl_phys(table); + ap = ((desc >> 4) & 3) | ((desc >> 7) & 4); + switch (desc & 3) { + case 0: /* Page translation fault. */ + code = 7; goto do_fault; + case 1: /* 64k page. */ + phys_addr = (desc & 0xffff0000) | (address & 0xffff); + xn = desc & (1 << 15); + break; + case 2: case 3: /* 4k page. */ + phys_addr = (desc & 0xfffff000) | (address & 0xfff); + xn = desc & 1; + break; + default: + /* Never happens, but compiler isn't smart enough to tell. */ + abort(); } - *phys_ptr = phys_addr; + code = 15; + } + if (xn && access_type == 2) + goto do_fault; + + *prot = check_ap(env, ap, domain, access_type, is_user); + if (!*prot) { + /* Access permission fault. */ + goto do_fault; } + *phys_ptr = phys_addr; return 0; do_fault: return code | (domain << 4); } +static int get_phys_addr_mpu(CPUState *env, uint32_t address, int access_type, + int is_user, uint32_t *phys_ptr, int *prot) +{ + int n; + uint32_t mask; + uint32_t base; + + *phys_ptr = address; + for (n = 7; n >= 0; n--) { + base = env->cp15.c6_region[n]; + if ((base & 1) == 0) + continue; + mask = 1 << ((base >> 1) & 0x1f); + /* Keep this shift separate from the above to avoid an + (undefined) << 32. */ + mask = (mask << 1) - 1; + if (((base ^ address) & ~mask) == 0) + break; + } + if (n < 0) + return 2; + + if (access_type == 2) { + mask = env->cp15.c5_insn; + } else { + mask = env->cp15.c5_data; + } + mask = (mask >> (n * 4)) & 0xf; + switch (mask) { + case 0: + return 1; + case 1: + if (is_user) + return 1; + *prot = PAGE_READ | PAGE_WRITE; + break; + case 2: + *prot = PAGE_READ; + if (!is_user) + *prot |= PAGE_WRITE; + break; + case 3: + *prot = PAGE_READ | PAGE_WRITE; + break; + case 5: + if (is_user) + return 1; + *prot = PAGE_READ; + break; + case 6: + *prot = PAGE_READ; + break; + default: + /* Bad permission. */ + return 1; + } + return 0; +} + +static inline int get_phys_addr(CPUState *env, uint32_t address, + int access_type, int is_user, + uint32_t *phys_ptr, int *prot) +{ + /* Fast Context Switch Extension. */ + if (address < 0x02000000) + address += env->cp15.c13_fcse; + + if ((env->cp15.c1_sys & 1) == 0) { + /* MMU/MPU disabled. */ + *phys_ptr = address; + *prot = PAGE_READ | PAGE_WRITE; + return 0; + } else if (arm_feature(env, ARM_FEATURE_MPU)) { + return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr, + prot); + } else if (env->cp15.c1_sys & (1 << 23)) { + return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr, + prot); + } else { + return get_phys_addr_v5(env, address, access_type, is_user, phys_ptr, + prot); + } +} + int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int access_type, int mmu_idx, int is_softmmu) { @@ -572,6 +1038,8 @@ int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, env->exception_index = EXCP_PREFETCH_ABORT; } else { env->cp15.c5_data = ret; + if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6)) + env->cp15.c5_data |= (1 << 11); env->cp15.c6_data = address; env->exception_index = EXCP_DATA_ABORT; } @@ -592,6 +1060,24 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) return phys_addr; } +/* Not really implemented. Need to figure out a sane way of doing this. + Maybe add generic watchpoint support and use that. */ + +void helper_mark_exclusive(CPUState *env, uint32_t addr) +{ + env->mmon_addr = addr; +} + +int helper_test_exclusive(CPUState *env, uint32_t addr) +{ + return (env->mmon_addr != addr); +} + +void helper_clrex(CPUState *env) +{ + env->mmon_addr = -1; +} + void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val) { int cp_num = (insn >> 8) & 0xf; @@ -649,13 +1135,20 @@ static uint32_t extended_mpu_ap_bits(uint32_t val) void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) { - uint32_t op2; - uint32_t crm; + int op1; + int op2; + int crm; + op1 = (insn >> 21) & 7; op2 = (insn >> 5) & 7; crm = insn & 0xf; switch ((insn >> 16) & 0xf) { - case 0: /* ID codes. */ + 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)) @@ -672,12 +1165,13 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) /* This may enable/disable the MMU, so do a TLB flush. */ tlb_flush(env, 1); break; - case 1: + case 1: /* Auxiliary cotrol register. */ if (arm_feature(env, ARM_FEATURE_XSCALE)) { env->cp15.c1_xscaleauxcr = val; break; } - goto bad_reg; + /* Not implemented. */ + break; case 2: if (arm_feature(env, ARM_FEATURE_XSCALE)) goto bad_reg; @@ -702,7 +1196,19 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) goto bad_reg; } } else { - env->cp15.c2_base = val; + switch (op2) { + case 0: + env->cp15.c2_base0 = val; + break; + case 1: + env->cp15.c2_base1 = val; + break; + case 2: + env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> val); + break; + default: + goto bad_reg; + } } break; case 3: /* MMU Domain access control / MPU write buffer control. */ @@ -751,7 +1257,8 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) case 0: env->cp15.c6_data = val; break; - case 1: + case 1: /* ??? This is WFAR on armv6 */ + case 2: env->cp15.c6_insn = val; break; default: @@ -763,6 +1270,7 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) env->cp15.c15_i_max = 0x000; env->cp15.c15_i_min = 0xff0; /* No cache, so nothing to do. */ + /* ??? MPCore has VA to PA translation functions. */ break; case 8: /* MMU TLB control. */ switch (op2) { @@ -783,6 +1291,13 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) tlb_flush(env, 1); #endif break; + case 2: /* Invalidate on ASID. */ + tlb_flush(env, val == 0); + break; + case 3: /* Invalidate single entry on MVA. */ + /* ??? This is like case 1, but ignores ASID. */ + tlb_flush(env, 1); + break; default: goto bad_reg; } @@ -792,17 +1307,26 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) break; switch (crm) { case 0: /* Cache lockdown. */ - switch (op2) { - case 0: - env->cp15.c9_data = val; - break; - case 1: - env->cp15.c9_insn = val; - break; - default: - goto bad_reg; - } - break; + switch (op1) { + case 0: /* L1 cache. */ + switch (op2) { + case 0: + env->cp15.c9_data = val; + break; + case 1: + env->cp15.c9_insn = val; + break; + default: + goto bad_reg; + } + break; + case 1: /* L2 cache. */ + /* Ignore writes to L2 lockdown/auxiliary registers. */ + break; + default: + goto bad_reg; + } + break; case 1: /* TCM memory region registers. */ /* Not implemented. */ goto bad_reg; @@ -832,6 +1356,15 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) tlb_flush(env, 0); env->cp15.c13_context = val; break; + case 2: + env->cp15.c13_tls1 = val; + break; + case 3: + env->cp15.c13_tls2 = val; + break; + case 4: + env->cp15.c13_tls3 = val; + break; default: goto bad_reg; } @@ -880,27 +1413,64 @@ void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) return; bad_reg: /* ??? For debugging only. Should raise illegal instruction exception. */ - cpu_abort(env, "Unimplemented cp15 register write\n"); + cpu_abort(env, "Unimplemented cp15 register write (c%d, c%d, {%d, %d})\n", + (insn >> 16) & 0xf, crm, op1, op2); } uint32_t helper_get_cp15(CPUState *env, uint32_t insn) { - uint32_t op2; - uint32_t crm; + int op1; + int op2; + int crm; + op1 = (insn >> 21) & 7; op2 = (insn >> 5) & 7; crm = insn & 0xf; switch ((insn >> 16) & 0xf) { case 0: /* ID codes. */ - switch (op2) { - default: /* Device ID. */ - return env->cp15.c0_cpuid; - case 1: /* Cache Type. */ - return env->cp15.c0_cachetype; - case 2: /* TCM status. */ + switch (op1) { + case 0: + switch (crm) { + case 0: + switch (op2) { + case 0: /* Device ID. */ + return env->cp15.c0_cpuid; + case 1: /* Cache Type. */ + return env->cp15.c0_cachetype; + case 2: /* TCM status. */ + return 0; + case 3: /* TLB type register. */ + return 0; /* No lockable TLB entries. */ + case 5: /* CPU ID */ + return env->cpu_index; + default: + goto bad_reg; + } + case 1: + if (!arm_feature(env, ARM_FEATURE_V6)) + goto bad_reg; + return env->cp15.c0_c1[op2]; + case 2: + if (!arm_feature(env, ARM_FEATURE_V6)) + goto bad_reg; + return env->cp15.c0_c2[op2]; + case 3: case 4: case 5: case 6: case 7: + return 0; + default: + goto bad_reg; + } + case 1: + /* These registers aren't documented on arm11 cores. However + Linux looks at them anyway. */ + if (!arm_feature(env, ARM_FEATURE_V6)) + goto bad_reg; + if (crm != 0) + goto bad_reg; if (arm_feature(env, ARM_FEATURE_XSCALE)) goto bad_reg; return 0; + default: + goto bad_reg; } case 1: /* System configuration. */ if (arm_feature(env, ARM_FEATURE_OMAPCP)) @@ -909,11 +1479,22 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn) case 0: /* Control register. */ return env->cp15.c1_sys; case 1: /* Auxiliary control register. */ - if (arm_feature(env, ARM_FEATURE_AUXCR)) - return 1; if (arm_feature(env, ARM_FEATURE_XSCALE)) return env->cp15.c1_xscaleauxcr; - goto bad_reg; + if (!arm_feature(env, ARM_FEATURE_AUXCR)) + goto bad_reg; + switch (ARM_CPUID(env)) { + case ARM_CPUID_ARM1026: + return 1; + case ARM_CPUID_ARM1136: + return 7; + case ARM_CPUID_ARM11MPCORE: + return 1; + case ARM_CPUID_CORTEXA8: + return 0; + default: + goto bad_reg; + } case 2: /* Coprocessor access register. */ if (arm_feature(env, ARM_FEATURE_XSCALE)) goto bad_reg; @@ -934,8 +1515,27 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn) goto bad_reg; } } else { - return env->cp15.c2_base; - } + switch (op2) { + case 0: + return env->cp15.c2_base0; + 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; + } + default: + goto bad_reg; + } + } case 3: /* MMU Domain access control / MPU write buffer control. */ return env->cp15.c3; case 4: /* Reserved. */ @@ -963,26 +1563,37 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn) default: goto bad_reg; } - case 6: /* MMU Fault address / MPU base/size. */ + case 6: /* MMU Fault address. */ if (arm_feature(env, ARM_FEATURE_MPU)) { - int n; - n = (insn & 0xf); - if (n >= 8) + if (crm >= 8) goto bad_reg; - return env->cp15.c6_region[n]; + return env->cp15.c6_region[crm]; } else { if (arm_feature(env, ARM_FEATURE_OMAPCP)) op2 = 0; - switch (op2) { - case 0: - return env->cp15.c6_data; - case 1: - /* Arm9 doesn't have an IFAR, but implementing it anyway - shouldn't do any harm. */ - return env->cp15.c6_insn; - default: - goto bad_reg; - } + switch (op2) { + case 0: + return env->cp15.c6_data; + case 1: + if (arm_feature(env, ARM_FEATURE_V6)) { + /* Watchpoint Fault Adrress. */ + return 0; /* Not implemented. */ + } else { + /* Instruction Fault Adrress. */ + /* Arm9 doesn't have an IFAR, but implementing it anyway + shouldn't do any harm. */ + return env->cp15.c6_insn; + } + case 2: + if (arm_feature(env, ARM_FEATURE_V6)) { + /* Instruction Fault Adrress. */ + return env->cp15.c6_insn; + } else { + goto bad_reg; + } + default: + goto bad_reg; + } } case 7: /* Cache control. */ /* ??? This is for test, clean and invaidate operations that set the @@ -993,13 +1604,23 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn) case 8: /* MMU TLB control. */ goto bad_reg; case 9: /* Cache lockdown. */ - if (arm_feature(env, ARM_FEATURE_OMAPCP)) + switch (op1) { + case 0: /* L1 cache. */ + if (arm_feature(env, ARM_FEATURE_OMAPCP)) + return 0; + switch (op2) { + case 0: + return env->cp15.c9_data; + case 1: + return env->cp15.c9_insn; + default: + goto bad_reg; + } + case 1: /* L2 cache */ + if (crm != 0) + goto bad_reg; + /* L2 Lockdown and Auxiliary control. */ return 0; - switch (op2) { - case 0: - return env->cp15.c9_data; - case 1: - return env->cp15.c9_insn; default: goto bad_reg; } @@ -1015,6 +1636,12 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn) return env->cp15.c13_fcse; case 1: return env->cp15.c13_context; + case 2: + return env->cp15.c13_tls1; + case 3: + return env->cp15.c13_tls2; + case 4: + return env->cp15.c13_tls3; default: goto bad_reg; } @@ -1048,10 +1675,125 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn) } bad_reg: /* ??? For debugging only. Should raise illegal instruction exception. */ - cpu_abort(env, "Unimplemented cp15 register read\n"); + cpu_abort(env, "Unimplemented cp15 register read (c%d, c%d, {%d, %d})\n", + (insn >> 16) & 0xf, crm, op1, op2); return 0; } +void helper_set_r13_banked(CPUState *env, int mode, uint32_t val) +{ + env->banked_r13[bank_number(mode)] = val; +} + +uint32_t helper_get_r13_banked(CPUState *env, int mode) +{ + return env->banked_r13[bank_number(mode)]; +} + +uint32_t helper_v7m_mrs(CPUState *env, int reg) +{ + switch (reg) { + case 0: /* APSR */ + return xpsr_read(env) & 0xf8000000; + case 1: /* IAPSR */ + return xpsr_read(env) & 0xf80001ff; + case 2: /* EAPSR */ + return xpsr_read(env) & 0xff00fc00; + case 3: /* xPSR */ + return xpsr_read(env) & 0xff00fdff; + case 5: /* IPSR */ + return xpsr_read(env) & 0x000001ff; + case 6: /* EPSR */ + return xpsr_read(env) & 0x0700fc00; + case 7: /* IEPSR */ + return xpsr_read(env) & 0x0700edff; + case 8: /* MSP */ + return env->v7m.current_sp ? env->v7m.other_sp : env->regs[13]; + case 9: /* PSP */ + return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp; + case 16: /* PRIMASK */ + return (env->uncached_cpsr & CPSR_I) != 0; + case 17: /* FAULTMASK */ + return (env->uncached_cpsr & CPSR_F) != 0; + case 18: /* BASEPRI */ + case 19: /* BASEPRI_MAX */ + return env->v7m.basepri; + case 20: /* CONTROL */ + return env->v7m.control; + default: + /* ??? For debugging only. */ + cpu_abort(env, "Unimplemented system register read (%d)\n", reg); + return 0; + } +} + +void helper_v7m_msr(CPUState *env, int reg, uint32_t val) +{ + switch (reg) { + case 0: /* APSR */ + xpsr_write(env, val, 0xf8000000); + break; + case 1: /* IAPSR */ + xpsr_write(env, val, 0xf8000000); + break; + case 2: /* EAPSR */ + xpsr_write(env, val, 0xfe00fc00); + break; + case 3: /* xPSR */ + xpsr_write(env, val, 0xfe00fc00); + break; + case 5: /* IPSR */ + /* IPSR bits are readonly. */ + break; + case 6: /* EPSR */ + xpsr_write(env, val, 0x0600fc00); + break; + case 7: /* IEPSR */ + xpsr_write(env, val, 0x0600fc00); + break; + case 8: /* MSP */ + if (env->v7m.current_sp) + env->v7m.other_sp = val; + else + env->regs[13] = val; + break; + case 9: /* PSP */ + if (env->v7m.current_sp) + env->regs[13] = val; + else + env->v7m.other_sp = val; + break; + case 16: /* PRIMASK */ + if (val & 1) + env->uncached_cpsr |= CPSR_I; + else + env->uncached_cpsr &= ~CPSR_I; + break; + case 17: /* FAULTMASK */ + if (val & 1) + env->uncached_cpsr |= CPSR_F; + else + env->uncached_cpsr &= ~CPSR_F; + break; + case 18: /* BASEPRI */ + env->v7m.basepri = val & 0xff; + break; + case 19: /* BASEPRI_MAX */ + val &= 0xff; + if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0)) + env->v7m.basepri = val; + break; + case 20: /* CONTROL */ + env->v7m.control = val & 3; + switch_v7m_sp(env, (val & 2) != 0); + break; + default: + /* ??? For debugging only. */ + cpu_abort(env, "Unimplemented system register write (%d)\n", reg); + return; + } +} + void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, void *opaque) |