mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-08-28 01:18:41 +00:00
kvm__setup_cpuid: use KVM_GET_SUPPORTED_CPUID to simplify cpuid setup
Let kernel setup "struct kvm_cpuid2" for us! https://kerneltrap.org/mailarchive/linux-kernel/2010/4/27/4563640 Avi Kivity: ''' No. KVM_GET_CPUID2 reads what was set by KVM_SET_CPUID, as modified by the guest executing the cpuid instruction. KVM_GET_SUPPORTED_CPUID tells userspace which bits are supported by the host cpu and kvm. The API for KVM_GET_SUPPORTED_CPUID returns all cpuid leaves supported in one go, IIRC. ''' Signed-off-by: Asias He <asias.hejun@gmail.com> Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
This commit is contained in:
@@ -1,254 +1,24 @@
|
||||
#include "kvm/kvm.h"
|
||||
|
||||
#include "kvm/cpufeature.h"
|
||||
#include "kvm/util.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static struct kvm_cpuid2 *kvm_cpuid__new(unsigned long nent)
|
||||
{
|
||||
struct kvm_cpuid2 *self;
|
||||
|
||||
self = calloc(1, sizeof(*self) + (sizeof(struct kvm_cpuid_entry2) * nent));
|
||||
if (!self)
|
||||
die("out of memory");
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void kvm_cpuid__delete(struct kvm_cpuid2 *self)
|
||||
{
|
||||
free(self);
|
||||
}
|
||||
|
||||
#define MAX_KVM_CPUID_ENTRIES 100
|
||||
|
||||
enum {
|
||||
CPUID_GET_VENDOR_ID = 0x00,
|
||||
CPUID_GET_HIGHEST_EXT_FUNCTION = 0x80000000,
|
||||
};
|
||||
|
||||
static uint32_t cpuid_highest_ext_func(void)
|
||||
{
|
||||
struct cpuid_regs regs;
|
||||
|
||||
regs = (struct cpuid_regs) {
|
||||
.eax = CPUID_GET_HIGHEST_EXT_FUNCTION,
|
||||
};
|
||||
host_cpuid(®s);
|
||||
|
||||
return regs.eax;
|
||||
}
|
||||
|
||||
static uint32_t cpuid_highest_func(void)
|
||||
{
|
||||
struct cpuid_regs regs;
|
||||
|
||||
regs = (struct cpuid_regs) {
|
||||
.eax = CPUID_GET_VENDOR_ID,
|
||||
};
|
||||
host_cpuid(®s);
|
||||
|
||||
return regs.eax;
|
||||
}
|
||||
|
||||
void kvm__setup_cpuid(struct kvm *self)
|
||||
{
|
||||
struct kvm_cpuid2 *kvm_cpuid;
|
||||
uint32_t highest_ext;
|
||||
uint32_t function;
|
||||
uint32_t highest;
|
||||
uint32_t ndx = 0;
|
||||
|
||||
kvm_cpuid = kvm_cpuid__new(MAX_KVM_CPUID_ENTRIES);
|
||||
highest = cpuid_highest_func();
|
||||
kvm_cpuid = calloc(1, sizeof(*kvm_cpuid) + MAX_KVM_CPUID_ENTRIES * sizeof(*kvm_cpuid->entries));
|
||||
|
||||
for (function = 0; function <= highest; function++) {
|
||||
/*
|
||||
* NOTE NOTE NOTE! Functions 0x0b and 0x0d seem to need special
|
||||
* treatment as per qemu sources but we treat them as regular
|
||||
* CPUID functions here because they are fairly exotic and the
|
||||
* Linux kernel is not interested in them during boot up.
|
||||
*/
|
||||
switch (function) {
|
||||
case 0x00: { /* Vendor-ID and Largest Standard Function */
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = 0,
|
||||
.index = 0,
|
||||
.flags = 0,
|
||||
.eax = 4,
|
||||
.ebx = CPUID_VENDOR_INTEL_1,
|
||||
.ecx = CPUID_VENDOR_INTEL_3,
|
||||
.edx = CPUID_VENDOR_INTEL_2,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 0x01: { /* Feature Information */
|
||||
struct cpuid_regs regs;
|
||||
|
||||
regs = (struct cpuid_regs) {
|
||||
.eax = function,
|
||||
};
|
||||
host_cpuid(®s);
|
||||
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 0,
|
||||
.flags = 0,
|
||||
.eax = regs.eax,
|
||||
.ebx = regs.ebx,
|
||||
.ecx =
|
||||
cpu_feature_disable(regs.ecx, KVM__X86_FEATURE_XSAVE) &
|
||||
cpu_feature_disable(regs.ecx, KVM__X86_FEATURE_VMX),
|
||||
.edx = regs.edx,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 0x02: { /* Processor configuration descriptor */
|
||||
struct cpuid_regs regs;
|
||||
uint32_t times;
|
||||
|
||||
regs = (struct cpuid_regs) {
|
||||
.eax = function,
|
||||
};
|
||||
host_cpuid(®s);
|
||||
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 0,
|
||||
.flags = KVM_CPUID_FLAG_STATEFUL_FUNC | KVM_CPUID_FLAG_STATE_READ_NEXT,
|
||||
.eax = regs.eax,
|
||||
.ebx = regs.ebx,
|
||||
.ecx = regs.ecx,
|
||||
.edx = regs.edx,
|
||||
};
|
||||
|
||||
times = regs.eax & 0xff; /* AL */
|
||||
|
||||
while (times-- > 0) {
|
||||
regs = (struct cpuid_regs) {
|
||||
.eax = function,
|
||||
};
|
||||
host_cpuid(®s);
|
||||
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 0,
|
||||
.flags = KVM_CPUID_FLAG_STATEFUL_FUNC,
|
||||
.eax = regs.eax,
|
||||
.ebx = regs.ebx,
|
||||
.ecx = regs.ecx,
|
||||
.edx = regs.edx,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x04: { /* Deterministic Cache Parameters */
|
||||
uint32_t eax;
|
||||
/*
|
||||
* eax for n cores
|
||||
* eax = (n - 1) << 26
|
||||
* eax for k threads
|
||||
* eax = (k - 1) << 14
|
||||
* they could be OR'ified
|
||||
*/
|
||||
eax = 0;
|
||||
|
||||
/* L1 dcache info */
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 0,
|
||||
.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX | KVM_CPUID_FLAG_STATE_READ_NEXT,
|
||||
.eax = eax | 0x0000121,
|
||||
.ebx = 0x1c0003f,
|
||||
.ecx = 0x000003f,
|
||||
.edx = 0x0000001,
|
||||
};
|
||||
|
||||
/* L1 icache info */
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 1,
|
||||
.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX | KVM_CPUID_FLAG_STATE_READ_NEXT,
|
||||
.eax = eax | 0x0000122,
|
||||
.ebx = 0x1c0003f,
|
||||
.ecx = 0x000003f,
|
||||
.edx = 0x0000001,
|
||||
};
|
||||
|
||||
/* L2 cache info */
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 2,
|
||||
.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX | KVM_CPUID_FLAG_STATE_READ_NEXT,
|
||||
.eax = eax | 0x0000143,
|
||||
.ebx = 0x3c0003f,
|
||||
.ecx = 0x0000fff,
|
||||
.edx = 0x0000001,
|
||||
};
|
||||
|
||||
/* End of list */
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 3,
|
||||
.flags = 0,
|
||||
.eax = 0,
|
||||
.ebx = 0,
|
||||
.ecx = 0,
|
||||
.edx = 0,
|
||||
};
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
struct cpuid_regs regs;
|
||||
|
||||
regs = (struct cpuid_regs) {
|
||||
.eax = function,
|
||||
};
|
||||
host_cpuid(®s);
|
||||
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 0,
|
||||
.flags = 0,
|
||||
.eax = regs.eax,
|
||||
.ebx = regs.ebx,
|
||||
.ecx = regs.ecx,
|
||||
.edx = regs.edx,
|
||||
};
|
||||
break;
|
||||
}};
|
||||
}
|
||||
|
||||
highest_ext = cpuid_highest_ext_func();
|
||||
|
||||
for (function = CPUID_GET_HIGHEST_EXT_FUNCTION; function <= highest_ext; function++) {
|
||||
struct cpuid_regs regs;
|
||||
|
||||
regs = (struct cpuid_regs) {
|
||||
.eax = function,
|
||||
};
|
||||
host_cpuid(®s);
|
||||
|
||||
kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) {
|
||||
.function = function,
|
||||
.index = 0,
|
||||
.flags = 0,
|
||||
.eax = regs.eax,
|
||||
.ebx = regs.ebx,
|
||||
.ecx = regs.ecx,
|
||||
.edx = regs.edx,
|
||||
};
|
||||
}
|
||||
|
||||
assert(ndx < MAX_KVM_CPUID_ENTRIES);
|
||||
|
||||
kvm_cpuid->nent = ndx;
|
||||
kvm_cpuid->nent = MAX_KVM_CPUID_ENTRIES;
|
||||
if (ioctl(self->sys_fd, KVM_GET_SUPPORTED_CPUID, kvm_cpuid) < 0)
|
||||
die_perror("KVM_GET_SUPPORTED_CPUID failed");
|
||||
|
||||
if (ioctl(self->vcpu_fd, KVM_SET_CPUID2, kvm_cpuid) < 0)
|
||||
die_perror("KVM_SET_CPUID2 failed");
|
||||
|
||||
kvm_cpuid__delete(kvm_cpuid);
|
||||
free(kvm_cpuid);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user