diff --git a/core/cpuid.c b/core/cpuid.c index 17427d2a..b82d379a 100644 --- a/core/cpuid.c +++ b/core/cpuid.c @@ -322,7 +322,10 @@ void cpuid_init_supported_features(void) uint32_t cpuid_guest_get_size(void) { - return sizeof(uint32_t) + CPUID_TOTAL_LEAVES * sizeof(hax_cpuid_entry); + // Both the external type 'hax_cpuid' and the internal type 'hax_cpuid_t' + // have the same size, so the size of either type can be calculated by this + // function. + return sizeof(hax_cpuid_t) + CPUID_TOTAL_LEAVES * sizeof(hax_cpuid_entry); } void cpuid_guest_init(hax_cpuid_t *cpuid) @@ -406,27 +409,35 @@ void cpuid_set_features_mask(hax_cpuid_t *cpuid, uint64_t features_mask) cpuid->features_mask = features_mask; } -void cpuid_get_guest_features(hax_cpuid_t *cpuid, hax_cpuid_entry *features) +int cpuid_get_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info) { - hax_cpuid_entry *entry; + size_t size; - if (cpuid == NULL || features == NULL) - return; + if (cpuid == NULL || cpuid_info == NULL) + return -EINVAL; - entry = find_cpuid_entry(cpuid->features, CPUID_TOTAL_LEAVES, - features->function, 0); - if (entry == NULL) - return; + if (cpuid_info->total < CPUID_TOTAL_LEAVES) { + cpuid_info->total = CPUID_TOTAL_LEAVES; + hax_log(HAX_LOGI, "%s: The buffer passed in is not enough to hold %lu " + "CPUID leaves.\n", __func__, cpuid_info->total); + return -ENOMEM; + } - *features = *entry; + size = CPUID_TOTAL_LEAVES * sizeof(hax_cpuid_entry); + + cpuid_info->total = CPUID_TOTAL_LEAVES; + cpuid_info->pad = 0; + memcpy_s(cpuid_info->entries, size, cpuid->features, size); + + return 0; } -void cpuid_set_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info) +int cpuid_set_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info) { int i; if (cpuid == NULL || cpuid_info == NULL) - return; + return -EINVAL; hax_log(HAX_LOGI, "%s: user setting:\n", __func__); dump_features(cpuid_info->entries, cpuid_info->total); @@ -440,6 +451,8 @@ void cpuid_set_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info) hax_log(HAX_LOGI, "%s: after:\n", __func__); dump_features(cpuid->features, CPUID_TOTAL_LEAVES); + + return 0; } static hax_cpuid_entry * find_cpuid_entry(hax_cpuid_entry *features, diff --git a/core/include/cpuid.h b/core/include/cpuid.h index b39969c9..4b53d322 100644 --- a/core/include/cpuid.h +++ b/core/include/cpuid.h @@ -263,7 +263,7 @@ void cpuid_guest_init(hax_cpuid_t *cpuid); void cpuid_execute(hax_cpuid_t *cpuid, cpuid_args_t *args); void cpuid_get_features_mask(hax_cpuid_t *cpuid, uint64_t *features_mask); void cpuid_set_features_mask(hax_cpuid_t *cpuid, uint64_t features_mask); -void cpuid_get_guest_features(hax_cpuid_t *cpuid, hax_cpuid_entry *features); -void cpuid_set_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info); +int cpuid_get_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info); +int cpuid_set_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info); #endif /* HAX_CORE_CPUID_H_ */ diff --git a/core/include/hax_core_interface.h b/core/include/hax_core_interface.h index 65ba0611..7951f1d1 100644 --- a/core/include/hax_core_interface.h +++ b/core/include/hax_core_interface.h @@ -46,6 +46,7 @@ int vcpu_get_fpu(struct vcpu_t *vcpu, struct fx_layout *fl); int vcpu_set_regs(struct vcpu_t *vcpu, struct vcpu_state_t *vs); int vcpu_get_regs(struct vcpu_t *vcpu, struct vcpu_state_t *vs); int vcpu_set_cpuid(struct vcpu_t *vcpu, hax_cpuid *cpuid_info); +int vcpu_get_cpuid(struct vcpu_t *vcpu, hax_cpuid *cpuid_info); void vcpu_debug(struct vcpu_t *vcpu, struct hax_debug_t *debug); void * get_vcpu_host(struct vcpu_t *vcpu); diff --git a/core/include/vcpu.h b/core/include/vcpu.h index ddfcfb10..56aa9fe6 100644 --- a/core/include/vcpu.h +++ b/core/include/vcpu.h @@ -276,6 +276,7 @@ int vcpu_put_fpu(struct vcpu_t *vcpu, struct fx_layout *fl); int vcpu_get_msr(struct vcpu_t *vcpu, uint64_t entry, uint64_t *val); int vcpu_put_msr(struct vcpu_t *vcpu, uint64_t entry, uint64_t val); int vcpu_set_cpuid(struct vcpu_t *vcpu, hax_cpuid *cpuid_info); +int vcpu_get_cpuid(struct vcpu_t *vcpu, hax_cpuid *cpuid_info); void vcpu_debug(struct vcpu_t *vcpu, struct hax_debug_t *debug); /* The declaration for OS wrapper code */ diff --git a/core/vcpu.c b/core/vcpu.c index b43eb228..b9ce5410 100644 --- a/core/vcpu.c +++ b/core/vcpu.c @@ -3977,9 +3977,15 @@ int vcpu_set_cpuid(struct vcpu_t *vcpu, hax_cpuid *cpuid_info) return -EFAULT; } - cpuid_set_guest_features(vcpu->guest_cpuid, cpuid_info); + return cpuid_set_guest_features(vcpu->guest_cpuid, cpuid_info); +} - return 0; +int vcpu_get_cpuid(struct vcpu_t *vcpu, hax_cpuid *cpuid_info) +{ + hax_log(HAX_LOGI, "%s: vCPU #%u is getting guest CPUID.\n", __func__, + vcpu->vcpu_id); + + return cpuid_get_guest_features(vcpu->guest_cpuid, cpuid_info); } void vcpu_debug(struct vcpu_t *vcpu, struct hax_debug_t *debug) diff --git a/docs/api.md b/docs/api.md index 73aa2687..43323e58 100644 --- a/docs/api.md +++ b/docs/api.md @@ -763,3 +763,60 @@ caller is smaller than the size of `struct hax_cpuid`. `HAX_MAX_CPUID_ENTRIES`. * `-EFAULT` (macOS): Failed to copy contents in `entries` to the memory in kernel space. + +#### HAX\_VCPU\_IOCTL\_GET\_CPUID +Retrieves the VCPU responses to the CPU identification (CPUID) instructions. + +HAXM initializes a minimal feature set for guest VCPUs in kernel space. Only the +CPUID instructions supported by HAXM will be emulated and cached during the +initialization phase. When the guest VCPU executes these CPUID instructions, +these cached values ​​will be returned as the execution result. + +This IOCTL is used to retrieve all supported CPUID features set and cached +values ​​of each instruction register. When the VCPUs are initialized, invoking +this IOCTL will return the default CPUID features set of HAXM; after having +invoked the IOCTL `HAX_VCPU_IOCTL_SET_CPUID`, invoking this IOCTL will return +the most recently set CPUID features set. + +The parameter `struct hax_cpuid` of this IOCTL is a variable-length type, so it +is required to allocate sufficient buffer before retrieving the CPUID features +set. Usually, the CPUID features set is retrieved by invoking this IOCTL twice +consecutively. The first time, directly pass in the variable address of +`hax_cpuid` and specify the member `total` as 0 to retrieve the total number of +supported CPUIDs; the second time, first allocate an extra buffer of +`total * sizeof(hax_cpuid_entry)` bytes for the variable-length array `entries`, +and specify `total` as the number of array elements, then the whole CPUID +features set can be retrived. + +Since All VCPUs share the same feature set in a VM, send this IOCTL to any VCPU +to retrieve CPUID features set, the results are all the same. + +* Since: Capability `HAX_CAP_CPUID` +* Parameter: `struct hax_cpuid cpuid`, (q.v. `HAX_VCPU_IOCTL_SET_CPUID`) + * (Input/Output) `total`: Number of CPUIDs in `entries`. The valid value +should be in the range [0, `HAX_MAX_CPUID_ENTRIES`]. If this parameter is set to +0, it outputs the total number of CPUID features supported by HAXM and the IOCTL +makes no use of the output parameter `entries`. + * (Output) `pad`: Ignored. + * (Output) `entries`: Array of `struct hax_cpuid_entry`. The array requires to +allocate sufficient buffer to receive the CPUID features set supported by HAXM. +The actual size of the array is indicated by `total`. If `total` is 0, it is not +necessary to allocate any extra buffer for the array. + + For each entry in `struct hax_cpuid_entry` + * (Output) `function`: CPUID function code, i.e., initial EAX value. + * (Output) `index`: Sub-leaf index. + * (Output) `flags`: Feature flags. + * (Output) `eax`: EAX register value. + * (Output) `ebx`: EBX register value. + * (Output) `ecx`: ECX register value. + * (Output) `edx`: EDX register value. + * (Output) `pad`: Ignored. +* Error codes: + * `STATUS_INVALID_PARAMETER` (Windows): The input buffer provided by the +caller is smaller than the size of `struct hax_cpuid`. + * `STATUS_UNSUCCESSFUL` (Windows): Failed to get CPUID features. + * `-E2BIG` (macOS): The input value of `total` is greater than +`HAX_MAX_CPUID_ENTRIES`. + * `-EFAULT` (macOS): Failed to copy contents in `entries` to the memory in +kernel space. diff --git a/include/darwin/hax_interface_mac.h b/include/darwin/hax_interface_mac.h index 2487cdf0..7b3be824 100644 --- a/include/darwin/hax_interface_mac.h +++ b/include/darwin/hax_interface_mac.h @@ -74,6 +74,7 @@ // a variable-length type. When ioctl() is invoked, the argument of user data // should pass the address of the pointer to `hax_cpuid`. #define HAX_VCPU_IOCTL_SET_CPUID _IOW(0, 0xca, struct hax_cpuid *) +#define HAX_VCPU_IOCTL_GET_CPUID _IOW(0, 0xcb, struct hax_cpuid *) #define HAX_KERNEL64_CS 0x80 #define HAX_KERNEL32_CS 0x08 diff --git a/include/linux/hax_interface_linux.h b/include/linux/hax_interface_linux.h index 97810934..01b05569 100644 --- a/include/linux/hax_interface_linux.h +++ b/include/linux/hax_interface_linux.h @@ -71,6 +71,7 @@ #define HAX_IOCTL_VCPU_DEBUG _IOW(0, 0xc9, struct hax_debug_t) #define HAX_VCPU_IOCTL_SET_CPUID _IOW(0, 0xca, struct hax_cpuid *) +#define HAX_VCPU_IOCTL_GET_CPUID _IOW(0, 0xcb, struct hax_cpuid *) #define HAX_KERNEL64_CS 0x80 #define HAX_KERNEL32_CS 0x08 diff --git a/platforms/darwin/com_intel_hax_ui.c b/platforms/darwin/com_intel_hax_ui.c index 8d803da4..d3d68fc0 100644 --- a/platforms/darwin/com_intel_hax_ui.c +++ b/platforms/darwin/com_intel_hax_ui.c @@ -104,13 +104,17 @@ static int hax_vcpu_major = 0; } \ if (copyin(uaddr, (dest), size)) { \ hax_log(HAX_LOGE, "%s: argument read error.\n", __func__); \ - unload_user_data(dest); \ + unload_user_data(dest, false); \ ret = -EFAULT; \ break; \ } -#define unload_user_data(dest) \ - if ((dest) != NULL) \ +#define unload_user_data(dest, overwrite) \ + if ((overwrite) && copyout((dest), uaddr, size)) { \ + hax_log(HAX_LOGE, "%s: failed to write data back.\n", __func__); \ + ret = -EFAULT; \ + } \ + if ((dest) != NULL) \ hax_vfree((dest), size); static void handle_unknown_ioctl(dev_t dev, ulong cmd, struct proc *p); @@ -282,7 +286,15 @@ static int hax_vcpu_ioctl(dev_t dev, ulong cmd, caddr_t data, int flag, load_user_data(cpuid, data, total, HAX_MAX_CPUID_ENTRIES, hax_cpuid, hax_cpuid_entry); ret = vcpu_set_cpuid(cvcpu, cpuid); - unload_user_data(cpuid); + unload_user_data(cpuid, false); + break; + } + case HAX_VCPU_IOCTL_GET_CPUID: { + struct hax_cpuid *cpuid; + load_user_data(cpuid, data, total, HAX_MAX_CPUID_ENTRIES, hax_cpuid, + hax_cpuid_entry); + ret = vcpu_get_cpuid(cvcpu, cpuid); + unload_user_data(cpuid, true); break; } default: { diff --git a/platforms/linux/components.c b/platforms/linux/components.c index a9adcd96..2a465d9d 100644 --- a/platforms/linux/components.c +++ b/platforms/linux/components.c @@ -67,13 +67,17 @@ } \ if (copy_from_user((dest), from, size)) { \ hax_log(HAX_LOGE, "%s: argument read error.\n", __func__); \ - unload_user_data(dest); \ + unload_user_data(dest, false); \ ret = -EFAULT; \ break; \ } -#define unload_user_data(dest) \ - if ((dest) != NULL) \ +#define unload_user_data(dest, overwrite) \ + if ((overwrite) && copy_to_user(from, (dest), size)) { \ + hax_log(HAX_LOGE, "%s: failed to write data back.\n", __func__); \ + ret = -EFAULT; \ + } \ + if ((dest) != NULL) \ hax_vfree((dest), size); typedef struct hax_vm_linux_t { @@ -484,7 +488,15 @@ static long hax_vcpu_ioctl(struct file *filp, unsigned int cmd, load_user_data(cpuid, argp, total, HAX_MAX_CPUID_ENTRIES, hax_cpuid, hax_cpuid_entry); ret = vcpu_set_cpuid(cvcpu, cpuid); - unload_user_data(cpuid); + unload_user_data(cpuid, false); + break; + } + case HAX_VCPU_IOCTL_GET_CPUID: { + struct hax_cpuid *cpuid; + load_user_data(cpuid, argp, total, HAX_MAX_CPUID_ENTRIES, hax_cpuid, + hax_cpuid_entry); + ret = vcpu_get_cpuid(cvcpu, cpuid); + unload_user_data(cpuid, true); break; } default: diff --git a/platforms/windows/hax_entry.c b/platforms/windows/hax_entry.c index 16167d14..97d800bd 100644 --- a/platforms/windows/hax_entry.c +++ b/platforms/windows/hax_entry.c @@ -258,7 +258,7 @@ NTSTATUS HaxVcpuControl(PDEVICE_OBJECT DeviceObject, PCHAR inBuf, outBuf; // pointer to Input and output buffer uint32_t vcpu_id, vm_id; struct vcpu_t *cvcpu; - int infret = 0; + int infret = 0, err; struct hax_vcpu_windows *vcpu = ext; PIO_STACK_LOCATION irpSp;// Pointer to current stack location @@ -444,6 +444,26 @@ NTSTATUS HaxVcpuControl(PDEVICE_OBJECT DeviceObject, } break; } + case HAX_VCPU_IOCTL_GET_CPUID: { + hax_cpuid *cpuid = (hax_cpuid *)outBuf; + if (outBufLength < sizeof(hax_cpuid) || outBufLength < + sizeof(hax_cpuid) + cpuid->total * + sizeof(hax_cpuid_entry)) { + ret = STATUS_INVALID_PARAMETER; + goto done; + } + err = vcpu_get_cpuid(cvcpu, cpuid); + if (err == -EINVAL) { + ret = STATUS_UNSUCCESSFUL; + break; + } + if (err == -ENOMEM) { + infret = sizeof(hax_cpuid); + break; + } + infret = sizeof(hax_cpuid) + cpuid->total * sizeof(hax_cpuid_entry); + break; + } default: hax_log(HAX_LOGE, "Unknow vcpu ioctl %lx\n", irpSp->Parameters.DeviceIoControl.IoControlCode); diff --git a/platforms/windows/hax_entry.h b/platforms/windows/hax_entry.h index 4659fe32..3dc1f876 100644 --- a/platforms/windows/hax_entry.h +++ b/platforms/windows/hax_entry.h @@ -167,5 +167,7 @@ extern PDRIVER_OBJECT HaxDriverObject; CTL_CODE(HAX_DEVICE_TYPE, 0x916, METHOD_BUFFERED, FILE_ANY_ACCESS) #define HAX_VCPU_IOCTL_SET_CPUID \ CTL_CODE(HAX_DEVICE_TYPE, 0x917, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define HAX_VCPU_IOCTL_GET_CPUID \ + CTL_CODE(HAX_DEVICE_TYPE, 0x918, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif // HAX_WINDOWS_HAX_ENTRY_H_