From: Christoffer Dall <christoffer.dall@linaro.org>
To: Peter Maydell <peter.maydell@linaro.org>
Cc: patches@linaro.org, qemu-devel@nongnu.org, kvmarm@lists.cs.columbia.edu
Subject: Re: [Qemu-devel] [PATCH 1/7] target-arm/kvm: Split 32 bit only code into its own file
Date: Mon, 16 Dec 2013 15:39:04 -0800 [thread overview]
Message-ID: <20131216233904.GA5711@cbox> (raw)
In-Reply-To: <1385645602-18662-2-git-send-email-peter.maydell@linaro.org>
On Thu, Nov 28, 2013 at 01:33:16PM +0000, Peter Maydell wrote:
> Split ARM KVM support code which is 32 bit specific out into its
> own file, which we only compile on 32 bit hosts. This will give
> us a place to add the 64 bit support code without adding lots of
> ifdefs to kvm.c.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Looks nice:
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
> ---
> target-arm/Makefile.objs | 1 +
> target-arm/kvm.c | 491 -------------------------------------------
> target-arm/kvm32.c | 515 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 516 insertions(+), 491 deletions(-)
> create mode 100644 target-arm/kvm32.c
>
> diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
> index 356fbfc..d1db77c 100644
> --- a/target-arm/Makefile.objs
> +++ b/target-arm/Makefile.objs
> @@ -6,3 +6,4 @@ obj-y += translate.o op_helper.o helper.o cpu.o
> obj-y += neon_helper.o iwmmxt_helper.o
> obj-y += gdbstub.o
> obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o
> +obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
> diff --git a/target-arm/kvm.c b/target-arm/kvm.c
> index f865dac..5cdb3b9 100644
> --- a/target-arm/kvm.c
> +++ b/target-arm/kvm.c
> @@ -100,120 +100,6 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
> }
> }
>
> -static inline void set_feature(uint64_t *features, int feature)
> -{
> - *features |= 1ULL << feature;
> -}
> -
> -bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
> -{
> - /* Identify the feature bits corresponding to the host CPU, and
> - * fill out the ARMHostCPUClass fields accordingly. To do this
> - * we have to create a scratch VM, create a single CPU inside it,
> - * and then query that CPU for the relevant ID registers.
> - */
> - int i, ret, fdarray[3];
> - uint32_t midr, id_pfr0, id_isar0, mvfr1;
> - uint64_t features = 0;
> - /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
> - * we know these will only support creating one kind of guest CPU,
> - * which is its preferred CPU type.
> - */
> - static const uint32_t cpus_to_try[] = {
> - QEMU_KVM_ARM_TARGET_CORTEX_A15,
> - QEMU_KVM_ARM_TARGET_NONE
> - };
> - struct kvm_vcpu_init init;
> - struct kvm_one_reg idregs[] = {
> - {
> - .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> - | ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
> - .addr = (uintptr_t)&midr,
> - },
> - {
> - .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> - | ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
> - .addr = (uintptr_t)&id_pfr0,
> - },
> - {
> - .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> - | ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
> - .addr = (uintptr_t)&id_isar0,
> - },
> - {
> - .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> - | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
> - .addr = (uintptr_t)&mvfr1,
> - },
> - };
> -
> - if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
> - return false;
> - }
> -
> - ahcc->target = init.target;
> -
> - /* This is not strictly blessed by the device tree binding docs yet,
> - * but in practice the kernel does not care about this string so
> - * there is no point maintaining an KVM_ARM_TARGET_* -> string table.
> - */
> - ahcc->dtb_compatible = "arm,arm-v7";
> -
> - for (i = 0; i < ARRAY_SIZE(idregs); i++) {
> - ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
> - if (ret) {
> - break;
> - }
> - }
> -
> - kvm_arm_destroy_scratch_host_vcpu(fdarray);
> -
> - if (ret) {
> - return false;
> - }
> -
> - /* Now we've retrieved all the register information we can
> - * set the feature bits based on the ID register fields.
> - * We can assume any KVM supporting CPU is at least a v7
> - * with VFPv3, LPAE and the generic timers; this in turn implies
> - * most of the other feature bits, but a few must be tested.
> - */
> - set_feature(&features, ARM_FEATURE_V7);
> - set_feature(&features, ARM_FEATURE_VFP3);
> - set_feature(&features, ARM_FEATURE_LPAE);
> - set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
> -
> - switch (extract32(id_isar0, 24, 4)) {
> - case 1:
> - set_feature(&features, ARM_FEATURE_THUMB_DIV);
> - break;
> - case 2:
> - set_feature(&features, ARM_FEATURE_ARM_DIV);
> - set_feature(&features, ARM_FEATURE_THUMB_DIV);
> - break;
> - default:
> - break;
> - }
> -
> - if (extract32(id_pfr0, 12, 4) == 1) {
> - set_feature(&features, ARM_FEATURE_THUMB2EE);
> - }
> - if (extract32(mvfr1, 20, 4) == 1) {
> - set_feature(&features, ARM_FEATURE_VFP_FP16);
> - }
> - if (extract32(mvfr1, 12, 4) == 1) {
> - set_feature(&features, ARM_FEATURE_NEON);
> - }
> - if (extract32(mvfr1, 28, 4) == 1) {
> - /* FMAC support implies VFPv4 */
> - set_feature(&features, ARM_FEATURE_VFP4);
> - }
> -
> - ahcc->features = features;
> -
> - return true;
> -}
> -
> static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
> {
> ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
> @@ -265,144 +151,6 @@ 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)
> -{
> - if (*(uint64_t *)a > *(uint64_t *)b) {
> - return 1;
> - }
> - if (*(uint64_t *)a < *(uint64_t *)b) {
> - return -1;
> - }
> - return 0;
> -}
> -
> -int kvm_arch_init_vcpu(CPUState *cs)
> -{
> - struct kvm_vcpu_init init;
> - 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);
> -
> - if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
> - fprintf(stderr, "KVM is not supported for this guest CPU type\n");
> - return -EINVAL;
> - }
> -
> - init.target = cpu->kvm_target;
> - memset(init.features, 0, sizeof(init.features));
> - if (cpu->start_powered_off) {
> - init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
> - }
> - ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
> - if (ret) {
> - return ret;
> - }
> - /* Query the kernel to make sure it supports 32 VFP
> - * registers: QEMU's "cortex-a15" CPU is always a
> - * VFP-D32 core. The simplest way to do this is just
> - * to attempt to read register d31.
> - */
> - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
> - r.addr = (uintptr_t)(&v);
> - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> - 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;
> - }
> -
> - /* Save a copy of the initial register values so that we can
> - * feed it back to the kernel on VCPU reset.
> - */
> - cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
> - cpu->cpreg_array_len *
> - sizeof(cpu->cpreg_values[0]));
> -
> -out:
> - g_free(rlp);
> - return ret;
> -}
> -
> /* We track all the KVM devices which need their memory addresses
> * passing to the kernel in a list of these structures.
> * When board init is complete we run through the list and
> @@ -563,232 +311,6 @@ bool write_list_to_kvmstate(ARMCPU *cpu)
> return ok;
> }
>
> -typedef struct Reg {
> - uint64_t id;
> - int offset;
> -} Reg;
> -
> -#define COREREG(KERNELNAME, QEMUFIELD) \
> - { \
> - KVM_REG_ARM | KVM_REG_SIZE_U32 | \
> - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
> - offsetof(CPUARMState, QEMUFIELD) \
> - }
> -
> -#define VFPSYSREG(R) \
> - { \
> - KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
> - KVM_REG_ARM_VFP_##R, \
> - offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
> - }
> -
> -static const Reg regs[] = {
> - /* R0_usr .. R14_usr */
> - COREREG(usr_regs.uregs[0], regs[0]),
> - COREREG(usr_regs.uregs[1], regs[1]),
> - COREREG(usr_regs.uregs[2], regs[2]),
> - COREREG(usr_regs.uregs[3], regs[3]),
> - COREREG(usr_regs.uregs[4], regs[4]),
> - COREREG(usr_regs.uregs[5], regs[5]),
> - COREREG(usr_regs.uregs[6], regs[6]),
> - COREREG(usr_regs.uregs[7], regs[7]),
> - COREREG(usr_regs.uregs[8], usr_regs[0]),
> - COREREG(usr_regs.uregs[9], usr_regs[1]),
> - COREREG(usr_regs.uregs[10], usr_regs[2]),
> - COREREG(usr_regs.uregs[11], usr_regs[3]),
> - COREREG(usr_regs.uregs[12], usr_regs[4]),
> - COREREG(usr_regs.uregs[13], banked_r13[0]),
> - COREREG(usr_regs.uregs[14], banked_r14[0]),
> - /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
> - COREREG(svc_regs[0], banked_r13[1]),
> - COREREG(svc_regs[1], banked_r14[1]),
> - COREREG(svc_regs[2], banked_spsr[1]),
> - COREREG(abt_regs[0], banked_r13[2]),
> - COREREG(abt_regs[1], banked_r14[2]),
> - COREREG(abt_regs[2], banked_spsr[2]),
> - COREREG(und_regs[0], banked_r13[3]),
> - COREREG(und_regs[1], banked_r14[3]),
> - COREREG(und_regs[2], banked_spsr[3]),
> - COREREG(irq_regs[0], banked_r13[4]),
> - COREREG(irq_regs[1], banked_r14[4]),
> - COREREG(irq_regs[2], banked_spsr[4]),
> - /* R8_fiq .. R14_fiq and SPSR_fiq */
> - COREREG(fiq_regs[0], fiq_regs[0]),
> - COREREG(fiq_regs[1], fiq_regs[1]),
> - COREREG(fiq_regs[2], fiq_regs[2]),
> - COREREG(fiq_regs[3], fiq_regs[3]),
> - COREREG(fiq_regs[4], fiq_regs[4]),
> - COREREG(fiq_regs[5], banked_r13[5]),
> - COREREG(fiq_regs[6], banked_r14[5]),
> - COREREG(fiq_regs[7], banked_spsr[5]),
> - /* R15 */
> - COREREG(usr_regs.uregs[15], regs[15]),
> - /* VFP system registers */
> - VFPSYSREG(FPSID),
> - VFPSYSREG(MVFR1),
> - VFPSYSREG(MVFR0),
> - VFPSYSREG(FPEXC),
> - VFPSYSREG(FPINST),
> - VFPSYSREG(FPINST2),
> -};
> -
> -int kvm_arch_put_registers(CPUState *cs, int level)
> -{
> - ARMCPU *cpu = ARM_CPU(cs);
> - CPUARMState *env = &cpu->env;
> - struct kvm_one_reg r;
> - int mode, bn;
> - int ret, i;
> - uint32_t cpsr, fpscr;
> -
> - /* Make sure the banked regs are properly set */
> - mode = env->uncached_cpsr & CPSR_M;
> - bn = bank_number(mode);
> - if (mode == ARM_CPU_MODE_FIQ) {
> - memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
> - } else {
> - memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
> - }
> - env->banked_r13[bn] = env->regs[13];
> - env->banked_r14[bn] = env->regs[14];
> - env->banked_spsr[bn] = env->spsr;
> -
> - /* Now we can safely copy stuff down to the kernel */
> - for (i = 0; i < ARRAY_SIZE(regs); i++) {
> - r.id = regs[i].id;
> - r.addr = (uintptr_t)(env) + regs[i].offset;
> - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
> - if (ret) {
> - return ret;
> - }
> - }
> -
> - /* Special cases which aren't a single CPUARMState field */
> - cpsr = cpsr_read(env);
> - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
> - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
> - r.addr = (uintptr_t)(&cpsr);
> - 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++) {
> - r.addr = (uintptr_t)(&env->vfp.regs[i]);
> - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
> - if (ret) {
> - return ret;
> - }
> - r.id++;
> - }
> -
> - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
> - KVM_REG_ARM_VFP_FPSCR;
> - 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;
> -}
> -
> -int kvm_arch_get_registers(CPUState *cs)
> -{
> - ARMCPU *cpu = ARM_CPU(cs);
> - CPUARMState *env = &cpu->env;
> - struct kvm_one_reg r;
> - int mode, bn;
> - int ret, i;
> - uint32_t cpsr, fpscr;
> -
> - for (i = 0; i < ARRAY_SIZE(regs); i++) {
> - r.id = regs[i].id;
> - r.addr = (uintptr_t)(env) + regs[i].offset;
> - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> - if (ret) {
> - return ret;
> - }
> - }
> -
> - /* Special cases which aren't a single CPUARMState field */
> - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
> - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
> - r.addr = (uintptr_t)(&cpsr);
> - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> - if (ret) {
> - return ret;
> - }
> - cpsr_write(env, cpsr, 0xffffffff);
> -
> - /* Make sure the current mode regs are properly set */
> - mode = env->uncached_cpsr & CPSR_M;
> - bn = bank_number(mode);
> - if (mode == ARM_CPU_MODE_FIQ) {
> - memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
> - } else {
> - memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
> - }
> - env->regs[13] = env->banked_r13[bn];
> - env->regs[14] = env->banked_r14[bn];
> - env->spsr = env->banked_spsr[bn];
> -
> - /* VFP registers */
> - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
> - for (i = 0; i < 32; i++) {
> - r.addr = (uintptr_t)(&env->vfp.regs[i]);
> - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> - if (ret) {
> - return ret;
> - }
> - r.id++;
> - }
> -
> - r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
> - KVM_REG_ARM_VFP_FPSCR;
> - r.addr = (uintptr_t)&fpscr;
> - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> - if (ret) {
> - return ret;
> - }
> - 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;
> -}
> -
> void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
> {
> }
> @@ -802,19 +324,6 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
> return 0;
> }
>
> -void kvm_arch_reset_vcpu(CPUState *cs)
> -{
> - /* Feed the kernel back its initial register state */
> - ARMCPU *cpu = ARM_CPU(cs);
> -
> - memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
> - cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
> -
> - if (!write_list_to_kvmstate(cpu)) {
> - abort();
> - }
> -}
> -
> bool kvm_arch_stop_on_emulation_error(CPUState *cs)
> {
> return true;
> diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c
> new file mode 100644
> index 0000000..a4fde07
> --- /dev/null
> +++ b/target-arm/kvm32.c
> @@ -0,0 +1,515 @@
> +/*
> + * ARM implementation of KVM hooks, 32 bit specific code.
> + *
> + * Copyright Christoffer Dall 2009-2010
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include <stdio.h>
> +#include <sys/types.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +
> +#include <linux/kvm.h>
> +
> +#include "qemu-common.h"
> +#include "qemu/timer.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/kvm.h"
> +#include "kvm_arm.h"
> +#include "cpu.h"
> +#include "hw/arm/arm.h"
> +
> +static inline void set_feature(uint64_t *features, int feature)
> +{
> + *features |= 1ULL << feature;
> +}
> +
> +bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
> +{
> + /* Identify the feature bits corresponding to the host CPU, and
> + * fill out the ARMHostCPUClass fields accordingly. To do this
> + * we have to create a scratch VM, create a single CPU inside it,
> + * and then query that CPU for the relevant ID registers.
> + */
> + int i, ret, fdarray[3];
> + uint32_t midr, id_pfr0, id_isar0, mvfr1;
> + uint64_t features = 0;
> + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
> + * we know these will only support creating one kind of guest CPU,
> + * which is its preferred CPU type.
> + */
> + static const uint32_t cpus_to_try[] = {
> + QEMU_KVM_ARM_TARGET_CORTEX_A15,
> + QEMU_KVM_ARM_TARGET_NONE
> + };
> + struct kvm_vcpu_init init;
> + struct kvm_one_reg idregs[] = {
> + {
> + .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> + | ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
> + .addr = (uintptr_t)&midr,
> + },
> + {
> + .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> + | ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
> + .addr = (uintptr_t)&id_pfr0,
> + },
> + {
> + .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> + | ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
> + .addr = (uintptr_t)&id_isar0,
> + },
> + {
> + .id = KVM_REG_ARM | KVM_REG_SIZE_U32
> + | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
> + .addr = (uintptr_t)&mvfr1,
> + },
> + };
> +
> + if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
> + return false;
> + }
> +
> + ahcc->target = init.target;
> +
> + /* This is not strictly blessed by the device tree binding docs yet,
> + * but in practice the kernel does not care about this string so
> + * there is no point maintaining an KVM_ARM_TARGET_* -> string table.
> + */
> + ahcc->dtb_compatible = "arm,arm-v7";
> +
> + for (i = 0; i < ARRAY_SIZE(idregs); i++) {
> + ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
> + if (ret) {
> + break;
> + }
> + }
> +
> + kvm_arm_destroy_scratch_host_vcpu(fdarray);
> +
> + if (ret) {
> + return false;
> + }
> +
> + /* Now we've retrieved all the register information we can
> + * set the feature bits based on the ID register fields.
> + * We can assume any KVM supporting CPU is at least a v7
> + * with VFPv3, LPAE and the generic timers; this in turn implies
> + * most of the other feature bits, but a few must be tested.
> + */
> + set_feature(&features, ARM_FEATURE_V7);
> + set_feature(&features, ARM_FEATURE_VFP3);
> + set_feature(&features, ARM_FEATURE_LPAE);
> + set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
> +
> + switch (extract32(id_isar0, 24, 4)) {
> + case 1:
> + set_feature(&features, ARM_FEATURE_THUMB_DIV);
> + break;
> + case 2:
> + set_feature(&features, ARM_FEATURE_ARM_DIV);
> + set_feature(&features, ARM_FEATURE_THUMB_DIV);
> + break;
> + default:
> + break;
> + }
> +
> + if (extract32(id_pfr0, 12, 4) == 1) {
> + set_feature(&features, ARM_FEATURE_THUMB2EE);
> + }
> + if (extract32(mvfr1, 20, 4) == 1) {
> + set_feature(&features, ARM_FEATURE_VFP_FP16);
> + }
> + if (extract32(mvfr1, 12, 4) == 1) {
> + set_feature(&features, ARM_FEATURE_NEON);
> + }
> + if (extract32(mvfr1, 28, 4) == 1) {
> + /* FMAC support implies VFPv4 */
> + set_feature(&features, ARM_FEATURE_VFP4);
> + }
> +
> + ahcc->features = features;
> +
> + return true;
> +}
> +
> +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)
> +{
> + if (*(uint64_t *)a > *(uint64_t *)b) {
> + return 1;
> + }
> + if (*(uint64_t *)a < *(uint64_t *)b) {
> + return -1;
> + }
> + return 0;
> +}
> +
> +int kvm_arch_init_vcpu(CPUState *cs)
> +{
> + struct kvm_vcpu_init init;
> + 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);
> +
> + if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
> + fprintf(stderr, "KVM is not supported for this guest CPU type\n");
> + return -EINVAL;
> + }
> +
> + init.target = cpu->kvm_target;
> + memset(init.features, 0, sizeof(init.features));
> + if (cpu->start_powered_off) {
> + init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
> + }
> + ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
> + if (ret) {
> + return ret;
> + }
> + /* Query the kernel to make sure it supports 32 VFP
> + * registers: QEMU's "cortex-a15" CPU is always a
> + * VFP-D32 core. The simplest way to do this is just
> + * to attempt to read register d31.
> + */
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
> + r.addr = (uintptr_t)(&v);
> + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> + 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;
> + }
> +
> + /* Save a copy of the initial register values so that we can
> + * feed it back to the kernel on VCPU reset.
> + */
> + cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
> + cpu->cpreg_array_len *
> + sizeof(cpu->cpreg_values[0]));
> +
> +out:
> + g_free(rlp);
> + return ret;
> +}
> +
> +typedef struct Reg {
> + uint64_t id;
> + int offset;
> +} Reg;
> +
> +#define COREREG(KERNELNAME, QEMUFIELD) \
> + { \
> + KVM_REG_ARM | KVM_REG_SIZE_U32 | \
> + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
> + offsetof(CPUARMState, QEMUFIELD) \
> + }
> +
> +#define VFPSYSREG(R) \
> + { \
> + KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
> + KVM_REG_ARM_VFP_##R, \
> + offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
> + }
> +
> +static const Reg regs[] = {
> + /* R0_usr .. R14_usr */
> + COREREG(usr_regs.uregs[0], regs[0]),
> + COREREG(usr_regs.uregs[1], regs[1]),
> + COREREG(usr_regs.uregs[2], regs[2]),
> + COREREG(usr_regs.uregs[3], regs[3]),
> + COREREG(usr_regs.uregs[4], regs[4]),
> + COREREG(usr_regs.uregs[5], regs[5]),
> + COREREG(usr_regs.uregs[6], regs[6]),
> + COREREG(usr_regs.uregs[7], regs[7]),
> + COREREG(usr_regs.uregs[8], usr_regs[0]),
> + COREREG(usr_regs.uregs[9], usr_regs[1]),
> + COREREG(usr_regs.uregs[10], usr_regs[2]),
> + COREREG(usr_regs.uregs[11], usr_regs[3]),
> + COREREG(usr_regs.uregs[12], usr_regs[4]),
> + COREREG(usr_regs.uregs[13], banked_r13[0]),
> + COREREG(usr_regs.uregs[14], banked_r14[0]),
> + /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
> + COREREG(svc_regs[0], banked_r13[1]),
> + COREREG(svc_regs[1], banked_r14[1]),
> + COREREG(svc_regs[2], banked_spsr[1]),
> + COREREG(abt_regs[0], banked_r13[2]),
> + COREREG(abt_regs[1], banked_r14[2]),
> + COREREG(abt_regs[2], banked_spsr[2]),
> + COREREG(und_regs[0], banked_r13[3]),
> + COREREG(und_regs[1], banked_r14[3]),
> + COREREG(und_regs[2], banked_spsr[3]),
> + COREREG(irq_regs[0], banked_r13[4]),
> + COREREG(irq_regs[1], banked_r14[4]),
> + COREREG(irq_regs[2], banked_spsr[4]),
> + /* R8_fiq .. R14_fiq and SPSR_fiq */
> + COREREG(fiq_regs[0], fiq_regs[0]),
> + COREREG(fiq_regs[1], fiq_regs[1]),
> + COREREG(fiq_regs[2], fiq_regs[2]),
> + COREREG(fiq_regs[3], fiq_regs[3]),
> + COREREG(fiq_regs[4], fiq_regs[4]),
> + COREREG(fiq_regs[5], banked_r13[5]),
> + COREREG(fiq_regs[6], banked_r14[5]),
> + COREREG(fiq_regs[7], banked_spsr[5]),
> + /* R15 */
> + COREREG(usr_regs.uregs[15], regs[15]),
> + /* VFP system registers */
> + VFPSYSREG(FPSID),
> + VFPSYSREG(MVFR1),
> + VFPSYSREG(MVFR0),
> + VFPSYSREG(FPEXC),
> + VFPSYSREG(FPINST),
> + VFPSYSREG(FPINST2),
> +};
> +
> +int kvm_arch_put_registers(CPUState *cs, int level)
> +{
> + ARMCPU *cpu = ARM_CPU(cs);
> + CPUARMState *env = &cpu->env;
> + struct kvm_one_reg r;
> + int mode, bn;
> + int ret, i;
> + uint32_t cpsr, fpscr;
> +
> + /* Make sure the banked regs are properly set */
> + mode = env->uncached_cpsr & CPSR_M;
> + bn = bank_number(mode);
> + if (mode == ARM_CPU_MODE_FIQ) {
> + memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
> + } else {
> + memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
> + }
> + env->banked_r13[bn] = env->regs[13];
> + env->banked_r14[bn] = env->regs[14];
> + env->banked_spsr[bn] = env->spsr;
> +
> + /* Now we can safely copy stuff down to the kernel */
> + for (i = 0; i < ARRAY_SIZE(regs); i++) {
> + r.id = regs[i].id;
> + r.addr = (uintptr_t)(env) + regs[i].offset;
> + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + }
> +
> + /* Special cases which aren't a single CPUARMState field */
> + cpsr = cpsr_read(env);
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
> + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
> + r.addr = (uintptr_t)(&cpsr);
> + 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++) {
> + r.addr = (uintptr_t)(&env->vfp.regs[i]);
> + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + r.id++;
> + }
> +
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
> + KVM_REG_ARM_VFP_FPSCR;
> + 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;
> +}
> +
> +int kvm_arch_get_registers(CPUState *cs)
> +{
> + ARMCPU *cpu = ARM_CPU(cs);
> + CPUARMState *env = &cpu->env;
> + struct kvm_one_reg r;
> + int mode, bn;
> + int ret, i;
> + uint32_t cpsr, fpscr;
> +
> + for (i = 0; i < ARRAY_SIZE(regs); i++) {
> + r.id = regs[i].id;
> + r.addr = (uintptr_t)(env) + regs[i].offset;
> + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + }
> +
> + /* Special cases which aren't a single CPUARMState field */
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
> + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
> + r.addr = (uintptr_t)(&cpsr);
> + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + cpsr_write(env, cpsr, 0xffffffff);
> +
> + /* Make sure the current mode regs are properly set */
> + mode = env->uncached_cpsr & CPSR_M;
> + bn = bank_number(mode);
> + if (mode == ARM_CPU_MODE_FIQ) {
> + memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
> + } else {
> + memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
> + }
> + env->regs[13] = env->banked_r13[bn];
> + env->regs[14] = env->banked_r14[bn];
> + env->spsr = env->banked_spsr[bn];
> +
> + /* VFP registers */
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
> + for (i = 0; i < 32; i++) {
> + r.addr = (uintptr_t)(&env->vfp.regs[i]);
> + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + r.id++;
> + }
> +
> + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
> + KVM_REG_ARM_VFP_FPSCR;
> + r.addr = (uintptr_t)&fpscr;
> + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
> + if (ret) {
> + return ret;
> + }
> + 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;
> +}
> +
> +void kvm_arch_reset_vcpu(CPUState *cs)
> +{
> + /* Feed the kernel back its initial register state */
> + ARMCPU *cpu = ARM_CPU(cs);
> +
> + memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
> + cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
> +
> + if (!write_list_to_kvmstate(cpu)) {
> + abort();
> + }
> +}
> --
> 1.7.9.5
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm
--
Christoffer
next prev parent reply other threads:[~2013-12-16 23:39 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-11-28 13:33 [Qemu-devel] [PATCH 0/7] target-arm: Support AArch64 KVM Peter Maydell
2013-11-28 13:33 ` [Qemu-devel] [PATCH 1/7] target-arm/kvm: Split 32 bit only code into its own file Peter Maydell
2013-12-16 23:39 ` Christoffer Dall [this message]
2013-11-28 13:33 ` [Qemu-devel] [PATCH 2/7] target-arm: Clean up handling of AArch64 PSTATE Peter Maydell
2013-12-16 23:39 ` Christoffer Dall
2013-12-17 0:15 ` Peter Maydell
2013-12-17 4:45 ` Christoffer Dall
2013-12-17 11:42 ` Peter Maydell
2013-12-17 18:44 ` Christoffer Dall
2013-11-28 13:33 ` [Qemu-devel] [PATCH 3/7] target-arm: Add minimal KVM AArch64 support Peter Maydell
2013-12-16 23:39 ` Christoffer Dall
2013-12-17 0:21 ` Peter Maydell
2013-12-17 4:46 ` Christoffer Dall
2013-11-28 13:33 ` [Qemu-devel] [PATCH 4/7] configure: Enable KVM for aarch64 host/target combination Peter Maydell
2013-12-16 23:40 ` Christoffer Dall
2013-11-28 13:33 ` [Qemu-devel] [PATCH 5/7] hw/arm/boot: Allow easier swapping in of different loader code Peter Maydell
2013-12-13 3:19 ` Peter Crosthwaite
2013-12-13 10:05 ` Peter Maydell
2013-12-17 0:52 ` Peter Crosthwaite
2013-12-17 0:58 ` Peter Maydell
2013-12-17 1:24 ` Peter Crosthwaite
2013-12-17 4:56 ` Christoffer Dall
2013-12-17 10:31 ` Peter Maydell
2013-12-17 11:36 ` Peter Crosthwaite
2013-12-17 11:47 ` Peter Maydell
2013-12-17 12:02 ` Peter Crosthwaite
2013-12-16 23:40 ` Christoffer Dall
2013-12-17 0:23 ` Peter Maydell
2013-11-28 13:33 ` [Qemu-devel] [PATCH 6/7] hw/arm/boot: Add boot support for AArch64 processor Peter Maydell
2013-12-16 23:40 ` Christoffer Dall
2013-12-17 0:25 ` Peter Maydell
2013-12-17 4:50 ` Christoffer Dall
2013-11-28 13:33 ` [Qemu-devel] [PATCH 7/7] default-configs: Add config for aarch64-softmmu Peter Maydell
2013-12-16 23:40 ` Christoffer Dall
2013-12-17 0:27 ` Peter Maydell
2013-12-17 13:33 ` Christopher Covington
2013-12-05 15:23 ` [Qemu-devel] [PATCH 0/7] target-arm: Support AArch64 KVM Peter Maydell
2013-12-12 16:41 ` Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20131216233904.GA5711@cbox \
--to=christoffer.dall@linaro.org \
--cc=kvmarm@lists.cs.columbia.edu \
--cc=patches@linaro.org \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.