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@xxxxxxxxxx> --- target-arm/helper.c | 6 ++ target-arm/kvm.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index 4968391..6a81101 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1573,6 +1573,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 } void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 0e33efc..95573f4 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -36,12 +36,200 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; +#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; +} ARMHostCPUClass; + +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; + + env->features = ahcc->features; +} + +static inline void set_feature(uint64_t *features, int feature) +{ + *features |= 1ULL << feature; +} + +static bool 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, kvmfd = -1, vmfd = -1, cpufd = -1; + uint32_t midr, id_pfr0, id_isar0, mvfr1; + uint64_t features = 0; + /* Any kernel old enough to not know about TARGET_HOST must be one which + * only supports guest A15 on host A15, so we fall back to trying A15. + */ + static const uint32_t cpus_to_try[] = { + /* TODO add KVM_ARM_TARGET_HOST when it appears in the ABI */ + KVM_ARM_TARGET_CORTEX_A15, + }; + 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, + }, + }; + + 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; + } + + for (i = 0; i < ARRAY_SIZE(cpus_to_try); i++) { + struct kvm_vcpu_init init; + + init.target = cpus_to_try[i]; + memset(init.features, 0, sizeof(init.features)); + ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, &init); + if (ret >= 0) { + ahcc->target = init.target; + break; + } + } + if (ret < 0) { + goto err; + } + + for (i = 0; i < ARRAY_SIZE(idregs); i++) { + ret = ioctl(cpufd, KVM_GET_ONE_REG, &idregs[i]); + if (ret) { + goto err; + } + } + close(cpufd); + close(vmfd); + close(kvmfd); + + /* 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; + +err: + if (cpufd >= 0) { + close(cpufd); + } + if (vmfd >= 0) { + close(vmfd); + } + if (kvmfd >= 0) { + close(kvmfd); + } + + return false; +} + +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 (!get_host_cpu_features(ahcc)) { + fprintf(stderr, "Failed to retrieve host CPU features!\n"); + abort(); + } +} + +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; } @@ -86,6 +274,13 @@ static bool kvm_arm_get_init_args(ARMCPU *cpu, struct kvm_vcpu_init *init) memset(init->features, 0, sizeof(init->features)); + if (object_dynamic_cast(obj, TYPE_ARM_HOST_CPU)) { + ARMHostCPUClass *ahcc = ARM_HOST_CPU_GET_CLASS(obj); + + init->target = ahcc->target; + return true; + } + for (i = 0; i < ARRAY_SIZE(kvm_cpus); i++) { if (object_dynamic_cast(obj, kvm_cpus[i].name)) { init->target = kvm_cpus[i].target; -- 1.7.9.5 _______________________________________________ kvmarm mailing list kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm