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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell