[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 1/7] target-arm/kvm: Split 32 bit only code into
From: |
Christoffer Dall |
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 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
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 <address@hidden>
Looks nice:
Reviewed-by: Christoffer Dall <address@hidden>
> ---
> 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
> address@hidden
> https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm
--
Christoffer
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Re: [Qemu-devel] [PATCH 1/7] target-arm/kvm: Split 32 bit only code into its own file,
Christoffer Dall <=