aboutsummaryrefslogtreecommitdiff
path: root/target-arm/kvm64.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-arm/kvm64.c')
-rw-r--r--target-arm/kvm64.c118
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, &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, &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, &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, &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, &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, &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, &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, &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;
}