From: Pekka Enberg Date: Wed, 31 Mar 2010 17:59:54 +0000 (+0300) Subject: kvm: Extract CPUID code into its own file X-Git-Tag: next-20110824~3^2~528^2~202 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=d34fad6c3cbcb389cfdf2d9ef69d070ed893c707;p=karo-tx-linux.git kvm: Extract CPUID code into its own file Signed-off-by: Pekka Enberg --- diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile index bb92bc450545..99b88c9295f5 100644 --- a/tools/kvm/Makefile +++ b/tools/kvm/Makefile @@ -1,6 +1,7 @@ PROGRAM = kvm OBJS += cpu.o +OBJS += cpuid.o OBJS += interrupt.o OBJS += kvm.o OBJS += main.o diff --git a/tools/kvm/cpuid.c b/tools/kvm/cpuid.c new file mode 100644 index 000000000000..0759ed75c267 --- /dev/null +++ b/tools/kvm/cpuid.c @@ -0,0 +1,206 @@ +#include "kvm/kvm.h" + +#include "kvm/util.h" + +#include +#include +#include + +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; +} + +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(); + + 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); +} diff --git a/tools/kvm/include/kvm/kvm.h b/tools/kvm/include/kvm/kvm.h index 1022177ab45b..d89a3f26fd5b 100644 --- a/tools/kvm/include/kvm/kvm.h +++ b/tools/kvm/include/kvm/kvm.h @@ -28,6 +28,7 @@ struct kvm { }; struct kvm *kvm__init(void); +void kvm__setup_cpuid(struct kvm *self); void kvm__enable_singlestep(struct kvm *self); bool kvm__load_kernel(struct kvm *kvm, const char *kernel_filename, const char *kernel_cmdline); void kvm__reset_vcpu(struct kvm *self); diff --git a/tools/kvm/kvm.c b/tools/kvm/kvm.c index 41f79431a12f..0b7776557927 100644 --- a/tools/kvm/kvm.c +++ b/tools/kvm/kvm.c @@ -311,209 +311,8 @@ 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) { diff --git a/tools/kvm/main.c b/tools/kvm/main.c index 184617c5cfa9..59303bfdb5f8 100644 --- a/tools/kvm/main.c +++ b/tools/kvm/main.c @@ -48,6 +48,8 @@ int main(int argc, char *argv[]) kvm = kvm__init(); + kvm__setup_cpuid(kvm); + if (!kvm__load_kernel(kvm, kernel_filename, kernel_cmdline)) die("unable to load kernel %s", kernel_filename);