qemu-devel
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]