target-arm: Provide '-cpu host' when running KVM
Implement '-cpu host' for ARM when we're using KVM, broadly in line with other KVM-supporting architectures. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Message-id: 1385140638-10444-11-git-send-email-peter.maydell@linaro.org
This commit is contained in:
parent
3541addc88
commit
a96c0514ab
@ -1842,6 +1842,12 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf)
|
||||
(*cpu_fprintf)(f, "Available CPUs:\n");
|
||||
g_slist_foreach(list, arm_cpu_list_entry, &s);
|
||||
g_slist_free(list);
|
||||
#ifdef CONFIG_KVM
|
||||
/* The 'host' CPU type is dynamically registered only if KVM is
|
||||
* enabled, so we have to special-case it here:
|
||||
*/
|
||||
(*cpu_fprintf)(f, " host (only available in KVM mode)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void arm_cpu_add_definition(gpointer data, gpointer user_data)
|
||||
|
224
target-arm/kvm.c
224
target-arm/kvm.c
@ -27,12 +27,236 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
};
|
||||
|
||||
bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
|
||||
int *fdarray,
|
||||
struct kvm_vcpu_init *init)
|
||||
{
|
||||
int ret, kvmfd = -1, vmfd = -1, cpufd = -1;
|
||||
|
||||
kvmfd = qemu_open("/dev/kvm", O_RDWR);
|
||||
if (kvmfd < 0) {
|
||||
goto err;
|
||||
}
|
||||
vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
|
||||
if (vmfd < 0) {
|
||||
goto err;
|
||||
}
|
||||
cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
|
||||
if (cpufd < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, init);
|
||||
if (ret >= 0) {
|
||||
ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
/* Old kernel which doesn't know about the
|
||||
* PREFERRED_TARGET ioctl: we know it will only support
|
||||
* creating one kind of guest CPU which is its preferred
|
||||
* CPU type.
|
||||
*/
|
||||
while (*cpus_to_try != QEMU_KVM_ARM_TARGET_NONE) {
|
||||
init->target = *cpus_to_try++;
|
||||
memset(init->features, 0, sizeof(init->features));
|
||||
ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init);
|
||||
if (ret >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
fdarray[0] = kvmfd;
|
||||
fdarray[1] = vmfd;
|
||||
fdarray[2] = cpufd;
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
if (cpufd >= 0) {
|
||||
close(cpufd);
|
||||
}
|
||||
if (vmfd >= 0) {
|
||||
close(vmfd);
|
||||
}
|
||||
if (kvmfd >= 0) {
|
||||
close(kvmfd);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 2; i >= 0; i--) {
|
||||
close(fdarray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* All we really need to set up for the 'host' CPU
|
||||
* is the feature bits -- we rely on the fact that the
|
||||
* various ID register values in ARMCPU are only used for
|
||||
* TCG CPUs.
|
||||
*/
|
||||
if (!kvm_arm_get_host_cpu_features(ahcc)) {
|
||||
fprintf(stderr, "Failed to retrieve host CPU features!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_arm_host_cpu_initfn(Object *obj)
|
||||
{
|
||||
ARMHostCPUClass *ahcc = ARM_HOST_CPU_GET_CLASS(obj);
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
cpu->kvm_target = ahcc->target;
|
||||
cpu->dtb_compatible = ahcc->dtb_compatible;
|
||||
env->features = ahcc->features;
|
||||
}
|
||||
|
||||
static const TypeInfo host_arm_cpu_type_info = {
|
||||
.name = TYPE_ARM_HOST_CPU,
|
||||
.parent = TYPE_ARM_CPU,
|
||||
.instance_init = kvm_arm_host_cpu_initfn,
|
||||
.class_init = kvm_arm_host_cpu_class_init,
|
||||
.class_size = sizeof(ARMHostCPUClass),
|
||||
};
|
||||
|
||||
int kvm_arch_init(KVMState *s)
|
||||
{
|
||||
/* For ARM interrupt delivery is always asynchronous,
|
||||
* whether we are using an in-kernel VGIC or not.
|
||||
*/
|
||||
kvm_async_interrupts_allowed = true;
|
||||
|
||||
type_register_static(&host_arm_cpu_type_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -62,4 +62,59 @@ bool write_list_to_kvmstate(ARMCPU *cpu);
|
||||
*/
|
||||
bool write_kvmstate_to_list(ARMCPU *cpu);
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
/**
|
||||
* kvm_arm_create_scratch_host_vcpu:
|
||||
* @cpus_to_try: array of QEMU_KVM_ARM_TARGET_* values (terminated with
|
||||
* QEMU_KVM_ARM_TARGET_NONE) to try as fallback if the kernel does not
|
||||
* know the PREFERRED_TARGET ioctl
|
||||
* @fdarray: filled in with kvmfd, vmfd, cpufd file descriptors in that order
|
||||
* @init: filled in with the necessary values for creating a host vcpu
|
||||
*
|
||||
* Create a scratch vcpu in its own VM of the type preferred by the host
|
||||
* kernel (as would be used for '-cpu host'), for purposes of probing it
|
||||
* for capabilities.
|
||||
*
|
||||
* Returns: true on success (and fdarray and init are filled in),
|
||||
* false on failure (and fdarray and init are not valid).
|
||||
*/
|
||||
bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
|
||||
int *fdarray,
|
||||
struct kvm_vcpu_init *init);
|
||||
|
||||
/**
|
||||
* kvm_arm_destroy_scratch_host_vcpu:
|
||||
* @fdarray: array of fds as set up by kvm_arm_create_scratch_host_vcpu
|
||||
*
|
||||
* Tear down the scratch vcpu created by kvm_arm_create_scratch_host_vcpu.
|
||||
*/
|
||||
void kvm_arm_destroy_scratch_host_vcpu(int *fdarray);
|
||||
|
||||
#define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU
|
||||
#define ARM_HOST_CPU_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(ARMHostCPUClass, (klass), TYPE_ARM_HOST_CPU)
|
||||
#define ARM_HOST_CPU_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(ARMHostCPUClass, (obj), TYPE_ARM_HOST_CPU)
|
||||
|
||||
typedef struct ARMHostCPUClass {
|
||||
/*< private >*/
|
||||
ARMCPUClass parent_class;
|
||||
/*< public >*/
|
||||
|
||||
uint64_t features;
|
||||
uint32_t target;
|
||||
const char *dtb_compatible;
|
||||
} ARMHostCPUClass;
|
||||
|
||||
/**
|
||||
* kvm_arm_get_host_cpu_features:
|
||||
* @ahcc: ARMHostCPUClass to fill in
|
||||
*
|
||||
* Probe the capabilities of the host kernel's preferred CPU and fill
|
||||
* in the ARMHostCPUClass struct accordingly.
|
||||
*/
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user