target-ppc: Add mechanism for synchronizing SPRs with KVM
authorDavid Gibson <david@gibson.dropbear.id.au>
Wed, 20 Feb 2013 16:41:50 +0000 (16:41 +0000)
committerAlexander Graf <agraf@suse.de>
Fri, 8 Mar 2013 20:04:52 +0000 (21:04 +0100)
Currently when runing under KVM on ppc, we synchronize a certain number of
vital SPRs to KVM through the SET_SREGS call.  This leaves out quite a lot
of important SPRs which are maintained in KVM.  It would be helpful to
have their contents in qemu for debugging purposes, and when we implement
migration it will be vital, since they include important guest state that
will need to be restored on the target.

This patch sets up for synchronization of any registers supported by the
KVM ONE_REG calls.  A new variant on spr_register() allows a ONE_REG id to
be stored with the SPR information.  When we set/get information to KVM
we also synchronize any SPRs so registered.

For now we set this mechanism up to synchronize a handful of important
registers that already have ONE_REG IDs, notably the DAR and DSISR.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
target-ppc/cpu.h
target-ppc/kvm.c
target-ppc/translate_init.c

index e4cf96ce2f5dbf35a785817dc64763cede8dd514..417abb0dd1077888e4807084c218de12515a891c 100644 (file)
@@ -329,6 +329,12 @@ struct ppc_spr_t {
     void (*hea_write)(void *opaque, int spr_num, int gpr_num);
 #endif
     const char *name;
+#ifdef CONFIG_KVM
+    /* We (ab)use the fact that all the SPRs will have ids for the
+     * ONE_REG interface will have KVM_REG_PPC to use 0 as meaning,
+     * don't sync this */
+    uint64_t one_reg_id;
+#endif
 };
 
 /* Altivec registers (128 bits) */
index a89c3cf7a51776d0916d5f93248adafaed89190b..02ab5598366510e36e0e34ee66c667974d940d90 100644 (file)
@@ -61,6 +61,7 @@ static int cap_ppc_smt;
 static int cap_ppc_rma;
 static int cap_spapr_tce;
 static int cap_hior;
+static int cap_one_reg;
 
 /* XXX We have a race condition where we actually have a level triggered
  *     interrupt, but the infrastructure can't expose that yet, so the guest
@@ -89,6 +90,7 @@ int kvm_arch_init(KVMState *s)
     cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT);
     cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA);
     cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE);
+    cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG);
     cap_hior = kvm_check_extension(s, KVM_CAP_PPC_HIOR);
 
     if (!cap_interrupt_level) {
@@ -449,6 +451,76 @@ static void kvm_sw_tlb_put(PowerPCCPU *cpu)
     g_free(bitmap);
 }
 
+static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+    union {
+        uint32_t u32;
+        uint64_t u64;
+    } val;
+    struct kvm_one_reg reg = {
+        .id = id,
+        .addr = (uintptr_t) &val,
+    };
+    int ret;
+
+    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+    if (ret != 0) {
+        fprintf(stderr, "Warning: Unable to retrieve SPR %d from KVM: %s\n",
+                spr, strerror(errno));
+    } else {
+        switch (id & KVM_REG_SIZE_MASK) {
+        case KVM_REG_SIZE_U32:
+            env->spr[spr] = val.u32;
+            break;
+
+        case KVM_REG_SIZE_U64:
+            env->spr[spr] = val.u64;
+            break;
+
+        default:
+            /* Don't handle this size yet */
+            abort();
+        }
+    }
+}
+
+static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+    union {
+        uint32_t u32;
+        uint64_t u64;
+    } val;
+    struct kvm_one_reg reg = {
+        .id = id,
+        .addr = (uintptr_t) &val,
+    };
+    int ret;
+
+    switch (id & KVM_REG_SIZE_MASK) {
+    case KVM_REG_SIZE_U32:
+        val.u32 = env->spr[spr];
+        break;
+
+    case KVM_REG_SIZE_U64:
+        val.u64 = env->spr[spr];
+        break;
+
+    default:
+        /* Don't handle this size yet */
+        abort();
+    }
+
+    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+    if (ret != 0) {
+        fprintf(stderr, "Warning: Unable to set SPR %d to KVM: %s\n",
+                spr, strerror(errno));
+    }
+}
+
 int kvm_arch_put_registers(CPUState *cs, int level)
 {
     PowerPCCPU *cpu = POWERPC_CPU(cs);
@@ -530,15 +602,22 @@ int kvm_arch_put_registers(CPUState *cs, int level)
     }
 
     if (cap_hior && (level >= KVM_PUT_RESET_STATE)) {
-        uint64_t hior = env->spr[SPR_HIOR];
-        struct kvm_one_reg reg = {
-            .id = KVM_REG_PPC_HIOR,
-            .addr = (uintptr_t) &hior,
-        };
+        kvm_put_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR);
+    }
 
-        ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
-        if (ret) {
-            return ret;
+    if (cap_one_reg) {
+        int i;
+
+        /* We deliberately ignore errors here, for kernels which have
+         * the ONE_REG calls, but don't support the specific
+         * registers, there's a reasonable chance things will still
+         * work, at least until we try to migrate. */
+        for (i = 0; i < 1024; i++) {
+            uint64_t id = env->spr_cb[i].one_reg_id;
+
+            if (id != 0) {
+                kvm_put_one_spr(cs, id, i);
+            }
         }
     }
 
@@ -721,6 +800,26 @@ int kvm_arch_get_registers(CPUState *cs)
         }
     }
 
+    if (cap_hior) {
+        kvm_get_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR);
+    }
+
+    if (cap_one_reg) {
+        int i;
+
+        /* We deliberately ignore errors here, for kernels which have
+         * the ONE_REG calls, but don't support the specific
+         * registers, there's a reasonable chance things will still
+         * work, at least until we try to migrate. */
+        for (i = 0; i < 1024; i++) {
+            uint64_t id = env->spr_cb[i].one_reg_id;
+
+            if (id != 0) {
+                kvm_get_one_spr(cs, id, i);
+            }
+        }
+    }
+
     return 0;
 }
 
index 1015077df3702edf65df48e4b86841c10da702d3..7fb314c038d12ea67d3d3fd5b700ecad6bb7fddd 100644 (file)
@@ -572,26 +572,42 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val)
     set_flush_to_zero(vscr_nj, &env->vec_status);
 }
 
-#if defined(CONFIG_USER_ONLY)
-#define spr_register(env, num, name, uea_read, uea_write,                     \
-                     oea_read, oea_write, initial_value)                      \
-do {                                                                          \
-     _spr_register(env, num, name, uea_read, uea_write, initial_value);       \
-} while (0)
-static inline void _spr_register (CPUPPCState *env, int num,
-                                  const char *name,
-                                  void (*uea_read)(void *opaque, int gprn, int sprn),
-                                  void (*uea_write)(void *opaque, int sprn, int gprn),
-                                  target_ulong initial_value)
+#ifdef CONFIG_USER_ONLY
+#define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
+                         oea_read, oea_write, one_reg_id, initial_value)       \
+    _spr_register(env, num, name, uea_read, uea_write, initial_value)
+#else
+#if !defined(CONFIG_KVM)
+#define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
+                         oea_read, oea_write, one_reg_id, initial_value) \
+    _spr_register(env, num, name, uea_read, uea_write,                         \
+                  oea_read, oea_write, initial_value)
 #else
-static inline void spr_register (CPUPPCState *env, int num,
+#define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
+                         oea_read, oea_write, one_reg_id, initial_value) \
+    _spr_register(env, num, name, uea_read, uea_write,                         \
+                  oea_read, oea_write, one_reg_id, initial_value)
+#endif
+#endif
+
+#define spr_register(env, num, name, uea_read, uea_write,                      \
+                     oea_read, oea_write, initial_value)                       \
+    spr_register_kvm(env, num, name, uea_read, uea_write,                      \
+                     oea_read, oea_write, 0, initial_value)
+
+static inline void _spr_register(CPUPPCState *env, int num,
                                  const char *name,
                                  void (*uea_read)(void *opaque, int gprn, int sprn),
                                  void (*uea_write)(void *opaque, int sprn, int gprn),
+#if !defined(CONFIG_USER_ONLY)
+
                                  void (*oea_read)(void *opaque, int gprn, int sprn),
                                  void (*oea_write)(void *opaque, int sprn, int gprn),
-                                 target_ulong initial_value)
 #endif
+#if defined(CONFIG_KVM)
+                                 uint64_t one_reg_id,
+#endif
+                                 target_ulong initial_value)
 {
     ppc_spr_t *spr;
 
@@ -667,14 +683,14 @@ static void gen_spr_generic (CPUPPCState *env)
 static void gen_spr_ne_601 (CPUPPCState *env)
 {
     /* Exception processing */
-    spr_register(env, SPR_DSISR, "DSISR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    spr_register(env, SPR_DAR, "DAR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
+    spr_register_kvm(env, SPR_DSISR, "DSISR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_DSISR, 0x00000000);
+    spr_register_kvm(env, SPR_DAR, "DAR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_DAR, 0x00000000);
     /* Timer */
     spr_register(env, SPR_DECR, "DECR",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -918,10 +934,10 @@ static void gen_spr_7xx (CPUPPCState *env)
 {
     /* Breakpoints */
     /* XXX : not implemented */
-    spr_register(env, SPR_DABR, "DABR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
+    spr_register_kvm(env, SPR_DABR, "DABR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_DABR, 0x00000000);
     /* XXX : not implemented */
     spr_register(env, SPR_IABR, "IABR",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -1047,10 +1063,10 @@ static void gen_spr_604 (CPUPPCState *env)
                  &spr_read_generic, &spr_write_generic,
                  0x00000000);
     /* XXX : not implemented */
-    spr_register(env, SPR_DABR, "DABR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
+    spr_register_kvm(env, SPR_DABR, "DABR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_DABR, 0x00000000);
     /* Performance counters */
     /* XXX : not implemented */
     spr_register(env, SPR_MMCR0, "MMCR0",
@@ -2305,14 +2321,14 @@ static void gen_spr_620 (CPUPPCState *env)
 static void gen_spr_5xx_8xx (CPUPPCState *env)
 {
     /* Exception processing */
-    spr_register(env, SPR_DSISR, "DSISR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
-    spr_register(env, SPR_DAR, "DAR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
+    spr_register_kvm(env, SPR_DSISR, "DSISR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_DSISR, 0x00000000);
+    spr_register_kvm(env, SPR_DAR, "DAR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_DAR, 0x00000000);
     /* Timer */
     spr_register(env, SPR_DECR, "DECR",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -7036,22 +7052,22 @@ static void init_proc_POWER7 (CPUPPCState *env)
                  0x00000000);
 #if !defined(CONFIG_USER_ONLY)
     /* PURR & SPURR: Hack - treat these as aliases for the TB for now */
-    spr_register(env, SPR_PURR,   "PURR",
-                 &spr_read_purr, SPR_NOACCESS,
-                 &spr_read_purr, SPR_NOACCESS,
-                 0x00000000);
-    spr_register(env, SPR_SPURR,   "SPURR",
-                 &spr_read_purr, SPR_NOACCESS,
-                 &spr_read_purr, SPR_NOACCESS,
-                 0x00000000);
+    spr_register_kvm(env, SPR_PURR,   "PURR",
+                     &spr_read_purr, SPR_NOACCESS,
+                     &spr_read_purr, SPR_NOACCESS,
+                     KVM_REG_PPC_PURR, 0x00000000);
+    spr_register_kvm(env, SPR_SPURR,   "SPURR",
+                     &spr_read_purr, SPR_NOACCESS,
+                     &spr_read_purr, SPR_NOACCESS,
+                     KVM_REG_PPC_SPURR, 0x00000000);
     spr_register(env, SPR_CFAR, "SPR_CFAR",
                  SPR_NOACCESS, SPR_NOACCESS,
                  &spr_read_cfar, &spr_write_cfar,
                  0x00000000);
-    spr_register(env, SPR_DSCR, "SPR_DSCR",
-                 SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
-                 0x00000000);
+    spr_register_kvm(env, SPR_DSCR, "SPR_DSCR",
+                     SPR_NOACCESS, SPR_NOACCESS,
+                     &spr_read_generic, &spr_write_generic,
+                     KVM_REG_PPC_DSCR, 0x00000000);
 #endif /* !CONFIG_USER_ONLY */
     /* Memory management */
     /* XXX : not implemented */