From fa64f76d2ce26a8b3d00cb5501116b5e816224c4 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Wed, 31 Mar 2010 19:45:23 +0300 Subject: [PATCH] kvm: Setup CPUID for the guest This patch sets up guest CPUID which is based on host CPUID. Signed-off-by: Pekka Enberg --- tools/kvm/kvm.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/tools/kvm/kvm.c b/tools/kvm/kvm.c index a9df1fe8b027..41f79431a12f 100644 --- a/tools/kvm/kvm.c +++ b/tools/kvm/kvm.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -310,8 +311,209 @@ static inline uint32_t selector_to_base(uint16_t selector) return (uint32_t)selector * 16; } +struct cpuid_regs { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; +}; + +static inline void +cpuid(struct cpuid_regs *regs) +{ + asm("cpuid" + : "=a" (regs->eax), + "=b" (regs->ebx), + "=c" (regs->ecx), + "=d" (regs->edx) + : "0" (regs->eax), "2" (regs->ecx)); +} + +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, + }; + 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, + }; + cpuid(®s); + + return regs.eax; +} + +static void 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(); + + 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 0x02: { /* Processor configuration descriptor */ + struct cpuid_regs regs; + uint32_t times; + + regs = (struct cpuid_regs) { + .eax = function, + }; + 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, + }; + 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: { + uint32_t index; + + for (index = 0; index < 2; index++) { + struct cpuid_regs regs = { + .eax = function, + .ecx = index, + }; + cpuid(®s); + + kvm_cpuid->entries[ndx++] = (struct kvm_cpuid_entry2) { + .function = function, + .index = index, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .eax = regs.eax, + .ebx = regs.ebx, + .ecx = regs.ecx, + .edx = regs.edx, + }; + } + break; + } + default: { + struct cpuid_regs regs; + + regs = (struct cpuid_regs) { + .eax = function, + }; + 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, + }; + 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; + + if (ioctl(self->vcpu_fd, KVM_SET_CPUID2, kvm_cpuid) < 0) + die_perror("KVM_SET_CPUID2 failed"); + + kvm_cpuid__delete(kvm_cpuid); +} + void kvm__reset_vcpu(struct kvm *self) { + setup_cpuid(self); + self->sregs = (struct kvm_sregs) { .cr0 = 0x60000010ULL, .cs = (struct kvm_segment) { -- 2.39.5