On Fri, Apr 11, 2014 at 2:15 AM, Peter Maydell <peter.mayd...@linaro.org> wrote: > 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 <peter.mayd...@linaro.org>
Reviewed-by: Peter Crosthwaite <peter.crosthwa...@xilinx.com> > --- > 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 ecdd7a7..28b9bda 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 276ecf2..27a3dc2 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 a527f02..de79dfc 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 ee72748..39c4364 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 42b1c90..c2c0780 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 51edd90..64a33dd 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; > -- > 1.9.1 > >