Skip to content
This repository has been archived by the owner on Jan 28, 2023. It is now read-only.

Commit

Permalink
Merge pull request #383 from intel/get-cpuid
Browse files Browse the repository at this point in the history
Enable getting CPUID features for guest VCPUs
  • Loading branch information
wcwang authored May 24, 2021
2 parents 83e6ee6 + 24f0fb9 commit b178462
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 25 deletions.
37 changes: 25 additions & 12 deletions core/cpuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions core/include/cpuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
1 change: 1 addition & 0 deletions core/include/hax_core_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions core/include/vcpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
10 changes: 8 additions & 2 deletions core/vcpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
57 changes: 57 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions include/darwin/hax_interface_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/linux/hax_interface_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 16 additions & 4 deletions platforms/darwin/com_intel_hax_ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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: {
Expand Down
20 changes: 16 additions & 4 deletions platforms/linux/components.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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:
Expand Down
22 changes: 21 additions & 1 deletion platforms/windows/hax_entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions platforms/windows/hax_entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_

0 comments on commit b178462

Please sign in to comment.