From ccd380876b79c3b46f85720c1be8e2cd40509460 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:37 +0100 Subject: [PATCH 01/51] target-arm: Split out private-to-target functions into internals.h Currently cpu.h defines a mixture of functions and types needed by the rest of QEMU and those needed only by files within target-arm/. Split the latter out into a new header so they aren't needlessly exposed further than required. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.c | 1 + target-arm/cpu.h | 20 ---------------- target-arm/helper.c | 1 + target-arm/internals.h | 49 ++++++++++++++++++++++++++++++++++++++ target-arm/kvm32.c | 1 + target-arm/op_helper.c | 1 + target-arm/translate-a64.c | 1 + target-arm/translate.c | 1 + 8 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 target-arm/internals.h diff --git a/target-arm/cpu.c b/target-arm/cpu.c index c32d8c4855b4..0b1ad1012f4d 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -19,6 +19,7 @@ */ #include "cpu.h" +#include "internals.h" #include "qemu-common.h" #include "hw/qdev-properties.h" #include "qapi/qmp/qerror.h" diff --git a/target-arm/cpu.h b/target-arm/cpu.h index bf37cd60d0a2..78d4fc5f2b2f 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -111,11 +111,6 @@ typedef struct ARMGenericTimer { #define GTIMER_VIRT 1 #define NUM_GTIMERS 2 -/* Scale factor for generic timers, ie number of ns per tick. - * This gives a 62.5MHz timer. - */ -#define GTIMER_SCALE 16 - typedef struct CPUARMState { /* Regs for current mode. */ uint32_t regs[16]; @@ -322,11 +317,7 @@ typedef struct CPUARMState { #include "cpu-qom.h" ARMCPU *cpu_arm_init(const char *cpu_model); -void arm_translate_init(void); -void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); int cpu_arm_exec(CPUARMState *s); -int bank_number(int mode); -void switch_mode(CPUARMState *, int); uint32_t do_arm_semihosting(CPUARMState *env); static inline bool is_a64(CPUARMState *env) @@ -548,17 +539,6 @@ static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val) vfp_set_fpscr(env, new_fpscr); } -enum arm_fprounding { - FPROUNDING_TIEEVEN, - FPROUNDING_POSINF, - FPROUNDING_NEGINF, - FPROUNDING_ZERO, - FPROUNDING_TIEAWAY, - FPROUNDING_ODD -}; - -int arm_rmode_to_sf(int rmode); - enum arm_cpu_mode { ARM_CPU_MODE_USR = 0x10, ARM_CPU_MODE_FIQ = 0x11, diff --git a/target-arm/helper.c b/target-arm/helper.c index 55077ed1b684..e642b30170e9 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1,4 +1,5 @@ #include "cpu.h" +#include "internals.h" #include "exec/gdbstub.h" #include "helper.h" #include "qemu/host-utils.h" diff --git a/target-arm/internals.h b/target-arm/internals.h new file mode 100644 index 000000000000..a38a57f4d4cd --- /dev/null +++ b/target-arm/internals.h @@ -0,0 +1,49 @@ +/* + * QEMU ARM CPU -- internal functions and types + * + * Copyright (c) 2014 Linaro Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + * + * + * This header defines functions, types, etc which need to be shared + * between different source files within target-arm/ but which are + * private to it and not required by the rest of QEMU. + */ + +#ifndef TARGET_ARM_INTERNALS_H +#define TARGET_ARM_INTERNALS_H + +/* Scale factor for generic timers, ie number of ns per tick. + * This gives a 62.5MHz timer. + */ +#define GTIMER_SCALE 16 + +int bank_number(int mode); +void switch_mode(CPUARMState *, int); +void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); +void arm_translate_init(void); + +enum arm_fprounding { + FPROUNDING_TIEEVEN, + FPROUNDING_POSINF, + FPROUNDING_NEGINF, + FPROUNDING_ZERO, + FPROUNDING_TIEAWAY, + FPROUNDING_ODD +}; + +int arm_rmode_to_sf(int rmode); + +#endif diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c index a4fde079698c..b21f84409664 100644 --- a/target-arm/kvm32.c +++ b/target-arm/kvm32.c @@ -21,6 +21,7 @@ #include "sysemu/kvm.h" #include "kvm_arm.h" #include "cpu.h" +#include "internals.h" #include "hw/arm/arm.h" static inline void set_feature(uint64_t *features, int feature) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 21ff58e7544d..4193eca36ad5 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -18,6 +18,7 @@ */ #include "cpu.h" #include "helper.h" +#include "internals.h" #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 9175e48797f6..668916575e1b 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -26,6 +26,7 @@ #include "tcg-op.h" #include "qemu/log.h" #include "translate.h" +#include "internals.h" #include "qemu/host-utils.h" #include "exec/gen-icount.h" diff --git a/target-arm/translate.c b/target-arm/translate.c index 56e3b4bf7f2f..f869bc62081f 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -25,6 +25,7 @@ #include #include "cpu.h" +#include "internals.h" #include "disas/disas.h" #include "tcg-op.h" #include "qemu/log.h" From c2b820fe587b18448967cdd6d408abd1dc0471f2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:37 +0100 Subject: [PATCH 02/51] target-arm: Implement AArch64 DAIF system register Implement the DAIF system register which is a view of the DAIF bits in PSTATE. To avoid needing a readfn, we widen the daif field in CPUARMState to uint64_t. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.h | 2 +- target-arm/helper.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 78d4fc5f2b2f..695a1e21f5d2 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -160,7 +160,7 @@ typedef struct CPUARMState { uint32_t GE; /* cpsr[19:16] */ uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */ uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ - uint32_t daif; /* exception masks, in the bits they are in in PSTATE */ + uint64_t daif; /* exception masks, in the bits they are in in PSTATE */ /* System control coprocessor (cp15) */ struct { diff --git a/target-arm/helper.c b/target-arm/helper.c index e642b30170e9..c8348dabde32 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1691,6 +1691,20 @@ static void aa64_fpsr_write(CPUARMState *env, const ARMCPRegInfo *ri, vfp_set_fpsr(env, value); } +static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri) +{ + if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UMA)) { + return CP_ACCESS_TRAP; + } + return CP_ACCESS_OK; +} + +static void aa64_daif_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->daif = value & PSTATE_DAIF; +} + static CPAccessResult aa64_cacheop_access(CPUARMState *env, const ARMCPRegInfo *ri) { @@ -1737,6 +1751,12 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "NZCV", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 0, .crn = 4, .crm = 2, .access = PL0_RW, .type = ARM_CP_NZCV }, + { .name = "DAIF", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 2, + .type = ARM_CP_NO_MIGRATE, + .access = PL0_RW, .accessfn = aa64_daif_access, + .fieldoffset = offsetof(CPUARMState, daif), + .writefn = aa64_daif_write, .resetfn = arm_cp_reset_ignore }, { .name = "FPCR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 0, .crn = 4, .crm = 4, .access = PL0_RW, .readfn = aa64_fpcr_read, .writefn = aa64_fpcr_write }, From abf1172fc6dbc9564e25039434d444d9a9f1e88a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:38 +0100 Subject: [PATCH 03/51] target-arm: Define exception record for AArch64 exceptions For AArch32 exceptions, the only information provided about the cause of an exception is the individual exception type (data abort, undef, etc), which we store in cs->exception_index. For AArch64, the CPU provides much more detail about the cause of the exception, which can be found in the syndrome register. Create a set of fields in CPUARMState which must be filled in whenever an exception is raised, so that exception entry can correctly fill in the syndrome register for the guest. This includes the information which in AArch32 appears in the DFAR and IFAR (fault address registers) and the DFSR and IFSR (fault status registers) for data aborts and prefetch aborts, since if we end up taking the MMU fault to AArch64 rather than AArch32 this will need to end up in different system registers. This patch does a refactoring which moves the setting of the AArch32 DFAR/DFSR/IFAR/IFSR from the point where the exception is raised to the point where it is taken. (This is no change for cores with an MMU, retains the existing clearly incorrect behaviour for ARM946 of trashing the MP access permissions registers which share the c5_data and c5_insn state fields, and has no effect for v7M because we don't implement its MPU fault status or address registers.) As a side effect of the cleanup we fix a bug in the AArch64 linux-user mode code where we were passing a 64 bit fault address through the 32 bit c6_data/c6_insn fields: it now goes via the always-64-bit exception.vaddress. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- linux-user/main.c | 56 +++++++++++++++++++------------------------- target-arm/cpu.h | 15 ++++++++++++ target-arm/helper.c | 23 +++++++++++------- target-arm/machine.c | 3 +++ 4 files changed, 56 insertions(+), 41 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index af924dcc9df0..947358a88643 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -483,17 +483,17 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) addr = env->regs[2]; if (get_user_u64(oldval, env->regs[0])) { - env->cp15.c6_data = env->regs[0]; + env->exception.vaddress = env->regs[0]; goto segv; }; if (get_user_u64(newval, env->regs[1])) { - env->cp15.c6_data = env->regs[1]; + env->exception.vaddress = env->regs[1]; goto segv; }; if (get_user_u64(val, addr)) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto segv; } @@ -501,7 +501,7 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) val = newval; if (put_user_u64(val, addr)) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto segv; }; @@ -523,7 +523,7 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->cp15.c6_data; + info._sifields._sigfault._addr = env->exception.vaddress; queue_signal(env, info.si_signo, &info); end_exclusive(); @@ -620,14 +620,14 @@ static int do_strex(CPUARMState *env) abort(); } if (segv) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto done; } if (size == 3) { uint32_t valhi; segv = get_user_u32(valhi, addr + 4); if (segv) { - env->cp15.c6_data = addr + 4; + env->exception.vaddress = addr + 4; goto done; } val = deposit64(val, 32, 32, valhi); @@ -650,14 +650,14 @@ static int do_strex(CPUARMState *env) break; } if (segv) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto done; } if (size == 3) { val = env->regs[(env->exclusive_info >> 12) & 0xf]; segv = put_user_u32(val, addr + 4); if (segv) { - env->cp15.c6_data = addr + 4; + env->exception.vaddress = addr + 4; goto done; } } @@ -832,12 +832,14 @@ void cpu_loop(CPUARMState *env) case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; + case EXCP_STREX: + if (!do_strex(env)) { + break; + } + /* fall through for segv */ case EXCP_PREFETCH_ABORT: - addr = env->cp15.c6_insn; - goto do_segv; case EXCP_DATA_ABORT: - addr = env->cp15.c6_data; - do_segv: + addr = env->exception.vaddress; { info.si_signo = SIGSEGV; info.si_errno = 0; @@ -865,12 +867,6 @@ void cpu_loop(CPUARMState *env) if (do_kernel_trap(env)) goto error; break; - case EXCP_STREX: - if (do_strex(env)) { - addr = env->cp15.c6_data; - goto do_segv; - } - break; default: error: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", @@ -933,7 +929,7 @@ static int do_strex_a64(CPUARMState *env) abort(); } if (segv) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto error; } if (val != env->exclusive_val) { @@ -946,7 +942,7 @@ static int do_strex_a64(CPUARMState *env) segv = get_user_u64(val, addr + 8); } if (segv) { - env->cp15.c6_data = addr + (size == 2 ? 4 : 8); + env->exception.vaddress = addr + (size == 2 ? 4 : 8); goto error; } if (val != env->exclusive_high) { @@ -981,7 +977,7 @@ static int do_strex_a64(CPUARMState *env) segv = put_user_u64(val, addr + 8); } if (segv) { - env->cp15.c6_data = addr + (size == 2 ? 4 : 8); + env->exception.vaddress = addr + (size == 2 ? 4 : 8); goto error; } } @@ -1037,12 +1033,14 @@ void cpu_loop(CPUARMState *env) info._sifields._sigfault._addr = env->pc; queue_signal(env, info.si_signo, &info); break; + case EXCP_STREX: + if (!do_strex_a64(env)) { + break; + } + /* fall through for segv */ case EXCP_PREFETCH_ABORT: - addr = env->cp15.c6_insn; - goto do_segv; case EXCP_DATA_ABORT: - addr = env->cp15.c6_data; - do_segv: + addr = env->exception.vaddress; info.si_signo = SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ @@ -1060,12 +1058,6 @@ void cpu_loop(CPUARMState *env) queue_signal(env, info.si_signo, &info); } break; - case EXCP_STREX: - if (do_strex_a64(env)) { - addr = env->cp15.c6_data; - goto do_segv; - } - break; default: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 695a1e21f5d2..7a48241fde92 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -233,6 +233,21 @@ typedef struct CPUARMState { int pending_exception; } v7m; + /* Information associated with an exception about to be taken: + * code which raises an exception must set cs->exception_index and + * the relevant parts of this structure; the cpu_do_interrupt function + * will then set the guest-visible registers as part of the exception + * entry process. + */ + struct { + uint32_t syndrome; /* AArch64 format syndrome register */ + uint32_t fsr; /* AArch32 format fault status register info */ + uint64_t vaddress; /* virtual addr associated with exception, if any */ + /* If we implement EL2 we will also need to store information + * about the intermediate physical address for stage 2 faults. + */ + } exception; + /* Thumb-2 EE state. */ uint32_t teecr; uint32_t teehbr; diff --git a/target-arm/helper.c b/target-arm/helper.c index c8348dabde32..fe642df74820 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2705,12 +2705,11 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + env->exception.vaddress = address; if (rw == 2) { cs->exception_index = EXCP_PREFETCH_ABORT; - env->cp15.c6_insn = address; } else { cs->exception_index = EXCP_DATA_ABORT; - env->cp15.c6_data = address; } return 1; } @@ -2928,6 +2927,9 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) return; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: + /* TODO: if we implemented the MPU registers, this is where we + * should set the MMFAR, etc from exception.fsr and exception.vaddress. + */ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); return; case EXCP_BKPT: @@ -3042,9 +3044,11 @@ void arm_cpu_do_interrupt(CPUState *cs) return; } } - env->cp15.c5_insn = 2; + env->exception.fsr = 2; /* Fall through to prefetch abort. */ case EXCP_PREFETCH_ABORT: + env->cp15.c5_insn = env->exception.fsr; + env->cp15.c6_insn = env->exception.vaddress; qemu_log_mask(CPU_LOG_INT, "...with IFSR 0x%x IFAR 0x%x\n", env->cp15.c5_insn, env->cp15.c6_insn); new_mode = ARM_CPU_MODE_ABT; @@ -3053,6 +3057,8 @@ void arm_cpu_do_interrupt(CPUState *cs) offset = 4; break; case EXCP_DATA_ABORT: + env->cp15.c5_data = env->exception.fsr; + env->cp15.c6_data = env->exception.vaddress; qemu_log_mask(CPU_LOG_INT, "...with DFSR 0x%x DFAR 0x%x\n", env->cp15.c5_data, env->cp15.c6_data); new_mode = ARM_CPU_MODE_ABT; @@ -3703,16 +3709,15 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, } if (access_type == 2) { - env->cp15.c5_insn = ret; - env->cp15.c6_insn = address; cs->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; + if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6)) { + ret |= (1 << 11); + } cs->exception_index = EXCP_DATA_ABORT; } + env->exception.vaddress = address; + env->exception.fsr = ret; return 1; } diff --git a/target-arm/machine.c b/target-arm/machine.c index 7ced87af580e..b9d9c0f4649c 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -257,6 +257,9 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT64(env.exclusive_val, ARMCPU), VMSTATE_UINT64(env.exclusive_high, ARMCPU), VMSTATE_UINT64(env.features, ARMCPU), + VMSTATE_UINT32(env.exception.syndrome, ARMCPU), + VMSTATE_UINT32(env.exception.fsr, ARMCPU), + VMSTATE_UINT64(env.exception.vaddress, ARMCPU), VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU), VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU), VMSTATE_END_OF_LIST() From 8bcbf37caa87ba89bc391bad70039f942a98c7e3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:38 +0100 Subject: [PATCH 04/51] target-arm: Provide correct syndrome information for cpreg access traps For exceptions taken to AArch64, if a coprocessor/system register access fails due to a trap or enable bit then the syndrome information must include details of the failing instruction (crn/crm/opc1/opc2 fields, etc). Make the decoder construct the syndrome information at translate time so it can be passed at runtime to the access-check helper function and used as required. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.h | 2 +- target-arm/internals.h | 128 +++++++++++++++++++++++++++++++++++++ target-arm/op_helper.c | 8 +-- target-arm/translate-a64.c | 8 ++- target-arm/translate.c | 45 ++++++++++++- 5 files changed, 184 insertions(+), 7 deletions(-) diff --git a/target-arm/helper.h b/target-arm/helper.h index 366c1b3ea59b..2cdeadd1e1ed 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -58,7 +58,7 @@ DEF_HELPER_1(cpsr_read, i32, env) DEF_HELPER_3(v7m_msr, void, env, i32, i32) DEF_HELPER_2(v7m_mrs, i32, env, i32) -DEF_HELPER_2(access_check_cp_reg, void, env, ptr) +DEF_HELPER_3(access_check_cp_reg, void, env, ptr, i32) DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) DEF_HELPER_2(get_cp_reg, i32, env, ptr) DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) diff --git a/target-arm/internals.h b/target-arm/internals.h index a38a57f4d4cd..cc3fbf9e88ba 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -46,4 +46,132 @@ enum arm_fprounding { int arm_rmode_to_sf(int rmode); +/* Valid Syndrome Register EC field values */ +enum arm_exception_class { + EC_UNCATEGORIZED = 0x00, + EC_WFX_TRAP = 0x01, + EC_CP15RTTRAP = 0x03, + EC_CP15RRTTRAP = 0x04, + EC_CP14RTTRAP = 0x05, + EC_CP14DTTRAP = 0x06, + EC_ADVSIMDFPACCESSTRAP = 0x07, + EC_FPIDTRAP = 0x08, + EC_CP14RRTTRAP = 0x0c, + EC_ILLEGALSTATE = 0x0e, + EC_AA32_SVC = 0x11, + EC_AA32_HVC = 0x12, + EC_AA32_SMC = 0x13, + EC_AA64_SVC = 0x15, + EC_AA64_HVC = 0x16, + EC_AA64_SMC = 0x17, + EC_SYSTEMREGISTERTRAP = 0x18, + EC_INSNABORT = 0x20, + EC_INSNABORT_SAME_EL = 0x21, + EC_PCALIGNMENT = 0x22, + EC_DATAABORT = 0x24, + EC_DATAABORT_SAME_EL = 0x25, + EC_SPALIGNMENT = 0x26, + EC_AA32_FPTRAP = 0x28, + EC_AA64_FPTRAP = 0x2c, + EC_SERROR = 0x2f, + EC_BREAKPOINT = 0x30, + EC_BREAKPOINT_SAME_EL = 0x31, + EC_SOFTWARESTEP = 0x32, + EC_SOFTWARESTEP_SAME_EL = 0x33, + EC_WATCHPOINT = 0x34, + EC_WATCHPOINT_SAME_EL = 0x35, + EC_AA32_BKPT = 0x38, + EC_VECTORCATCH = 0x3a, + EC_AA64_BKPT = 0x3c, +}; + +#define ARM_EL_EC_SHIFT 26 +#define ARM_EL_IL_SHIFT 25 +#define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) + +/* Utility functions for constructing various kinds of syndrome value. + * Note that in general we follow the AArch64 syndrome values; in a + * few cases the value in HSR for exceptions taken to AArch32 Hyp + * mode differs slightly, so if we ever implemented Hyp mode then the + * syndrome value would need some massaging on exception entry. + * (One example of this is that AArch64 defaults to IL bit set for + * exceptions which don't specifically indicate information about the + * trapping instruction, whereas AArch32 defaults to IL bit clear.) + */ +static inline uint32_t syn_uncategorized(void) +{ + return (EC_UNCATEGORIZED << ARM_EL_EC_SHIFT) | ARM_EL_IL; +} + +static inline uint32_t syn_aa64_svc(uint32_t imm16) +{ + return (EC_AA64_SVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); +} + +static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_thumb) +{ + return (EC_AA32_SVC << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) + | (is_thumb ? 0 : ARM_EL_IL); +} + +static inline uint32_t syn_aa64_bkpt(uint32_t imm16) +{ + return (EC_AA64_BKPT << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); +} + +static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_thumb) +{ + return (EC_AA32_BKPT << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) + | (is_thumb ? 0 : ARM_EL_IL); +} + +static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, + int crn, int crm, int rt, + int isread) +{ + return (EC_SYSTEMREGISTERTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL + | (op0 << 20) | (op2 << 17) | (op1 << 14) | (crn << 10) | (rt << 5) + | (crm << 1) | isread; +} + +static inline uint32_t syn_cp14_rt_trap(int cv, int cond, int opc1, int opc2, + int crn, int crm, int rt, int isread, + bool is_thumb) +{ + return (EC_CP14RTTRAP << ARM_EL_EC_SHIFT) + | (is_thumb ? 0 : ARM_EL_IL) + | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) + | (crn << 10) | (rt << 5) | (crm << 1) | isread; +} + +static inline uint32_t syn_cp15_rt_trap(int cv, int cond, int opc1, int opc2, + int crn, int crm, int rt, int isread, + bool is_thumb) +{ + return (EC_CP15RTTRAP << ARM_EL_EC_SHIFT) + | (is_thumb ? 0 : ARM_EL_IL) + | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) + | (crn << 10) | (rt << 5) | (crm << 1) | isread; +} + +static inline uint32_t syn_cp14_rrt_trap(int cv, int cond, int opc1, int crm, + int rt, int rt2, int isread, + bool is_thumb) +{ + return (EC_CP14RRTTRAP << ARM_EL_EC_SHIFT) + | (is_thumb ? 0 : ARM_EL_IL) + | (cv << 24) | (cond << 20) | (opc1 << 16) + | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; +} + +static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, + int rt, int rt2, int isread, + bool is_thumb) +{ + return (EC_CP15RRTTRAP << ARM_EL_EC_SHIFT) + | (is_thumb ? 0 : ARM_EL_IL) + | (cv << 24) | (cond << 20) | (opc1 << 16) + | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; +} + #endif diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 4193eca36ad5..bacfbc0c59ef 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -294,17 +294,17 @@ void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val) } } -void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip) +void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome) { const ARMCPRegInfo *ri = rip; switch (ri->accessfn(env, ri)) { case CP_ACCESS_OK: return; case CP_ACCESS_TRAP: + env->exception.syndrome = syndrome; + break; case CP_ACCESS_TRAP_UNCATEGORIZED: - /* These cases will eventually need to generate different - * syndrome information. - */ + env->exception.syndrome = syn_uncategorized(); break; default: g_assert_not_reached(); diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 668916575e1b..37399df15338 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1242,10 +1242,16 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, * runtime; this may result in an exception. */ TCGv_ptr tmpptr; + TCGv_i32 tcg_syn; + uint32_t syndrome; + gen_a64_set_pc_im(s->pc - 4); tmpptr = tcg_const_ptr(ri); - gen_helper_access_check_cp_reg(cpu_env, tmpptr); + syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); + tcg_syn = tcg_const_i32(syndrome); + gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn); tcg_temp_free_ptr(tmpptr); + tcg_temp_free_i32(tcg_syn); } /* Handle special cases first */ diff --git a/target-arm/translate.c b/target-arm/translate.c index f869bc62081f..d31f5c100d50 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -6862,10 +6862,53 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) * runtime; this may result in an exception. */ TCGv_ptr tmpptr; + TCGv_i32 tcg_syn; + uint32_t syndrome; + + /* Note that since we are an implementation which takes an + * exception on a trapped conditional instruction only if the + * instruction passes its condition code check, we can take + * advantage of the clause in the ARM ARM that allows us to set + * the COND field in the instruction to 0xE in all cases. + * We could fish the actual condition out of the insn (ARM) + * or the condexec bits (Thumb) but it isn't necessary. + */ + switch (cpnum) { + case 14: + if (is64) { + syndrome = syn_cp14_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, s->thumb); + } else { + syndrome = syn_cp14_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, s->thumb); + } + break; + case 15: + if (is64) { + syndrome = syn_cp15_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, s->thumb); + } else { + syndrome = syn_cp15_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, s->thumb); + } + break; + default: + /* ARMv8 defines that only coprocessors 14 and 15 exist, + * so this can only happen if this is an ARMv7 or earlier CPU, + * in which case the syndrome information won't actually be + * guest visible. + */ + assert(!arm_feature(env, ARM_FEATURE_V8)); + syndrome = syn_uncategorized(); + break; + } + gen_set_pc_im(s, s->pc); tmpptr = tcg_const_ptr(ri); - gen_helper_access_check_cp_reg(cpu_env, tmpptr); + tcg_syn = tcg_const_i32(syndrome); + gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn); tcg_temp_free_ptr(tmpptr); + tcg_temp_free_i32(tcg_syn); } /* Handle special cases first */ From d4a2dc675bde36443c3a73051ca863d878d8bc31 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:38 +0100 Subject: [PATCH 05/51] target-arm: Add support for generating exceptions with syndrome information Add new helpers exception_with_syndrome (for generating an exception with syndrome information) and exception_uncategorized (for generating an exception with "Unknown or Uncategorized Reason", which have a syndrome register value of zero), and use them to generate the correct syndrome information for exceptions which are raised directly from generated code. This patch includes moving the A32/T32 gen_exception_insn functions further up in the source file; they will be needed for "VFP/Neon disabled" exception generation later. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.h | 3 +- target-arm/internals.h | 14 +++++ target-arm/op_helper.c | 21 +++++++- target-arm/translate-a64.c | 49 +++++++++++++----- target-arm/translate.c | 103 +++++++++++++++++++++++-------------- target-arm/translate.h | 4 ++ 6 files changed, 140 insertions(+), 54 deletions(-) diff --git a/target-arm/helper.h b/target-arm/helper.h index 2cdeadd1e1ed..0abdb0c8e0c9 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -48,7 +48,8 @@ DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32) DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_2(exception, void, env, i32) +DEF_HELPER_2(exception_internal, void, env, i32) +DEF_HELPER_3(exception_with_syndrome, void, env, i32, i32) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(wfe, void, env) diff --git a/target-arm/internals.h b/target-arm/internals.h index cc3fbf9e88ba..0300ba305c6b 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -25,6 +25,20 @@ #ifndef TARGET_ARM_INTERNALS_H #define TARGET_ARM_INTERNALS_H +static inline bool excp_is_internal(int excp) +{ + /* Return true if this exception number represents a QEMU-internal + * exception that will not be passed to the guest. + */ + return excp == EXCP_INTERRUPT + || excp == EXCP_HLT + || excp == EXCP_DEBUG + || excp == EXCP_HALTED + || excp == EXCP_EXCEPTION_EXIT + || excp == EXCP_KERNEL_TRAP + || excp == EXCP_STREX; +} + /* Scale factor for generic timers, ie number of ns per tick. * This gives a 62.5MHz timer. */ diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index bacfbc0c59ef..51edd9076ce1 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -244,11 +244,30 @@ void HELPER(wfe)(CPUARMState *env) cpu_loop_exit(cs); } -void HELPER(exception)(CPUARMState *env, uint32_t excp) +/* Raise an internal-to-QEMU exception. This is limited to only + * those EXCP values which are special cases for QEMU to interrupt + * execution and not to be used for exceptions which are passed to + * the guest (those must all have syndrome information and thus should + * use exception_with_syndrome). + */ +void HELPER(exception_internal)(CPUARMState *env, uint32_t excp) +{ + CPUState *cs = CPU(arm_env_get_cpu(env)); + + assert(excp_is_internal(excp)); + cs->exception_index = excp; + cpu_loop_exit(cs); +} + +/* Raise an exception with the specified syndrome register value */ +void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp, + uint32_t syndrome) { CPUState *cs = CPU(arm_env_get_cpu(env)); + assert(!excp_is_internal(excp)); cs->exception_index = excp; + env->exception.syndrome = syndrome; cpu_loop_exit(cs); } diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 37399df15338..210df2d6b4ec 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -176,18 +176,37 @@ void gen_a64_set_pc_im(uint64_t val) tcg_gen_movi_i64(cpu_pc, val); } -static void gen_exception(int excp) +static void gen_exception_internal(int excp) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, excp); - gen_helper_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + TCGv_i32 tcg_excp = tcg_const_i32(excp); + + assert(excp_is_internal(excp)); + gen_helper_exception_internal(cpu_env, tcg_excp); + tcg_temp_free_i32(tcg_excp); +} + +static void gen_exception(int excp, uint32_t syndrome) +{ + TCGv_i32 tcg_excp = tcg_const_i32(excp); + TCGv_i32 tcg_syn = tcg_const_i32(syndrome); + + gen_helper_exception_with_syndrome(cpu_env, tcg_excp, tcg_syn); + tcg_temp_free_i32(tcg_syn); + tcg_temp_free_i32(tcg_excp); +} + +static void gen_exception_internal_insn(DisasContext *s, int offset, int excp) +{ + gen_a64_set_pc_im(s->pc - offset); + gen_exception_internal(excp); + s->is_jmp = DISAS_EXC; } -static void gen_exception_insn(DisasContext *s, int offset, int excp) +static void gen_exception_insn(DisasContext *s, int offset, int excp, + uint32_t syndrome) { gen_a64_set_pc_im(s->pc - offset); - gen_exception(excp); + gen_exception(excp, syndrome); s->is_jmp = DISAS_EXC; } @@ -219,7 +238,7 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) } else { gen_a64_set_pc_im(dest); if (s->singlestep_enabled) { - gen_exception(EXCP_DEBUG); + gen_exception_internal(EXCP_DEBUG); } tcg_gen_exit_tb(0); s->is_jmp = DISAS_JUMP; @@ -228,7 +247,8 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) static void unallocated_encoding(DisasContext *s) { - gen_exception_insn(s, 4, EXCP_UDEF); + /* Unallocated and reserved encodings are uncategorized */ + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized()); } #define unsupported_encoding(s, insn) \ @@ -1373,6 +1393,7 @@ static void disas_exc(DisasContext *s, uint32_t insn) { int opc = extract32(insn, 21, 3); int op2_ll = extract32(insn, 0, 5); + int imm16 = extract32(insn, 5, 16); switch (opc) { case 0: @@ -1383,7 +1404,7 @@ static void disas_exc(DisasContext *s, uint32_t insn) unallocated_encoding(s); break; } - gen_exception_insn(s, 0, EXCP_SWI); + gen_exception_insn(s, 0, EXCP_SWI, syn_aa64_svc(imm16)); break; case 1: if (op2_ll != 0) { @@ -1391,7 +1412,7 @@ static void disas_exc(DisasContext *s, uint32_t insn) break; } /* BRK */ - gen_exception_insn(s, 0, EXCP_BKPT); + gen_exception_insn(s, 0, EXCP_BKPT, syn_aa64_bkpt(imm16)); break; case 2: if (op2_ll != 0) { @@ -1540,7 +1561,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_mov_i64(cpu_exclusive_test, addr); tcg_gen_movi_i32(cpu_exclusive_info, size | is_pair << 2 | (rd << 4) | (rt << 9) | (rt2 << 14)); - gen_exception_insn(s, 4, EXCP_STREX); + gen_exception_internal_insn(s, 4, EXCP_STREX); } #else static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, @@ -10338,7 +10359,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == dc->pc) { - gen_exception_insn(dc, 0, EXCP_DEBUG); + gen_exception_internal_insn(dc, 0, EXCP_DEBUG); /* Advance PC so that clearing the breakpoint will invalidate this TB. */ dc->pc += 2; @@ -10401,7 +10422,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, if (dc->is_jmp != DISAS_JUMP) { gen_a64_set_pc_im(dc->pc); } - gen_exception(EXCP_DEBUG); + gen_exception_internal(EXCP_DEBUG); } else { switch (dc->is_jmp) { case DISAS_NEXT: diff --git a/target-arm/translate.c b/target-arm/translate.c index d31f5c100d50..84700ca7fd78 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -183,12 +183,23 @@ static inline void gen_set_cpsr(TCGv_i32 var, uint32_t mask) /* Set NZCV flags from the high 4 bits of var. */ #define gen_set_nzcv(var) gen_set_cpsr(var, CPSR_NZCV) -static void gen_exception(int excp) +static void gen_exception_internal(int excp) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, excp); - gen_helper_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + TCGv_i32 tcg_excp = tcg_const_i32(excp); + + assert(excp_is_internal(excp)); + gen_helper_exception_internal(cpu_env, tcg_excp); + tcg_temp_free_i32(tcg_excp); +} + +static void gen_exception(int excp, uint32_t syndrome) +{ + TCGv_i32 tcg_excp = tcg_const_i32(excp); + TCGv_i32 tcg_syn = tcg_const_i32(syndrome); + + gen_helper_exception_with_syndrome(cpu_env, tcg_excp, tcg_syn); + tcg_temp_free_i32(tcg_syn); + tcg_temp_free_i32(tcg_excp); } static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b) @@ -900,6 +911,33 @@ static inline void gen_set_pc_im(DisasContext *s, target_ulong val) tcg_gen_movi_i32(cpu_R[15], val); } +static inline void +gen_set_condexec (DisasContext *s) +{ + if (s->condexec_mask) { + uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_movi_i32(tmp, val); + store_cpu_field(tmp, condexec_bits); + } +} + +static void gen_exception_internal_insn(DisasContext *s, int offset, int excp) +{ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - offset); + gen_exception_internal(excp); + s->is_jmp = DISAS_JUMP; +} + +static void gen_exception_insn(DisasContext *s, int offset, int excp, int syn) +{ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - offset); + gen_exception(excp, syn); + s->is_jmp = DISAS_JUMP; +} + /* Force a TB lookup after an instruction that changes the CPU state. */ static inline void gen_lookup_tb(DisasContext *s) { @@ -3913,25 +3951,6 @@ static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) s->is_jmp = DISAS_UPDATE; } -static inline void -gen_set_condexec (DisasContext *s) -{ - if (s->condexec_mask) { - uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, val); - store_cpu_field(tmp, condexec_bits); - } -} - -static void gen_exception_insn(DisasContext *s, int offset, int excp) -{ - gen_set_condexec(s); - gen_set_pc_im(s, s->pc - offset); - gen_exception(excp); - s->is_jmp = DISAS_JUMP; -} - static void gen_nop_hint(DisasContext *s, int val) { switch (val) { @@ -7160,7 +7179,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_extu_i32_i64(cpu_exclusive_test, addr); tcg_gen_movi_i32(cpu_exclusive_info, size | (rd << 4) | (rt << 8) | (rt2 << 12)); - gen_exception_insn(s, 4, EXCP_STREX); + gen_exception_internal_insn(s, 4, EXCP_STREX); } #else static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, @@ -7670,6 +7689,8 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) store_reg(s, rd, tmp); break; case 7: + { + int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4); /* SMC instruction (op1 == 3) and undefined instructions (op1 == 0 || op1 == 2) will trap */ @@ -7678,8 +7699,9 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } /* bkpt */ ARCH(5); - gen_exception_insn(s, 4, EXCP_BKPT); + gen_exception_insn(s, 4, EXCP_BKPT, syn_aa32_bkpt(imm16, false)); break; + } case 0x8: /* signed multiply */ case 0xa: case 0xc: @@ -8686,11 +8708,12 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) case 0xf: /* swi */ gen_set_pc_im(s, s->pc); + s->svc_imm = extract32(insn, 0, 24); s->is_jmp = DISAS_SWI; break; default: illegal_op: - gen_exception_insn(s, 4, EXCP_UDEF); + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized()); break; } } @@ -10501,9 +10524,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) break; case 0xe: /* bkpt */ + { + int imm8 = extract32(insn, 0, 8); ARCH(5); - gen_exception_insn(s, 2, EXCP_BKPT); + gen_exception_insn(s, 2, EXCP_BKPT, syn_aa32_bkpt(imm8, true)); break; + } case 0xa: /* rev */ ARCH(6); @@ -10620,6 +10646,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (cond == 0xf) { /* swi */ gen_set_pc_im(s, s->pc); + s->svc_imm = extract32(insn, 0, 8); s->is_jmp = DISAS_SWI; break; } @@ -10655,11 +10682,11 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } return; undef32: - gen_exception_insn(s, 4, EXCP_UDEF); + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized()); return; illegal_op: undef: - gen_exception_insn(s, 2, EXCP_UDEF); + gen_exception_insn(s, 2, EXCP_UDEF, syn_uncategorized()); } /* generate intermediate code in gen_opc_buf and gen_opparam_buf for @@ -10780,7 +10807,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, if (dc->pc >= 0xffff0000) { /* We always get here via a jump, so know we are not in a conditional execution block. */ - gen_exception(EXCP_KERNEL_TRAP); + gen_exception_internal(EXCP_KERNEL_TRAP); dc->is_jmp = DISAS_UPDATE; break; } @@ -10788,7 +10815,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, if (dc->pc >= 0xfffffff0 && IS_M(env)) { /* We always get here via a jump, so know we are not in a conditional execution block. */ - gen_exception(EXCP_EXCEPTION_EXIT); + gen_exception_internal(EXCP_EXCEPTION_EXIT); dc->is_jmp = DISAS_UPDATE; break; } @@ -10797,7 +10824,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == dc->pc) { - gen_exception_insn(dc, 0, EXCP_DEBUG); + gen_exception_internal_insn(dc, 0, EXCP_DEBUG); /* Advance PC so that clearing the breakpoint will invalidate this TB. */ dc->pc += 2; @@ -10877,9 +10904,9 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, if (dc->condjmp) { gen_set_condexec(dc); if (dc->is_jmp == DISAS_SWI) { - gen_exception(EXCP_SWI); + gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); } else { - gen_exception(EXCP_DEBUG); + gen_exception_internal(EXCP_DEBUG); } gen_set_label(dc->condlabel); } @@ -10889,11 +10916,11 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, } gen_set_condexec(dc); if (dc->is_jmp == DISAS_SWI && !dc->condjmp) { - gen_exception(EXCP_SWI); + gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); } else { /* FIXME: Single stepping a WFI insn will not halt the CPU. */ - gen_exception(EXCP_DEBUG); + gen_exception_internal(EXCP_DEBUG); } } else { /* While branches must always occur at the end of an IT block, @@ -10925,7 +10952,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, gen_helper_wfe(cpu_env); break; case DISAS_SWI: - gen_exception(EXCP_SWI); + gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); break; } if (dc->condjmp) { diff --git a/target-arm/translate.h b/target-arm/translate.h index 3525ffcecb6c..5c18dc9108c6 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -23,6 +23,10 @@ typedef struct DisasContext { int vfp_enabled; int vec_len; int vec_stride; + /* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI + * so that top level loop can generate correct syndrome information. + */ + uint32_t svc_imm; int aarch64; int current_pl; GHashTable *cp_regs; From 00892383c9f5f663230921c6cf6b6d3a8a61b45b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 15 Apr 2014 19:18:39 +0100 Subject: [PATCH 06/51] target-arm: Provide syndrome information for MMU faults Set up the required syndrome information when we detect an MMU fault. Signed-off-by: Rob Herring [PMM: split out from exception handling patch, tweaked to bring in line with how we create other kinds of syndrome information] Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 12 ++++++++++++ target-arm/internals.h | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index fe642df74820..9866e50ab7a3 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -3696,6 +3696,8 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, target_ulong page_size; int prot; int ret, is_user; + uint32_t syn; + bool same_el = (arm_current_pl(env) != 0); is_user = mmu_idx == MMU_USER_IDX; ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot, @@ -3708,14 +3710,24 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, return 0; } + /* AArch64 syndrome does not have an LPAE bit */ + syn = ret & ~(1 << 9); + + /* For insn and data aborts we assume there is no instruction syndrome + * information; this is always true for exceptions reported to EL1. + */ if (access_type == 2) { + syn = syn_insn_abort(same_el, 0, 0, syn); cs->exception_index = EXCP_PREFETCH_ABORT; } else { + syn = syn_data_abort(same_el, 0, 0, 0, access_type == 1, syn); if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6)) { ret |= (1 << 11); } cs->exception_index = EXCP_DATA_ABORT; } + + env->exception.syndrome = syn; env->exception.vaddress = address; env->exception.fsr = ret; return 1; diff --git a/target-arm/internals.h b/target-arm/internals.h index 0300ba305c6b..fad203bbaa7a 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -188,4 +188,17 @@ static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; } +static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) +{ + return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) + | (ea << 9) | (s1ptw << 7) | fsc; +} + +static inline uint32_t syn_data_abort(int same_el, int ea, int cm, int s1ptw, + int wnr, int fsc) +{ + return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) + | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; +} + #endif From 8c6afa6ab158467d1938cc92022135bc7a872006 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:39 +0100 Subject: [PATCH 07/51] target-arm: A64: Correctly fault FP/Neon if CPACR.FPEN set For the A64 instruction set, the only FP/Neon disable trap is the CPACR FPEN bits, which may indicate "enabled", "disabled" or "disabled for EL0". Add a bit to the AArch64 tb flags indicating whether FP/Neon access is currently enabled and make the decoder emit code to raise exceptions on use of FP/Neon insns if it is not. We use a new flag in DisasContext rather than borrowing the existing vfp_enabled flag because the A32/T32 decoder is going to need both. Signed-off-by: Peter Maydell Acked-by: Peter Crosthwaite --- I'm aware this is a rather hard to review patch; sorry. I have done an exhaustive check that we have fp access checks in all code paths with the aid of the assertions added in the next patch plus the code-coverage hack patch I posted to the list earlier. This patch is correct as of 09e037354 target-arm: A64: Add saturating accumulate ops (USQADD/SUQADD) which was the last of the Neon insns to be added, so assuming no refactoring of the code it should be fine. --- target-arm/cpu.c | 7 + target-arm/cpu.h | 9 ++ target-arm/internals.h | 7 + target-arm/translate-a64.c | 300 ++++++++++++++++++++++++++++++++++++- target-arm/translate.h | 3 +- 5 files changed, 320 insertions(+), 6 deletions(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 0b1ad1012f4d..d62b792b29a1 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -100,8 +100,15 @@ static void arm_cpu_reset(CPUState *s) env->pstate = PSTATE_MODE_EL0t; /* Userspace expects access to CTL_EL0 and the cache ops */ env->cp15.c1_sys |= SCTLR_UCT | SCTLR_UCI; + /* and to the FP/Neon instructions */ + env->cp15.c1_coproc = deposit64(env->cp15.c1_coproc, 20, 2, 3); #else env->pstate = PSTATE_MODE_EL1h; +#endif + } else { +#if defined(CONFIG_USER_ONLY) + /* Userspace expects access to cp10 and cp11 for FP/Neon */ + env->cp15.c1_coproc = deposit64(env->cp15.c1_coproc, 20, 4, 0xf); #endif } diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 7a48241fde92..72c4c7a9b5d2 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1108,6 +1108,8 @@ static inline int cpu_mmu_index (CPUARMState *env) /* Bit usage when in AArch64 state */ #define ARM_TBFLAG_AA64_EL_SHIFT 0 #define ARM_TBFLAG_AA64_EL_MASK (0x3 << ARM_TBFLAG_AA64_EL_SHIFT) +#define ARM_TBFLAG_AA64_FPEN_SHIFT 2 +#define ARM_TBFLAG_AA64_FPEN_MASK (1 << ARM_TBFLAG_AA64_FPEN_SHIFT) /* some convenience accessor macros */ #define ARM_TBFLAG_AARCH64_STATE(F) \ @@ -1128,14 +1130,21 @@ static inline int cpu_mmu_index (CPUARMState *env) (((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT) #define ARM_TBFLAG_AA64_EL(F) \ (((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT) +#define ARM_TBFLAG_AA64_FPEN(F) \ + (((F) & ARM_TBFLAG_AA64_FPEN_MASK) >> ARM_TBFLAG_AA64_FPEN_SHIFT) static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { + int fpen = extract32(env->cp15.c1_coproc, 20, 2); + if (is_a64(env)) { *pc = env->pc; *flags = ARM_TBFLAG_AARCH64_STATE_MASK | (arm_current_pl(env) << ARM_TBFLAG_AA64_EL_SHIFT); + if (fpen == 3 || (fpen == 1 && arm_current_pl(env) != 0)) { + *flags |= ARM_TBFLAG_AA64_FPEN_MASK; + } } else { int privmode; *pc = env->regs[15]; diff --git a/target-arm/internals.h b/target-arm/internals.h index fad203bbaa7a..a527f02ff5a6 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -188,6 +188,13 @@ static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; } +static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_thumb) +{ + return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) + | (is_thumb ? 0 : ARM_EL_IL) + | (cv << 24) | (cond << 20); +} + static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) { return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 210df2d6b4ec..2f67af3e6cfe 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -890,6 +890,23 @@ static void do_vec_ld(DisasContext *s, int destidx, int element, tcg_temp_free_i64(tcg_tmp); } +/* Check that FP/Neon access is enabled. If it is, return + * true. If not, emit code to generate an appropriate exception, + * and return false; the caller should not emit any code for + * the instruction. Note that this check must happen after all + * unallocated-encoding checks (otherwise the syndrome information + * for the resulting exception will be incorrect). + */ +static inline bool fp_access_check(DisasContext *s) +{ + if (s->cpacr_fpen) { + return true; + } + + gen_exception_insn(s, 4, EXCP_UDEF, syn_fp_access_trap(1, 0xe, false)); + return false; +} + /* * This utility function is for doing register extension with an * optional shift. You will likely want to pass a temporary for the @@ -1728,6 +1745,9 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) return; } size = 2 + opc; + if (!fp_access_check(s)) { + return; + } } else { if (opc == 3) { /* PRFM (literal) : prefetch */ @@ -1837,6 +1857,10 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) break; } + if (is_vector && !fp_access_check(s)) { + return; + } + offset <<= size; if (rn == 31) { @@ -1930,6 +1954,9 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn) return; } is_store = ((opc & 1) == 0); + if (!fp_access_check(s)) { + return; + } } else { if (size == 3 && opc == 2) { /* PRFM - prefetch */ @@ -2050,6 +2077,9 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn) return; } is_store = !extract32(opc, 0, 1); + if (!fp_access_check(s)) { + return; + } } else { if (size == 3 && opc == 2) { /* PRFM - prefetch */ @@ -2130,6 +2160,9 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn) return; } is_store = !extract32(opc, 0, 1); + if (!fp_access_check(s)) { + return; + } } else { if (size == 3 && opc == 2) { /* PRFM - prefetch */ @@ -2272,6 +2305,10 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + if (rn == 31) { gen_check_sp_alignment(s); } @@ -2398,6 +2435,10 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) g_assert_not_reached(); } + if (!fp_access_check(s)) { + return; + } + ebytes = 1 << scale; if (rn == 31) { @@ -3874,6 +3915,10 @@ static void disas_fp_compare(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + handle_fp_compare(s, type, rn, rm, opc & 1, opc & 2); } @@ -3902,6 +3947,10 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + if (cond < 0x0e) { /* not always */ int label_match = gen_new_label(); label_continue = gen_new_label(); @@ -3958,6 +4007,10 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + if (cond < 0x0e) { /* not always */ int label_match = gen_new_label(); label_continue = gen_new_label(); @@ -4175,6 +4228,10 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } + handle_fp_fcvt(s, opcode, rd, rn, dtype, type); break; } @@ -4184,9 +4241,17 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) /* 32-to-32 and 64-to-64 ops */ switch (type) { case 0: + if (!fp_access_check(s)) { + return; + } + handle_fp_1src_single(s, opcode, rd, rn); break; case 1: + if (!fp_access_check(s)) { + return; + } + handle_fp_1src_double(s, opcode, rd, rn); break; default: @@ -4326,9 +4391,15 @@ static void disas_fp_2src(DisasContext *s, uint32_t insn) switch (type) { case 0: + if (!fp_access_check(s)) { + return; + } handle_fp_2src_single(s, opcode, rd, rn, rm); break; case 1: + if (!fp_access_check(s)) { + return; + } handle_fp_2src_double(s, opcode, rd, rn, rm); break; default: @@ -4430,9 +4501,15 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn) switch (type) { case 0: + if (!fp_access_check(s)) { + return; + } handle_fp_3src_single(s, o0, o1, rd, rn, rm, ra); break; case 1: + if (!fp_access_check(s)) { + return; + } handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra); break; default: @@ -4459,6 +4536,10 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + /* The imm8 encodes the sign bit, enough bits to represent * an exponent in the range 01....1xx to 10....0xx, * and the most significant 4 bits of the mantissa; see @@ -4645,6 +4726,10 @@ static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + handle_fpfpcvt(s, rd, rn, opcode, itof, FPROUNDING_ZERO, scale, sf, type); } @@ -4744,6 +4829,9 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) break; } + if (!fp_access_check(s)) { + return; + } handle_fmov(s, rd, rn, type, itof); } else { /* actual FP conversions */ @@ -4754,6 +4842,9 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type); } } @@ -4854,6 +4945,10 @@ static void disas_simd_ext(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + tcg_resh = tcg_temp_new_i64(); tcg_resl = tcg_temp_new_i64(); @@ -4924,6 +5019,10 @@ static void disas_simd_tb(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + /* This does a table lookup: for every byte element in the input * we index into a table formed from up to four vector registers, * and then the output is the result of the lookups. Our helper @@ -4994,6 +5093,10 @@ static void disas_simd_zip_trn(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + tcg_resl = tcg_const_i64(0); tcg_resh = tcg_const_i64(0); tcg_res = tcg_temp_new_i64(); @@ -5127,6 +5230,10 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + esize = 8 << size; elements = (is_q ? 128 : 64) / esize; @@ -5259,6 +5366,10 @@ static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn, return; } + if (!fp_access_check(s)) { + return; + } + index = imm5 >> (size + 1); tmp = tcg_temp_new_i64(); @@ -5293,6 +5404,10 @@ static void handle_simd_dupes(DisasContext *s, int rd, int rn, return; } + if (!fp_access_check(s)) { + return; + } + index = imm5 >> (size + 1); /* This instruction just extracts the specified element and @@ -5325,6 +5440,11 @@ static void handle_simd_dupg(DisasContext *s, int is_q, int rd, int rn, unallocated_encoding(s); return; } + + if (!fp_access_check(s)) { + return; + } + for (i = 0; i < elements; i++) { write_vec_element(s, cpu_reg(s, rn), rd, i, size); } @@ -5354,6 +5474,11 @@ static void handle_simd_inse(DisasContext *s, int rd, int rn, unallocated_encoding(s); return; } + + if (!fp_access_check(s)) { + return; + } + dst_index = extract32(imm5, 1+size, 5); src_index = extract32(imm4, size, 4); @@ -5386,6 +5511,10 @@ static void handle_simd_insg(DisasContext *s, int rd, int rn, int imm5) return; } + if (!fp_access_check(s)) { + return; + } + idx = extract32(imm5, 1 + size, 4 - size); write_vec_element(s, cpu_reg(s, rn), rd, idx, size); } @@ -5423,6 +5552,11 @@ static void handle_simd_umov_smov(DisasContext *s, int is_q, int is_signed, return; } } + + if (!fp_access_check(s)) { + return; + } + element = extract32(imm5, 1+size, 4); tcg_rd = cpu_reg(s, rd); @@ -5515,6 +5649,10 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + /* See AdvSIMDExpandImm() in ARM ARM */ switch (cmode_3_1) { case 0: /* Replicate(Zeros(24):imm8, 2) */ @@ -5663,6 +5801,10 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } + TCGV_UNUSED_PTR(fpst); break; case 0xc: /* FMAXNMP */ @@ -5675,6 +5817,10 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } + size = extract32(size, 0, 1) ? 3 : 2; fpst = get_fpstatus_ptr(); break; @@ -5893,6 +6039,10 @@ static void handle_scalar_simd_shri(DisasContext *s, return; } + if (!fp_access_check(s)) { + return; + } + switch (opcode) { case 0x02: /* SSRA / USRA (accumulate) */ accumulate = true; @@ -5950,6 +6100,10 @@ static void handle_scalar_simd_shli(DisasContext *s, bool insert, return; } + if (!fp_access_check(s)) { + return; + } + tcg_rn = read_fp_dreg(s, rn); tcg_rd = insert ? read_fp_dreg(s, rd) : tcg_temp_new_i64(); @@ -6005,6 +6159,10 @@ static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, return; } + if (!fp_access_check(s)) { + return; + } + if (is_u_shift) { narrowfn = unsigned_narrow_fns[size]; } else { @@ -6087,6 +6245,10 @@ static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, } } + if (!fp_access_check(s)) { + return; + } + if (size == 3) { TCGv_i64 tcg_shift = tcg_const_i64(shift); static NeonGenTwo64OpEnvFn * const fns[2][2] = { @@ -6247,6 +6409,11 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, return; } } + + if (!fp_access_check(s)) { + return; + } + /* immh == 0 would be a failure of the decode logic */ g_assert(immh); @@ -6275,6 +6442,10 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, return; } + if (!fp_access_check(s)) { + return; + } + assert(!(is_scalar && is_q)); tcg_rmode = tcg_const_i32(arm_rmode_to_sf(FPROUNDING_ZERO)); @@ -6438,6 +6609,10 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + if (size == 2) { TCGv_i64 tcg_op1 = tcg_temp_new_i64(); TCGv_i64 tcg_op2 = tcg_temp_new_i64(); @@ -6822,6 +6997,10 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + handle_3same_float(s, extract32(size, 0, 1), 1, fpopcode, rd, rn, rm); return; } @@ -6854,6 +7033,10 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + tcg_rd = tcg_temp_new_i64(); if (size == 3) { @@ -7057,7 +7240,13 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, int size, int rn, int rd) { bool is_double = (size == 3); - TCGv_ptr fpst = get_fpstatus_ptr(); + TCGv_ptr fpst; + + if (!fp_access_check(s)) { + return; + } + + fpst = get_fpstatus_ptr(); if (is_double) { TCGv_i64 tcg_op = tcg_temp_new_i64(); @@ -7464,6 +7653,9 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) switch (opcode) { case 0x3: /* USQADD / SUQADD*/ + if (!fp_access_check(s)) { + return; + } handle_2misc_satacc(s, true, u, false, size, rn, rd); return; case 0x7: /* SQABS / SQNEG */ @@ -7493,6 +7685,9 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_2misc_narrow(s, true, opcode, u, false, size, rn, rd); return; case 0xc ... 0xf: @@ -7515,12 +7710,18 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) case 0x5d: /* UCVTF */ { bool is_signed = (opcode == 0x1d); + if (!fp_access_check(s)) { + return; + } handle_simd_intfp_conv(s, rd, rn, 1, is_signed, 0, size); return; } case 0x3d: /* FRECPE */ case 0x3f: /* FRECPX */ case 0x7d: /* FRSQRTE */ + if (!fp_access_check(s)) { + return; + } handle_2misc_reciprocal(s, opcode, true, u, true, size, rn, rd); return; case 0x1a: /* FCVTNS */ @@ -7545,6 +7746,9 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_2misc_narrow(s, true, opcode, u, false, size - 1, rn, rd); return; default: @@ -7557,6 +7761,10 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + if (is_fcvt) { tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); @@ -7660,6 +7868,10 @@ static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u, return; } + if (!fp_access_check(s)) { + return; + } + switch (opcode) { case 0x02: /* SSRA / USRA (accumulate) */ accumulate = true; @@ -7731,6 +7943,10 @@ static void handle_vec_simd_shli(DisasContext *s, bool is_q, bool insert, return; } + if (!fp_access_check(s)) { + return; + } + for (i = 0; i < elements; i++) { read_vec_element(s, tcg_rn, rn, i, size); if (insert) { @@ -7766,6 +7982,10 @@ static void handle_vec_simd_wshli(DisasContext *s, bool is_q, bool is_u, return; } + if (!fp_access_check(s)) { + return; + } + /* For the LL variants the store is larger than the load, * so if rd == rn we would overwrite parts of our input. * So load everything right now and use shifts in the main loop. @@ -7800,6 +8020,10 @@ static void handle_vec_simd_shrn(DisasContext *s, bool is_q, return; } + if (!fp_access_check(s)) { + return; + } + tcg_rn = tcg_temp_new_i64(); tcg_rd = tcg_temp_new_i64(); tcg_final = tcg_temp_new_i64(); @@ -8296,6 +8520,9 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_3rd_wide(s, is_q, is_u, size, opcode, rd, rn, rm); break; case 4: /* ADDHN, ADDHN2, RADDHN, RADDHN2 */ @@ -8305,6 +8532,9 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_3rd_narrowing(s, is_q, is_u, size, opcode, rd, rn, rm); break; case 14: /* PMULL, PMULL2 */ @@ -8317,6 +8547,9 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_pmull_64(s, is_q, rd, rn, rm); return; } @@ -8342,6 +8575,10 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) return; } is_widening: + if (!fp_access_check(s)) { + return; + } + handle_3rd_widening(s, is_q, is_u, size, opcode, rd, rn, rm); break; default: @@ -8360,11 +8597,15 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) int size = extract32(insn, 22, 2); bool is_u = extract32(insn, 29, 1); bool is_q = extract32(insn, 30, 1); - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res[2]; + TCGv_i64 tcg_op1, tcg_op2, tcg_res[2]; int pass; + if (!fp_access_check(s)) { + return; + } + + tcg_op1 = tcg_temp_new_i64(); + tcg_op2 = tcg_temp_new_i64(); tcg_res[0] = tcg_temp_new_i64(); tcg_res[1] = tcg_temp_new_i64(); @@ -8467,6 +8708,10 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, TCGV_UNUSED_PTR(fpst); } + if (!fp_access_check(s)) { + return; + } + /* These operations work on the concatenated rm:rn, with each pair of * adjacent elements being operated on to produce an element in the result. */ @@ -8659,6 +8904,10 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x5f: /* FDIV */ case 0x7a: /* FABD */ case 0x7c: /* FCMGT */ + if (!fp_access_check(s)) { + return; + } + handle_3same_float(s, size, elements, fpopcode, rd, rn, rm); return; default: @@ -8713,6 +8962,10 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) break; } + if (!fp_access_check(s)) { + return; + } + if (size == 3) { for (pass = 0; pass < (is_q ? 2 : 1); pass++) { TCGv_i64 tcg_op1 = tcg_temp_new_i64(); @@ -9077,6 +9330,10 @@ static void handle_rev(DisasContext *s, int opcode, bool u, return; } + if (!fp_access_check(s)) { + return; + } + if (size == 0) { /* Special case bytes, use bswap op on each group of elements */ int groups = dsize / (8 << grp_size); @@ -9279,6 +9536,10 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } + handle_2misc_narrow(s, false, opcode, u, is_q, size, rn, rd); return; case 0x4: /* CLS, CLZ */ @@ -9293,6 +9554,9 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_2misc_pairwise(s, opcode, u, is_q, size, rn, rd); return; case 0x13: /* SHLL, SHLL2 */ @@ -9300,6 +9564,9 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_shll(s, is_q, size, rn, rd); return; case 0xa: /* CMLT */ @@ -9321,6 +9588,9 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_2misc_satacc(s, false, u, is_q, size, rn, rd); return; case 0x7: /* SQABS, SQNEG */ @@ -9356,6 +9626,9 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_simd_intfp_conv(s, rd, rn, elements, is_signed, 0, size); return; } @@ -9414,6 +9687,9 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (!fp_access_check(s)) { + return; + } handle_2misc_reciprocal(s, opcode, false, u, is_q, size, rn, rd); return; case 0x56: /* FCVTXN, FCVTXN2 */ @@ -9426,9 +9702,15 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) /* handle_2misc_narrow does a 2*size -> size operation, but these * instructions encode the source size rather than dest size. */ + if (!fp_access_check(s)) { + return; + } handle_2misc_narrow(s, false, opcode, 0, is_q, size - 1, rn, rd); return; case 0x17: /* FCVTL, FCVTL2 */ + if (!fp_access_check(s)) { + return; + } handle_2misc_widening(s, opcode, is_q, size, rn, rd); return; case 0x18: /* FRINTN */ @@ -9473,6 +9755,10 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) return; } + if (!fp_access_check(s)) { + return; + } + if (need_fpstatus) { tcg_fpstatus = get_fpstatus_ptr(); } else { @@ -9836,6 +10122,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } + if (!fp_access_check(s)) { + return; + } + if (is_fp) { fpst = get_fpstatus_ptr(); } else { @@ -10334,7 +10624,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, #if !defined(CONFIG_USER_ONLY) dc->user = (ARM_TBFLAG_AA64_EL(tb->flags) == 0); #endif - dc->vfp_enabled = 0; + dc->cpacr_fpen = ARM_TBFLAG_AA64_FPEN(tb->flags); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = cpu->cp_regs; diff --git a/target-arm/translate.h b/target-arm/translate.h index 5c18dc9108c6..4536f82cdc88 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -20,7 +20,8 @@ typedef struct DisasContext { #if !defined(CONFIG_USER_ONLY) int user; #endif - int vfp_enabled; + bool cpacr_fpen; /* FP enabled via CPACR.FPEN */ + bool vfp_enabled; /* FP enabled via FPSCR.EN */ int vec_len; int vec_stride; /* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI From 90e496386fe7fd32c189561f846b7913f95b8cf4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:40 +0100 Subject: [PATCH 08/51] target-arm: A64: Add assertion that FP access was checked Because unallocated encodings generate different exception syndrome information from traps due to FP being disabled, we can't do a single "is fp access disabled" check at a high level in the decode tree. To help in catching bugs where the access check was forgotten in some code path, we set this flag when the access check is done, and assert that it is set at the point where we actually touch the FP regs. This requires us to pass the DisasContext to the vec_reg_offset and fp_reg_offset functions. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/translate-a64.c | 75 ++++++++++++++++++++++++++------------ target-arm/translate.h | 8 ++++ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 2f67af3e6cfe..0ec2f6558661 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -353,11 +353,30 @@ static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) return v; } +/* We should have at some point before trying to access an FP register + * done the necessary access check, so assert that + * (a) we did the check and + * (b) we didn't then just plough ahead anyway if it failed. + * Print the instruction pattern in the abort message so we can figure + * out what we need to fix if a user encounters this problem in the wild. + */ +static inline void assert_fp_access_checked(DisasContext *s) +{ +#ifdef CONFIG_DEBUG_TCG + if (unlikely(!s->fp_access_checked || !s->cpacr_fpen)) { + fprintf(stderr, "target-arm: FP access check missing for " + "instruction 0x%08x\n", s->insn); + abort(); + } +#endif +} + /* Return the offset into CPUARMState of an element of specified * size, 'element' places in from the least significant end of * the FP/vector register Qn. */ -static inline int vec_reg_offset(int regno, int element, TCGMemOp size) +static inline int vec_reg_offset(DisasContext *s, int regno, + int element, TCGMemOp size) { int offs = offsetof(CPUARMState, vfp.regs[regno * 2]); #ifdef HOST_WORDS_BIGENDIAN @@ -372,6 +391,7 @@ static inline int vec_reg_offset(int regno, int element, TCGMemOp size) #else offs += element * (1 << size); #endif + assert_fp_access_checked(s); return offs; } @@ -380,18 +400,20 @@ static inline int vec_reg_offset(int regno, int element, TCGMemOp size) * Dn, Sn, Hn or Bn). * (Note that this is not the same mapping as for A32; see cpu.h) */ -static inline int fp_reg_offset(int regno, TCGMemOp size) +static inline int fp_reg_offset(DisasContext *s, int regno, TCGMemOp size) { int offs = offsetof(CPUARMState, vfp.regs[regno * 2]); #ifdef HOST_WORDS_BIGENDIAN offs += (8 - (1 << size)); #endif + assert_fp_access_checked(s); return offs; } /* Offset of the high half of the 128 bit vector Qn */ -static inline int fp_reg_hi_offset(int regno) +static inline int fp_reg_hi_offset(DisasContext *s, int regno) { + assert_fp_access_checked(s); return offsetof(CPUARMState, vfp.regs[regno * 2 + 1]); } @@ -405,7 +427,7 @@ static TCGv_i64 read_fp_dreg(DisasContext *s, int reg) { TCGv_i64 v = tcg_temp_new_i64(); - tcg_gen_ld_i64(v, cpu_env, fp_reg_offset(reg, MO_64)); + tcg_gen_ld_i64(v, cpu_env, fp_reg_offset(s, reg, MO_64)); return v; } @@ -413,7 +435,7 @@ static TCGv_i32 read_fp_sreg(DisasContext *s, int reg) { TCGv_i32 v = tcg_temp_new_i32(); - tcg_gen_ld_i32(v, cpu_env, fp_reg_offset(reg, MO_32)); + tcg_gen_ld_i32(v, cpu_env, fp_reg_offset(s, reg, MO_32)); return v; } @@ -421,8 +443,8 @@ static void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v) { TCGv_i64 tcg_zero = tcg_const_i64(0); - tcg_gen_st_i64(v, cpu_env, fp_reg_offset(reg, MO_64)); - tcg_gen_st_i64(tcg_zero, cpu_env, fp_reg_hi_offset(reg)); + tcg_gen_st_i64(v, cpu_env, fp_reg_offset(s, reg, MO_64)); + tcg_gen_st_i64(tcg_zero, cpu_env, fp_reg_hi_offset(s, reg)); tcg_temp_free_i64(tcg_zero); } @@ -693,14 +715,14 @@ static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) { /* This writes the bottom N bits of a 128 bit wide vector to memory */ TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_ld_i64(tmp, cpu_env, fp_reg_offset(srcidx, MO_64)); + tcg_gen_ld_i64(tmp, cpu_env, fp_reg_offset(s, srcidx, MO_64)); if (size < 4) { tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TE + size); } else { TCGv_i64 tcg_hiaddr = tcg_temp_new_i64(); tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TEQ); tcg_gen_qemu_st64(tmp, tcg_addr, get_mem_index(s)); - tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(srcidx)); + tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(s, srcidx)); tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); tcg_gen_qemu_st_i64(tmp, tcg_hiaddr, get_mem_index(s), MO_TEQ); tcg_temp_free_i64(tcg_hiaddr); @@ -733,8 +755,8 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) tcg_temp_free_i64(tcg_hiaddr); } - tcg_gen_st_i64(tmplo, cpu_env, fp_reg_offset(destidx, MO_64)); - tcg_gen_st_i64(tmphi, cpu_env, fp_reg_hi_offset(destidx)); + tcg_gen_st_i64(tmplo, cpu_env, fp_reg_offset(s, destidx, MO_64)); + tcg_gen_st_i64(tmphi, cpu_env, fp_reg_hi_offset(s, destidx)); tcg_temp_free_i64(tmplo); tcg_temp_free_i64(tmphi); @@ -756,7 +778,7 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx, int element, TCGMemOp memop) { - int vect_off = vec_reg_offset(srcidx, element, memop & MO_SIZE); + int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); switch (memop) { case MO_8: tcg_gen_ld8u_i64(tcg_dest, cpu_env, vect_off); @@ -788,7 +810,7 @@ static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx, static void read_vec_element_i32(DisasContext *s, TCGv_i32 tcg_dest, int srcidx, int element, TCGMemOp memop) { - int vect_off = vec_reg_offset(srcidx, element, memop & MO_SIZE); + int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); switch (memop) { case MO_8: tcg_gen_ld8u_i32(tcg_dest, cpu_env, vect_off); @@ -815,7 +837,7 @@ static void read_vec_element_i32(DisasContext *s, TCGv_i32 tcg_dest, int srcidx, static void write_vec_element(DisasContext *s, TCGv_i64 tcg_src, int destidx, int element, TCGMemOp memop) { - int vect_off = vec_reg_offset(destidx, element, memop & MO_SIZE); + int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); switch (memop) { case MO_8: tcg_gen_st8_i64(tcg_src, cpu_env, vect_off); @@ -837,7 +859,7 @@ static void write_vec_element(DisasContext *s, TCGv_i64 tcg_src, int destidx, static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src, int destidx, int element, TCGMemOp memop) { - int vect_off = vec_reg_offset(destidx, element, memop & MO_SIZE); + int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); switch (memop) { case MO_8: tcg_gen_st8_i32(tcg_src, cpu_env, vect_off); @@ -899,6 +921,9 @@ static void do_vec_ld(DisasContext *s, int destidx, int element, */ static inline bool fp_access_check(DisasContext *s) { + assert(!s->fp_access_checked); + s->fp_access_checked = true; + if (s->cpacr_fpen) { return true; } @@ -4748,9 +4773,9 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) /* 32 bit */ TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_ext32u_i64(tmp, tcg_rn); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_offset(rd, MO_64)); + tcg_gen_st_i64(tmp, cpu_env, fp_reg_offset(s, rd, MO_64)); tcg_gen_movi_i64(tmp, 0); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(rd)); + tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd)); tcg_temp_free_i64(tmp); break; } @@ -4758,14 +4783,14 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) { /* 64 bit */ TCGv_i64 tmp = tcg_const_i64(0); - tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_offset(rd, MO_64)); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(rd)); + tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_offset(s, rd, MO_64)); + tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd)); tcg_temp_free_i64(tmp); break; } case 2: /* 64 bit to top half. */ - tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(rd)); + tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(s, rd)); break; } } else { @@ -4774,15 +4799,15 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) switch (type) { case 0: /* 32 bit */ - tcg_gen_ld32u_i64(tcg_rd, cpu_env, fp_reg_offset(rn, MO_32)); + tcg_gen_ld32u_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_32)); break; case 1: /* 64 bit */ - tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_offset(rn, MO_64)); + tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_64)); break; case 2: /* 64 bits from top half */ - tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_hi_offset(rn)); + tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_hi_offset(s, rn)); break; } } @@ -5727,7 +5752,7 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) tcg_rd = new_tmp_a64(s); for (i = 0; i < 2; i++) { - int foffs = i ? fp_reg_hi_offset(rd) : fp_reg_offset(rd, MO_64); + int foffs = i ? fp_reg_hi_offset(s, rd) : fp_reg_offset(s, rd, MO_64); if (i == 1 && !is_q) { /* non-quad ops clear high half of vector */ @@ -10557,6 +10582,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->insn = insn; s->pc += 4; + s->fp_access_checked = false; + switch (extract32(insn, 25, 4)) { case 0x0: case 0x1: case 0x2: case 0x3: /* UNALLOCATED */ unallocated_encoding(s); diff --git a/target-arm/translate.h b/target-arm/translate.h index 4536f82cdc88..3f7d5ca24d38 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -32,6 +32,14 @@ typedef struct DisasContext { int current_pl; GHashTable *cp_regs; uint64_t features; /* CPU features bits */ + /* Because unallocated encodings generate different exception syndrome + * information from traps due to FP being disabled, we can't do a single + * "is fp access disabled" check at a high level in the decode tree. + * To help in catching bugs where the access check was forgotten in some + * code path, we set this flag when the access check is done, and assert + * that it is set at the point where we actually touch the FP regs. + */ + bool fp_access_checked; #define TMP_A64_MAX 16 int tmp_a64_count; TCGv_i64 tmp_a64[TMP_A64_MAX]; From 2c7ffc414d8591018248b5487757e45f7bb6bd3c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:40 +0100 Subject: [PATCH 09/51] target-arm: Fix VFP enables for AArch32 EL0 under AArch64 EL1 The current A32/T32 decoder bases its "is VFP/Neon enabled?" check on the FPSCR.EN bit. This is correct if EL1 is AArch32, but for an AArch64 EL1 the logic is different: it must act as if FPSCR.EN is always set. Instead, trapping must happen according to CPACR bits for cp10/cp11; these cover all of FP/Neon, including the FPSCR/FPSID/MVFR register accesses which FPSCR.EN does not affect. Add support for CPACR checks (which are also required for ARMv7, but were unimplemented because Linux happens not to use them) and make sure they generate exceptions with the correct syndrome. We actually return incorrect syndrome information for cases where FP is disabled but the specific instruction bit pattern is unallocated: strictly these should be the Uncategorized exception, not a "SIMD disabled" exception. This should be mostly harmless, and the structure of the A32/T32 VFP/Neon decoder makes it painful to put the 'FP disabled?' checks in the right places. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.h | 10 +++++++++- target-arm/translate.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 72c4c7a9b5d2..ff56519e6513 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1104,6 +1104,8 @@ static inline int cpu_mmu_index (CPUARMState *env) #define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) #define ARM_TBFLAG_BSWAP_CODE_SHIFT 16 #define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT) +#define ARM_TBFLAG_CPACR_FPEN_SHIFT 17 +#define ARM_TBFLAG_CPACR_FPEN_MASK (1 << ARM_TBFLAG_CPACR_FPEN_SHIFT) /* Bit usage when in AArch64 state */ #define ARM_TBFLAG_AA64_EL_SHIFT 0 @@ -1128,6 +1130,8 @@ static inline int cpu_mmu_index (CPUARMState *env) (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) #define ARM_TBFLAG_BSWAP_CODE(F) \ (((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT) +#define ARM_TBFLAG_CPACR_FPEN(F) \ + (((F) & ARM_TBFLAG_CPACR_FPEN_MASK) >> ARM_TBFLAG_CPACR_FPEN_SHIFT) #define ARM_TBFLAG_AA64_EL(F) \ (((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT) #define ARM_TBFLAG_AA64_FPEN(F) \ @@ -1161,9 +1165,13 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, if (privmode) { *flags |= ARM_TBFLAG_PRIV_MASK; } - if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { + if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30) + || arm_el_is_aa64(env, 1)) { *flags |= ARM_TBFLAG_VFPEN_MASK; } + if (fpen == 3 || (fpen == 1 && arm_current_pl(env) != 0)) { + *flags |= ARM_TBFLAG_CPACR_FPEN_MASK; + } } *cs_base = 0; diff --git a/target-arm/translate.c b/target-arm/translate.c index 84700ca7fd78..03e2c00e94a3 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -2952,6 +2952,16 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) if (!arm_feature(env, ARM_FEATURE_VFP)) return 1; + /* FIXME: this access check should not take precedence over UNDEF + * for invalid encodings; we will generate incorrect syndrome information + * for attempts to execute invalid vfp/neon encodings with FP disabled. + */ + if (!s->cpacr_fpen) { + gen_exception_insn(s, 4, EXCP_UDEF, + syn_fp_access_trap(1, 0xe, s->thumb)); + return 0; + } + if (!s->vfp_enabled) { /* VFP disabled. Only allow fmxr/fmrx to/from some control regs. */ if ((insn & 0x0fe00fff) != 0x0ee00a10) @@ -4232,6 +4242,16 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) TCGv_i32 tmp2; TCGv_i64 tmp64; + /* FIXME: this access check should not take precedence over UNDEF + * for invalid encodings; we will generate incorrect syndrome information + * for attempts to execute invalid vfp/neon encodings with FP disabled. + */ + if (!s->cpacr_fpen) { + gen_exception_insn(s, 4, EXCP_UDEF, + syn_fp_access_trap(1, 0xe, s->thumb)); + return 0; + } + if (!s->vfp_enabled) return 1; VFP_DREG_D(rd, insn); @@ -4954,6 +4974,16 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins TCGv_i32 tmp, tmp2, tmp3, tmp4, tmp5; TCGv_i64 tmp64; + /* FIXME: this access check should not take precedence over UNDEF + * for invalid encodings; we will generate incorrect syndrome information + * for attempts to execute invalid vfp/neon encodings with FP disabled. + */ + if (!s->cpacr_fpen) { + gen_exception_insn(s, 4, EXCP_UDEF, + syn_fp_access_trap(1, 0xe, s->thumb)); + return 0; + } + if (!s->vfp_enabled) return 1; q = (insn & (1 << 6)) != 0; @@ -10736,6 +10766,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, #if !defined(CONFIG_USER_ONLY) dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); #endif + dc->cpacr_fpen = ARM_TBFLAG_CPACR_FPEN(tb->flags); dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); From 2c8dd318639a37c308d7946d9f6548727dfc32ca Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 15 Apr 2014 19:18:40 +0100 Subject: [PATCH 10/51] target-arm: Add v8 mmu translation support Add support for v8 page table walks. This supports stage 1 translations for 4KB, 16KB and 64KB page sizes starting with 0 or 1 level. Signed-off-by: Rob Herring [PMM: fix style nits, fold in 16/64K page support patch, use arm_el_is_aa64() to decide whether to do 64 bit page table walk] Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 109 +++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 32 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 9866e50ab7a3..4b6c1b63622b 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -10,7 +10,7 @@ #include /* For crc32 */ #ifndef CONFIG_USER_ONLY -static inline int get_phys_addr(CPUARMState *env, uint32_t address, +static inline int get_phys_addr(CPUARMState *env, target_ulong address, int access_type, int is_user, hwaddr *phys_ptr, int *prot, target_ulong *page_size); @@ -1151,14 +1151,15 @@ static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) #ifndef CONFIG_USER_ONLY /* get_phys_addr() isn't present for user-mode-only targets */ -/* Return true if extended addresses are enabled, ie this is an - * LPAE implementation and we are using the long-descriptor translation - * table format because the TTBCR EAE bit is set. +/* Return true if extended addresses are enabled. + * This is always the case if our translation regime is 64 bit, + * but depends on TTBCR.EAE for 32 bit. */ static inline bool extended_addresses_enabled(CPUARMState *env) { - return arm_feature(env, ARM_FEATURE_LPAE) - && (env->cp15.c2_control & (1U << 31)); + return arm_el_is_aa64(env, 1) + || ((arm_feature(env, ARM_FEATURE_LPAE) + && (env->cp15.c2_control & (1U << 31)))); } static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri) @@ -3402,7 +3403,7 @@ typedef enum { permission_fault = 3, } MMUFaultType; -static int get_phys_addr_lpae(CPUARMState *env, uint32_t address, +static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, int access_type, int is_user, hwaddr *phys_ptr, int *prot, target_ulong *page_size_ptr) @@ -3412,26 +3413,46 @@ static int get_phys_addr_lpae(CPUARMState *env, uint32_t address, MMUFaultType fault_type = translation_fault; uint32_t level = 1; uint32_t epd; - uint32_t tsz; + int32_t tsz; + uint32_t tg; uint64_t ttbr; int ttbr_select; - int n; - hwaddr descaddr; + hwaddr descaddr, descmask; uint32_t tableattrs; target_ulong page_size; uint32_t attrs; + int32_t granule_sz = 9; + int32_t va_size = 32; + int32_t tbi = 0; + + if (arm_el_is_aa64(env, 1)) { + va_size = 64; + if (extract64(address, 55, 1)) + tbi = extract64(env->cp15.c2_control, 38, 1); + else + tbi = extract64(env->cp15.c2_control, 37, 1); + tbi *= 8; + } /* Determine whether this address is in the region controlled by * TTBR0 or TTBR1 (or if it is in neither region and should fault). * This is a Non-secure PL0/1 stage 1 translation, so controlled by * TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32: */ - uint32_t t0sz = extract32(env->cp15.c2_control, 0, 3); - uint32_t t1sz = extract32(env->cp15.c2_control, 16, 3); - if (t0sz && !extract32(address, 32 - t0sz, t0sz)) { + uint32_t t0sz = extract32(env->cp15.c2_control, 0, 6); + if (arm_el_is_aa64(env, 1)) { + t0sz = MIN(t0sz, 39); + t0sz = MAX(t0sz, 16); + } + uint32_t t1sz = extract32(env->cp15.c2_control, 16, 6); + if (arm_el_is_aa64(env, 1)) { + t1sz = MIN(t1sz, 39); + t1sz = MAX(t1sz, 16); + } + if (t0sz && !extract64(address, va_size - t0sz, t0sz - tbi)) { /* there is a ttbr0 region and we are in it (high bits all zero) */ ttbr_select = 0; - } else if (t1sz && !extract32(~address, 32 - t1sz, t1sz)) { + } else if (t1sz && !extract64(~address, va_size - t1sz, t1sz - tbi)) { /* there is a ttbr1 region and we are in it (high bits all one) */ ttbr_select = 1; } else if (!t0sz) { @@ -3457,10 +3478,26 @@ static int get_phys_addr_lpae(CPUARMState *env, uint32_t address, ttbr = env->cp15.ttbr0_el1; epd = extract32(env->cp15.c2_control, 7, 1); tsz = t0sz; + + tg = extract32(env->cp15.c2_control, 14, 2); + if (tg == 1) { /* 64KB pages */ + granule_sz = 13; + } + if (tg == 2) { /* 16KB pages */ + granule_sz = 11; + } } else { ttbr = env->cp15.ttbr1_el1; epd = extract32(env->cp15.c2_control, 23, 1); tsz = t1sz; + + tg = extract32(env->cp15.c2_control, 30, 2); + if (tg == 3) { /* 64KB pages */ + granule_sz = 13; + } + if (tg == 1) { /* 16KB pages */ + granule_sz = 11; + } } if (epd) { @@ -3468,34 +3505,37 @@ static int get_phys_addr_lpae(CPUARMState *env, uint32_t address, goto do_fault; } - /* If the region is small enough we will skip straight to a 2nd level - * lookup. This affects the number of bits of the address used in - * combination with the TTBR to find the first descriptor. ('n' here - * matches the usage in the ARM ARM sB3.6.6, where bits [39..n] are - * from the TTBR, [n-1..3] from the vaddr, and [2..0] always zero). + /* The starting level depends on the virtual address size which can be + * up to 48-bits and the translation granule size. */ - if (tsz > 1) { - level = 2; - n = 14 - tsz; + if ((va_size - tsz) > (granule_sz * 4 + 3)) { + level = 0; + } else if ((va_size - tsz) > (granule_sz * 3 + 3)) { + level = 1; } else { - n = 5 - tsz; + level = 2; } /* Clear the vaddr bits which aren't part of the within-region address, * so that we don't have to special case things when calculating the * first descriptor address. */ - address &= (0xffffffffU >> tsz); + if (tsz) { + address &= (1ULL << (va_size - tsz)) - 1; + } + + descmask = (1ULL << (granule_sz + 3)) - 1; /* Now we can extract the actual base address from the TTBR */ - descaddr = extract64(ttbr, 0, 40); - descaddr &= ~((1ULL << n) - 1); + descaddr = extract64(ttbr, 0, 48); + descaddr &= ~((1ULL << (va_size - tsz - (granule_sz * (4 - level)))) - 1); tableattrs = 0; for (;;) { uint64_t descriptor; - descaddr |= ((address >> (9 * (4 - level))) & 0xff8); + descaddr |= (address >> (granule_sz * (4 - level))) & descmask; + descaddr &= ~7ULL; descriptor = ldq_phys(cs->as, descaddr); if (!(descriptor & 1) || (!(descriptor & 2) && (level == 3))) { @@ -3518,11 +3558,16 @@ static int get_phys_addr_lpae(CPUARMState *env, uint32_t address, * These are basically the same thing, although the number * of bits we pull in from the vaddr varies. */ - page_size = (1 << (39 - (9 * level))); + page_size = (1 << ((granule_sz * (4 - level)) + 3)); descaddr |= (address & (page_size - 1)); /* Extract attributes from the descriptor and merge with table attrs */ - attrs = extract64(descriptor, 2, 10) - | (extract64(descriptor, 52, 12) << 10); + if (arm_feature(env, ARM_FEATURE_V8)) { + attrs = extract64(descriptor, 2, 10) + | (extract64(descriptor, 53, 11) << 10); + } else { + attrs = extract64(descriptor, 2, 10) + | (extract64(descriptor, 52, 12) << 10); + } attrs |= extract32(tableattrs, 0, 2) << 11; /* XN, PXN */ attrs |= extract32(tableattrs, 3, 1) << 5; /* APTable[1] => AP[2] */ /* The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1 @@ -3656,7 +3701,7 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, * @prot: set to the permissions for the page containing phys_ptr * @page_size: set to the size of the page containing phys_ptr */ -static inline int get_phys_addr(CPUARMState *env, uint32_t address, +static inline int get_phys_addr(CPUARMState *env, target_ulong address, int access_type, int is_user, hwaddr *phys_ptr, int *prot, target_ulong *page_size) @@ -3705,7 +3750,7 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, if (ret == 0) { /* Map a single [sub]page. */ phys_addr &= ~(hwaddr)0x3ff; - address &= ~(uint32_t)0x3ff; + address &= ~(target_ulong)0x3ff; tlb_set_page(cs, address, phys_addr, prot, mmu_idx, page_size); return 0; } From 9225d739e7f6ec8d2139f79c3d2e3282cc725364 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:41 +0100 Subject: [PATCH 11/51] target-arm: Don't mention PMU in debug feature register Suppress the ID_AA64DFR0_EL1 PMUVer field, even if the CPU specific value claims that it exists. QEMU doesn't currently implement it, and not advertising it prevents the guest from trying to use it and getting UNDEFs on unimplemented registers. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- This is arguably a hack, but otherwise Linux tries to prod half a dozen PMU sysregs. --- target-arm/helper.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 4b6c1b63622b..62f7fd32805c 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2036,7 +2036,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_aa64dfr0 }, + /* We mask out the PMUVer field, beacuse we don't currently + * implement the PMU. Not advertising it prevents the guest + * from trying to use it and getting UNDEFs on registers we + * don't implement. + */ + .resetvalue = cpu->id_aa64dfr0 & ~0xf00 }, { .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, From aca3f40b374428e9c01068cf96294483cbb760a0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:41 +0100 Subject: [PATCH 12/51] target-arm: A64: Implement DC ZVA Implement the DC ZVA instruction, which clears a block of memory. The fast path obtains a pointer to the underlying RAM via the TCG TLB data structure so we can do a direct memset(), with fallback to a simple byte-store loop in the slow path. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Acked-by: Peter Crosthwaite --- include/exec/softmmu_exec.h | 52 +++++++++++++++ target-arm/cpu-qom.h | 2 + target-arm/cpu.h | 3 +- target-arm/cpu64.c | 1 + target-arm/helper.c | 122 ++++++++++++++++++++++++++++++++++-- target-arm/helper.h | 1 + target-arm/translate-a64.c | 5 ++ 7 files changed, 180 insertions(+), 6 deletions(-) diff --git a/include/exec/softmmu_exec.h b/include/exec/softmmu_exec.h index 6fde154527c6..470db2017491 100644 --- a/include/exec/softmmu_exec.h +++ b/include/exec/softmmu_exec.h @@ -162,3 +162,55 @@ #define stw(p, v) stw_data(p, v) #define stl(p, v) stl_data(p, v) #define stq(p, v) stq_data(p, v) + +/** + * tlb_vaddr_to_host: + * @env: CPUArchState + * @addr: guest virtual address to look up + * @access_type: 0 for read, 1 for write, 2 for execute + * @mmu_idx: MMU index to use for lookup + * + * Look up the specified guest virtual index in the TCG softmmu TLB. + * If the TLB contains a host virtual address suitable for direct RAM + * access, then return it. Otherwise (TLB miss, TLB entry is for an + * I/O access, etc) return NULL. + * + * This is the equivalent of the initial fast-path code used by + * TCG backends for guest load and store accesses. + */ +static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr, + int access_type, int mmu_idx) +{ + int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + CPUTLBEntry *tlbentry = &env->tlb_table[mmu_idx][index]; + target_ulong tlb_addr; + uintptr_t haddr; + + switch (access_type) { + case 0: + tlb_addr = tlbentry->addr_read; + break; + case 1: + tlb_addr = tlbentry->addr_write; + break; + case 2: + tlb_addr = tlbentry->addr_code; + break; + default: + g_assert_not_reached(); + } + + if ((addr & TARGET_PAGE_MASK) + != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + /* TLB entry is for a different page */ + return NULL; + } + + if (tlb_addr & ~TARGET_PAGE_MASK) { + /* IO access */ + return NULL; + } + + haddr = addr + env->tlb_table[mmu_idx][index].addend; + return (void *)haddr; +} diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 00234e1d3d72..41caa6c78076 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -150,6 +150,8 @@ typedef struct ARMCPU { uint32_t reset_cbar; uint32_t reset_auxcr; bool reset_hivecs; + /* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */ + uint32_t dcz_blocksize; } ARMCPU; #define TYPE_AARCH64_CPU "aarch64-cpu" diff --git a/target-arm/cpu.h b/target-arm/cpu.h index ff56519e6513..a00ff73fb12d 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -758,7 +758,8 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) #define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8)) #define ARM_CP_NZCV (ARM_CP_SPECIAL | (3 << 8)) #define ARM_CP_CURRENTEL (ARM_CP_SPECIAL | (4 << 8)) -#define ARM_LAST_SPECIAL ARM_CP_CURRENTEL +#define ARM_CP_DC_ZVA (ARM_CP_SPECIAL | (5 << 8)) +#define ARM_LAST_SPECIAL ARM_CP_DC_ZVA /* Used only as a terminator for ARMCPRegInfo lists */ #define ARM_CP_SENTINEL 0xffff /* Mask of only the flag bits in a type field */ diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index 8426bf1333f8..fccecc252794 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -46,6 +46,7 @@ static void aarch64_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_AARCH64); cpu->ctr = 0x80030003; /* 32 byte I and D cacheline size, VIPT icache */ + cpu->dcz_blocksize = 7; /* 512 bytes */ } #endif diff --git a/target-arm/helper.c b/target-arm/helper.c index 62f7fd32805c..2ffc5885759b 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -10,6 +10,8 @@ #include /* For crc32 */ #ifndef CONFIG_USER_ONLY +#include "exec/softmmu_exec.h" + static inline int get_phys_addr(CPUARMState *env, target_ulong address, int access_type, int is_user, hwaddr *phys_ptr, int *prot, @@ -1745,6 +1747,29 @@ static void tlbi_aa64_asid_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush(CPU(cpu), asid == 0); } +static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* We don't implement EL2, so the only control on DC ZVA is the + * bit in the SCTLR which can prohibit access for EL0. + */ + if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_DZE)) { + return CP_ACCESS_TRAP; + } + return CP_ACCESS_OK; +} + +static uint64_t aa64_dczid_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + int dzp_bit = 1 << 4; + + /* DZP indicates whether DC ZVA access is allowed */ + if (aa64_zva_access(env, NULL) != CP_ACCESS_OK) { + dzp_bit = 0; + } + return cpu->dcz_blocksize | dzp_bit; +} + static const ARMCPRegInfo v8_cp_reginfo[] = { /* Minimal set of EL0-visible registers. This will need to be expanded * significantly for system emulation of AArch64 CPUs. @@ -1764,13 +1789,18 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "FPSR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4, .access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write }, - /* Prohibit use of DC ZVA. OPTME: implement DC ZVA and allow its use. - * For system mode the DZP bit here will need to be computed, not constant. - */ { .name = "DCZID_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0, - .access = PL0_R, .type = ARM_CP_CONST, - .resetvalue = 0x10 }, + .access = PL0_R, .type = ARM_CP_NO_MIGRATE, + .readfn = aa64_dczid_read }, + { .name = "DC_ZVA", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 4, .opc2 = 1, + .access = PL0_W, .type = ARM_CP_DC_ZVA, +#ifndef CONFIG_USER_ONLY + /* Avoid overhead of an access check that always passes in user-mode */ + .accessfn = aa64_zva_access, +#endif + }, { .name = "CURRENTEL", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .opc2 = 2, .crn = 4, .crm = 2, .access = PL1_R, .type = ARM_CP_CURRENTEL }, @@ -3930,6 +3960,88 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) #endif +void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) +{ + /* Implement DC ZVA, which zeroes a fixed-length block of memory. + * Note that we do not implement the (architecturally mandated) + * alignment fault for attempts to use this on Device memory + * (which matches the usual QEMU behaviour of not implementing either + * alignment faults or any memory attribute handling). + */ + + ARMCPU *cpu = arm_env_get_cpu(env); + uint64_t blocklen = 4 << cpu->dcz_blocksize; + uint64_t vaddr = vaddr_in & ~(blocklen - 1); + +#ifndef CONFIG_USER_ONLY + { + /* Slightly awkwardly, QEMU's TARGET_PAGE_SIZE may be less than + * the block size so we might have to do more than one TLB lookup. + * We know that in fact for any v8 CPU the page size is at least 4K + * and the block size must be 2K or less, but TARGET_PAGE_SIZE is only + * 1K as an artefact of legacy v5 subpage support being present in the + * same QEMU executable. + */ + int maxidx = DIV_ROUND_UP(blocklen, TARGET_PAGE_SIZE); + void *hostaddr[maxidx]; + int try, i; + + for (try = 0; try < 2; try++) { + + for (i = 0; i < maxidx; i++) { + hostaddr[i] = tlb_vaddr_to_host(env, + vaddr + TARGET_PAGE_SIZE * i, + 1, cpu_mmu_index(env)); + if (!hostaddr[i]) { + break; + } + } + if (i == maxidx) { + /* If it's all in the TLB it's fair game for just writing to; + * we know we don't need to update dirty status, etc. + */ + for (i = 0; i < maxidx - 1; i++) { + memset(hostaddr[i], 0, TARGET_PAGE_SIZE); + } + memset(hostaddr[i], 0, blocklen - (i * TARGET_PAGE_SIZE)); + return; + } + /* OK, try a store and see if we can populate the tlb. This + * might cause an exception if the memory isn't writable, + * in which case we will longjmp out of here. We must for + * this purpose use the actual register value passed to us + * so that we get the fault address right. + */ + helper_ret_stb_mmu(env, vaddr_in, 0, cpu_mmu_index(env), GETRA()); + /* Now we can populate the other TLB entries, if any */ + for (i = 0; i < maxidx; i++) { + uint64_t va = vaddr + TARGET_PAGE_SIZE * i; + if (va != (vaddr_in & TARGET_PAGE_MASK)) { + helper_ret_stb_mmu(env, va, 0, cpu_mmu_index(env), GETRA()); + } + } + } + + /* Slow path (probably attempt to do this to an I/O device or + * similar, or clearing of a block of code we have translations + * cached for). Just do a series of byte writes as the architecture + * demands. It's not worth trying to use a cpu_physical_memory_map(), + * memset(), unmap() sequence here because: + * + we'd need to account for the blocksize being larger than a page + * + the direct-RAM access case is almost always going to be dealt + * with in the fastpath code above, so there's no speed benefit + * + we would have to deal with the map returning NULL because the + * bounce buffer was in use + */ + for (i = 0; i < blocklen; i++) { + helper_ret_stb_mmu(env, vaddr + i, 0, cpu_mmu_index(env), GETRA()); + } + } +#else + memset(g2h(vaddr), 0, blocklen); +#endif +} + /* Note that signed overflow is undefined in C. The following routines are careful to use unsigned types where modulo arithmetic is required. Failure to do so _will_ break on newer gcc. */ diff --git a/target-arm/helper.h b/target-arm/helper.h index 0abdb0c8e0c9..597716924c18 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -515,6 +515,7 @@ DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32) DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) +DEF_HELPER_2(dc_zva, void, env, i64) #ifdef TARGET_AARCH64 #include "helper-a64.h" diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 0ec2f6558661..4c5402a0f7e6 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1335,6 +1335,11 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, tcg_rt = cpu_reg(s, rt); tcg_gen_movi_i64(tcg_rt, s->current_pl << 2); return; + case ARM_CP_DC_ZVA: + /* Writes clear the aligned block of memory which rt points into. */ + tcg_rt = cpu_reg(s, rt); + gen_helper_dc_zva(cpu_env, tcg_rt); + return; default: break; } From 7e09797c299712cafa7bc05dd57c1b13afcc6039 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:41 +0100 Subject: [PATCH 13/51] target-arm: Use dedicated CPU state fields for ARM946 access bit registers The ARM946 model currently uses the c5_data and c5_insn fields in the CPU state struct to store the contents of its access permission registers. This is confusing and a good source of bugs because for all the MMU-based CPUs those fields are fault status and fault address registers, which behave completely differently; they just happen to use the same cpreg encoding. Split them out to use their own fields instead. These registers are only present in PMSAv5 MPU systems (of which the ARM946 is our only current example); PMSAv6 and PMSAv7 (which we have no implementations of) handle access permissions differently. We name the new state fields accordingly. Note that this change fixes a bug where a data abort or prefetch abort on the ARM946 would accidentally corrupt the access permission registers because the interrupt handling code assumed the c5_data and c5_insn fields were always fault status registers. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.h | 2 ++ target-arm/helper.c | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index a00ff73fb12d..42fa7db43395 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -179,6 +179,8 @@ typedef struct CPUARMState { uint32_t c2_insn; /* MPU instruction cachable bits. */ uint32_t c3; /* MMU domain access control register MPU write buffer control. */ + uint32_t pmsav5_data_ap; /* PMSAv5 MPU data access permissions */ + uint32_t pmsav5_insn_ap; /* PMSAv5 MPU insn access permissions */ uint32_t c5_insn; /* Fault status registers. */ uint32_t c5_data; uint32_t c6_region[8]; /* MPU base/size registers. */ diff --git a/target-arm/helper.c b/target-arm/helper.c index 2ffc5885759b..289a5c071043 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1275,40 +1275,44 @@ static uint32_t extended_mpu_ap_bits(uint32_t val) static void pmsav5_data_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - env->cp15.c5_data = extended_mpu_ap_bits(value); + env->cp15.pmsav5_data_ap = extended_mpu_ap_bits(value); } static uint64_t pmsav5_data_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return simple_mpu_ap_bits(env->cp15.c5_data); + return simple_mpu_ap_bits(env->cp15.pmsav5_data_ap); } static void pmsav5_insn_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - env->cp15.c5_insn = extended_mpu_ap_bits(value); + env->cp15.pmsav5_insn_ap = extended_mpu_ap_bits(value); } static uint64_t pmsav5_insn_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return simple_mpu_ap_bits(env->cp15.c5_insn); + return simple_mpu_ap_bits(env->cp15.pmsav5_insn_ap); } static const ARMCPRegInfo pmsav5_cp_reginfo[] = { { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, - .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap), + .resetvalue = 0, .readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, }, { .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, - .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap), + .resetvalue = 0, .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, }, { .name = "DATA_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, }, + .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap), + .resetvalue = 0, }, { .name = "INSN_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 3, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, }, + .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap), + .resetvalue = 0, }, { .name = "DCACHE_CFG", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c2_data), .resetvalue = 0, }, @@ -3676,9 +3680,9 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, return 2; if (access_type == 2) { - mask = env->cp15.c5_insn; + mask = env->cp15.pmsav5_insn_ap; } else { - mask = env->cp15.c5_data; + mask = env->cp15.pmsav5_data_ap; } mask = (mask >> (n * 4)) & 0xf; switch (mask) { From 6cd8a2649aafb8e53db4a8f0bf0ea629441f8c03 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 15 Apr 2014 19:18:42 +0100 Subject: [PATCH 14/51] target-arm: Implement AArch64 views of fault status and data registers Implement AArch64 views of ESR_EL1 and FAR_EL1, and make the 32 bit DFSR, DFAR, IFAR share state with them as architecturally specified. The IFSR doesn't share state with any AArch64 register visible at EL1, so just rename the state field without widening it to 64 bits. Signed-off-by: Rob Herring [PMM: Minor tweaks; fix some bugs involving inconsistencies between use of offsetof() or offsetoflow32() and struct field width] Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.c | 2 +- target-arm/cpu.h | 7 +++---- target-arm/helper.c | 38 +++++++++++++++++++++++++------------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index d62b792b29a1..a3c749284689 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -425,7 +425,7 @@ static void arm1026_initfn(Object *obj) ARMCPRegInfo ifar = { .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c6_insn), + .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el1), .resetvalue = 0 }; define_one_arm_cp_reg(cpu, &ifar); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 42fa7db43395..4ca75cd91db9 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -181,11 +181,10 @@ typedef struct CPUARMState { MPU write buffer control. */ uint32_t pmsav5_data_ap; /* PMSAv5 MPU data access permissions */ uint32_t pmsav5_insn_ap; /* PMSAv5 MPU insn access permissions */ - uint32_t c5_insn; /* Fault status registers. */ - uint32_t c5_data; + uint32_t ifsr_el2; /* Fault status registers. */ + uint64_t esr_el1; uint32_t c6_region[8]; /* MPU base/size registers. */ - uint32_t c6_insn; /* Fault address registers. */ - uint32_t c6_data; + uint64_t far_el1; /* Fault address registers. */ uint32_t c7_par; /* Translation result. */ uint32_t c7_par_hi; /* Translation result, high 32 bits */ uint32_t c9_insn; /* Cache lockdown registers. */ diff --git a/target-arm/helper.c b/target-arm/helper.c index 289a5c071043..5f6233b2c4f8 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -475,7 +475,8 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "DMB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5, .access = PL0_W, .type = ARM_CP_NOP }, { .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_insn), + .access = PL1_RW, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el1), .resetvalue = 0, }, /* Watchpoint Fault Address Register : should actually only be present * for 1136, 1176, 11MPCore. @@ -1414,11 +1415,16 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, }, + .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, + .fieldoffset = offsetoflow32(CPUARMState, cp15.esr_el1), + .resetfn = arm_cp_reset_ignore, }, { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, }, + .fieldoffset = offsetof(CPUARMState, cp15.ifsr_el2), .resetvalue = 0, }, + { .name = "ESR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.esr_el1), .resetvalue = 0, }, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1), @@ -1436,8 +1442,10 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .writefn = vmsa_ttbcr_write, .resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write, .fieldoffset = offsetoflow32(CPUARMState, cp15.c2_control) }, - { .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data), + /* 64-bit FAR; this entry also gives us the AArch32 DFAR */ + { .name = "FAR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el1), .resetvalue = 0, }, REGINFO_SENTINEL }; @@ -1477,7 +1485,8 @@ static void omap_cachemaint_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo omap_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_OVERRIDE, - .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, }, + .fieldoffset = offsetoflow32(CPUARMState, cp15.esr_el1), + .resetvalue = 0, }, { .name = "", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP }, { .name = "TICONFIG", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, @@ -3087,20 +3096,23 @@ void arm_cpu_do_interrupt(CPUState *cs) env->exception.fsr = 2; /* Fall through to prefetch abort. */ case EXCP_PREFETCH_ABORT: - env->cp15.c5_insn = env->exception.fsr; - env->cp15.c6_insn = env->exception.vaddress; + env->cp15.ifsr_el2 = env->exception.fsr; + env->cp15.far_el1 = deposit64(env->cp15.far_el1, 32, 32, + env->exception.vaddress); qemu_log_mask(CPU_LOG_INT, "...with IFSR 0x%x IFAR 0x%x\n", - env->cp15.c5_insn, env->cp15.c6_insn); + env->cp15.ifsr_el2, (uint32_t)env->exception.vaddress); new_mode = ARM_CPU_MODE_ABT; addr = 0x0c; mask = CPSR_A | CPSR_I; offset = 4; break; case EXCP_DATA_ABORT: - env->cp15.c5_data = env->exception.fsr; - env->cp15.c6_data = env->exception.vaddress; + env->cp15.esr_el1 = env->exception.fsr; + env->cp15.far_el1 = deposit64(env->cp15.far_el1, 0, 32, + env->exception.vaddress); qemu_log_mask(CPU_LOG_INT, "...with DFSR 0x%x DFAR 0x%x\n", - env->cp15.c5_data, env->cp15.c6_data); + (uint32_t)env->cp15.esr_el1, + (uint32_t)env->exception.vaddress); new_mode = ARM_CPU_MODE_ABT; addr = 0x10; mask = CPSR_A | CPSR_I; From a0618a1990e4df30a76cf5b441b4aa7f002b0d64 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:42 +0100 Subject: [PATCH 15/51] target-arm: Add AArch64 ELR_EL1 register. Add the AArch64 ELR_EL1 register. Note that this does not live in env->cp15: for KVM migration compatibility we need to migrate it separately rather than as part of the system registers, because the KVM-to-userspace interface puts it in the struct kvm_regs rather than making them visible via the ONE_REG ioctls. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.h | 2 ++ target-arm/helper.c | 4 ++++ target-arm/kvm64.c | 15 ++++++++++++++- target-arm/machine.c | 7 ++++--- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 4ca75cd91db9..ecdd7a73a623 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -162,6 +162,8 @@ typedef struct CPUARMState { uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ uint64_t daif; /* exception masks, in the bits they are in in PSTATE */ + uint64_t elr_el1; /* AArch64 ELR_EL1 */ + /* System control coprocessor (cp15) */ struct { uint32_t c0_cpuid; diff --git a/target-arm/helper.c b/target-arm/helper.c index 5f6233b2c4f8..276ecf28dc32 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1911,6 +1911,10 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "OSLAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, .access = PL1_W, .type = ARM_CP_NOP }, + { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_NO_MIGRATE, + .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, elr_el1) }, REGINFO_SENTINEL }; diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index 1b7ca9037463..ee727487b8c5 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -144,9 +144,15 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + reg.id = AARCH64_CORE_REG(elr_el1); + reg.addr = (uintptr_t) &env->elr_el1; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + /* TODO: * SP_EL1 - * ELR_EL1 * SPSR[] * FP state * system registers @@ -195,6 +201,13 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } + reg.id = AARCH64_CORE_REG(elr_el1); + reg.addr = (uintptr_t) &env->elr_el1; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + /* TODO: other registers */ return ret; } diff --git a/target-arm/machine.c b/target-arm/machine.c index b9d9c0f4649c..42b1c90ac372 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -222,9 +222,9 @@ static int cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_arm_cpu = { .name = "cpu", - .version_id = 14, - .minimum_version_id = 14, - .minimum_version_id_old = 14, + .version_id = 15, + .minimum_version_id = 15, + .minimum_version_id_old = 15, .pre_save = cpu_pre_save, .post_load = cpu_post_load, .fields = (VMStateField[]) { @@ -243,6 +243,7 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT32_ARRAY(env.banked_r14, ARMCPU, 6), VMSTATE_UINT32_ARRAY(env.usr_regs, ARMCPU, 5), VMSTATE_UINT32_ARRAY(env.fiq_regs, ARMCPU, 5), + VMSTATE_UINT64(env.elr_el1, ARMCPU), /* The length-check must come before the arrays to avoid * incoming data possibly overflowing the array. */ From f502cfc207ff288ec1f3dac10024c51ffe64a65d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:43 +0100 Subject: [PATCH 16/51] target-arm: Implement SP_EL0, SP_EL1 Implement handling for the AArch64 SP_EL0 system register. This holds the EL0 stack pointer, and is only accessible when it's not being used as the stack pointer, ie when we're in EL1 and EL1 is using its own stack pointer. We also provide a definition of the SP_EL1 register; this isn't guest visible as a system register for an implementation like QEMU which doesn't provide EL2 or EL3; however it is useful for ensuring the underlying state is migrated. We need to update the state fields in the CPU state whenever we switch stack pointers; this happens when we take an exception and also when SPSEL is used to change the bit in PSTATE which indicates which stack pointer EL1 should use. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.h | 2 ++ target-arm/helper.c | 34 ++++++++++++++++++++++++++++++++++ target-arm/internals.h | 25 +++++++++++++++++++++++++ target-arm/kvm64.c | 37 ++++++++++++++++++++++++++++++++++--- target-arm/machine.c | 7 ++++--- target-arm/op_helper.c | 2 +- 6 files changed, 100 insertions(+), 7 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index ecdd7a73a623..28b9bda21841 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -163,6 +163,7 @@ typedef struct CPUARMState { uint64_t daif; /* exception masks, in the bits they are in in PSTATE */ uint64_t elr_el1; /* AArch64 ELR_EL1 */ + uint64_t sp_el[2]; /* AArch64 banked stack pointers */ /* System control coprocessor (cp15) */ struct { @@ -434,6 +435,7 @@ int arm_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, * Only these are valid when in AArch64 mode; in * AArch32 mode SPSRs are basically CPSR-format. */ +#define PSTATE_SP (1U) #define PSTATE_M (0xFU) #define PSTATE_nRW (1U << 4) #define PSTATE_F (1U << 6) diff --git a/target-arm/helper.c b/target-arm/helper.c index 276ecf28dc32..27a3dc2a7556 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1783,6 +1783,27 @@ static uint64_t aa64_dczid_read(CPUARMState *env, const ARMCPRegInfo *ri) return cpu->dcz_blocksize | dzp_bit; } +static CPAccessResult sp_el0_access(CPUARMState *env, const ARMCPRegInfo *ri) +{ + if (!env->pstate & PSTATE_SP) { + /* Access to SP_EL0 is undefined if it's being used as + * the stack pointer. + */ + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + return CP_ACCESS_OK; +} + +static uint64_t spsel_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pstate & PSTATE_SP; +} + +static void spsel_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t val) +{ + update_spsel(env, val); +} + static const ARMCPRegInfo v8_cp_reginfo[] = { /* Minimal set of EL0-visible registers. This will need to be expanded * significantly for system emulation of AArch64 CPUs. @@ -1915,6 +1936,19 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .type = ARM_CP_NO_MIGRATE, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, elr_el1) }, + /* We rely on the access checks not allowing the guest to write to the + * state field when SPSel indicates that it's being used as the stack + * pointer. + */ + { .name = "SP_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 1, .opc2 = 0, + .access = PL1_RW, .accessfn = sp_el0_access, + .type = ARM_CP_NO_MIGRATE, + .fieldoffset = offsetof(CPUARMState, sp_el[0]) }, + { .name = "SPSel", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE, + .access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write }, REGINFO_SENTINEL }; diff --git a/target-arm/internals.h b/target-arm/internals.h index a527f02ff5a6..de79dfc3164d 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -60,6 +60,31 @@ enum arm_fprounding { int arm_rmode_to_sf(int rmode); +static inline void update_spsel(CPUARMState *env, uint32_t imm) +{ + /* Update PSTATE SPSel bit; this requires us to update the + * working stack pointer in xregs[31]. + */ + if (!((imm ^ env->pstate) & PSTATE_SP)) { + return; + } + env->pstate = deposit32(env->pstate, 0, 1, imm); + + /* EL0 has no access rights to update SPSel, and this code + * assumes we are updating SP for EL1 while running as EL1. + */ + assert(arm_current_pl(env) == 1); + if (env->pstate & PSTATE_SP) { + /* Switch from using SP_EL0 to using SP_ELx */ + env->sp_el[0] = env->xregs[31]; + env->xregs[31] = env->sp_el[1]; + } else { + /* Switch from SP_EL0 to SP_ELx */ + env->sp_el[1] = env->xregs[31]; + env->xregs[31] = env->sp_el[0]; + } +} + /* Valid Syndrome Register EC field values */ enum arm_exception_class { EC_UNCATEGORIZED = 0x00, diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index ee727487b8c5..39c4364593f8 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -121,8 +121,24 @@ int kvm_arch_put_registers(CPUState *cs, int level) } } + /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the + * QEMU side we keep the current SP in xregs[31] as well. + */ + if (env->pstate & PSTATE_SP) { + env->sp_el[1] = env->xregs[31]; + } else { + env->sp_el[0] = env->xregs[31]; + } + reg.id = AARCH64_CORE_REG(regs.sp); - reg.addr = (uintptr_t) &env->xregs[31]; + reg.addr = (uintptr_t) &env->sp_el[0]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + + reg.id = AARCH64_CORE_REG(sp_el1); + reg.addr = (uintptr_t) &env->sp_el[1]; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret) { return ret; @@ -152,7 +168,6 @@ int kvm_arch_put_registers(CPUState *cs, int level) } /* TODO: - * SP_EL1 * SPSR[] * FP state * system registers @@ -180,7 +195,14 @@ int kvm_arch_get_registers(CPUState *cs) } reg.id = AARCH64_CORE_REG(regs.sp); - reg.addr = (uintptr_t) &env->xregs[31]; + reg.addr = (uintptr_t) &env->sp_el[0]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + + reg.id = AARCH64_CORE_REG(sp_el1); + reg.addr = (uintptr_t) &env->sp_el[1]; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret) { return ret; @@ -194,6 +216,15 @@ int kvm_arch_get_registers(CPUState *cs) } pstate_write(env, val); + /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the + * QEMU side we keep the current SP in xregs[31] as well. + */ + if (env->pstate & PSTATE_SP) { + env->xregs[31] = env->sp_el[1]; + } else { + env->xregs[31] = env->sp_el[0]; + } + reg.id = AARCH64_CORE_REG(regs.pc); reg.addr = (uintptr_t) &env->pc; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); diff --git a/target-arm/machine.c b/target-arm/machine.c index 42b1c90ac372..c2c0780d4f96 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -222,9 +222,9 @@ static int cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_arm_cpu = { .name = "cpu", - .version_id = 15, - .minimum_version_id = 15, - .minimum_version_id_old = 15, + .version_id = 16, + .minimum_version_id = 16, + .minimum_version_id_old = 16, .pre_save = cpu_pre_save, .post_load = cpu_post_load, .fields = (VMStateField[]) { @@ -244,6 +244,7 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT32_ARRAY(env.usr_regs, ARMCPU, 5), VMSTATE_UINT32_ARRAY(env.fiq_regs, ARMCPU, 5), VMSTATE_UINT64(env.elr_el1, ARMCPU), + VMSTATE_UINT64_ARRAY(env.sp_el, ARMCPU, 2), /* The length-check must come before the arrays to avoid * incoming data possibly overflowing the array. */ diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 51edd9076ce1..64a33dd44976 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -371,7 +371,7 @@ void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm) switch (op) { case 0x05: /* SPSel */ - env->pstate = deposit32(env->pstate, 0, 1, imm); + update_spsel(env, imm); break; case 0x1e: /* DAIFSet */ env->daif |= (imm << 6) & PSTATE_DAIF; From a65f1de982302f5c33f668ad25a120eba7993d37 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:43 +0100 Subject: [PATCH 17/51] target-arm: Implement AArch64 SPSR_EL1 Implement the AArch64 SPSR_EL1. For compatibility with how KVM handles SPSRs and with the architectural mapping between AArch32 and AArch64, we put this in the banked_spsr[] array in the slot that is used for SVC in AArch32. This means we need to extend the array from uint32_t to uint64_t, which requires some reworking of the 32 bit KVM save/restore code. Signed-off-by: Peter Maydell Acked-by: Peter Crosthwaite --- target-arm/cpu.h | 2 +- target-arm/helper.c | 4 ++++ target-arm/kvm32.c | 18 +++++++++++++----- target-arm/kvm64.c | 19 ++++++++++++++++++- target-arm/machine.c | 8 ++++---- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 28b9bda21841..0ce4c26d1611 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -143,7 +143,7 @@ typedef struct CPUARMState { uint32_t spsr; /* Banked registers. */ - uint32_t banked_spsr[6]; + uint64_t banked_spsr[6]; uint32_t banked_r13[6]; uint32_t banked_r14[6]; diff --git a/target-arm/helper.c b/target-arm/helper.c index 27a3dc2a7556..68f8c6a832de 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1936,6 +1936,10 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .type = ARM_CP_NO_MIGRATE, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, elr_el1) }, + { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_NO_MIGRATE, + .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[0]) }, /* We rely on the access checks not allowing the guest to write to the * state field when SPSel indicates that it's being used as the stack * pointer. diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c index b21f84409664..a690d9935f39 100644 --- a/target-arm/kvm32.c +++ b/target-arm/kvm32.c @@ -295,6 +295,14 @@ typedef struct Reg { offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \ } +/* Like COREREG, but handle fields which are in a uint64_t in CPUARMState. */ +#define COREREG64(KERNELNAME, QEMUFIELD) \ + { \ + KVM_REG_ARM | KVM_REG_SIZE_U32 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \ + offsetoflow32(CPUARMState, QEMUFIELD) \ + } + static const Reg regs[] = { /* R0_usr .. R14_usr */ COREREG(usr_regs.uregs[0], regs[0]), @@ -315,16 +323,16 @@ static const Reg regs[] = { /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */ COREREG(svc_regs[0], banked_r13[1]), COREREG(svc_regs[1], banked_r14[1]), - COREREG(svc_regs[2], banked_spsr[1]), + COREREG64(svc_regs[2], banked_spsr[1]), COREREG(abt_regs[0], banked_r13[2]), COREREG(abt_regs[1], banked_r14[2]), - COREREG(abt_regs[2], banked_spsr[2]), + COREREG64(abt_regs[2], banked_spsr[2]), COREREG(und_regs[0], banked_r13[3]), COREREG(und_regs[1], banked_r14[3]), - COREREG(und_regs[2], banked_spsr[3]), + COREREG64(und_regs[2], banked_spsr[3]), COREREG(irq_regs[0], banked_r13[4]), COREREG(irq_regs[1], banked_r14[4]), - COREREG(irq_regs[2], banked_spsr[4]), + COREREG64(irq_regs[2], banked_spsr[4]), /* R8_fiq .. R14_fiq and SPSR_fiq */ COREREG(fiq_regs[0], fiq_regs[0]), COREREG(fiq_regs[1], fiq_regs[1]), @@ -333,7 +341,7 @@ static const Reg regs[] = { COREREG(fiq_regs[4], fiq_regs[4]), COREREG(fiq_regs[5], banked_r13[5]), COREREG(fiq_regs[6], banked_r14[5]), - COREREG(fiq_regs[7], banked_spsr[5]), + COREREG64(fiq_regs[7], banked_spsr[5]), /* R15 */ COREREG(usr_regs.uregs[15], regs[15]), /* VFP system registers */ diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index 39c4364593f8..e115879d9abb 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -167,8 +167,16 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + for (i = 0; i < KVM_NR_SPSR; i++) { + reg.id = AARCH64_CORE_REG(spsr[i]); + reg.addr = (uintptr_t) &env->banked_spsr[i - 1]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + } + /* TODO: - * SPSR[] * FP state * system registers */ @@ -239,6 +247,15 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } + for (i = 0; i < KVM_NR_SPSR; i++) { + reg.id = AARCH64_CORE_REG(spsr[i]); + reg.addr = (uintptr_t) &env->banked_spsr[i - 1]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + } + /* TODO: other registers */ return ret; } diff --git a/target-arm/machine.c b/target-arm/machine.c index c2c0780d4f96..b967223fc063 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -222,9 +222,9 @@ static int cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_arm_cpu = { .name = "cpu", - .version_id = 16, - .minimum_version_id = 16, - .minimum_version_id_old = 16, + .version_id = 17, + .minimum_version_id = 17, + .minimum_version_id_old = 17, .pre_save = cpu_pre_save, .post_load = cpu_post_load, .fields = (VMStateField[]) { @@ -238,7 +238,7 @@ const VMStateDescription vmstate_arm_cpu = { .offset = 0, }, VMSTATE_UINT32(env.spsr, ARMCPU), - VMSTATE_UINT32_ARRAY(env.banked_spsr, ARMCPU, 6), + VMSTATE_UINT64_ARRAY(env.banked_spsr, ARMCPU, 6), VMSTATE_UINT32_ARRAY(env.banked_r13, ARMCPU, 6), VMSTATE_UINT32_ARRAY(env.banked_r14, ARMCPU, 6), VMSTATE_UINT32_ARRAY(env.usr_regs, ARMCPU, 5), From 2f2a00aec9838bc52ac4f5dd452f4cf533effa88 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:43 +0100 Subject: [PATCH 18/51] target-arm: Move arm_log_exception() into internals.h Move arm_log_exception() into internals.h so we can use it from helper-a64.c for the AArch64 exception entry code. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 31 ------------------------------- target-arm/internals.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 68f8c6a832de..e9b64f364aca 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2957,37 +2957,6 @@ static void do_v7m_exception_exit(CPUARMState *env) pointer. */ } -/* Exception names for debug logging; note that not all of these - * precisely correspond to architectural exceptions. - */ -static const char * const excnames[] = { - [EXCP_UDEF] = "Undefined Instruction", - [EXCP_SWI] = "SVC", - [EXCP_PREFETCH_ABORT] = "Prefetch Abort", - [EXCP_DATA_ABORT] = "Data Abort", - [EXCP_IRQ] = "IRQ", - [EXCP_FIQ] = "FIQ", - [EXCP_BKPT] = "Breakpoint", - [EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit", - [EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage", - [EXCP_STREX] = "QEMU intercept of STREX", -}; - -static inline void arm_log_exception(int idx) -{ - if (qemu_loglevel_mask(CPU_LOG_INT)) { - const char *exc = NULL; - - if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { - exc = excnames[idx]; - } - if (!exc) { - exc = "unknown"; - } - qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s]\n", idx, exc); - } -} - void arm_v7m_cpu_do_interrupt(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); diff --git a/target-arm/internals.h b/target-arm/internals.h index de79dfc3164d..d63a975a7e1f 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -39,6 +39,37 @@ static inline bool excp_is_internal(int excp) || excp == EXCP_STREX; } +/* Exception names for debug logging; note that not all of these + * precisely correspond to architectural exceptions. + */ +static const char * const excnames[] = { + [EXCP_UDEF] = "Undefined Instruction", + [EXCP_SWI] = "SVC", + [EXCP_PREFETCH_ABORT] = "Prefetch Abort", + [EXCP_DATA_ABORT] = "Data Abort", + [EXCP_IRQ] = "IRQ", + [EXCP_FIQ] = "FIQ", + [EXCP_BKPT] = "Breakpoint", + [EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit", + [EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage", + [EXCP_STREX] = "QEMU intercept of STREX", +}; + +static inline void arm_log_exception(int idx) +{ + if (qemu_loglevel_mask(CPU_LOG_INT)) { + const char *exc = NULL; + + if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { + exc = excnames[idx]; + } + if (!exc) { + exc = "unknown"; + } + qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s]\n", idx, exc); + } +} + /* Scale factor for generic timers, ie number of ns per tick. * This gives a 62.5MHz timer. */ From 52e60cdd342dc48116edb81b443ba8c0a0c6f1a3 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 15 Apr 2014 19:18:44 +0100 Subject: [PATCH 19/51] target-arm: Implement AArch64 EL1 exception handling Implement exception handling for AArch64 EL1. Exceptions from AArch64 or AArch32 EL0 are supported. Signed-off-by: Rob Herring [PMM: fixed minor style nits; updated to match changes in previous patches; added some of the simpler cases of illegal-exception-return support] Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu-qom.h | 2 + target-arm/cpu64.c | 1 + target-arm/helper-a64.c | 76 ++++++++++++++++++++++++++++++++++++++ target-arm/helper.h | 1 + target-arm/op_helper.c | 60 ++++++++++++++++++++++++++++++ target-arm/translate-a64.c | 3 ++ 6 files changed, 143 insertions(+) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 41caa6c78076..afdee9d683c0 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -202,6 +202,8 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags); int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +void aarch64_cpu_do_interrupt(CPUState *cs); #endif #endif diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index fccecc252794..d4fb1de4fad1 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -85,6 +85,7 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); + cc->do_interrupt = aarch64_cpu_do_interrupt; cc->dump_state = aarch64_cpu_dump_state; cc->set_pc = aarch64_cpu_set_pc; cc->gdb_read_register = aarch64_cpu_gdb_read_register; diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index ec0258295fde..bf921ccdc06b 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -23,6 +23,7 @@ #include "qemu/host-utils.h" #include "sysemu/sysemu.h" #include "qemu/bitops.h" +#include "internals.h" /* C2.4.7 Multiply and divide */ /* special cases for 0 and LLONG_MIN are mandated by the standard */ @@ -436,3 +437,78 @@ float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) set_float_exception_flags(exflags, fpst); return r; } + +/* Handle a CPU exception. */ +void aarch64_cpu_do_interrupt(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + target_ulong addr = env->cp15.c12_vbar; + int i; + + if (arm_current_pl(env) == 0) { + if (env->aarch64) { + addr += 0x400; + } else { + addr += 0x600; + } + } else if (pstate_read(env) & PSTATE_SP) { + addr += 0x200; + } + + arm_log_exception(cs->exception_index); + qemu_log_mask(CPU_LOG_INT, "...from EL%d\n", arm_current_pl(env)); + if (qemu_loglevel_mask(CPU_LOG_INT) + && !excp_is_internal(cs->exception_index)) { + qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%" PRIx32 "\n", + env->exception.syndrome); + } + + env->cp15.esr_el1 = env->exception.syndrome; + env->cp15.far_el1 = env->exception.vaddress; + + switch (cs->exception_index) { + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + qemu_log_mask(CPU_LOG_INT, "...with FAR 0x%" PRIx64 "\n", + env->cp15.far_el1); + break; + case EXCP_BKPT: + case EXCP_UDEF: + case EXCP_SWI: + break; + case EXCP_IRQ: + addr += 0x80; + break; + case EXCP_FIQ: + addr += 0x100; + break; + default: + cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + } + + if (is_a64(env)) { + env->banked_spsr[0] = pstate_read(env); + env->sp_el[arm_current_pl(env)] = env->xregs[31]; + env->xregs[31] = env->sp_el[1]; + env->elr_el1 = env->pc; + } else { + env->banked_spsr[0] = cpsr_read(env); + if (!env->thumb) { + env->cp15.esr_el1 |= 1 << 25; + } + env->elr_el1 = env->regs[15]; + + for (i = 0; i < 15; i++) { + env->xregs[i] = env->regs[i]; + } + + env->condexec_bits = 0; + } + + pstate_write(env, PSTATE_DAIF | PSTATE_MODE_EL1h); + env->aarch64 = 1; + + env->pc = addr; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; +} diff --git a/target-arm/helper.h b/target-arm/helper.h index 597716924c18..a5449e7b6f60 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -66,6 +66,7 @@ DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) DEF_HELPER_2(get_cp_reg64, i64, env, ptr) DEF_HELPER_3(msr_i_pstate, void, env, i32, i32) +DEF_HELPER_1(exception_return, void, env) DEF_HELPER_2(get_r13_banked, i32, env, i32) DEF_HELPER_3(set_r13_banked, void, env, i32, i32) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 64a33dd44976..57e7d9c48052 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -384,6 +384,66 @@ void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm) } } +void HELPER(exception_return)(CPUARMState *env) +{ + uint32_t spsr = env->banked_spsr[0]; + int new_el, i; + + if (env->pstate & PSTATE_SP) { + env->sp_el[1] = env->xregs[31]; + } else { + env->sp_el[0] = env->xregs[31]; + } + + env->exclusive_addr = -1; + + if (spsr & PSTATE_nRW) { + env->aarch64 = 0; + new_el = 0; + env->uncached_cpsr = 0x10; + cpsr_write(env, spsr, ~0); + for (i = 0; i < 15; i++) { + env->regs[i] = env->xregs[i]; + } + + env->regs[15] = env->elr_el1 & ~0x1; + } else { + new_el = extract32(spsr, 2, 2); + if (new_el > 1) { + /* Return to unimplemented EL */ + goto illegal_return; + } + if (extract32(spsr, 1, 1)) { + /* Return with reserved M[1] bit set */ + goto illegal_return; + } + if (new_el == 0 && (spsr & PSTATE_SP)) { + /* Return to EL1 with M[0] bit set */ + goto illegal_return; + } + env->aarch64 = 1; + pstate_write(env, spsr); + env->xregs[31] = env->sp_el[new_el]; + env->pc = env->elr_el1; + } + + return; + +illegal_return: + /* Illegal return events of various kinds have architecturally + * mandated behaviour: + * restore NZCV and DAIF from SPSR_ELx + * set PSTATE.IL + * restore PC from ELR_ELx + * no change to exception level, execution state or stack pointer + */ + env->pstate |= PSTATE_IL; + env->pc = env->elr_el1; + spsr &= PSTATE_NZCV | PSTATE_DAIF; + spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); + pstate_write(env, spsr); +} + /* ??? Flag setting arithmetic is awkward because we need to do comparisons. The only way to do that in TCG is a conditional branch, which clobbers all our temporaries. For now implement these as helper functions. */ diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 4c5402a0f7e6..188287d031f1 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1512,6 +1512,9 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) tcg_gen_movi_i64(cpu_reg(s, 30), s->pc); break; case 4: /* ERET */ + gen_helper_exception_return(cpu_env); + s->is_jmp = DISAS_JUMP; + return; case 5: /* DRPS */ if (rn != 0x1f) { unallocated_encoding(s); From a50c0f51339ed1df0a2e289513e3e2bf5b5ca45c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:44 +0100 Subject: [PATCH 20/51] target-arm: Implement ARMv8 MVFR registers For ARMv8 there are two changes to the MVFR media feature registers: * there is a new MVFR2 which is accessible from 32 bit code * 64 bit code accesses these via the usual sysreg instructions rather than with a floating-point specific instruction Implement this. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu-qom.h | 1 + target-arm/cpu.c | 1 + target-arm/cpu.h | 1 + target-arm/helper.c | 12 ++++++++++++ target-arm/translate.c | 10 ++++++++-- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index afdee9d683c0..2b6b3708f817 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -116,6 +116,7 @@ typedef struct ARMCPU { uint32_t reset_fpsid; uint32_t mvfr0; uint32_t mvfr1; + uint32_t mvfr2; uint32_t ctr; uint32_t reset_sctlr; uint32_t id_pfr0; diff --git a/target-arm/cpu.c b/target-arm/cpu.c index a3c749284689..a78a36b64351 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -88,6 +88,7 @@ static void arm_cpu_reset(CPUState *s) env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid; env->vfp.xregs[ARM_VFP_MVFR0] = cpu->mvfr0; env->vfp.xregs[ARM_VFP_MVFR1] = cpu->mvfr1; + env->vfp.xregs[ARM_VFP_MVFR2] = cpu->mvfr2; if (arm_feature(env, ARM_FEATURE_IWMMXT)) { env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 0ce4c26d1611..ec0306b7a3b6 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -572,6 +572,7 @@ enum arm_cpu_mode { /* VFP system registers. */ #define ARM_VFP_FPSID 0 #define ARM_VFP_FPSCR 1 +#define ARM_VFP_MVFR2 5 #define ARM_VFP_MVFR1 6 #define ARM_VFP_MVFR0 7 #define ARM_VFP_FPEXC 8 diff --git a/target-arm/helper.c b/target-arm/helper.c index e9b64f364aca..cf5fab85a530 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2155,6 +2155,18 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_aa64mmfr1 }, + { .name = "MVFR0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->mvfr0 }, + { .name = "MVFR1_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->mvfr1 }, + { .name = "MVFR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->mvfr2 }, REGINFO_SENTINEL }; define_arm_cp_regs(cpu, v8_idregs); diff --git a/target-arm/translate.c b/target-arm/translate.c index 03e2c00e94a3..f7b5dafcb765 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -2967,9 +2967,10 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) if ((insn & 0x0fe00fff) != 0x0ee00a10) return 1; rn = (insn >> 16) & 0xf; - if (rn != ARM_VFP_FPSID && rn != ARM_VFP_FPEXC - && rn != ARM_VFP_MVFR1 && rn != ARM_VFP_MVFR0) + if (rn != ARM_VFP_FPSID && rn != ARM_VFP_FPEXC && rn != ARM_VFP_MVFR2 + && rn != ARM_VFP_MVFR1 && rn != ARM_VFP_MVFR0) { return 1; + } } if (extract32(insn, 28, 4) == 0xf) { @@ -3115,6 +3116,11 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) gen_helper_vfp_get_fpscr(tmp, cpu_env); } break; + case ARM_VFP_MVFR2: + if (!arm_feature(env, ARM_FEATURE_V8)) { + return 1; + } + /* fall through */ case ARM_VFP_MVFR0: case ARM_VFP_MVFR1: if (IS_USER(s) From cb1fa941c121d76dc17cecb794cf18dabd38d014 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:44 +0100 Subject: [PATCH 21/51] target-arm: Add Cortex-A57 processor Add Cortex-A57 processor. Signed-off-by: Peter Maydell Acked-by: Peter Crosthwaite --- target-arm/cpu64.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index d4fb1de4fad1..5be7d72b67f3 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -32,6 +32,48 @@ static inline void set_feature(CPUARMState *env, int feature) env->features |= 1ULL << feature; } +static void aarch64_a57_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_VFP4); + set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57; + cpu->midr = 0x411fd070; + cpu->reset_fpsid = 0x41034070; + cpu->mvfr0 = 0x10110222; + cpu->mvfr1 = 0x12111111; + cpu->mvfr2 = 0x00000043; + cpu->ctr = 0x8444c004; + cpu->reset_sctlr = 0x00c50838; + cpu->id_pfr0 = 0x00000131; + cpu->id_pfr1 = 0x00011011; + cpu->id_dfr0 = 0x03010066; + cpu->id_afr0 = 0x00000000; + cpu->id_mmfr0 = 0x10101105; + cpu->id_mmfr1 = 0x40000000; + cpu->id_mmfr2 = 0x01260000; + cpu->id_mmfr3 = 0x02102211; + cpu->id_isar0 = 0x02101110; + cpu->id_isar1 = 0x13112111; + cpu->id_isar2 = 0x21232042; + cpu->id_isar3 = 0x01112131; + cpu->id_isar4 = 0x00011142; + cpu->id_aa64pfr0 = 0x00002222; + cpu->id_aa64dfr0 = 0x10305106; + cpu->id_aa64isar0 = 0x00010000; + cpu->id_aa64mmfr0 = 0x00001124; + cpu->clidr = 0x0a200023; + cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ + cpu->ccsidr[2] = 0x70ffe07a; /* 2048KB L2 cache */ + cpu->dcz_blocksize = 4; /* 64 bytes */ +} + #ifdef CONFIG_USER_ONLY static void aarch64_any_initfn(Object *obj) { @@ -57,6 +99,7 @@ typedef struct ARMCPUInfo { } ARMCPUInfo; static const ARMCPUInfo aarch64_cpus[] = { + { .name = "cortex-a57", .initfn = aarch64_a57_initfn }, #ifdef CONFIG_USER_ONLY { .name = "any", .initfn = aarch64_any_initfn }, #endif From 0ff644a786aa041a8616ce449382806d8e29d04c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:45 +0100 Subject: [PATCH 22/51] target-arm: Implement AArch64 views of AArch32 ID registers All the AArch32 ID registers are visible from AArch64 (in addition to the AArch64-specific ID_AA64* registers). Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 73 +++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index cf5fab85a530..655c5abed60d 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -745,7 +745,8 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { /* Auxiliary ID register: this actually has an IMPDEF value but for now * just RAZ for all cores: */ - { .name = "AIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 7, + { .name = "AIDR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, /* MAIR can just read-as-written because we don't implement caches * and so don't need to care about memory attributes. @@ -2022,47 +2023,61 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_V6)) { /* The ID registers all have impdef reset values */ ARMCPRegInfo v6_idregs[] = { - { .name = "ID_PFR0", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_PFR0", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_pfr0 }, - { .name = "ID_PFR1", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_PFR1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_pfr1 }, - { .name = "ID_DFR0", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_DFR0", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_dfr0 }, - { .name = "ID_AFR0", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_AFR0", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_afr0 }, - { .name = "ID_MMFR0", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_MMFR0", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 4, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_mmfr0 }, - { .name = "ID_MMFR1", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_MMFR1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 5, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_mmfr1 }, - { .name = "ID_MMFR2", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_MMFR2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_mmfr2 }, - { .name = "ID_MMFR3", .cp = 15, .crn = 0, .crm = 1, - .opc1 = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_MMFR3", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_mmfr3 }, - { .name = "ID_ISAR0", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_ISAR0", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_isar0 }, - { .name = "ID_ISAR1", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_ISAR1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_isar1 }, - { .name = "ID_ISAR2", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_ISAR2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_isar2 }, - { .name = "ID_ISAR3", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_ISAR3", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_isar3 }, - { .name = "ID_ISAR4", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_ISAR4", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 4, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_isar4 }, - { .name = "ID_ISAR5", .cp = 15, .crn = 0, .crm = 2, - .opc1 = 0, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, + { .name = "ID_ISAR5", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 5, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_isar5 }, /* 6..7 are as yet unallocated and must RAZ */ { .name = "ID_ISAR6", .cp = 15, .crn = 0, .crm = 2, From 014406b510faae91b801c8c6fd408a7609f6de0b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:45 +0100 Subject: [PATCH 23/51] target-arm: Implement AArch64 view of CONTEXTIDR Implement AArch64 view of the CONTEXTIDR register. We tighten up the condition when we flush the TLB on a CONTEXTIDR write to avoid needlessly flushing the TLB every time on a 64 bit system (and also on a 32 bit system using LPAE, as a bonus). Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.h | 2 +- target-arm/helper.c | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index ec0306b7a3b6..d0f42fd72af2 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -201,7 +201,7 @@ typedef struct CPUARMState { uint64_t mair_el1; uint64_t c12_vbar; /* vector base address register */ uint32_t c13_fcse; /* FCSE PID. */ - uint32_t c13_context; /* Context ID. */ + uint64_t contextidr_el1; /* Context ID. */ uint64_t tpidr_el0; /* User RW Thread register. */ uint64_t tpidrro_el0; /* User RO Thread register. */ uint64_t tpidr_el1; /* Privileged Thread register. */ diff --git a/target-arm/helper.c b/target-arm/helper.c index 655c5abed60d..10300aa331c7 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -304,6 +304,17 @@ void init_cpreg_list(ARMCPU *cpu) g_list_free(keys); } +/* Return true if extended addresses are enabled. + * This is always the case if our translation regime is 64 bit, + * but depends on TTBCR.EAE for 32 bit. + */ +static inline bool extended_addresses_enabled(CPUARMState *env) +{ + return arm_el_is_aa64(env, 1) + || ((arm_feature(env, ARM_FEATURE_LPAE) + && (env->cp15.c2_control & (1U << 31)))); +} + static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -330,14 +341,15 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, { ARMCPU *cpu = arm_env_get_cpu(env); - if (env->cp15.c13_context != value && !arm_feature(env, ARM_FEATURE_MPU)) { + if (env->cp15.contextidr_el1 != value && !arm_feature(env, ARM_FEATURE_MPU) + && !extended_addresses_enabled(env)) { /* For VMSA (when not using the LPAE long descriptor page table * format) this register includes the ASID, so do a TLB flush. * For PMSA it is purely a process ID and no action is needed. */ tlb_flush(CPU(cpu), 1); } - env->cp15.c13_context = value; + env->cp15.contextidr_el1 = value; } static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -391,8 +403,10 @@ static const ARMCPRegInfo cp_reginfo[] = { { .name = "FCSEIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, - { .name = "CONTEXTIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_context), + { .name = "CONTEXTIDR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el1), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, /* ??? This covers not just the impdef TLB lockdown registers but also * some v7VMSA registers relating to TEX remap, so it is overly broad. @@ -1155,17 +1169,6 @@ static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) #ifndef CONFIG_USER_ONLY /* get_phys_addr() isn't present for user-mode-only targets */ -/* Return true if extended addresses are enabled. - * This is always the case if our translation regime is 64 bit, - * but depends on TTBCR.EAE for 32 bit. - */ -static inline bool extended_addresses_enabled(CPUARMState *env) -{ - return arm_el_is_aa64(env, 1) - || ((arm_feature(env, ARM_FEATURE_LPAE) - && (env->cp15.c2_control & (1U << 31)))); -} - static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri) { if (ri->opc2 & 4) { From 2eef0bf82146034f756d39cb02c8c8dd561a8942 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:45 +0100 Subject: [PATCH 24/51] target-arm: Implement AArch64 view of ACTLR Implement the AArch64 view of the ACTLR (auxiliary control register). Note that QEMU internally tends to call this AUXCR for historical reasons. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 10300aa331c7..32af1df530df 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2316,7 +2316,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_AUXCR)) { ARMCPRegInfo auxcr = { - .name = "AUXCR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, + .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = cpu->reset_auxcr }; From 1090b9c6ccfe837f1c76dafb7e56031bd7844075 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:46 +0100 Subject: [PATCH 25/51] target-arm: Implement ISR_EL1 register Implement the ISR_EL1 register. This is actually present in ARMv7 as well but was previously unimplemented. It is a read-only register that indicates whether interrupts are currently pending. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index 32af1df530df..825c8c99ebfd 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -665,6 +665,21 @@ static void csselr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.c0_cssel = value & 0xf; } +static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + CPUState *cs = ENV_GET_CPU(env); + uint64_t ret = 0; + + if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + ret |= CPSR_I; + } + if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { + ret |= CPSR_F; + } + /* External aborts are not possible in QEMU so A bit is always clear */ + return ret; +} + static const ARMCPRegInfo v7_cp_reginfo[] = { /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped * debug components @@ -782,6 +797,9 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el1), .resetfn = arm_cp_reset_ignore }, + { .name = "ISR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 1, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE, .access = PL1_R, .readfn = isr_read }, REGINFO_SENTINEL }; From 85acfa9c38880d5d2b3cc71a5e541a267c9f7396 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:46 +0100 Subject: [PATCH 26/51] target-arm: Remove THUMB2EE feature from AArch64 'any' CPU The AArch64 usermode 'any' CPU type was accidentally specified with the ARM_FEATURE_THUMB2EE bit set. This is incorrect since ARMv8 removes Thumb2EE completely. Since we never implemented Thumb2EE anyway having the feature bit set was fairly harmless for user-mode, but the correct thing is to not set it at all. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu64.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index 5be7d72b67f3..b8b4fa68bc9f 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -83,7 +83,6 @@ static void aarch64_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_AARCH64); From 00a29f3ddcd506f2fdba4d115e6c8df676f203db Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:47 +0100 Subject: [PATCH 27/51] target-arm: Don't expose wildcard ID register definitions for ARMv8 In ARMv8 the 32 bit coprocessor ID register space is tidied up to remove the wildcarded aliases of the MIDR and the RAZ behaviour for the unassigned space where crm = 3..7. Make sure we don't expose thes wildcards for v8 cores. This means we need to have a specific implementation for REVIDR, an IMPDEF register which may be the same as the MIDR (and which we always implement as such). Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 61 ++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 825c8c99ebfd..8706407b32bd 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2258,8 +2258,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) * be read-only (ie write causes UNDEF exception). */ { - ARMCPRegInfo id_cp_reginfo[] = { - /* Note that the MIDR isn't a simple constant register because + ARMCPRegInfo id_pre_v8_midr_cp_reginfo[] = { + /* Pre-v8 MIDR space. + * Note that the MIDR isn't a simple constant register because * of the TI925 behaviour where writes to another register can * cause the MIDR value to change. * @@ -2273,22 +2274,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) .writefn = arm_cp_write_ignore, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), .type = ARM_CP_OVERRIDE }, - { .name = "MIDR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .opc2 = 0, .crn = 0, .crm = 0, - .access = PL1_R, .resetvalue = cpu->midr, .type = ARM_CP_CONST }, - { .name = "CTR", - .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, - { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0, - .access = PL0_R, .accessfn = ctr_el0_access, - .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, - { .name = "TCMTR", - .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "TLBTR", - .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, /* crn = 0 op1 = 0 crm = 3..7 : currently unassigned; we RAZ. */ { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 3, .opc1 = 0, .opc2 = CP_ANY, @@ -2307,6 +2292,37 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; + ARMCPRegInfo id_v8_midr_cp_reginfo[] = { + /* v8 MIDR -- the wildcard isn't necessary, and nor is the + * variable-MIDR TI925 behaviour. Instead we have a single + * (strictly speaking IMPDEF) alias of the MIDR, REVIDR. + */ + { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr }, + { .name = "REVIDR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr }, + REGINFO_SENTINEL + }; + ARMCPRegInfo id_cp_reginfo[] = { + /* These are common to v8 and pre-v8 */ + { .name = "CTR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, + { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0, + .access = PL0_R, .accessfn = ctr_el0_access, + .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, + /* TCMTR and TLBTR exist in v8 but have no 64-bit versions */ + { .name = "TCMTR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "TLBTR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL + }; ARMCPRegInfo crn0_wi_reginfo = { .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W, @@ -2321,10 +2337,19 @@ void register_cp_regs_for_features(ARMCPU *cpu) * UNDEF. */ define_one_arm_cp_reg(cpu, &crn0_wi_reginfo); + for (r = id_pre_v8_midr_cp_reginfo; + r->type != ARM_CP_SENTINEL; r++) { + r->access = PL1_RW; + } for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) { r->access = PL1_RW; } } + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo); + } else { + define_arm_cp_regs(cpu, id_pre_v8_midr_cp_reginfo); + } define_arm_cp_regs(cpu, id_cp_reginfo); } From 9449fdf61fc32e50e29d9bc5b531f1d238c13c97 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:47 +0100 Subject: [PATCH 28/51] target-arm: Replace wildcarded cpreg definitions with precise ones for ARMv8 Many of the reginfo definitions in cp_reginfo[] use CP_ANY wildcards. This is for a combination of reasons: * early ARM implementations really did underdecode * earlier versions of QEMU underdecoded and we can't tighten this up because we don't know if guests really require this or not * implementation convenience For ARMv8 the architecture has tightened things up and system and coprocessor registers are always specifically decoded. We take advantage of this opportunity for a clean break by restricting our CP_ANY wildcarded reginfo to pre-v8 CPUs, and providing specifically decoded versions where necessary for v8 CPUs. Signed-off-by: Peter Maydell Acked-by: Peter Crosthwaite --- target-arm/helper.c | 96 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 8706407b32bd..ce1b59ed8519 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -395,11 +395,6 @@ static const ARMCPRegInfo cp_reginfo[] = { */ { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* MMU Domain access control / MPU write buffer control */ - { .name = "DACR", .cp = 15, - .crn = 3, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), - .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, }, { .name = "FCSEIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, @@ -408,6 +403,18 @@ static const ARMCPRegInfo cp_reginfo[] = { .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el1), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo not_v8_cp_reginfo[] = { + /* NB: Some of these registers exist in v8 but with more precise + * definitions that don't use CP_ANY wildcards (mostly in v8_cp_reginfo[]). + */ + /* MMU Domain access control / MPU write buffer control */ + { .name = "DACR", .cp = 15, + .crn = 3, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), + .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, }, /* ??? This covers not just the impdef TLB lockdown registers but also * some v7VMSA registers relating to TEX remap, so it is overly broad. */ @@ -1944,6 +1951,78 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 7, .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = tlbi_aa64_vaa_write }, + /* 32 bit TLB invalidates, Inner Shareable */ + { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + /* 32 bit ITLB invalidates */ + { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + /* 32 bit DTLB invalidates */ + { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + /* 32 bit TLB invalidates */ + { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, + .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + /* 32 bit cache operations */ + { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "BPIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 6, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "ICIALLU", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 0, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "ICIMVAU", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 1, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "BPIALL", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 6, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "BPIMVA", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 7, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "DCIMVAC", .cp = 15, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 1, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "DCISW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 2, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "DCCMVAC", .cp = 15, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 1, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "DCCSW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "DCCMVAU", .cp = 15, .opc1 = 0, .crn = 7, .crm = 11, .opc2 = 1, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "DCCIMVAC", .cp = 15, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 1, + .type = ARM_CP_NOP, .access = PL1_W }, + { .name = "DCCISW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, + .type = ARM_CP_NOP, .access = PL1_W }, + /* MMU Domain access control / MPU write buffer control */ + { .name = "DACR", .cp = 15, + .opc1 = 0, .crn = 3, .crm = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), + .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, }, /* Dummy implementation of monitor debug system control register: * we don't support debug. */ @@ -2041,6 +2120,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) } define_arm_cp_regs(cpu, cp_reginfo); + if (!arm_feature(env, ARM_FEATURE_V8)) { + /* Must go early as it is full of wildcards that may be + * overridden by later definitions. + */ + define_arm_cp_regs(cpu, not_v8_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V6)) { /* The ID registers all have impdef reset values */ ARMCPRegInfo v6_idregs[] = { From f32cdad55de242a23aae9842cdb659e6de116352 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:47 +0100 Subject: [PATCH 29/51] target-arm: Implement auxiliary fault status registers Implement the auxiliary fault status registers AFSR0_EL1 and AFSR1_EL1. These are present on v7 and later, and have IMPDEF behaviour; we choose to RAZ/WI for all cores. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/helper.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index ce1b59ed8519..1c07d81cc955 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -784,6 +784,15 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "AIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* Auxiliary fault status registers: these also are IMPDEF, and we + * choose to RAZ/WI for all cores. + */ + { .name = "AFSR0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, /* MAIR can just read-as-written because we don't implement caches * and so don't need to care about memory attributes. */ From 19525524a755a98f72b80c92c4f6bdeea3002da5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:48 +0100 Subject: [PATCH 30/51] target-arm: Implement AArch64 address translation operations Implement the AArch64 address translation operations. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.h | 3 +-- target-arm/helper.c | 53 ++++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index d0f42fd72af2..bebb333381ef 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -188,8 +188,7 @@ typedef struct CPUARMState { uint64_t esr_el1; uint32_t c6_region[8]; /* MPU base/size registers. */ uint64_t far_el1; /* Fault address registers. */ - uint32_t c7_par; /* Translation result. */ - uint32_t c7_par_hi; /* Translation result, high 32 bits */ + uint64_t par_el1; /* Translation result. */ uint32_t c9_insn; /* Cache lockdown registers. */ uint32_t c9_data; uint32_t c9_pmcr; /* performance monitor control register */ diff --git a/target-arm/helper.c b/target-arm/helper.c index 1c07d81cc955..0bcad0c193d4 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1192,11 +1192,11 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { if (arm_feature(env, ARM_FEATURE_LPAE)) { - env->cp15.c7_par = value; + env->cp15.par_el1 = value; } else if (arm_feature(env, ARM_FEATURE_V7)) { - env->cp15.c7_par = value & 0xfffff6ff; + env->cp15.par_el1 = value & 0xfffff6ff; } else { - env->cp15.c7_par = value & 0xfffff1ff; + env->cp15.par_el1 = value & 0xfffff1ff; } } @@ -1243,8 +1243,7 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) * fault. */ } - env->cp15.c7_par = par64; - env->cp15.c7_par_hi = par64 >> 32; + env->cp15.par_el1 = par64; } else { /* ret is a DFSR/IFSR value for the short descriptor * translation table format (with WnR always clear). @@ -1254,16 +1253,15 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) /* We do not set any attribute bits in the PAR */ if (page_size == (1 << 24) && arm_feature(env, ARM_FEATURE_V7)) { - env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1; + env->cp15.par_el1 = (phys_addr & 0xff000000) | 1 << 1; } else { - env->cp15.c7_par = phys_addr & 0xfffff000; + env->cp15.par_el1 = phys_addr & 0xfffff000; } } else { - env->cp15.c7_par = ((ret & (1 << 10)) >> 5) | + env->cp15.par_el1 = ((ret & (1 << 10)) >> 5) | ((ret & (1 << 12)) >> 6) | ((ret & 0xf) << 1) | 1; } - env->cp15.c7_par_hi = 0; } } #endif @@ -1271,7 +1269,7 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) static const ARMCPRegInfo vapa_cp_reginfo[] = { { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .resetvalue = 0, - .fieldoffset = offsetof(CPUARMState, cp15.c7_par), + .fieldoffset = offsetoflow32(CPUARMState, cp15.par_el1), .writefn = par_write }, #ifndef CONFIG_USER_ONLY { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, @@ -1674,24 +1672,6 @@ static const ARMCPRegInfo mpidr_cp_reginfo[] = { REGINFO_SENTINEL }; -static uint64_t par64_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - return ((uint64_t)env->cp15.c7_par_hi << 32) | env->cp15.c7_par; -} - -static void par64_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - env->cp15.c7_par_hi = value >> 32; - env->cp15.c7_par = value; -} - -static void par64_reset(CPUARMState *env, const ARMCPRegInfo *ri) -{ - env->cp15.c7_par_hi = 0; - env->cp15.c7_par = 0; -} - static const ARMCPRegInfo lpae_cp_reginfo[] = { /* NOP AMAIR0/1: the override is because these clash with the rather * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo. @@ -1711,7 +1691,7 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, { .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0, .access = PL1_RW, .type = ARM_CP_64BIT, - .readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset }, + .fieldoffset = offsetof(CPUARMState, cp15.par_el1), .resetvalue = 0 }, { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0, .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1), @@ -1960,6 +1940,21 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 7, .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = tlbi_aa64_vaa_write }, +#ifndef CONFIG_USER_ONLY + /* 64 bit address translation operations */ + { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, +#endif /* 32 bit TLB invalidates, Inner Shareable */ { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, From 3933443e38f37576d63247a846ca342da53e7d43 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:48 +0100 Subject: [PATCH 31/51] target-arm: Implement RVBAR register Implement the AArch64 RVBAR register, which indicates the reset address. Since the reset address is implementation defined and usually configurable by setting config signals in hardware, we also provide a QOM property so it can be set at board level if necessary. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu-qom.h | 1 + target-arm/cpu.c | 9 +++++++++ target-arm/helper.c | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 2b6b3708f817..743985ec2d66 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -153,6 +153,7 @@ typedef struct ARMCPU { bool reset_hivecs; /* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */ uint32_t dcz_blocksize; + uint64_t rvbar; } ARMCPU; #define TYPE_AARCH64_CPU "aarch64-cpu" diff --git a/target-arm/cpu.c b/target-arm/cpu.c index a78a36b64351..783fc73a7ad7 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -105,6 +105,7 @@ static void arm_cpu_reset(CPUState *s) env->cp15.c1_coproc = deposit64(env->cp15.c1_coproc, 20, 2, 3); #else env->pstate = PSTATE_MODE_EL1h; + env->pc = cpu->rvbar; #endif } else { #if defined(CONFIG_USER_ONLY) @@ -266,6 +267,9 @@ static Property arm_cpu_reset_cbar_property = static Property arm_cpu_reset_hivecs_property = DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); +static Property arm_cpu_rvbar_property = + DEFINE_PROP_UINT64("rvbar", ARMCPU, rvbar, 0); + static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -279,6 +283,11 @@ static void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property, &error_abort); } + + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + qdev_property_add_static(DEVICE(obj), &arm_cpu_rvbar_property, + &error_abort); + } } static void arm_cpu_finalizefn(Object *obj) diff --git a/target-arm/helper.c b/target-arm/helper.c index 0bcad0c193d4..2bcd400ff445 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2295,6 +2295,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) .resetvalue = cpu->mvfr2 }, REGINFO_SENTINEL }; + ARMCPRegInfo rvbar = { + .name = "RVBAR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 2, + .type = ARM_CP_CONST, .access = PL1_R, .resetvalue = cpu->rvbar + }; + define_one_arm_cp_reg(cpu, &rvbar); define_arm_cp_regs(cpu, v8_idregs); define_arm_cp_regs(cpu, v8_cp_reginfo); define_aarch64_debug_regs(cpu); From 377a44ec8f2fac5b7bef41d212dfbabf53c8c810 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:48 +0100 Subject: [PATCH 32/51] target-arm: Implement Cortex-A57 implementation-defined system registers Implement a subset of the Cortex-A57's implementation defined system registers. We provide RAZ/WI or reads-as-constant/writes-ignored implementations of the various control and syndrome reigsters. We do not implement registers which provide direct access to and manipulation of the L1 cache, since QEMU doesn't implement caches. Signed-off-by: Peter Maydell Acked-by: Peter Crosthwaite --- target-arm/cpu64.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index b8b4fa68bc9f..70a83fc4d0c6 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -32,6 +32,61 @@ static inline void set_feature(CPUARMState *env, int feature) env->features |= 1ULL << feature; } +#ifndef CONFIG_USER_ONLY +static uint64_t a57_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Number of processors is in [25:24]; otherwise we RAZ */ + return (smp_cpus - 1) << 24; +} +#endif + +static const ARMCPRegInfo cortexa57_cp_reginfo[] = { +#ifndef CONFIG_USER_ONLY + { .name = "L2CTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 2, + .access = PL1_RW, .readfn = a57_l2ctlr_read, + .writefn = arm_cp_write_ignore }, + { .name = "L2CTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 2, + .access = PL1_RW, .readfn = a57_l2ctlr_read, + .writefn = arm_cp_write_ignore }, +#endif + { .name = "L2ECTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2ECTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2ACTLR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUACTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUACTLR", + .cp = 15, .opc1 = 0, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "CPUECTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUECTLR", + .cp = 15, .opc1 = 1, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "CPUMERRSR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUMERRSR", + .cp = 15, .opc1 = 2, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "L2MERRSR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2MERRSR", + .cp = 15, .opc1 = 3, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + static void aarch64_a57_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); From f318cec6adcb73c688d68b0874686a30c0f34a2e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:49 +0100 Subject: [PATCH 33/51] target-arm: Implement CBAR for Cortex-A57 The Cortex-A57, like most of the other ARM cores, has a CBAR register which defines the base address of the per-CPU peripherals. However it has a 64-bit view as well as a 32-bit view; expand the QOM reset-cbar property from UINT32 to UINT64 so this can be specified, and implement the 32-bit and 64-bit views of a 64-bit CBAR. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu-qom.h | 2 +- target-arm/cpu.c | 8 ++++++-- target-arm/cpu.h | 1 + target-arm/cpu64.c | 1 + target-arm/helper.c | 39 +++++++++++++++++++++++++++++++++------ 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 743985ec2d66..82f1bc71738e 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -148,7 +148,7 @@ typedef struct ARMCPU { * in the order L1DCache, L1ICache, L2DCache, L2ICache, etc. */ uint32_t ccsidr[16]; - uint32_t reset_cbar; + uint64_t reset_cbar; uint32_t reset_auxcr; bool reset_hivecs; /* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */ diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 783fc73a7ad7..f9f61870b096 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -262,7 +262,7 @@ static void arm_cpu_initfn(Object *obj) } static Property arm_cpu_reset_cbar_property = - DEFINE_PROP_UINT32("reset-cbar", ARMCPU, reset_cbar, 0); + DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0); static Property arm_cpu_reset_hivecs_property = DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); @@ -274,7 +274,8 @@ static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); - if (arm_feature(&cpu->env, ARM_FEATURE_CBAR)) { + if (arm_feature(&cpu->env, ARM_FEATURE_CBAR) || + arm_feature(&cpu->env, ARM_FEATURE_CBAR_RO)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property, &error_abort); } @@ -349,6 +350,9 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) set_feature(env, ARM_FEATURE_V7MP); set_feature(env, ARM_FEATURE_PXN); } + if (arm_feature(env, ARM_FEATURE_CBAR_RO)) { + set_feature(env, ARM_FEATURE_CBAR); + } if (cpu->reset_hivecs) { cpu->reset_sctlr |= (1 << 13); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index bebb333381ef..c83f2495a861 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -630,6 +630,7 @@ enum arm_features { ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */ ARM_FEATURE_CBAR, /* has cp15 CBAR */ ARM_FEATURE_CRC, /* ARMv8 CRC instructions */ + ARM_FEATURE_CBAR_RO, /* has cp15 CBAR and it is read-only */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index 70a83fc4d0c6..9a0c4310daf2 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -97,6 +97,7 @@ static void aarch64_a57_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57; cpu->midr = 0x411fd070; cpu->reset_fpsid = 0x41034070; diff --git a/target-arm/helper.c b/target-arm/helper.c index 2bcd400ff445..43c1b4f01dc9 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2464,12 +2464,39 @@ void register_cp_regs_for_features(ARMCPU *cpu) } if (arm_feature(env, ARM_FEATURE_CBAR)) { - ARMCPRegInfo cbar = { - .name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0, - .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar, - .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address) - }; - define_one_arm_cp_reg(cpu, &cbar); + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + /* 32 bit view is [31:18] 0...0 [43:32]. */ + uint32_t cbar32 = (extract64(cpu->reset_cbar, 18, 14) << 18) + | extract64(cpu->reset_cbar, 32, 12); + ARMCPRegInfo cbar_reginfo[] = { + { .name = "CBAR", + .type = ARM_CP_CONST, + .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0, + .access = PL1_R, .resetvalue = cpu->reset_cbar }, + { .name = "CBAR_EL1", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_CONST, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 0, + .access = PL1_R, .resetvalue = cbar32 }, + REGINFO_SENTINEL + }; + /* We don't implement a r/w 64 bit CBAR currently */ + assert(arm_feature(env, ARM_FEATURE_CBAR_RO)); + define_arm_cp_regs(cpu, cbar_reginfo); + } else { + ARMCPRegInfo cbar = { + .name = "CBAR", + .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0, + .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar, + .fieldoffset = offsetof(CPUARMState, + cp15.c15_config_base_address) + }; + if (arm_feature(env, ARM_FEATURE_CBAR_RO)) { + cbar.access = PL1_R; + cbar.fieldoffset = 0; + cbar.type = ARM_CP_CONST; + } + define_one_arm_cp_reg(cpu, &cbar); + } } /* Generic registers whose values depend on the implementation */ From c29f9a0a29e76b36fd1bb6f5adf7841c6fdbc0b3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:49 +0100 Subject: [PATCH 34/51] target-arm: Make Cortex-A15 CBAR read-only The Cortex-A15's CBAR register is actually read-only (unlike that of the Cortex-A9). Correct our model to match the hardware. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite --- target-arm/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index f9f61870b096..c0ddc3e3dfda 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -744,7 +744,7 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - set_feature(&cpu->env, ARM_FEATURE_CBAR); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_LPAE); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; From 7633378d5fbe932c9d38ae8961ef035d1ed26bfd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:18:49 +0100 Subject: [PATCH 35/51] target-arm: Handle the CPU being in AArch32 mode in the AArch64 set_pc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AArch64 implementation of the set_pc method needs to be updated to handle the possibility that the CPU is in AArch32 mode; otherwise there are weird crashes when doing interprocessing in system emulation mode when an interrupt occurs and we fail to resynchronize the 32-bit PC with the TB we need to execute next. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Peter Crosthwaite --- target-arm/cpu64.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index 9a0c4310daf2..c673ac273824 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -172,11 +172,15 @@ static void aarch64_cpu_finalizefn(Object *obj) static void aarch64_cpu_set_pc(CPUState *cs, vaddr value) { ARMCPU *cpu = ARM_CPU(cs); - /* - * TODO: this will need updating for system emulation, - * when the core may be in AArch32 mode. + /* It's OK to look at env for the current mode here, because it's + * never possible for an AArch64 TB to chain to an AArch32 TB. + * (Otherwise we would need to use synchronize_from_tb instead.) */ - cpu->env.pc = value; + if (is_a64(&cpu->env)) { + cpu->env.pc = value; + } else { + cpu->env.regs[15] = value; + } } static void aarch64_cpu_class_init(ObjectClass *oc, void *data) From 177311157c9dbd408728620771b1fceb65d26846 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 Apr 2014 19:19:15 +0100 Subject: [PATCH 36/51] target-arm: Dump 32-bit CPU state if 64 bit CPU is in AArch32 For system mode, we may have a 64 bit CPU which is currently executing in AArch32 state; if we're dumping CPU state to the logs we should therefore show the correct state for the current execution state, rather than hardwiring it based on the type of the CPU. For consistency with how we handle translation, we leave the 32 bit dump function as the default, and have it hand off control to the 64 bit dump code if we're in AArch64 mode. Reported-by: Rob Herring Signed-off-by: Peter Maydell --- target-arm/cpu-qom.h | 2 -- target-arm/cpu64.c | 1 - target-arm/translate.c | 5 +++++ target-arm/translate.h | 8 ++++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 82f1bc71738e..edc7f262fcb4 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -200,8 +200,6 @@ void arm_gt_ptimer_cb(void *opaque); void arm_gt_vtimer_cb(void *opaque); #ifdef TARGET_AARCH64 -void aarch64_cpu_dump_state(CPUState *cs, FILE *f, - fprintf_function cpu_fprintf, int flags); int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index c673ac273824..8daa622bdcee 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -188,7 +188,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); cc->do_interrupt = aarch64_cpu_do_interrupt; - cc->dump_state = aarch64_cpu_dump_state; cc->set_pc = aarch64_cpu_set_pc; cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; diff --git a/target-arm/translate.c b/target-arm/translate.c index f7b5dafcb765..0c08cc1b81c2 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -11047,6 +11047,11 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int i; uint32_t psr; + if (is_a64(env)) { + aarch64_cpu_dump_state(cs, f, cpu_fprintf, flags); + return; + } + for(i=0;i<16;i++) { cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]); if ((i % 4) == 3) diff --git a/target-arm/translate.h b/target-arm/translate.h index 3f7d5ca24d38..34328f466005 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -72,6 +72,8 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, TranslationBlock *tb, bool search_pc); void gen_a64_set_pc_im(uint64_t val); +void aarch64_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags); #else static inline void a64_translate_init(void) { @@ -86,6 +88,12 @@ static inline void gen_intermediate_code_internal_a64(ARMCPU *cpu, static inline void gen_a64_set_pc_im(uint64_t val) { } + +static inline void aarch64_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, + int flags) +{ +} #endif void arm_gen_test_cc(int cc, int label); From b5cde1da0a67aa9841da46ca804b48098e63e838 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 18 Mar 2014 08:31:16 +0800 Subject: [PATCH 37/51] target-arm/gdbstub64.c: remove useless 'break' statement. Clean up useless 'break' statement after 'return' statement. Signed-off-by: Chen Gang Reviewed-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- target-arm/gdbstub64.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target-arm/gdbstub64.c b/target-arm/gdbstub64.c index e8a82952a413..8f3b8d177820 100644 --- a/target-arm/gdbstub64.c +++ b/target-arm/gdbstub64.c @@ -32,10 +32,8 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) switch (n) { case 31: return gdb_get_reg64(mem_buf, env->xregs[31]); - break; case 32: return gdb_get_reg64(mem_buf, env->pc); - break; case 33: return gdb_get_reg32(mem_buf, pstate_read(env)); } From f727d0e6219e6d5a9f91326f01b85aa563e37bb9 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Mon, 31 Mar 2014 21:31:09 -0700 Subject: [PATCH 38/51] timer: cadence_ttc: Fix match register write logic This switch logic should not fall through. Fix. Signed-off-by: Peter Crosthwaite Message-id: 74147b4c017c904364955cc73107f90e6ac8ba74.1396326389.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/timer/cadence_ttc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c index a279bced78de..28cb328f9bab 100644 --- a/hw/timer/cadence_ttc.c +++ b/hw/timer/cadence_ttc.c @@ -346,11 +346,13 @@ static void cadence_ttc_write(void *opaque, hwaddr offset, case 0x34: case 0x38: s->reg_match[0] = value & 0xffff; + break; case 0x3c: /* match register */ case 0x40: case 0x44: s->reg_match[1] = value & 0xffff; + break; case 0x48: /* match register */ case 0x4c: From 1c70aa6264ea7f3d7be6f3acb65a1e0aac8b3944 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 25 Mar 2014 19:22:04 +0100 Subject: [PATCH 39/51] allwinner-a10-pic: set vector address when an interrupt is pending This patch implements proper updating of the vector register which should hold, according to the A10 user manual, the vector address for the interrupt currently active on the CPU IRQ input. Interrupt priority is not implemented at the moment and thus the first pending interrupt is returned. Signed-off-by: Beniamino Galvani Reviewed-by: Peter Crosthwaite Reviewed-by: Li Guang Message-id: 1395771730-16882-2-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell --- hw/intc/allwinner-a10-pic.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index 407d56351489..00f3c11b4667 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -23,11 +23,20 @@ static void aw_a10_pic_update(AwA10PICState *s) { uint8_t i; - int irq = 0, fiq = 0; + int irq = 0, fiq = 0, pending; + + s->vector = 0; for (i = 0; i < AW_A10_PIC_REG_NUM; i++) { irq |= s->irq_pending[i] & ~s->mask[i]; fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i]; + + if (!s->vector) { + pending = ffs(s->irq_pending[i] & ~s->mask[i]); + if (pending) { + s->vector = (i * 32 + pending - 1) * 4; + } + } } qemu_set_irq(s->parent_irq, !!irq); @@ -84,9 +93,6 @@ static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value, uint8_t index = (offset & 0xc) / 4; switch (offset) { - case AW_A10_PIC_VECTOR: - s->vector = value & ~0x3; - break; case AW_A10_PIC_BASE_ADDR: s->base_addr = value & ~0x3; case AW_A10_PIC_PROTECT: From 2237094d9619a64b669ca9f7fb1430b9b329f9cf Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 25 Mar 2014 19:22:05 +0100 Subject: [PATCH 40/51] allwinner-a10-pic: fix behaviour of pending register The pending register is read-only and the value returned upon a read reflects the state of irq input pins (interrupts are level triggered). This patch implements such behaviour. Signed-off-by: Beniamino Galvani Reviewed-by: Li Guang Reviewed-by: Peter Crosthwaite Message-id: 1395771730-16882-3-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell --- hw/intc/allwinner-a10-pic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index 00f3c11b4667..0924d9855c8a 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -49,6 +49,8 @@ static void aw_a10_pic_set_irq(void *opaque, int irq, int level) if (level) { set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); + } else { + clear_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); } aw_a10_pic_update(s); } @@ -102,7 +104,11 @@ static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value, s->nmi = value; break; case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8: - s->irq_pending[index] &= ~value; + /* + * The register is read-only; nevertheless, Linux (including + * the version originally shipped by Allwinner) pretends to + * write to the register. Just ignore it. + */ break; case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8: s->fiq_pending[index] &= ~value; From 323a8771cfb96ab752c9d27e939a2fbac1380b1c Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 25 Mar 2014 19:22:06 +0100 Subject: [PATCH 41/51] allwinner-a10-pit: avoid generation of spurious interrupts The model was generating interrupts for all enabled timers after the expiration of one of them. Avoid this by passing explicitly the timer index to the callback function. Signed-off-by: Beniamino Galvani Reviewed-by: Li Guang Reviewed-by: Peter Crosthwaite Message-id: 1395771730-16882-4-git-send-email-b.galvani@gmail.com [PMM: avoid duplicate typedef of AwA10PITState] Signed-off-by: Peter Maydell --- hw/timer/allwinner-a10-pit.c | 25 ++++++++++++++----------- include/hw/timer/allwinner-a10-pit.h | 12 ++++++++++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index b27fce8cd2d0..696b7d9977fe 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -193,18 +193,17 @@ static void a10_pit_reset(DeviceState *dev) static void a10_pit_timer_cb(void *opaque) { - AwA10PITState *s = AW_A10_PIT(opaque); - uint8_t i; + AwA10TimerContext *tc = opaque; + AwA10PITState *s = tc->container; + uint8_t i = tc->index; - for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { - if (s->control[i] & AW_A10_PIT_TIMER_EN) { - s->irq_status |= 1 << i; - if (s->control[i] & AW_A10_PIT_TIMER_MODE) { - ptimer_stop(s->timer[i]); - s->control[i] &= ~AW_A10_PIT_TIMER_EN; - } - qemu_irq_pulse(s->irq[i]); + if (s->control[i] & AW_A10_PIT_TIMER_EN) { + s->irq_status |= 1 << i; + if (s->control[i] & AW_A10_PIT_TIMER_MODE) { + ptimer_stop(s->timer[i]); + s->control[i] &= ~AW_A10_PIT_TIMER_EN; } + qemu_irq_pulse(s->irq[i]); } } @@ -223,7 +222,11 @@ static void a10_pit_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { - bh[i] = qemu_bh_new(a10_pit_timer_cb, s); + AwA10TimerContext *tc = &s->timer_context[i]; + + tc->container = s; + tc->index = i; + bh[i] = qemu_bh_new(a10_pit_timer_cb, tc); s->timer[i] = ptimer_init(bh[i]); ptimer_set_freq(s->timer[i], 240000); } diff --git a/include/hw/timer/allwinner-a10-pit.h b/include/hw/timer/allwinner-a10-pit.h index 15efab8b5f69..2158fc0d7b4c 100644 --- a/include/hw/timer/allwinner-a10-pit.h +++ b/include/hw/timer/allwinner-a10-pit.h @@ -35,12 +35,20 @@ #define AW_A10_PIT_DEFAULT_CLOCK 0x4 -typedef struct AwA10PITState { +typedef struct AwA10PITState AwA10PITState; + +typedef struct AwA10TimerContext { + AwA10PITState *container; + int index; +} AwA10TimerContext; + +struct AwA10PITState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ qemu_irq irq[AW_A10_PIT_TIMER_NR]; ptimer_state * timer[AW_A10_PIT_TIMER_NR]; + AwA10TimerContext timer_context[AW_A10_PIT_TIMER_NR]; MemoryRegion iomem; uint32_t irq_enable; @@ -53,6 +61,6 @@ typedef struct AwA10PITState { uint32_t count_lo; uint32_t count_hi; uint32_t count_ctl; -} AwA10PITState; +}; #endif From a63f9f85e387791e7a94480379ca5421a0b6d580 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 25 Mar 2014 19:22:07 +0100 Subject: [PATCH 42/51] allwinner-a10-pit: use level triggered interrupts Convert the interrupt generation logic to the use of level triggered interrupts. Signed-off-by: Beniamino Galvani Reviewed-by: Peter Crosthwaite Message-id: 1395771730-16882-5-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell --- hw/timer/allwinner-a10-pit.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index 696b7d9977fe..5aa78a91fdfb 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -19,6 +19,15 @@ #include "sysemu/sysemu.h" #include "hw/timer/allwinner-a10-pit.h" +static void a10_pit_update_irq(AwA10PITState *s) +{ + int i; + + for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { + qemu_set_irq(s->irq[i], !!(s->irq_status & s->irq_enable & (1 << i))); + } +} + static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size) { AwA10PITState *s = AW_A10_PIT(opaque); @@ -74,9 +83,11 @@ static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, switch (offset) { case AW_A10_PIT_TIMER_IRQ_EN: s->irq_enable = value; + a10_pit_update_irq(s); break; case AW_A10_PIT_TIMER_IRQ_ST: s->irq_status &= ~value; + a10_pit_update_irq(s); break; case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: index = offset & 0xf0; @@ -178,6 +189,8 @@ static void a10_pit_reset(DeviceState *dev) s->irq_enable = 0; s->irq_status = 0; + a10_pit_update_irq(s); + for (i = 0; i < 6; i++) { s->control[i] = AW_A10_PIT_DEFAULT_CLOCK; s->interval[i] = 0; @@ -203,7 +216,7 @@ static void a10_pit_timer_cb(void *opaque) ptimer_stop(s->timer[i]); s->control[i] &= ~AW_A10_PIT_TIMER_EN; } - qemu_irq_pulse(s->irq[i]); + a10_pit_update_irq(s); } } From 286226a47980d25f95afe920d430d3bbcb883546 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 25 Mar 2014 19:22:08 +0100 Subject: [PATCH 43/51] allwinner-a10-pit: implement prescaler and source selection This implements the prescaler and source fields of the timer control register. The source for each timer can be selected among 4 clock inputs whose frequencies are set through model properties. Signed-off-by: Beniamino Galvani Reviewed-by: Peter Crosthwaite Message-id: 1395771730-16882-6-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell --- hw/arm/cubieboard.c | 13 +++++++++++++ hw/timer/allwinner-a10-pit.c | 28 +++++++++++++++++++++++++++- include/hw/timer/allwinner-a10-pit.h | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index d95a7f35ebb2..9d158c72486b 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -43,6 +43,19 @@ static void cubieboard_init(QEMUMachineInitArgs *args) exit(1); } + object_property_set_int(OBJECT(&s->a10->timer), 32768, "clk0-freq", &err); + if (err != NULL) { + error_report("Couldn't set clk0 frequency: %s", error_get_pretty(err)); + exit(1); + } + + object_property_set_int(OBJECT(&s->a10->timer), 24000000, "clk1-freq", + &err); + if (err != NULL) { + error_report("Couldn't set clk1 frequency: %s", error_get_pretty(err)); + exit(1); + } + object_property_set_bool(OBJECT(s->a10), true, "realized", &err); if (err != NULL) { error_report("Couldn't realize Allwinner A10: %s", diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index 5aa78a91fdfb..d3c02ea18f51 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -74,6 +74,22 @@ static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size) return 0; } +static void a10_pit_set_freq(AwA10PITState *s, int index) +{ + uint32_t prescaler, source, source_freq; + + prescaler = 1 << extract32(s->control[index], 4, 3); + source = extract32(s->control[index], 2, 2); + source_freq = s->clk_freq[source]; + + if (source_freq) { + ptimer_set_freq(s->timer[index], source_freq / prescaler); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid clock source %u\n", + __func__, source); + } +} + static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { @@ -96,6 +112,7 @@ static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, switch (offset & 0x0f) { case AW_A10_PIT_TIMER_CONTROL: s->control[index] = value; + a10_pit_set_freq(s, index); if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) { ptimer_set_count(s->timer[index], s->interval[index]); } @@ -161,6 +178,14 @@ static const MemoryRegionOps a10_pit_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static Property a10_pit_properties[] = { + DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0), + DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0), + DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0), + DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_a10_pit = { .name = "a10.pit", .version_id = 1, @@ -196,6 +221,7 @@ static void a10_pit_reset(DeviceState *dev) s->interval[i] = 0; s->count[i] = 0; ptimer_stop(s->timer[i]); + a10_pit_set_freq(s, i); } s->watch_dog_mode = 0; s->watch_dog_control = 0; @@ -241,7 +267,6 @@ static void a10_pit_init(Object *obj) tc->index = i; bh[i] = qemu_bh_new(a10_pit_timer_cb, tc); s->timer[i] = ptimer_init(bh[i]); - ptimer_set_freq(s->timer[i], 240000); } } @@ -250,6 +275,7 @@ static void a10_pit_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = a10_pit_reset; + dc->props = a10_pit_properties; dc->desc = "allwinner a10 timer"; dc->vmsd = &vmstate_a10_pit; } diff --git a/include/hw/timer/allwinner-a10-pit.h b/include/hw/timer/allwinner-a10-pit.h index 2158fc0d7b4c..770bdc03c1ea 100644 --- a/include/hw/timer/allwinner-a10-pit.h +++ b/include/hw/timer/allwinner-a10-pit.h @@ -50,6 +50,7 @@ struct AwA10PITState { ptimer_state * timer[AW_A10_PIT_TIMER_NR]; AwA10TimerContext timer_context[AW_A10_PIT_TIMER_NR]; MemoryRegion iomem; + uint32_t clk_freq[4]; uint32_t irq_enable; uint32_t irq_status; From 103db49a106d1f015a16432eb73cc4046792fbad Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 25 Mar 2014 19:22:09 +0100 Subject: [PATCH 44/51] allwinner-emac: set autonegotiation complete bit on link up Signed-off-by: Beniamino Galvani Reviewed-by: Peter Crosthwaite Message-id: 1395771730-16882-7-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell --- hw/net/allwinner_emac.c | 4 ++-- include/hw/net/allwinner_emac.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index 469f2f0edee9..91931acad651 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -27,11 +27,11 @@ static uint8_t padding[60]; static void mii_set_link(RTL8201CPState *mii, bool link_ok) { if (link_ok) { - mii->bmsr |= MII_BMSR_LINK_ST; + mii->bmsr |= MII_BMSR_LINK_ST | MII_BMSR_AN_COMP; mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_10FD | MII_ANAR_10 | MII_ANAR_CSMACD; } else { - mii->bmsr &= ~MII_BMSR_LINK_ST; + mii->bmsr &= ~(MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); mii->anlpar = MII_ANAR_TX; } } diff --git a/include/hw/net/allwinner_emac.h b/include/hw/net/allwinner_emac.h index a5e944af0596..5ae7717300ac 100644 --- a/include/hw/net/allwinner_emac.h +++ b/include/hw/net/allwinner_emac.h @@ -144,6 +144,7 @@ #define MII_BMSR_10T_FD (1 << 12) #define MII_BMSR_10T_HD (1 << 11) #define MII_BMSR_MFPS (1 << 6) +#define MII_BMSR_AN_COMP (1 << 5) #define MII_BMSR_AUTONEG (1 << 3) #define MII_BMSR_LINK_ST (1 << 2) From 6619bc5c55c1322a89009e2edd530113da95d551 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 25 Mar 2014 19:22:10 +0100 Subject: [PATCH 45/51] allwinner-emac: update irq status after writes to interrupt registers The irq line status must be updated after writes to the INT_CTL and INT_STA registers. Signed-off-by: Beniamino Galvani Reviewed-by: Peter Crosthwaite Message-id: 1395771730-16882-8-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell --- hw/net/allwinner_emac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index 91931acad651..d780ba0fcb85 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -391,9 +391,11 @@ static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value, break; case EMAC_INT_CTL_REG: s->int_ctl = value; + aw_emac_update_irq(s); break; case EMAC_INT_STA_REG: s->int_sta &= ~value; + aw_emac_update_irq(s); break; case EMAC_MAC_MADR_REG: s->phy_target = value; From db302f8f9364580a90b383d8531d28500e4e5bb5 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 15 Apr 2014 19:49:11 +0100 Subject: [PATCH 46/51] misc: zynq-slcr: Rewrite Near total rewrite of this device model. It is stylistically obsolete, has numerous coverity fails and is not up to date with latest Xilinx documentation. Fix. The registers are flattened into a single array. This greatly simplifies the MMIO accessor functions. We take the oppurtunity to update the register Macro definitions to match the latest TRM. Xilinx has de-documented some regs hence there are some straight deletions. We only do this however in the case or a stock read-as-written reset-zero register. Non-zero resets are always preserved. New register definitions are added as needed. This all comes with a VMSD version break as the union layout from before was a bit strange and we are better off without it. Signed-off-by: Peter Crosthwaite Message-id: 3aa016167b352ed224666909217137285fd3351d.1396503037.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/misc/zynq_slcr.c | 672 +++++++++++++++++++------------------------- 1 file changed, 294 insertions(+), 378 deletions(-) diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index d1cc23303abd..cc294442ccb0 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -33,88 +33,139 @@ #define R_PSS_RST_CTRL_SOFT_RST 0x1 -typedef enum { - ARM_PLL_CTRL, - DDR_PLL_CTRL, - IO_PLL_CTRL, - PLL_STATUS, - ARM_PPL_CFG, - DDR_PLL_CFG, - IO_PLL_CFG, - PLL_BG_CTRL, - PLL_MAX -} PLLValues; - -typedef enum { - ARM_CLK_CTRL, - DDR_CLK_CTRL, - DCI_CLK_CTRL, - APER_CLK_CTRL, - USB0_CLK_CTRL, - USB1_CLK_CTRL, - GEM0_RCLK_CTRL, - GEM1_RCLK_CTRL, - GEM0_CLK_CTRL, - GEM1_CLK_CTRL, - SMC_CLK_CTRL, - LQSPI_CLK_CTRL, - SDIO_CLK_CTRL, - UART_CLK_CTRL, - SPI_CLK_CTRL, - CAN_CLK_CTRL, - CAN_MIOCLK_CTRL, - DBG_CLK_CTRL, - PCAP_CLK_CTRL, - TOPSW_CLK_CTRL, - CLK_MAX -} ClkValues; - -typedef enum { - CLK_CTRL, - THR_CTRL, - THR_CNT, - THR_STA, - FPGA_MAX -} FPGAValues; - -typedef enum { - SYNC_CTRL, - SYNC_STATUS, - BANDGAP_TRIP, - CC_TEST, - PLL_PREDIVISOR, - CLK_621_TRUE, - PICTURE_DBG, - PICTURE_DBG_UCNT, - PICTURE_DBG_LCNT, - MISC_MAX -} MiscValues; - -typedef enum { - PSS, - DDDR, - DMAC = 3, - USB, - GEM, - SDIO, - SPI, - CAN, - I2C, - UART, - GPIO, - LQSPI, - SMC, - OCM, - DEVCI, - FPGA, - A9_CPU, - RS_AWDT, - RST_REASON, - RST_REASON_CLR, - REBOOT_STATUS, - BOOT_MODE, - RESET_MAX -} ResetValues; +enum { + SCL = 0x000 / 4, + LOCK, + UNLOCK, + LOCKSTA, + + ARM_PLL_CTRL = 0x100 / 4, + DDR_PLL_CTRL, + IO_PLL_CTRL, + PLL_STATUS, + ARM_PLL_CFG, + DDR_PLL_CFG, + IO_PLL_CFG, + + ARM_CLK_CTRL = 0x120 / 4, + DDR_CLK_CTRL, + DCI_CLK_CTRL, + APER_CLK_CTRL, + USB0_CLK_CTRL, + USB1_CLK_CTRL, + GEM0_RCLK_CTRL, + GEM1_RCLK_CTRL, + GEM0_CLK_CTRL, + GEM1_CLK_CTRL, + SMC_CLK_CTRL, + LQSPI_CLK_CTRL, + SDIO_CLK_CTRL, + UART_CLK_CTRL, + SPI_CLK_CTRL, + CAN_CLK_CTRL, + CAN_MIOCLK_CTRL, + DBG_CLK_CTRL, + PCAP_CLK_CTRL, + TOPSW_CLK_CTRL, + +#define FPGA_CTRL_REGS(n, start) \ + FPGA ## n ## _CLK_CTRL = (start) / 4, \ + FPGA ## n ## _THR_CTRL, \ + FPGA ## n ## _THR_CNT, \ + FPGA ## n ## _THR_STA, + FPGA_CTRL_REGS(0, 0x170) + FPGA_CTRL_REGS(1, 0x180) + FPGA_CTRL_REGS(2, 0x190) + FPGA_CTRL_REGS(3, 0x1a0) + + BANDGAP_TRIP = 0x1b8 / 4, + PLL_PREDIVISOR = 0x1c0 / 4, + CLK_621_TRUE, + + PSS_RST_CTRL = 0x200 / 4, + DDR_RST_CTRL, + TOPSW_RESET_CTRL, + DMAC_RST_CTRL, + USB_RST_CTRL, + GEM_RST_CTRL, + SDIO_RST_CTRL, + SPI_RST_CTRL, + CAN_RST_CTRL, + I2C_RST_CTRL, + UART_RST_CTRL, + GPIO_RST_CTRL, + LQSPI_RST_CTRL, + SMC_RST_CTRL, + OCM_RST_CTRL, + FPGA_RST_CTRL = 0x240 / 4, + A9_CPU_RST_CTRL, + + RS_AWDT_CTRL = 0x24c / 4, + RST_REASON, + + REBOOT_STATUS = 0x258 / 4, + BOOT_MODE, + + APU_CTRL = 0x300 / 4, + WDT_CLK_SEL, + + TZ_DMA_NS = 0x440 / 4, + TZ_DMA_IRQ_NS, + TZ_DMA_PERIPH_NS, + + PSS_IDCODE = 0x530 / 4, + + DDR_URGENT = 0x600 / 4, + DDR_CAL_START = 0x60c / 4, + DDR_REF_START = 0x614 / 4, + DDR_CMD_STA, + DDR_URGENT_SEL, + DDR_DFI_STATUS, + + MIO = 0x700 / 4, +#define MIO_LENGTH 54 + + MIO_LOOPBACK = 0x804 / 4, + MIO_MST_TRI0, + MIO_MST_TRI1, + + SD0_WP_CD_SEL = 0x830 / 4, + SD1_WP_CD_SEL, + + LVL_SHFTR_EN = 0x900 / 4, + OCM_CFG = 0x910 / 4, + + CPU_RAM = 0xa00 / 4, + + IOU = 0xa30 / 4, + + DMAC_RAM = 0xa50 / 4, + + AFI0 = 0xa60 / 4, + AFI1 = AFI0 + 3, + AFI2 = AFI1 + 3, + AFI3 = AFI2 + 3, +#define AFI_LENGTH 3 + + OCM = 0xa90 / 4, + + DEVCI_RAM = 0xaa0 / 4, + + CSG_RAM = 0xab0 / 4, + + GPIOB_CTRL = 0xb00 / 4, + GPIOB_CFG_CMOS18, + GPIOB_CFG_CMOS25, + GPIOB_CFG_CMOS33, + GPIOB_CFG_HSTL = 0xb14 / 4, + GPIOB_DRVR_BIAS_CTRL, + + DDRIOB = 0xb40 / 4, +#define DDRIOB_LENGTH 14 +}; + +#define ZYNQ_SLCR_MMIO_SIZE 0x1000 +#define ZYNQ_SLCR_NUM_REGS (ZYNQ_SLCR_MMIO_SIZE / 4) #define TYPE_ZYNQ_SLCR "xilinx,zynq_slcr" #define ZYNQ_SLCR(obj) OBJECT_CHECK(ZynqSLCRState, (obj), TYPE_ZYNQ_SLCR) @@ -124,42 +175,7 @@ typedef struct ZynqSLCRState { MemoryRegion iomem; - union { - struct { - uint16_t scl; - uint16_t lockval; - uint32_t pll[PLL_MAX]; /* 0x100 - 0x11C */ - uint32_t clk[CLK_MAX]; /* 0x120 - 0x16C */ - uint32_t fpga[4][FPGA_MAX]; /* 0x170 - 0x1AC */ - uint32_t misc[MISC_MAX]; /* 0x1B0 - 0x1D8 */ - uint32_t reset[RESET_MAX]; /* 0x200 - 0x25C */ - uint32_t apu_ctrl; /* 0x300 */ - uint32_t wdt_clk_sel; /* 0x304 */ - uint32_t tz_ocm[3]; /* 0x400 - 0x408 */ - uint32_t tz_ddr; /* 0x430 */ - uint32_t tz_dma[3]; /* 0x440 - 0x448 */ - uint32_t tz_misc[3]; /* 0x450 - 0x458 */ - uint32_t tz_fpga[2]; /* 0x484 - 0x488 */ - uint32_t dbg_ctrl; /* 0x500 */ - uint32_t pss_idcode; /* 0x530 */ - uint32_t ddr[8]; /* 0x600 - 0x620 - 0x604-missing */ - uint32_t mio[54]; /* 0x700 - 0x7D4 */ - uint32_t mio_func[4]; /* 0x800 - 0x810 */ - uint32_t sd[2]; /* 0x830 - 0x834 */ - uint32_t lvl_shftr_en; /* 0x900 */ - uint32_t ocm_cfg; /* 0x910 */ - uint32_t cpu_ram[8]; /* 0xA00 - 0xA1C */ - uint32_t iou[7]; /* 0xA30 - 0xA48 */ - uint32_t dmac_ram; /* 0xA50 */ - uint32_t afi[4][3]; /* 0xA60 - 0xA8C */ - uint32_t ocm[3]; /* 0xA90 - 0xA98 */ - uint32_t devci_ram; /* 0xAA0 */ - uint32_t csg_ram; /* 0xAB0 */ - uint32_t gpiob[12]; /* 0xB00 - 0xB2C */ - uint32_t ddriob[14]; /* 0xB40 - 0xB74 */ - }; - uint8_t data[0x1000]; - }; + uint32_t regs[ZYNQ_SLCR_NUM_REGS]; } ZynqSLCRState; static void zynq_slcr_reset(DeviceState *d) @@ -169,177 +185,169 @@ static void zynq_slcr_reset(DeviceState *d) DB_PRINT("RESET\n"); - s->lockval = 1; + s->regs[LOCKSTA] = 1; /* 0x100 - 0x11C */ - s->pll[ARM_PLL_CTRL] = 0x0001A008; - s->pll[DDR_PLL_CTRL] = 0x0001A008; - s->pll[IO_PLL_CTRL] = 0x0001A008; - s->pll[PLL_STATUS] = 0x0000003F; - s->pll[ARM_PPL_CFG] = 0x00014000; - s->pll[DDR_PLL_CFG] = 0x00014000; - s->pll[IO_PLL_CFG] = 0x00014000; + s->regs[ARM_PLL_CTRL] = 0x0001A008; + s->regs[DDR_PLL_CTRL] = 0x0001A008; + s->regs[IO_PLL_CTRL] = 0x0001A008; + s->regs[PLL_STATUS] = 0x0000003F; + s->regs[ARM_PLL_CFG] = 0x00014000; + s->regs[DDR_PLL_CFG] = 0x00014000; + s->regs[IO_PLL_CFG] = 0x00014000; /* 0x120 - 0x16C */ - s->clk[ARM_CLK_CTRL] = 0x1F000400; - s->clk[DDR_CLK_CTRL] = 0x18400003; - s->clk[DCI_CLK_CTRL] = 0x01E03201; - s->clk[APER_CLK_CTRL] = 0x01FFCCCD; - s->clk[USB0_CLK_CTRL] = s->clk[USB1_CLK_CTRL] = 0x00101941; - s->clk[GEM0_RCLK_CTRL] = s->clk[GEM1_RCLK_CTRL] = 0x00000001; - s->clk[GEM0_CLK_CTRL] = s->clk[GEM1_CLK_CTRL] = 0x00003C01; - s->clk[SMC_CLK_CTRL] = 0x00003C01; - s->clk[LQSPI_CLK_CTRL] = 0x00002821; - s->clk[SDIO_CLK_CTRL] = 0x00001E03; - s->clk[UART_CLK_CTRL] = 0x00003F03; - s->clk[SPI_CLK_CTRL] = 0x00003F03; - s->clk[CAN_CLK_CTRL] = 0x00501903; - s->clk[DBG_CLK_CTRL] = 0x00000F03; - s->clk[PCAP_CLK_CTRL] = 0x00000F01; + s->regs[ARM_CLK_CTRL] = 0x1F000400; + s->regs[DDR_CLK_CTRL] = 0x18400003; + s->regs[DCI_CLK_CTRL] = 0x01E03201; + s->regs[APER_CLK_CTRL] = 0x01FFCCCD; + s->regs[USB0_CLK_CTRL] = s->regs[USB1_CLK_CTRL] = 0x00101941; + s->regs[GEM0_RCLK_CTRL] = s->regs[GEM1_RCLK_CTRL] = 0x00000001; + s->regs[GEM0_CLK_CTRL] = s->regs[GEM1_CLK_CTRL] = 0x00003C01; + s->regs[SMC_CLK_CTRL] = 0x00003C01; + s->regs[LQSPI_CLK_CTRL] = 0x00002821; + s->regs[SDIO_CLK_CTRL] = 0x00001E03; + s->regs[UART_CLK_CTRL] = 0x00003F03; + s->regs[SPI_CLK_CTRL] = 0x00003F03; + s->regs[CAN_CLK_CTRL] = 0x00501903; + s->regs[DBG_CLK_CTRL] = 0x00000F03; + s->regs[PCAP_CLK_CTRL] = 0x00000F01; /* 0x170 - 0x1AC */ - s->fpga[0][CLK_CTRL] = s->fpga[1][CLK_CTRL] = s->fpga[2][CLK_CTRL] = - s->fpga[3][CLK_CTRL] = 0x00101800; - s->fpga[0][THR_STA] = s->fpga[1][THR_STA] = s->fpga[2][THR_STA] = - s->fpga[3][THR_STA] = 0x00010000; + s->regs[FPGA0_CLK_CTRL] = s->regs[FPGA1_CLK_CTRL] = s->regs[FPGA2_CLK_CTRL] + = s->regs[FPGA3_CLK_CTRL] = 0x00101800; + s->regs[FPGA0_THR_STA] = s->regs[FPGA1_THR_STA] = s->regs[FPGA2_THR_STA] + = s->regs[FPGA3_THR_STA] = 0x00010000; /* 0x1B0 - 0x1D8 */ - s->misc[BANDGAP_TRIP] = 0x0000001F; - s->misc[PLL_PREDIVISOR] = 0x00000001; - s->misc[CLK_621_TRUE] = 0x00000001; + s->regs[BANDGAP_TRIP] = 0x0000001F; + s->regs[PLL_PREDIVISOR] = 0x00000001; + s->regs[CLK_621_TRUE] = 0x00000001; /* 0x200 - 0x25C */ - s->reset[FPGA] = 0x01F33F0F; - s->reset[RST_REASON] = 0x00000040; + s->regs[FPGA_RST_CTRL] = 0x01F33F0F; + s->regs[RST_REASON] = 0x00000040; + + s->regs[BOOT_MODE] = 0x00000001; /* 0x700 - 0x7D4 */ for (i = 0; i < 54; i++) { - s->mio[i] = 0x00001601; + s->regs[MIO + i] = 0x00001601; } for (i = 2; i <= 8; i++) { - s->mio[i] = 0x00000601; + s->regs[MIO + i] = 0x00000601; } - /* MIO_MST_TRI0, MIO_MST_TRI1 */ - s->mio_func[2] = s->mio_func[3] = 0xFFFFFFFF; + s->regs[MIO_MST_TRI0] = s->regs[MIO_MST_TRI1] = 0xFFFFFFFF; - s->cpu_ram[0] = s->cpu_ram[1] = s->cpu_ram[3] = - s->cpu_ram[4] = s->cpu_ram[7] = 0x00010101; - s->cpu_ram[2] = s->cpu_ram[5] = 0x01010101; - s->cpu_ram[6] = 0x00000001; + s->regs[CPU_RAM + 0] = s->regs[CPU_RAM + 1] = s->regs[CPU_RAM + 3] + = s->regs[CPU_RAM + 4] = s->regs[CPU_RAM + 7] + = 0x00010101; + s->regs[CPU_RAM + 2] = s->regs[CPU_RAM + 5] = 0x01010101; + s->regs[CPU_RAM + 6] = 0x00000001; - s->iou[0] = s->iou[1] = s->iou[2] = s->iou[3] = 0x09090909; - s->iou[4] = s->iou[5] = 0x00090909; - s->iou[6] = 0x00000909; + s->regs[IOU + 0] = s->regs[IOU + 1] = s->regs[IOU + 2] = s->regs[IOU + 3] + = 0x09090909; + s->regs[IOU + 4] = s->regs[IOU + 5] = 0x00090909; + s->regs[IOU + 6] = 0x00000909; - s->dmac_ram = 0x00000009; + s->regs[DMAC_RAM] = 0x00000009; - s->afi[0][0] = s->afi[0][1] = 0x09090909; - s->afi[1][0] = s->afi[1][1] = 0x09090909; - s->afi[2][0] = s->afi[2][1] = 0x09090909; - s->afi[3][0] = s->afi[3][1] = 0x09090909; - s->afi[0][2] = s->afi[1][2] = s->afi[2][2] = s->afi[3][2] = 0x00000909; + s->regs[AFI0 + 0] = s->regs[AFI0 + 1] = 0x09090909; + s->regs[AFI1 + 0] = s->regs[AFI1 + 1] = 0x09090909; + s->regs[AFI2 + 0] = s->regs[AFI2 + 1] = 0x09090909; + s->regs[AFI3 + 0] = s->regs[AFI3 + 1] = 0x09090909; + s->regs[AFI0 + 2] = s->regs[AFI1 + 2] = s->regs[AFI2 + 2] + = s->regs[AFI3 + 2] = 0x00000909; - s->ocm[0] = 0x01010101; - s->ocm[1] = s->ocm[2] = 0x09090909; + s->regs[OCM + 0] = 0x01010101; + s->regs[OCM + 1] = s->regs[OCM + 2] = 0x09090909; - s->devci_ram = 0x00000909; - s->csg_ram = 0x00000001; + s->regs[DEVCI_RAM] = 0x00000909; + s->regs[CSG_RAM] = 0x00000001; - s->ddriob[0] = s->ddriob[1] = s->ddriob[2] = s->ddriob[3] = 0x00000e00; - s->ddriob[4] = s->ddriob[5] = s->ddriob[6] = 0x00000e00; - s->ddriob[12] = 0x00000021; + s->regs[DDRIOB + 0] = s->regs[DDRIOB + 1] = s->regs[DDRIOB + 2] + = s->regs[DDRIOB + 3] = 0x00000e00; + s->regs[DDRIOB + 4] = s->regs[DDRIOB + 5] = s->regs[DDRIOB + 6] + = 0x00000e00; + s->regs[DDRIOB + 12] = 0x00000021; } -static inline uint32_t zynq_slcr_read_imp(void *opaque, - hwaddr offset) -{ - ZynqSLCRState *s = (ZynqSLCRState *)opaque; +static bool zynq_slcr_check_offset(hwaddr offset, bool rnw) +{ switch (offset) { - case 0x0: /* SCL */ - return s->scl; - case 0x4: /* LOCK */ - case 0x8: /* UNLOCK */ - DB_PRINT("Reading SCLR_LOCK/UNLOCK is not enabled\n"); - return 0; - case 0x0C: /* LOCKSTA */ - return s->lockval; - case 0x100 ... 0x11C: - return s->pll[(offset - 0x100) / 4]; - case 0x120 ... 0x16C: - return s->clk[(offset - 0x120) / 4]; - case 0x170 ... 0x1AC: - return s->fpga[0][(offset - 0x170) / 4]; - case 0x1B0 ... 0x1D8: - return s->misc[(offset - 0x1B0) / 4]; - case 0x200 ... 0x258: - return s->reset[(offset - 0x200) / 4]; - case 0x25c: - return 1; - case 0x300: - return s->apu_ctrl; - case 0x304: - return s->wdt_clk_sel; - case 0x400 ... 0x408: - return s->tz_ocm[(offset - 0x400) / 4]; - case 0x430: - return s->tz_ddr; - case 0x440 ... 0x448: - return s->tz_dma[(offset - 0x440) / 4]; - case 0x450 ... 0x458: - return s->tz_misc[(offset - 0x450) / 4]; - case 0x484 ... 0x488: - return s->tz_fpga[(offset - 0x484) / 4]; - case 0x500: - return s->dbg_ctrl; - case 0x530: - return s->pss_idcode; - case 0x600 ... 0x620: - if (offset == 0x604) { - goto bad_reg; - } - return s->ddr[(offset - 0x600) / 4]; - case 0x700 ... 0x7D4: - return s->mio[(offset - 0x700) / 4]; - case 0x800 ... 0x810: - return s->mio_func[(offset - 0x800) / 4]; - case 0x830 ... 0x834: - return s->sd[(offset - 0x830) / 4]; - case 0x900: - return s->lvl_shftr_en; - case 0x910: - return s->ocm_cfg; - case 0xA00 ... 0xA1C: - return s->cpu_ram[(offset - 0xA00) / 4]; - case 0xA30 ... 0xA48: - return s->iou[(offset - 0xA30) / 4]; - case 0xA50: - return s->dmac_ram; - case 0xA60 ... 0xA8C: - return s->afi[0][(offset - 0xA60) / 4]; - case 0xA90 ... 0xA98: - return s->ocm[(offset - 0xA90) / 4]; - case 0xAA0: - return s->devci_ram; - case 0xAB0: - return s->csg_ram; - case 0xB00 ... 0xB2C: - return s->gpiob[(offset - 0xB00) / 4]; - case 0xB40 ... 0xB74: - return s->ddriob[(offset - 0xB40) / 4]; + case LOCK: + case UNLOCK: + case DDR_CAL_START: + case DDR_REF_START: + return !rnw; /* Write only */ + case LOCKSTA: + case FPGA0_THR_STA: + case FPGA1_THR_STA: + case FPGA2_THR_STA: + case FPGA3_THR_STA: + case BOOT_MODE: + case PSS_IDCODE: + case DDR_CMD_STA: + case DDR_DFI_STATUS: + case PLL_STATUS: + return rnw;/* read only */ + case SCL: + case ARM_PLL_CTRL ... IO_PLL_CTRL: + case ARM_PLL_CFG ... IO_PLL_CFG: + case ARM_CLK_CTRL ... TOPSW_CLK_CTRL: + case FPGA0_CLK_CTRL ... FPGA0_THR_CNT: + case FPGA1_CLK_CTRL ... FPGA1_THR_CNT: + case FPGA2_CLK_CTRL ... FPGA2_THR_CNT: + case FPGA3_CLK_CTRL ... FPGA3_THR_CNT: + case BANDGAP_TRIP: + case PLL_PREDIVISOR: + case CLK_621_TRUE: + case PSS_RST_CTRL ... A9_CPU_RST_CTRL: + case RS_AWDT_CTRL: + case RST_REASON: + case REBOOT_STATUS: + case APU_CTRL: + case WDT_CLK_SEL: + case TZ_DMA_NS ... TZ_DMA_PERIPH_NS: + case DDR_URGENT: + case DDR_URGENT_SEL: + case MIO ... MIO + MIO_LENGTH - 1: + case MIO_LOOPBACK ... MIO_MST_TRI1: + case SD0_WP_CD_SEL: + case SD1_WP_CD_SEL: + case LVL_SHFTR_EN: + case OCM_CFG: + case CPU_RAM: + case IOU: + case DMAC_RAM: + case AFI0 ... AFI3 + AFI_LENGTH - 1: + case OCM: + case DEVCI_RAM: + case CSG_RAM: + case GPIOB_CTRL ... GPIOB_CFG_CMOS33: + case GPIOB_CFG_HSTL: + case GPIOB_DRVR_BIAS_CTRL: + case DDRIOB ... DDRIOB + DDRIOB_LENGTH - 1: + return true; default: - bad_reg: - DB_PRINT("Bad register offset 0x%x\n", (int)offset); - return 0; + return false; } } static uint64_t zynq_slcr_read(void *opaque, hwaddr offset, unsigned size) { - uint32_t ret = zynq_slcr_read_imp(opaque, offset); + ZynqSLCRState *s = opaque; + offset /= 4; + uint32_t ret = s->regs[offset]; - DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret); + if (!zynq_slcr_check_offset(offset, true)) { + qemu_log_mask(LOG_GUEST_ERROR, "zynq_slcr: Invalid read access to " + " addr %" HWADDR_PRIx "\n", offset * 4); + } + + DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset * 4, ret); return ret; } @@ -347,148 +355,55 @@ static void zynq_slcr_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { ZynqSLCRState *s = (ZynqSLCRState *)opaque; + offset /= 4; + + DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx64 "\n", offset * 4, val); - DB_PRINT("offset: %08x data: %08x\n", (unsigned)offset, (unsigned)val); + if (!zynq_slcr_check_offset(offset, false)) { + qemu_log_mask(LOG_GUEST_ERROR, "zynq_slcr: Invalid write access to " + "addr %" HWADDR_PRIx "\n", offset * 4); + return; + } switch (offset) { - case 0x00: /* SCL */ - s->scl = val & 0x1; - return; - case 0x4: /* SLCR_LOCK */ + case SCL: + s->regs[SCL] = val & 0x1; + return; + case LOCK: if ((val & 0xFFFF) == XILINX_LOCK_KEY) { DB_PRINT("XILINX LOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, (unsigned)val & 0xFFFF); - s->lockval = 1; + s->regs[LOCKSTA] = 1; } else { DB_PRINT("WRONG XILINX LOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, (unsigned)val & 0xFFFF); } return; - case 0x8: /* SLCR_UNLOCK */ + case UNLOCK: if ((val & 0xFFFF) == XILINX_UNLOCK_KEY) { DB_PRINT("XILINX UNLOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, (unsigned)val & 0xFFFF); - s->lockval = 0; + s->regs[LOCKSTA] = 0; } else { DB_PRINT("WRONG XILINX UNLOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, (unsigned)val & 0xFFFF); } return; - case 0xc: /* LOCKSTA */ - DB_PRINT("Writing SCLR_LOCKSTA is not enabled\n"); - return; } - if (!s->lockval) { - switch (offset) { - case 0x100 ... 0x11C: - if (offset == 0x10C) { - goto bad_reg; - } - s->pll[(offset - 0x100) / 4] = val; - break; - case 0x120 ... 0x16C: - s->clk[(offset - 0x120) / 4] = val; - break; - case 0x170 ... 0x1AC: - s->fpga[0][(offset - 0x170) / 4] = val; - break; - case 0x1B0 ... 0x1D8: - s->misc[(offset - 0x1B0) / 4] = val; - break; - case 0x200 ... 0x25C: - if (offset == 0x250) { - goto bad_reg; - } - s->reset[(offset - 0x200) / 4] = val; - if (offset == 0x200 && (val & R_PSS_RST_CTRL_SOFT_RST)) { - qemu_system_reset_request(); - } - break; - case 0x300: - s->apu_ctrl = val; - break; - case 0x304: - s->wdt_clk_sel = val; - break; - case 0x400 ... 0x408: - s->tz_ocm[(offset - 0x400) / 4] = val; - break; - case 0x430: - s->tz_ddr = val; - break; - case 0x440 ... 0x448: - s->tz_dma[(offset - 0x440) / 4] = val; - break; - case 0x450 ... 0x458: - s->tz_misc[(offset - 0x450) / 4] = val; - break; - case 0x484 ... 0x488: - s->tz_fpga[(offset - 0x484) / 4] = val; - break; - case 0x500: - s->dbg_ctrl = val; - break; - case 0x530: - s->pss_idcode = val; - break; - case 0x600 ... 0x620: - if (offset == 0x604) { - goto bad_reg; - } - s->ddr[(offset - 0x600) / 4] = val; - break; - case 0x700 ... 0x7D4: - s->mio[(offset - 0x700) / 4] = val; - break; - case 0x800 ... 0x810: - s->mio_func[(offset - 0x800) / 4] = val; - break; - case 0x830 ... 0x834: - s->sd[(offset - 0x830) / 4] = val; - break; - case 0x900: - s->lvl_shftr_en = val; - break; - case 0x910: - break; - case 0xA00 ... 0xA1C: - s->cpu_ram[(offset - 0xA00) / 4] = val; - break; - case 0xA30 ... 0xA48: - s->iou[(offset - 0xA30) / 4] = val; - break; - case 0xA50: - s->dmac_ram = val; - break; - case 0xA60 ... 0xA8C: - s->afi[0][(offset - 0xA60) / 4] = val; - break; - case 0xA90: - s->ocm[0] = val; - break; - case 0xAA0: - s->devci_ram = val; - break; - case 0xAB0: - s->csg_ram = val; - break; - case 0xB00 ... 0xB2C: - if (offset == 0xB20 || offset == 0xB2C) { - goto bad_reg; - } - s->gpiob[(offset - 0xB00) / 4] = val; - break; - case 0xB40 ... 0xB74: - s->ddriob[(offset - 0xB40) / 4] = val; - break; - default: - bad_reg: - DB_PRINT("Bad register write %x <= %08x\n", (int)offset, - (unsigned)val); - } + if (!s->regs[LOCKSTA]) { + s->regs[offset / 4] = val; } else { DB_PRINT("SCLR registers are locked. Unlock them first\n"); + return; + } + + switch (offset) { + case PSS_RST_CTRL: + if (val & R_PSS_RST_CTRL_SOFT_RST) { + qemu_system_reset_request(); + } + break; } } @@ -502,7 +417,8 @@ static int zynq_slcr_init(SysBusDevice *dev) { ZynqSLCRState *s = ZYNQ_SLCR(dev); - memory_region_init_io(&s->iomem, OBJECT(s), &slcr_ops, s, "slcr", 0x1000); + memory_region_init_io(&s->iomem, OBJECT(s), &slcr_ops, s, "slcr", + ZYNQ_SLCR_MMIO_SIZE); sysbus_init_mmio(dev, &s->iomem); return 0; @@ -510,11 +426,11 @@ static int zynq_slcr_init(SysBusDevice *dev) static const VMStateDescription vmstate_zynq_slcr = { .name = "zynq_slcr", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(data, ZynqSLCRState, 0x1000), + VMSTATE_UINT32_ARRAY(regs, ZynqSLCRState, ZYNQ_SLCR_NUM_REGS), VMSTATE_END_OF_LIST() } }; From 15e3611e1ca7a3b4d2cee505df83ea2744c2874d Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 15 Apr 2014 19:49:11 +0100 Subject: [PATCH 47/51] misc: zynq_slcr: Convert SBD::init to object init To bring it up to date with styling guidelines. Signed-off-by: Peter Crosthwaite Message-id: 2e837af80a18216c21e73241032e048f39d78b99.1396503037.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/misc/zynq_slcr.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index cc294442ccb0..06eba08f7f15 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -413,15 +413,13 @@ static const MemoryRegionOps slcr_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int zynq_slcr_init(SysBusDevice *dev) +static void zynq_slcr_init(Object *obj) { - ZynqSLCRState *s = ZYNQ_SLCR(dev); + ZynqSLCRState *s = ZYNQ_SLCR(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &slcr_ops, s, "slcr", + memory_region_init_io(&s->iomem, obj, &slcr_ops, s, "slcr", ZYNQ_SLCR_MMIO_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - return 0; + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } static const VMStateDescription vmstate_zynq_slcr = { @@ -438,9 +436,7 @@ static const VMStateDescription vmstate_zynq_slcr = { static void zynq_slcr_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - sdc->init = zynq_slcr_init; dc->vmsd = &vmstate_zynq_slcr; dc->reset = zynq_slcr_reset; } @@ -450,6 +446,7 @@ static const TypeInfo zynq_slcr_info = { .name = TYPE_ZYNQ_SLCR, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ZynqSLCRState), + .instance_init = zynq_slcr_init, }; static void zynq_slcr_register_types(void) From 6954a1cd97f7669b320967d11833fe0d6d7a742d Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Tue, 15 Apr 2014 19:49:11 +0100 Subject: [PATCH 48/51] misc: zynq_slcr: Make DB_PRINTs always compile Change the DB_PRINT macro over to a regular if() rather than conditional compilation to give constant compile testing of formats. Signed-off-by: Peter Crosthwaite Message-id: 942477847353c5cff5f45a228cc88c633dc012f3.1396503037.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- hw/misc/zynq_slcr.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index 06eba08f7f15..2e53a2e21f3a 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -19,14 +19,16 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" -#ifdef ZYNQ_ARM_SLCR_ERR_DEBUG +#ifndef ZYNQ_SLCR_ERR_DEBUG +#define ZYNQ_SLCR_ERR_DEBUG 0 +#endif + #define DB_PRINT(...) do { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ + if (ZYNQ_SLCR_ERR_DEBUG) { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } \ } while (0); -#else - #define DB_PRINT(...) -#endif #define XILINX_LOCK_KEY 0x767b #define XILINX_UNLOCK_KEY 0xdf0d From 553893736885e4f2dda424bff3e2200e1b6482a5 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Thu, 3 Apr 2014 23:55:19 -0700 Subject: [PATCH 49/51] net: cadence_gem: Make phy respond to broadcast Phys must respond to address 0 by specification. Implement. Signed-off-by: Nathan Rossi Signed-off-by: Peter Crosthwaite Message-id: 6f4d53b04ddbfb19895bfb61a595e69f1c08859a.1396594056.git.peter.crosthwaite@xilinx.com Reviewed-by: Beniamino Galvani Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 92dc2f21fa62..e34b25e734fe 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1093,7 +1093,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) uint32_t phy_addr, reg_num; phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == BOARD_PHY_ADDRESS) { + if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) { reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; retval &= 0xFFFF0000; retval |= gem_phy_read(s, reg_num); @@ -1193,7 +1193,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, uint32_t phy_addr, reg_num; phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == BOARD_PHY_ADDRESS) { + if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) { reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; gem_phy_write(s, reg_num, val); } From 33bbd75a7c3321432fe40a8cbacd64619c56138c Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Wed, 16 Apr 2014 20:20:52 -0700 Subject: [PATCH 50/51] arm: translate.c: Fix smlald Instruction The smlald (and probably smlsld) instruction was doing incorrect sign extensions of the operands amongst 64bit result calculation. The instruction psuedo-code is: operand2 = if m_swap then ROR(R[m],16) else R[m]; product1 = SInt(R[n]<15:0>) * SInt(operand2<15:0>); product2 = SInt(R[n]<31:16>) * SInt(operand2<31:16>); result = product1 + product2 + SInt(R[dHi]:R[dLo]); R[dHi] = result<63:32>; R[dLo] = result<31:0>; The result calculation should be done in 64 bit arithmetic, and hence product1 and product2 should be sign extended to 64b before calculation. The current implementation was adding product1 and product2 together then sign-extending the intermediate result leading to false negatives. E.G. if product1 = product2 = 0x4000000, their sum = 0x80000000, which will be incorrectly interpreted as -ve on sign extension. We fix by doing the 64b extensions on both product1 and product2 before any addition/subtraction happens. We also fix where we were possibly incorrectly setting the Q saturation flag for SMLSLD, which the ARM ARM specifically says is not set. Reported-by: Christina Smith Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Message-id: 2cddb6f5a15be4ab8d2160f3499d128ae93d304d.1397704570.git.peter.crosthwaite@xilinx.com Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell --- target-arm/translate.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 0c08cc1b81c2..a4d920b62911 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -8430,27 +8430,39 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (insn & (1 << 5)) gen_swap_half(tmp2); gen_smul_dual(tmp, tmp2); - if (insn & (1 << 6)) { - /* This subtraction cannot overflow. */ - tcg_gen_sub_i32(tmp, tmp, tmp2); - } else { - /* This addition cannot overflow 32 bits; - * however it may overflow considered as a signed - * operation, in which case we must set the Q flag. - */ - gen_helper_add_setq(tmp, cpu_env, tmp, tmp2); - } - tcg_temp_free_i32(tmp2); if (insn & (1 << 22)) { /* smlald, smlsld */ + TCGv_i64 tmp64_2; + tmp64 = tcg_temp_new_i64(); + tmp64_2 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); + tcg_gen_ext_i32_i64(tmp64_2, tmp2); tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + if (insn & (1 << 6)) { + tcg_gen_sub_i64(tmp64, tmp64, tmp64_2); + } else { + tcg_gen_add_i64(tmp64, tmp64, tmp64_2); + } + tcg_temp_free_i64(tmp64_2); gen_addq(s, tmp64, rd, rn); gen_storeq_reg(s, rd, rn, tmp64); tcg_temp_free_i64(tmp64); } else { /* smuad, smusd, smlad, smlsd */ + if (insn & (1 << 6)) { + /* This subtraction cannot overflow. */ + tcg_gen_sub_i32(tmp, tmp, tmp2); + } else { + /* This addition cannot overflow 32 bits; + * however it may overflow considered as a + * signed operation, in which case we must set + * the Q flag. + */ + gen_helper_add_setq(tmp, cpu_env, tmp, tmp2); + } + tcg_temp_free_i32(tmp2); if (rd != 15) { tmp2 = load_reg(s, rd); From e44a90c59697cf98e05619fbb6f77a403d347495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 16 Apr 2014 12:29:39 +0100 Subject: [PATCH 51/51] target-arm: A64: fix unallocated test of scalar SQXTUN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test for the U bit was incorrectly inverted in the scalar case of SQXTUN. This doesn't affect the vector case as the U bit is used to select XTN(2). Reported-by: Hao Liu Signed-off-by: Alex Bennée Reviewed-by: Claudio Fontana Reviewed-by: Peter Maydell Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell --- target-arm/translate-a64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 188287d031f1..d86b8ffa55cb 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -7708,7 +7708,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) } break; case 0x12: /* SQXTUN */ - if (u) { + if (!u) { unallocated_encoding(s); return; }