From patchwork Fri May 17 13:23:55 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 244653 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AB7F12C00AF for ; Fri, 17 May 2013 23:28:49 +1000 (EST) Received: from localhost ([::1]:59556 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UdKiF-0002B2-Tv for incoming@patchwork.ozlabs.org; Fri, 17 May 2013 09:28:47 -0400 Received: from eggs.gnu.org ([208.118.235.92]:45787) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UdKf7-0005vB-8k for qemu-devel@nongnu.org; Fri, 17 May 2013 09:25:37 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UdKf0-0005WR-IX for qemu-devel@nongnu.org; Fri, 17 May 2013 09:25:33 -0400 Received: from 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.1.0.0.b.8.0.1.0.0.2.ip6.arpa ([2001:8b0:1d0::1]:56929 helo=mnementh.archaic.org.uk) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UdKf0-0005WJ-86 for qemu-devel@nongnu.org; Fri, 17 May 2013 09:25:26 -0400 Received: from pm215 by mnementh.archaic.org.uk with local (Exim 4.72) (envelope-from ) id 1UdKdZ-00071h-VA; Fri, 17 May 2013 14:23:57 +0100 From: Peter Maydell To: qemu-devel@nongnu.org Date: Fri, 17 May 2013 14:23:55 +0100 Message-Id: <1368797037-26976-6-git-send-email-peter.maydell@linaro.org> X-Mailer: git-send-email 1.7.2.5 In-Reply-To: <1368797037-26976-1-git-send-email-peter.maydell@linaro.org> References: <1368797037-26976-1-git-send-email-peter.maydell@linaro.org> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:8b0:1d0::1 Cc: Juan Quintela , kvmarm@lists.cs.columbia.edu, Andre Przywara , patches@linaro.org Subject: [Qemu-devel] [PATCH 5/7] target-arm: Initialize cpreg list from KVM when using KVM X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org When using KVM, use the kernel's initial state to set up the cpreg list, and sync to and from the kernel when doing migration. Signed-off-by: Peter Maydell --- target-arm/Makefile.objs | 1 + target-arm/kvm.c | 164 +++++++++++++++++++++++++++++++++++++++++++++- target-arm/kvm_arm.h | 33 ++++++++++ target-arm/machine.c | 30 +++++++-- 4 files changed, 222 insertions(+), 6 deletions(-) diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index d89b57c..4a6e52e 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 4aea7c3..746ae02 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; diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index b1c54ff..5d14887 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 076dc16..6d4c2d4 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;