diff options
Diffstat (limited to 'target-arm/kvm64.c')
-rw-r--r-- | target-arm/kvm64.c | 118 |
1 files changed, 113 insertions, 5 deletions
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index 8cf3a627ec..93c1ca8b21 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -15,6 +15,7 @@ #include <linux/kvm.h> +#include "config-host.h" #include "qemu-common.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -126,12 +127,20 @@ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) #define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) +#define AARCH64_SIMD_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U128 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + +#define AARCH64_SIMD_CTRL_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U32 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + int kvm_arch_put_registers(CPUState *cs, int level) { struct kvm_one_reg reg; + uint32_t fpr; uint64_t val; int i; int ret; + unsigned int el; ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -198,22 +207,70 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + /* Saved Program State Registers + * + * Before we restore from the banked_spsr[] array we need to + * ensure that any modifications to env->spsr are correctly + * reflected in the banks. + */ + el = arm_current_el(env); + if (el > 0 && !is_a64(env)) { + i = bank_number(env->uncached_cpsr & CPSR_M); + env->banked_spsr[i] = env->spsr; + } + + /* KVM 0-4 map to QEMU banks 1-5 */ for (i = 0; i < KVM_NR_SPSR; i++) { reg.id = AARCH64_CORE_REG(spsr[i]); - reg.addr = (uintptr_t) &env->banked_spsr[i - 1]; + reg.addr = (uintptr_t) &env->banked_spsr[i + 1]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + } + + /* Advanced SIMD and FP registers + * We map Qn = regs[2n+1]:regs[2n] + */ + for (i = 0; i < 32; i++) { + int rd = i << 1; + uint64_t fp_val[2]; +#ifdef HOST_WORDS_BIGENDIAN + fp_val[0] = env->vfp.regs[rd + 1]; + fp_val[1] = env->vfp.regs[rd]; +#else + fp_val[1] = env->vfp.regs[rd + 1]; + fp_val[0] = env->vfp.regs[rd]; +#endif + reg.id = AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]); + reg.addr = (uintptr_t)(&fp_val); ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret) { return ret; } } + reg.addr = (uintptr_t)(&fpr); + fpr = vfp_get_fpsr(env); + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpsr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + + fpr = vfp_get_fpcr(env); + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpcr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + if (!write_list_to_kvmstate(cpu)) { return EINVAL; } - /* TODO: - * FP state - */ + kvm_arm_sync_mpstate_to_kvm(cpu); + return ret; } @@ -221,6 +278,8 @@ int kvm_arch_get_registers(CPUState *cs) { struct kvm_one_reg reg; uint64_t val; + uint32_t fpr; + unsigned int el; int i; int ret; @@ -293,15 +352,62 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } + /* Fetch the SPSR registers + * + * KVM SPSRs 0-4 map to QEMU banks 1-5 + */ for (i = 0; i < KVM_NR_SPSR; i++) { reg.id = AARCH64_CORE_REG(spsr[i]); - reg.addr = (uintptr_t) &env->banked_spsr[i - 1]; + reg.addr = (uintptr_t) &env->banked_spsr[i + 1]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + } + + el = arm_current_el(env); + if (el > 0 && !is_a64(env)) { + i = bank_number(env->uncached_cpsr & CPSR_M); + env->spsr = env->banked_spsr[i]; + } + + /* Advanced SIMD and FP registers + * We map Qn = regs[2n+1]:regs[2n] + */ + for (i = 0; i < 32; i++) { + uint64_t fp_val[2]; + reg.id = AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]); + reg.addr = (uintptr_t)(&fp_val); ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret) { return ret; + } else { + int rd = i << 1; +#ifdef HOST_WORDS_BIGENDIAN + env->vfp.regs[rd + 1] = fp_val[0]; + env->vfp.regs[rd] = fp_val[1]; +#else + env->vfp.regs[rd + 1] = fp_val[1]; + env->vfp.regs[rd] = fp_val[0]; +#endif } } + reg.addr = (uintptr_t)(&fpr); + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpsr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + vfp_set_fpsr(env, fpr); + + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpcr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + vfp_set_fpcr(env, fpr); + if (!write_kvmstate_to_list(cpu)) { return EINVAL; } @@ -310,6 +416,8 @@ int kvm_arch_get_registers(CPUState *cs) */ write_list_to_cpustate(cpu); + kvm_arm_sync_mpstate_to_qemu(cpu); + /* TODO: other registers */ return ret; } |