summaryrefslogtreecommitdiffstats
path: root/target-ppc
diff options
context:
space:
mode:
Diffstat (limited to 'target-ppc')
-rw-r--r--target-ppc/cpu.h6
-rw-r--r--target-ppc/kvm.c115
-rw-r--r--target-ppc/translate_init.c114
3 files changed, 178 insertions, 57 deletions
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index e4cf96c..417abb0 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -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) */
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index a89c3cf..02ab559 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -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;
}
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 1015077..7fb314c 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -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 */
OpenPOWER on IntegriCloud