From df78681e93f91b2969b95228aef58919e4471c60 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 8 Mar 2020 12:26:09 +0400 Subject: [PATCH] Add set_cpuid ioctl that enables setting cpuid values to be returned by haxm Signed-off-by: Alexey Romko --- core/include/hax_core_interface.h | 1 + core/include/vcpu.h | 1 + core/include/vm.h | 2 + core/vcpu.c | 88 ++++++++++++++++++++++++--- core/vm.c | 29 +++++++++ include/darwin/hax_interface_mac.h | 1 + include/hax_interface.h | 28 +++++++++ include/linux/hax_interface_linux.h | 1 + include/netbsd/hax_interface_netbsd.h | 1 + platforms/darwin/com_intel_hax_ui.c | 27 ++++++++ platforms/linux/components.c | 27 ++++++++ platforms/netbsd/hax_entry_vm.c | 27 ++++++++ platforms/windows/hax_entry.c | 24 ++++++++ platforms/windows/hax_entry.h | 2 + 14 files changed, 250 insertions(+), 9 deletions(-) diff --git a/core/include/hax_core_interface.h b/core/include/hax_core_interface.h index f764ae3c..87388fd5 100644 --- a/core/include/hax_core_interface.h +++ b/core/include/hax_core_interface.h @@ -88,6 +88,7 @@ int hax_vm_core_open(struct vm_t *vm); /* Corresponding hax_get_vm with refer == 1 */ int hax_put_vm(struct vm_t *vm); int hax_vm_set_qemuversion(struct vm_t *vm, struct hax_qemu_version *ver); +int hax_vm_set_cpuid(struct vm_t *vm, struct hax_cpuid *cpuid_data); struct vm_t * hax_create_vm(int *vm_id); int hax_teardown_vm(struct vm_t *vm); diff --git a/core/include/vcpu.h b/core/include/vcpu.h index dcaa07c6..212af9f1 100644 --- a/core/include/vcpu.h +++ b/core/include/vcpu.h @@ -288,5 +288,6 @@ static inline bool valid_vcpu_id(int vcpu_id) bool vcpu_is_panic(struct vcpu_t *vcpu); void vcpu_set_panic(struct vcpu_t *vcpu); +bool is_cpuid_supported(struct hax_cpuid *cpuid_data); #endif // HAX_CORE_VCPU_H_ diff --git a/core/include/vm.h b/core/include/vm.h index a4178178..e5b82578 100644 --- a/core/include/vm.h +++ b/core/include/vm.h @@ -84,6 +84,7 @@ struct vm_t { uint64_t spare_ramsize; uint ram_entry_num; struct hax_vcpu_mem *ram_entry; + struct hax_cpuid *cpuid_data; }; struct hva_entry { @@ -128,6 +129,7 @@ int _hax_teardown_vm(struct vm_t *vm); void hax_teardown_vcpus(struct vm_t *vm); int hax_destroy_host_interface(void); int hax_vm_set_qemuversion(struct vm_t *vm, struct hax_qemu_version *ver); +//int hax_vm_set_cpuid(struct vm_t *vm, struct hax_cpuid *cpuid_data); uint64_t vm_get_eptp(struct vm_t *vm); diff --git a/core/vcpu.c b/core/vcpu.c index cac000fc..2476bfc7 100644 --- a/core/vcpu.c +++ b/core/vcpu.c @@ -2506,22 +2506,92 @@ static int exit_cpuid(struct vcpu_t *vcpu, struct hax_tunnel *htun) return HAX_RESUME; } +bool is_cpuid_supported(struct hax_cpuid *cpuid_data) +{ + uint32_t cpuid_i; + for (cpuid_i = 0; cpuid_i < cpuid_data->nent; cpuid_i++) { + switch (cpuid_data->entries[cpuid_i].function) { + case 0: { + if (cpuid_data->entries[cpuid_i].eax > 0xa) { + hax_log(HAX_LOGE, "Unsupported cpuid level %d\n", + cpuid_data->entries[cpuid_i].eax); + return false; + } + break; + } + case 0x80000000: { + if (cpuid_data->entries[cpuid_i].eax > 0x80000008) { + hax_log(HAX_LOGE, "Unsupported cpuid xlevel %d\n", + cpuid_data->entries[cpuid_i].eax); + return false; + } + break; + } + case 1: { + // Disallow to clear these feature bits, since MSR handling + // code is written as if these are supported. + uint32_t nonDisabledFlags = FEATURE(MCE) | FEATURE(APIC) | + FEATURE(MTRR) | FEATURE(PAT); + if ((cpuid_data->entries[cpuid_i].edx & nonDisabledFlags) != + nonDisabledFlags) { + hax_log(HAX_LOGE, "MCE/APIC/MTRR/PAT disabling in cpuid " + "not supported\n"); + return false; + } + break; + } + } + } + return true; +} + +static int get_vm_cpuid(struct vcpu_t *vcpu, uint32_t a, uint32_t c) +{ + uint32_t cpuid_i; + struct hax_cpuid_entry *cpuid_entry; + struct vcpu_state_t *state = vcpu->state; + + cpuid_entry = &vcpu->vm->cpuid_data->entries[0]; + for (cpuid_i = 0; cpuid_i < vcpu->vm->cpuid_data->nent; + cpuid_i++) { + if (cpuid_entry[cpuid_i].function == a && + (!(cpuid_entry[cpuid_i].flags & HAX_CPUID_FLAG_SIGNIFCANT_INDEX) || + cpuid_entry[cpuid_i].index == c)) { + + state->_eax = cpuid_entry[cpuid_i].eax; + state->_ecx = cpuid_entry[cpuid_i].ecx; + state->_edx = cpuid_entry[cpuid_i].edx; + state->_ebx = cpuid_entry[cpuid_i].ebx; + return 1; + } + } + + state->_eax = 0; + state->_ecx = 0; + state->_edx = 0; + state->_ebx = 0; + return 0; +} + static void handle_cpuid(struct vcpu_t *vcpu, struct hax_tunnel *htun) { struct vcpu_state_t *state = vcpu->state; uint32_t a = state->_eax, c = state->_ecx; cpuid_args_t args; - args.eax = state->_eax; - args.ecx = state->_ecx; - asm_cpuid(&args); - state->_eax = args.eax; - state->_ecx = args.ecx; - state->_edx = args.edx; - state->_ebx = args.ebx; - - handle_cpuid_virtual(vcpu, a, c); + if(vcpu->vm->cpuid_data) { + get_vm_cpuid(vcpu, a, c); + } else { + args.eax = state->_eax; + args.ecx = state->_ecx; + asm_cpuid(&args); + state->_eax = args.eax; + state->_ecx = args.ecx; + state->_edx = args.edx; + state->_ebx = args.ebx; + handle_cpuid_virtual(vcpu, a, c); + } hax_log(HAX_LOGD, "CPUID %08x %08x: %08x %08x %08x %08x\n", a, c, state->_eax, state->_ebx, state->_ecx, state->_edx); htun->_exit_reason = vmx(vcpu, exit_reason).basic_reason; diff --git a/core/vm.c b/core/vm.c index 31d0ad5f..ff6ef064 100644 --- a/core/vm.c +++ b/core/vm.c @@ -81,6 +81,35 @@ int hax_vm_set_qemuversion(struct vm_t *vm, struct hax_qemu_version *ver) return 0; } +int hax_vm_set_cpuid(struct vm_t *vm, struct hax_cpuid *cpuid_data) +{ + uint32_t size; + + if (vm->cpuid_data) { + size = sizeof(struct hax_cpuid) + + sizeof(struct hax_cpuid_entry) * vm->cpuid_data->nent; + + hax_vfree(vm->cpuid_data, size); + vm->cpuid_data = 0; + } + + if (!is_cpuid_supported(cpuid_data)) { + hax_log(HAX_LOGE, "%s: is_cpuid_supported failed\n", __func__); + return -EINVAL; + } + size = sizeof(struct hax_cpuid) + + sizeof(struct hax_cpuid_entry) * cpuid_data->nent; + + vm->cpuid_data = hax_vmalloc(size, HAX_MEM_NONPAGE); + if (!vm->cpuid_data) { + hax_log(HAX_LOGE, "%s: Not enough memory for cpuid_data\n", __func__); + return -ENOMEM; + } + + memcpy(vm->cpuid_data, cpuid_data, size); + return 0; +} + uint64_t vm_get_eptp(struct vm_t *vm) { return vm->ept_tree.eptp.value; diff --git a/include/darwin/hax_interface_mac.h b/include/darwin/hax_interface_mac.h index 43a9719d..1301a539 100644 --- a/include/darwin/hax_interface_mac.h +++ b/include/darwin/hax_interface_mac.h @@ -67,6 +67,7 @@ /* API 2.0 */ #define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION _IOW(0, 0x84, struct hax_qemu_version) +#define HAX_VM_IOCTL_SET_CPUID _IOW(0, 0x88, struct hax_cpuid) #define HAX_IOCTL_VCPU_DEBUG _IOW(0, 0xc9, struct hax_debug_t) diff --git a/include/hax_interface.h b/include/hax_interface.h index 25c08b31..269bfbe8 100644 --- a/include/hax_interface.h +++ b/include/hax_interface.h @@ -297,4 +297,32 @@ struct hax_debug_t { uint64_t dr[8]; } PACKED; +#define HAX_CPUID_FLAG_SIGNIFCANT_INDEX (1 << 0) + +struct hax_cpuid_entry { + uint32_t function; + uint32_t index; + uint32_t flags; + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t padding[3]; +} PACKED; + +struct hax_cpuid { + uint32_t nent; + uint32_t padding; + struct hax_cpuid_entry entries[0]; +} PACKED; + +#define HAX_MAX_CPUID_ENTRIES 50 + +#ifndef HAX_PLATFORM_WINDOWS +struct hax_cpuid_data { + struct hax_cpuid cpuid; + struct hax_cpuid_entry entries[HAX_MAX_CPUID_ENTRIES]; +} PACKED; +#endif + #endif // HAX_INTERFACE_H_ diff --git a/include/linux/hax_interface_linux.h b/include/linux/hax_interface_linux.h index ecb13fd0..693d4415 100644 --- a/include/linux/hax_interface_linux.h +++ b/include/linux/hax_interface_linux.h @@ -68,6 +68,7 @@ /* API 2.0 */ #define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION _IOW(0, 0x84, struct hax_qemu_version) +#define HAX_VM_IOCTL_SET_CPUID _IOW(0, 0x88, struct hax_cpuid) #define HAX_IOCTL_VCPU_DEBUG _IOW(0, 0xc9, struct hax_debug_t) diff --git a/include/netbsd/hax_interface_netbsd.h b/include/netbsd/hax_interface_netbsd.h index 08bdb643..cec8219c 100644 --- a/include/netbsd/hax_interface_netbsd.h +++ b/include/netbsd/hax_interface_netbsd.h @@ -71,6 +71,7 @@ /* API 2.0 */ #define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION _IOW(0, 0x84, struct hax_qemu_version) +#define HAX_VM_IOCTL_SET_CPUID _IOW(0, 0x88, struct hax_cpuid) #define HAX_IOCTL_VCPU_DEBUG _IOW(0, 0xc9, struct hax_debug_t) diff --git a/platforms/darwin/com_intel_hax_ui.c b/platforms/darwin/com_intel_hax_ui.c index 22fb15be..c41072d7 100644 --- a/platforms/darwin/com_intel_hax_ui.c +++ b/platforms/darwin/com_intel_hax_ui.c @@ -455,6 +455,33 @@ static int hax_vm_ioctl(dev_t dev, ulong cmd, caddr_t data, int flag, ret = hax_vm_set_qemuversion(cvm, info); break; } + case HAX_VM_IOCTL_SET_CPUID: { + struct hax_cpuid_data info; + uint32_t size; + + if (copyin(&info, data, sizeof(struct hax_cpuid))) { + ret = -EFAULT; + break; + } + + if (info.cpuid.nent > HAX_MAX_CPUID_ENTRIES) { + ret = -E2BIG; + break; + } + + size = sizeof(struct hax_cpuid) + + sizeof(struct hax_cpuid_entry) * info.cpuid.nent; + + if (copyin(&info, data, size)) { + ret = -EFAULT; + break; + } + + if (hax_vm_set_cpuid(cvm, &(info.cpuid))) { + ret = EFAULT; + } + break; + } default: { handle_unknown_ioctl(dev, cmd, p); ret = -ENOSYS; diff --git a/platforms/linux/components.c b/platforms/linux/components.c index 1560f4f3..ef371cc7 100644 --- a/platforms/linux/components.c +++ b/platforms/linux/components.c @@ -610,6 +610,33 @@ static long hax_vm_ioctl(struct file *filp, unsigned int cmd, ret = hax_vm_set_qemuversion(cvm, &info); break; } + case HAX_VM_IOCTL_SET_CPUID: { + struct hax_cpuid_data info; + uint32_t size; + + if (copy_from_user(&info, argp, sizeof(struct hax_cpuid))) { + ret = -EFAULT; + break; + } + + if (info.cpuid.nent > HAX_MAX_CPUID_ENTRIES) { + ret = -E2BIG; + break; + } + + size = sizeof(struct hax_cpuid) + + sizeof(struct hax_cpuid_entry) * info.cpuid.nent; + + if (copy_from_user(&info, argp, size)) { + ret = -EFAULT; + break; + } + + if (hax_vm_set_cpuid(cvm, &info)) { + ret = EFAULT; + } + break; + } default: // TODO: Print information about the process that sent the ioctl. hax_log(HAX_LOGE, "Unknown VM IOCTL 0x%lx\n", cmd); diff --git a/platforms/netbsd/hax_entry_vm.c b/platforms/netbsd/hax_entry_vm.c index 9931ee64..92ab7614 100644 --- a/platforms/netbsd/hax_entry_vm.c +++ b/platforms/netbsd/hax_entry_vm.c @@ -214,6 +214,33 @@ int hax_vm_ioctl(dev_t self __unused, u_long cmd, void *data, int flag, ret = hax_vm_set_qemuversion(cvm, info); break; } + case HAX_VM_IOCTL_SET_CPUID: { + struct hax_cpuid_data info; + uint32_t size; + + if (copyin(&info, data, sizeof(struct hax_cpuid))) { + ret = -EFAULT; + break; + } + + if (info.cpuid.nent > HAX_MAX_CPUID_ENTRIES) { + ret = -E2BIG; + break; + } + + size = sizeof(struct hax_cpuid) + + sizeof(struct hax_cpuid_entry) * info.cpuid.nent; + + if (copyin(&info, data, size)) { + ret = -EFAULT; + break; + } + + if (hax_vm_set_cpuid(cvm, &info)) { + ret = EFAULT; + } + break; + } default: // TODO: Print information about the process that sent the ioctl. hax_log(HAX_LOGE, "Unknown VM IOCTL %#lx, pid=%d ('%s')\n", cmd, diff --git a/platforms/windows/hax_entry.c b/platforms/windows/hax_entry.c index 4dc7c306..86f9ba1b 100644 --- a/platforms/windows/hax_entry.c +++ b/platforms/windows/hax_entry.c @@ -613,6 +613,30 @@ NTSTATUS HaxVmControl(PDEVICE_OBJECT DeviceObject, struct hax_vm_windows *ext, hax_vm_set_qemuversion(cvm, info); break; } + case HAX_VM_IOCTL_SET_CPUID: { + struct hax_cpuid *info; + uint32_t size; + + if (inBufLength < sizeof(struct hax_cpuid)) { + ret = STATUS_INVALID_PARAMETER; + goto done; + } + + info = (struct hax_cpuid *)inBuf; + size = sizeof(struct hax_cpuid) + + sizeof(struct hax_cpuid_entry) * info->nent; + + if (inBufLength < size || info->nent > HAX_MAX_CPUID_ENTRIES) { + ret = -E2BIG; + goto done; + } + + if (hax_vm_set_cpuid(cvm, info)) { + ret = STATUS_UNSUCCESSFUL; + goto done; + } + break; + } default: ret = STATUS_INVALID_PARAMETER; break; diff --git a/platforms/windows/hax_entry.h b/platforms/windows/hax_entry.h index 52613f7d..9e407701 100644 --- a/platforms/windows/hax_entry.h +++ b/platforms/windows/hax_entry.h @@ -162,6 +162,8 @@ extern PDRIVER_OBJECT HaxDriverObject; /* API version 2.0 */ #define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION \ CTL_CODE(HAX_DEVICE_TYPE, 0x910, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define HAX_VM_IOCTL_SET_CPUID \ + CTL_CODE(HAX_DEVICE_TYPE, 0x917, METHOD_BUFFERED, FILE_ANY_ACCESS) #define HAX_IOCTL_VCPU_DEBUG \ CTL_CODE(HAX_DEVICE_TYPE, 0x916, METHOD_BUFFERED, FILE_ANY_ACCESS)