aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2013-05-13 11:38:09 +0100
committerPeter Maydell <peter.maydell@linaro.org>2013-05-13 15:19:35 +0100
commit2d2d74ee0a3b801fbf2d90e640e248f52f32da2f (patch)
tree1c6964ffd4126bf98c4703ae475f691dc09f9484
parent7985769cf26da0340b9782369e291c019ed372a0 (diff)
downloadqemu-arm-2d2d74ee0a3b801fbf2d90e640e248f52f32da2f.tar.gz
target-arm: Use tuple list to sync cp regs with KVM
Use the tuple list of cp registers for syncing KVM state to QEMU and for migration of register state when using KVM. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--target-arm/Makefile.objs1
-rw-r--r--target-arm/kvm.c267
-rw-r--r--target-arm/kvm_arm.h33
-rw-r--r--target-arm/machine.c30
4 files changed, 255 insertions, 76 deletions
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index d89b57c114..4a6e52e528 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -1,5 +1,6 @@
obj-y += arm-semi.o
obj-$(CONFIG_SOFTMMU) += machine.o
obj-$(CONFIG_KVM) += kvm.o
+obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index 4aea7c3390..b9c9127d9c 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -50,12 +50,35 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
return cpu->cpu_index;
}
+static bool reg_syncs_via_tuple_list(uint64_t regidx)
+{
+ /* Return true if the regidx is a register we should synchronize
+ * via the cpreg_tuples array (ie is not a core reg we sync by
+ * hand in kvm_arch_get/put_registers())
+ */
+ switch (regidx & KVM_REG_ARM_COPROC_MASK) {
+ case KVM_REG_ARM_CORE:
+ case KVM_REG_ARM_VFP:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int compare_u64(const void *a, const void *b)
+{
+ return *(uint64_t *)a - *(uint64_t *)b;
+}
+
int kvm_arch_init_vcpu(CPUState *cs)
{
struct kvm_vcpu_init init;
- int ret;
+ int i, ret, arraylen;
uint64_t v;
struct kvm_one_reg r;
+ struct kvm_reg_list rl;
+ struct kvm_reg_list *rlp;
+ ARMCPU *cpu = ARM_CPU(cs);
init.target = KVM_ARM_TARGET_CORTEX_A15;
memset(init.features, 0, sizeof(init.features));
@@ -74,6 +97,73 @@ int kvm_arch_init_vcpu(CPUState *cs)
if (ret == -ENOENT) {
return -EINVAL;
}
+
+ /* Populate the cpreg list based on the kernel's idea
+ * of what registers exist (and throw away the TCG-created list).
+ */
+ rl.n = 0;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
+ if (ret != -E2BIG) {
+ return ret;
+ }
+ rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
+ rlp->n = rl.n;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
+ if (ret) {
+ goto out;
+ }
+ /* Sort the list we get back from the kernel, since cpreg_tuples
+ * must be in strictly ascending order.
+ */
+ qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
+
+ for (i = 0, arraylen = 0; i < rlp->n; i++) {
+ if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
+ continue;
+ }
+ switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
+ case KVM_REG_SIZE_U32:
+ case KVM_REG_SIZE_U64:
+ break;
+ default:
+ fprintf(stderr, "Can't handle size of register in kernel list\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ arraylen++;
+ }
+
+ cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
+ cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
+ cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
+ arraylen);
+ cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
+ arraylen);
+ cpu->cpreg_array_len = arraylen;
+ cpu->cpreg_vmstate_array_len = arraylen;
+
+ for (i = 0, arraylen = 0; i < rlp->n; i++) {
+ uint64_t regidx = rlp->reg[i];
+ if (!reg_syncs_via_tuple_list(regidx)) {
+ continue;
+ }
+ cpu->cpreg_indexes[arraylen] = regidx;
+ arraylen++;
+ }
+ assert(cpu->cpreg_array_len == arraylen);
+
+ if (!write_kvmstate_to_list(cpu)) {
+ /* Shouldn't happen unless kernel is inconsistent about
+ * what registers exist.
+ */
+ fprintf(stderr, "Initial read of kernel register state failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+out:
+ g_free(rlp);
return ret;
}
@@ -163,6 +253,78 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid)
QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries);
}
+bool write_kvmstate_to_list(ARMCPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ int i;
+ bool ok = true;
+
+ for (i = 0; i < cpu->cpreg_array_len; i++) {
+ struct kvm_one_reg r;
+ uint64_t regidx = cpu->cpreg_indexes[i];
+ uint32_t v32;
+ int ret;
+
+ r.id = regidx;
+
+ switch (regidx & KVM_REG_SIZE_MASK) {
+ case KVM_REG_SIZE_U32:
+ r.addr = (uintptr_t)&v32;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (!ret) {
+ cpu->cpreg_values[i] = v32;
+ }
+ break;
+ case KVM_REG_SIZE_U64:
+ r.addr = (uintptr_t)(cpu->cpreg_values + i);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ break;
+ default:
+ abort();
+ }
+ if (ret) {
+ ok = false;
+ }
+ }
+ return ok;
+}
+
+bool write_list_to_kvmstate(ARMCPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ int i;
+ bool ok = true;
+
+ for (i = 0; i < cpu->cpreg_array_len; i++) {
+ struct kvm_one_reg r;
+ uint64_t regidx = cpu->cpreg_indexes[i];
+ uint32_t v32;
+ int ret;
+
+ r.id = regidx;
+ switch (regidx & KVM_REG_SIZE_MASK) {
+ case KVM_REG_SIZE_U32:
+ v32 = cpu->cpreg_values[i];
+ r.addr = (uintptr_t)&v32;
+ break;
+ case KVM_REG_SIZE_U64:
+ r.addr = (uintptr_t)(cpu->cpreg_values + i);
+ break;
+ default:
+ abort();
+ }
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ /* We might fail for "unknown register" and also for
+ * "you tried to set a register which is constant with
+ * a different value from what it actually contains".
+ */
+ ok = false;
+ }
+ }
+ return ok;
+}
+
typedef struct Reg {
uint64_t id;
int offset;
@@ -175,17 +337,6 @@ typedef struct Reg {
offsetof(CPUARMState, QEMUFIELD) \
}
-#define CP15REG(CRN, CRM, OPC1, OPC2, QEMUFIELD) \
- { \
- KVM_REG_ARM | KVM_REG_SIZE_U32 | \
- (15 << KVM_REG_ARM_COPROC_SHIFT) | \
- ((CRN) << KVM_REG_ARM_32_CRN_SHIFT) | \
- ((CRM) << KVM_REG_ARM_CRM_SHIFT) | \
- ((OPC1) << KVM_REG_ARM_OPC1_SHIFT) | \
- ((OPC2) << KVM_REG_ARM_32_OPC2_SHIFT), \
- offsetof(CPUARMState, QEMUFIELD) \
- }
-
#define VFPSYSREG(R) \
{ \
KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
@@ -234,12 +385,6 @@ static const Reg regs[] = {
COREREG(fiq_regs[7], banked_spsr[5]),
/* R15 */
COREREG(usr_regs.uregs[15], regs[15]),
- /* A non-comprehensive set of cp15 registers.
- * TODO: drive this from the cp_regs hashtable instead.
- */
- CP15REG(1, 0, 0, 0, cp15.c1_sys), /* SCTLR */
- CP15REG(2, 0, 0, 2, cp15.c2_control), /* TTBCR */
- CP15REG(3, 0, 0, 0, cp15.c3), /* DACR */
/* VFP system registers */
VFPSYSREG(FPSID),
VFPSYSREG(MVFR1),
@@ -257,7 +402,6 @@ int kvm_arch_put_registers(CPUState *cs, int level)
int mode, bn;
int ret, i;
uint32_t cpsr, fpscr;
- uint64_t ttbr;
/* Make sure the banked regs are properly set */
mode = env->uncached_cpsr & CPSR_M;
@@ -291,26 +435,6 @@ int kvm_arch_put_registers(CPUState *cs, int level)
return ret;
}
- /* TTBR0: cp15 crm=2 opc1=0 */
- ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) |
- (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT);
- r.addr = (uintptr_t)(&ttbr);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
-
- /* TTBR1: cp15 crm=2 opc1=1 */
- ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) |
- (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT);
- r.addr = (uintptr_t)(&ttbr);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
-
/* VFP registers */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
for (i = 0; i < 32; i++) {
@@ -327,6 +451,31 @@ int kvm_arch_put_registers(CPUState *cs, int level)
fpscr = vfp_get_fpscr(env);
r.addr = (uintptr_t)&fpscr;
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+
+ /* Note that we do not call write_cpustate_to_list()
+ * here, so we are only writing the tuple list back to
+ * KVM. This is safe because nothing can change the
+ * CPUARMState cp15 fields (in particular gdb accesses cannot)
+ * and so there are no changes to sync. In fact syncing would
+ * be wrong at this point: for a constant register where TCG and
+ * KVM disagree about its value, the preceding write_list_to_cpustate()
+ * would not have had any effect on the CPUARMState value (since the
+ * register is read-only), and a write_cpustate_to_list() here would
+ * then try to write the TCG value back into KVM -- this would either
+ * fail or incorrectly change the value the guest sees.
+ *
+ * If we ever want to allow the user to modify cp15 registers via
+ * the gdb stub, we would need to be more clever here (for instance
+ * tracking the set of registers kvm_arch_get_registers() successfully
+ * managed to update the CPUARMState with, and only allowing those
+ * to be written back up into the kernel).
+ */
+ if (!write_list_to_kvmstate(cpu)) {
+ return EINVAL;
+ }
return ret;
}
@@ -339,7 +488,6 @@ int kvm_arch_get_registers(CPUState *cs)
int mode, bn;
int ret, i;
uint32_t cpsr, fpscr;
- uint64_t ttbr;
for (i = 0; i < ARRAY_SIZE(regs); i++) {
r.id = regs[i].id;
@@ -360,28 +508,6 @@ int kvm_arch_get_registers(CPUState *cs)
}
cpsr_write(env, cpsr, 0xffffffff);
- /* TTBR0: cp15 crm=2 opc1=0 */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) |
- (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT);
- r.addr = (uintptr_t)(&ttbr);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- env->cp15.c2_base0_hi = ttbr >> 32;
- env->cp15.c2_base0 = ttbr;
-
- /* TTBR1: cp15 crm=2 opc1=1 */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) |
- (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT);
- r.addr = (uintptr_t)(&ttbr);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- env->cp15.c2_base1_hi = ttbr >> 32;
- env->cp15.c2_base1 = ttbr;
-
/* Make sure the current mode regs are properly set */
mode = env->uncached_cpsr & CPSR_M;
bn = bank_number(mode);
@@ -394,15 +520,6 @@ int kvm_arch_get_registers(CPUState *cs)
env->regs[14] = env->banked_r14[bn];
env->spsr = env->banked_spsr[bn];
- /* The main GET_ONE_REG loop above set c2_control, but we need to
- * update some extra cached precomputed values too.
- * When this is driven from the cp_regs hashtable then this ugliness
- * can disappear because we'll use the access function which sets
- * these values automatically.
- */
- env->cp15.c2_mask = ~(0xffffffffu >> env->cp15.c2_control);
- env->cp15.c2_base_mask = ~(0x3fffu >> env->cp15.c2_control);
-
/* VFP registers */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
for (i = 0; i < 32; i++) {
@@ -423,6 +540,14 @@ int kvm_arch_get_registers(CPUState *cs)
}
vfp_set_fpscr(env, fpscr);
+ if (!write_kvmstate_to_list(cpu)) {
+ return EINVAL;
+ }
+ /* Note that it's OK to have registers which aren't in CPUState,
+ * so we can ignore a failure return here.
+ */
+ write_list_to_cpustate(cpu);
+
return 0;
}
diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h
index b1c54ffb5d..5d14887e66 100644
--- a/target-arm/kvm_arm.h
+++ b/target-arm/kvm_arm.h
@@ -29,4 +29,37 @@
*/
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid);
+/**
+ * write_list_to_kvmstate:
+ * @cpu: ARMCPU
+ *
+ * For each register listed in the ARMCPU cpreg_indexes list, write
+ * its value from the cpreg_values list into the kernel (via ioctl).
+ * This updates KVM's working data structures from TCG data or
+ * from incoming migration state.
+ *
+ * Returns: true if all register values were updated correctly,
+ * false if some register was unknown to the kernel or could not
+ * be written (eg constant register with the wrong value).
+ * Note that we do not stop early on failure -- we will attempt
+ * writing all registers in the list.
+ */
+bool write_list_to_kvmstate(ARMCPU *cpu);
+
+/**
+ * write_kvmstate_to_list:
+ * @cpu: ARMCPU
+ *
+ * For each register listed in the ARMCPU cpreg_indexes list, write
+ * its value from the kernel into the cpreg_values list. This is used to
+ * copy info from KVM's working data structures into TCG or
+ * for outbound migration.
+ *
+ * Returns: true if all register values were read correctly,
+ * false if some register was unknown or could not be read.
+ * Note that we do not stop early on failure -- we will attempt
+ * reading all registers in the list.
+ */
+bool write_kvmstate_to_list(ARMCPU *cpu);
+
#endif
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 076dc1672d..6d4c2d4ed0 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -1,5 +1,7 @@
#include "hw/hw.h"
#include "hw/boards.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
static bool vfp_needed(void *opaque)
{
@@ -152,9 +154,16 @@ static void cpu_pre_save(void *opaque)
{
ARMCPU *cpu = opaque;
- if (!write_cpustate_to_list(cpu)) {
- /* This should never fail. */
- abort();
+ if (kvm_enabled()) {
+ if (!write_kvmstate_to_list(cpu)) {
+ /* This should never fail */
+ abort();
+ }
+ } else {
+ if (!write_cpustate_to_list(cpu)) {
+ /* This should never fail. */
+ abort();
+ }
}
cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len;
@@ -193,8 +202,19 @@ static int cpu_post_load(void *opaque, int version_id)
v++;
}
- if (!write_list_to_cpustate(cpu)) {
- return -1;
+ if (kvm_enabled()) {
+ if (!write_list_to_kvmstate(cpu)) {
+ return -1;
+ }
+ /* Note that it's OK for the TCG side not to know about
+ * every register in the list; KVM is authoritative if
+ * we're using it.
+ */
+ write_list_to_cpustate(cpu);
+ } else {
+ if (!write_list_to_cpustate(cpu)) {
+ return -1;
+ }
}
return 0;