visit
For most workloads it will be necessary to determine characteristics of CPU on which they are run. Most processors have some way of querying capabilities. x86 CPU uses
CPUID
instruction.Unfortunately you cannot just start a VM and then execute the CPUID instruction. The program will not crash but you will not get useful information either. The reason for this is that sometimes we'd like to tell guests what is available and what is not available for use. At this point we will go with a simple approach - we will let our guest OS do whatever KVM features are available.KVM has an ioctl to do that -
KVM_GET_SUPPORTED_CPUID
. It also has KVM_SET_CPUID2
that defines vCPU response to CPUID instruction.Before we can make a call to
KVM_GET_SUPPORTED_CPUID
we need to create kvm_cpuid2
structure and allocate enough space for it. The
kvm_cpuid2
structure is defined as follows:struct kvm_cpuid2 {
__u32 nent;
__u32 padding;
struct kvm_cpuid_entry2 entries[0];
};
struct kvm_cpuid_entry2 {
__u32 function;
__u32 index;
__u32 flags;
__u32 eax;
__u32 ebx;
__u32 ecx;
__u32 edx;
__u32 padding[3];
};
You can see that it has two elements but it also has a trailing array. The way we allocate memory for this type of structure is
sizeof(kvm_cpuid2) + number_of_entries * sizeof(kvm_cpuid_entry2)
← pseudo-code ⚠️Following is piece of code that allocates memory for
kvm_cpuid2
structure and uses KVM_GET_SUPPORTED_CPUID
to populate it.struct kvm_cpuid2 *cpuid;
int nent = 40;
unsigned long size = sizeof(*cpuid) + nent * sizeof(*cpuid->entries);
cpuid = (struct kvm_cpuid2*) malloc(size);
bzero(cpuid, size);
cpuid->nent = nent;
if(ioctl(kvm, KVM_GET_SUPPORTED_CPUID, cpuid) == -1) {
printf("KVM_GET_SUPPORTED_CPUID could not read CPUID info. Error code: %d\n", ret);
return -1;
}
for(int i = 0; i < cpuid->nent; i++) {
printf("F: 0x%08x, idx: 0x%08x, flags: 0x%08x, eax: 0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x\n", cpuid->entries[i].function, cpuid->entries[i].index, cpuid->entries[i].flags, cpuid->entries[i].eax, cpuid->entries[i].ebx, cpuid->entries[i].ecx, cpuid->entries[i].edx);
}
Now we can use our pointer to
cpuid
structure to configure vCPU.if(ioctl(vcpufd, KVM_SET_CPUID2, cpuid) == -1) {
printf("KVM_SET_CPUID2 could not set CPUID info. Error code: %d\n", ret);
return -1;
}