266 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * ARM implementation of KVM hooks, 64 bit specific code
 | 
						|
 *
 | 
						|
 * Copyright Mian-M. Hamayun 2013, Virtual Open Systems
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
     * For AArch64 we currently don't care about ID registers at
 | 
						|
     * all; we just want to know the CPU type.
 | 
						|
     */
 | 
						|
    int fdarray[3];
 | 
						|
    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. Fortunately these old kernels
 | 
						|
     * support only a very limited number of CPUs.
 | 
						|
     */
 | 
						|
    static const uint32_t cpus_to_try[] = {
 | 
						|
        KVM_ARM_TARGET_AEM_V8,
 | 
						|
        KVM_ARM_TARGET_FOUNDATION_V8,
 | 
						|
        KVM_ARM_TARGET_CORTEX_A57,
 | 
						|
        QEMU_KVM_ARM_TARGET_NONE
 | 
						|
    };
 | 
						|
    struct kvm_vcpu_init init;
 | 
						|
 | 
						|
    if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    ahcc->target = init.target;
 | 
						|
    ahcc->dtb_compatible = "arm,arm-v8";
 | 
						|
 | 
						|
    kvm_arm_destroy_scratch_host_vcpu(fdarray);
 | 
						|
 | 
						|
   /* We can assume any KVM supporting CPU is at least a v8
 | 
						|
     * with VFPv4+Neon; this in turn implies most of the other
 | 
						|
     * feature bits.
 | 
						|
     */
 | 
						|
    set_feature(&features, ARM_FEATURE_V8);
 | 
						|
    set_feature(&features, ARM_FEATURE_VFP4);
 | 
						|
    set_feature(&features, ARM_FEATURE_NEON);
 | 
						|
    set_feature(&features, ARM_FEATURE_AARCH64);
 | 
						|
 | 
						|
    ahcc->features = features;
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
int kvm_arch_init_vcpu(CPUState *cs)
 | 
						|
{
 | 
						|
    ARMCPU *cpu = ARM_CPU(cs);
 | 
						|
    struct kvm_vcpu_init init;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
 | 
						|
        !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
 | 
						|
        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);
 | 
						|
 | 
						|
    /* TODO : support for save/restore/reset of system regs via tuple list */
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
#define AARCH64_CORE_REG(x)   (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
 | 
						|
                 KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x))
 | 
						|
 | 
						|
int kvm_arch_put_registers(CPUState *cs, int level)
 | 
						|
{
 | 
						|
    struct kvm_one_reg reg;
 | 
						|
    uint64_t val;
 | 
						|
    int i;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ARMCPU *cpu = ARM_CPU(cs);
 | 
						|
    CPUARMState *env = &cpu->env;
 | 
						|
 | 
						|
    for (i = 0; i < 31; i++) {
 | 
						|
        reg.id = AARCH64_CORE_REG(regs.regs[i]);
 | 
						|
        reg.addr = (uintptr_t) &env->xregs[i];
 | 
						|
        ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
 | 
						|
        if (ret) {
 | 
						|
            return ret;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the
 | 
						|
     * QEMU side we keep the current SP in xregs[31] as well.
 | 
						|
     */
 | 
						|
    if (env->pstate & PSTATE_SP) {
 | 
						|
        env->sp_el[1] = env->xregs[31];
 | 
						|
    } else {
 | 
						|
        env->sp_el[0] = env->xregs[31];
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(regs.sp);
 | 
						|
    reg.addr = (uintptr_t) &env->sp_el[0];
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(sp_el1);
 | 
						|
    reg.addr = (uintptr_t) &env->sp_el[1];
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Note that KVM thinks pstate is 64 bit but we use a uint32_t */
 | 
						|
    val = pstate_read(env);
 | 
						|
    reg.id = AARCH64_CORE_REG(regs.pstate);
 | 
						|
    reg.addr = (uintptr_t) &val;
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(regs.pc);
 | 
						|
    reg.addr = (uintptr_t) &env->pc;
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(elr_el1);
 | 
						|
    reg.addr = (uintptr_t) &env->elr_el1;
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < KVM_NR_SPSR; i++) {
 | 
						|
        reg.id = AARCH64_CORE_REG(spsr[i]);
 | 
						|
        reg.addr = (uintptr_t) &env->banked_spsr[i - 1];
 | 
						|
        ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
 | 
						|
        if (ret) {
 | 
						|
            return ret;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* TODO:
 | 
						|
     * FP state
 | 
						|
     * system registers
 | 
						|
     */
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
int kvm_arch_get_registers(CPUState *cs)
 | 
						|
{
 | 
						|
    struct kvm_one_reg reg;
 | 
						|
    uint64_t val;
 | 
						|
    int i;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ARMCPU *cpu = ARM_CPU(cs);
 | 
						|
    CPUARMState *env = &cpu->env;
 | 
						|
 | 
						|
    for (i = 0; i < 31; i++) {
 | 
						|
        reg.id = AARCH64_CORE_REG(regs.regs[i]);
 | 
						|
        reg.addr = (uintptr_t) &env->xregs[i];
 | 
						|
        ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
 | 
						|
        if (ret) {
 | 
						|
            return ret;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(regs.sp);
 | 
						|
    reg.addr = (uintptr_t) &env->sp_el[0];
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(sp_el1);
 | 
						|
    reg.addr = (uintptr_t) &env->sp_el[1];
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(regs.pstate);
 | 
						|
    reg.addr = (uintptr_t) &val;
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
    pstate_write(env, val);
 | 
						|
 | 
						|
    /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the
 | 
						|
     * QEMU side we keep the current SP in xregs[31] as well.
 | 
						|
     */
 | 
						|
    if (env->pstate & PSTATE_SP) {
 | 
						|
        env->xregs[31] = env->sp_el[1];
 | 
						|
    } else {
 | 
						|
        env->xregs[31] = env->sp_el[0];
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(regs.pc);
 | 
						|
    reg.addr = (uintptr_t) &env->pc;
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    reg.id = AARCH64_CORE_REG(elr_el1);
 | 
						|
    reg.addr = (uintptr_t) &env->elr_el1;
 | 
						|
    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
 | 
						|
    if (ret) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < KVM_NR_SPSR; i++) {
 | 
						|
        reg.id = AARCH64_CORE_REG(spsr[i]);
 | 
						|
        reg.addr = (uintptr_t) &env->banked_spsr[i - 1];
 | 
						|
        ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
 | 
						|
        if (ret) {
 | 
						|
            return ret;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* TODO: other registers */
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
void kvm_arch_reset_vcpu(CPUState *cs)
 | 
						|
{
 | 
						|
}
 |