On Wed, 2007-09-05 at 20:45 +0300, [EMAIL PROTECTED] wrote: > Hi, > > It's a pity not to use a host CPU feature if it is available. This patch > exposes host CPU features to guests. It allows fine-tuning the presented > features from the command-line. > > The code could use some serious clean ups, but I think it is interesting > enough right now. I'd be happy to hear your opinion and suggestions. > The diff are done against qemu cvs. I tried it with kvm, but I thinkg it > should be useful also for kqemu.
I like this idea but I have some suggestions about the general approach. I think instead of defining another machine type, it would be better to just have a command line option like -cpuid that took a comma separate string of features with "all" meaning all features that the host has. I also think it would be nicer to use cpuid() directly instead of attempting to parse /proc/cpuinfo. Regards, Anthony Liguori > Regards, > > Dan. > > Index: vl.c > =================================================================== > RCS file: /sources/qemu/qemu/vl.c,v > retrieving revision 1.336 > diff -u -p -r1.336 vl.c > --- vl.c 28 Aug 2007 22:21:40 -0000 1.336 > +++ vl.c 5 Sep 2007 15:48:10 -0000 > @@ -6575,12 +6575,24 @@ int qemu_register_machine(QEMUMachine *m > return 0; > } > > +const char *machine_flavor = 0; > + > QEMUMachine *find_machine(const char *name) > { > QEMUMachine *m; > > + int n; > + machine_flavor = strchr(name, ','); > + if (machine_flavor) { > + n = machine_flavor - name; > + machine_flavor++; > + } else { > + n = strlen(name); > + machine_flavor = ""; > + } > + > for(m = first_machine; m != NULL; m = m->next) { > - if (!strcmp(m->name, name)) > + if (!strncmp(m->name, name, n)) > return m; > } > return NULL; > @@ -7343,6 +7355,7 @@ static void read_passwords(void) > void register_machines(void) > { > #if defined(TARGET_I386) > + qemu_register_machine(&host_pc_machine); > qemu_register_machine(&pc_machine); > qemu_register_machine(&isapc_machine); > #elif defined(TARGET_PPC) > Index: vl.h > =================================================================== > RCS file: /sources/qemu/qemu/vl.h,v > retrieving revision 1.264 > diff -u -p -r1.264 vl.h > --- vl.h 26 Aug 2007 17:46:00 -0000 1.264 > +++ vl.h 5 Sep 2007 15:48:10 -0000 > @@ -1156,6 +1156,7 @@ void piix4_smbus_register_device(SMBusDe > void acpi_bios_init(void); > > /* pc.c */ > +extern QEMUMachine host_pc_machine; > extern QEMUMachine pc_machine; > extern QEMUMachine isapc_machine; > extern int fd_bootchk; > Index: hw/pc.c > =================================================================== > RCS file: /sources/qemu/qemu/hw/pc.c,v > retrieving revision 1.83 > diff -u -p -r1.83 pc.c > --- hw/pc.c 26 Aug 2007 17:51:39 -0000 1.83 > +++ hw/pc.c 5 Sep 2007 15:48:10 -0000 > @@ -965,6 +965,28 @@ static void pc_init_isa(int ram_size, in > initrd_filename, 0); > } > > +int use_hostlike_cpu = 0; > + > +static void pc_init_hostlike(ram_addr_t ram_size, int vga_ram_size, int > boot_device, > + DisplayState *ds, const char **fd_filename, > + int snapshot, > + const char *kernel_filename, > + const char *kernel_cmdline, > + const char *initrd_filename) > +{ > + use_hostlike_cpu = 1; > + pc_init1(ram_size, vga_ram_size, boot_device, > + ds, fd_filename, snapshot, > + kernel_filename, kernel_cmdline, > + initrd_filename, 1); > +} > + > +QEMUMachine host_pc_machine = { > + "host", > + "Standard PC with host-like CPU", > + pc_init_hostlike, > +}; > + > QEMUMachine pc_machine = { > "pc", > "Standard PC", > Index: target-i386/cpu.h > =================================================================== > RCS file: /sources/qemu/qemu/target-i386/cpu.h,v > retrieving revision 1.45 > diff -u -p -r1.45 cpu.h > --- target-i386/cpu.h 11 Jul 2007 22:48:58 -0000 1.45 > +++ target-i386/cpu.h 5 Sep 2007 15:48:10 -0000 > @@ -267,21 +267,44 @@ > #define CPUID_CMOV (1 << 15) > #define CPUID_PAT (1 << 16) > #define CPUID_PSE36 (1 << 17) > +#define CPUID_PN (1 << 18) > #define CPUID_CLFLUSH (1 << 19) > -/* ... */ > +#define CPUID_DTS (1 << 21) > +#define CPUID_ACPI (1 << 22) > #define CPUID_MMX (1 << 23) > #define CPUID_FXSR (1 << 24) > #define CPUID_SSE (1 << 25) > #define CPUID_SSE2 (1 << 26) > +#define CPUID_SS (1 << 27) > +#define CPUID_HT (1 << 28) > +#define CPUID_TM (1 << 29) > +#define CPUID_IA64 (1 << 30) > +#define CPUID_PBE (1 << 31) > > #define CPUID_EXT_SSE3 (1 << 0) > #define CPUID_EXT_MONITOR (1 << 3) > +#define CPUID_EXT_DSCPL (1 << 4) > +#define CPUID_EXT_VMX (1 << 5) > +#define CPUID_EXT_SMX (1 << 6) > +#define CPUID_EXT_EST (1 << 7) > +#define CPUID_EXT_TM2 (1 << 8) > +#define CPUID_EXT_SSSE3 (1 << 9) > +#define CPUID_EXT_CID (1 << 10) > #define CPUID_EXT_CX16 (1 << 13) > +#define CPUID_EXT_XTPR (1 << 14) > +#define CPUID_EXT_DCA (1 << 17) > +#define CPUID_EXT_POPCNT (1 << 22) > > #define CPUID_EXT2_SYSCALL (1 << 11) > +#define CPUID_EXT2_MP (1 << 19) > #define CPUID_EXT2_NX (1 << 20) > +#define CPUID_EXT2_MMXEXT (1 << 22) > #define CPUID_EXT2_FFXSR (1 << 25) > +#define CPUID_EXT2_PDPE1GB (1 << 26) > +#define CPUID_EXT2_RDTSCP (1 << 27) > #define CPUID_EXT2_LM (1 << 29) > +#define CPUID_EXT2_3DNOWEXT (1 << 30) > +#define CPUID_EXT2_3DNOW (1 << 31) > > #define EXCP00_DIVZ 0 > #define EXCP01_SSTP 1 > Index: target-i386/helper2.c > =================================================================== > RCS file: /sources/qemu/qemu/target-i386/helper2.c,v > retrieving revision 1.48 > diff -u -p -r1.48 helper2.c > --- target-i386/helper2.c 31 Jul 2007 23:09:18 -0000 1.48 > +++ target-i386/helper2.c 5 Sep 2007 15:48:10 -0000 > @@ -45,6 +45,272 @@ int modify_ldt(int func, void *ptr, unsi > #endif > #endif /* USE_CODE_COPY */ > > +/* x86_cap_flags taken from Linux's arch/i386/kernel/cpu/proc.c */ > +static const char * const x86_cap_flags[] = { > + /* Intel-defined */ > + "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", > + "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov", > + "pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx", > + "fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "pbe", > + > + /* AMD-defined */ > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL, > + NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", "3dnowext", "3dnow", > + > + /* Transmeta-defined */ > + "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + > + /* Other (Linux-defined) */ > + "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", > + NULL, NULL, NULL, NULL, > + "constant_tsc", "up", NULL, NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + > + /* Intel-defined (#2) */ > + "pni", NULL, NULL, "monitor", "ds_cpl", "vmx", "smx", "est", > + "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL, > + NULL, NULL, "dca", NULL, NULL, NULL, NULL, "popcnt", > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + > + /* VIA/Cyrix/Centaur-defined */ > + NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en", > + "ace2", "ace2_en", "phe", "phe_en", "pmm", "pmm_en", NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + > + /* AMD-defined (#2) */ > + "lahf_lm", "cmp_legacy", "svm", "extapic", "cr8legacy", "abm", > + "sse4a", "misalignsse", > + "3dnowprefetch", "osvw", "ibs", NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > +}; > + > +void add_flagname_to_bitmaps(char *flagname, int *features, int > *ext_features, int *ext2_features) > +{ > + int i; > + for ( i = 0 ; i < 32 ; i++ ) > + if (x86_cap_flags[i] && !strcmp (flagname, x86_cap_flags[i])) { > + *features |= 1 << i; > + return; > + } > + for ( i = 0 ; i < 32 ; i++ ) > + if (x86_cap_flags[32*4+i] && !strcmp (flagname, > x86_cap_flags[32*4+i])) { > + *ext_features |= 1 << i; > + return; > + } > + for ( i = 0 ; i < 32 ; i++ ) > + if (x86_cap_flags[32*1+i] && !strcmp (flagname, > x86_cap_flags[32*1+i])) { > + *ext2_features |= 1 << i; > + return; > + } > + /* Silently ignore Linux-defined features */ > + for ( i = 0 ; i < 32 ; i++ ) > + if (x86_cap_flags[32*3+i] && !strcmp (flagname, > x86_cap_flags[32*3+i])) { > + return; > + } > + fprintf(stderr, "CPU feature %s not found\n", flagname); > +} > + > +static void apply_machine_flavor(CPUX86State *env) > +{ > + int plus_features = 0, plus_ext_features = 0, plus_ext2_features = 0; > + int minus_features = 0, minus_ext_features = 0, minus_ext2_features = 0; > + int family = -1, model = -1, stepping = -1; > + > + extern char *machine_flavor; > + char *s = strdup(machine_flavor); > + char *featurestr = strtok(s, ","); > + > + while (featurestr) { > + char *val; > + if (featurestr[0] == '+') { > + add_flagname_to_bitmaps(featurestr + 1, &plus_features, > &plus_ext_features, &plus_ext2_features); > + } > + else if (featurestr[0] == '-') { > + add_flagname_to_bitmaps(featurestr + 1, &minus_features, > &minus_ext_features, &minus_ext2_features); > + } > + else if ((val = strchr(featurestr, '='))) { > + *val = 0; val++; > + if (!strcmp(featurestr, "family")) { > + family = atoi(val); > + if (family <= 0) { > + fprintf(stderr, "bad numerical value %s\n", val); > + exit(1); > + } > + env->cpuid_version &= 0xFF; > + env->cpuid_version |= (family << 8); > + } else if (!strcmp(featurestr, "model")) { > + model = atoi(val); > + if (model <= 0) { > + fprintf(stderr, "bad numerical value %s\n", val); > + exit(1); > + } > + env->cpuid_version &= 0xFFFFFF0F; > + env->cpuid_version |= (model << 4); > + } else if (!strcmp(featurestr, "stepping")) { > + stepping = atoi(val); > + if (stepping <= 0) { > + fprintf(stderr, "bad numerical value %s\n", val); > + exit(1); > + } > + env->cpuid_version &= 0xFFFFFFF0; > + env->cpuid_version |= stepping; > + } else { > + fprintf(stderr, "unknown feature %s\n", featurestr); > + exit(1); > + } > + } else { > + fprintf(stderr, "feature string `%s' not in format > (+feature|-feature|feature=xyz)\n", featurestr); > + exit(1); > + } > + featurestr = strtok(NULL, ","); > + } > + free(s); > + env->cpuid_features |= plus_features; > + env->cpuid_ext_features |= plus_ext_features; > + env->cpuid_ext2_features |= plus_ext2_features; > + env->cpuid_features &= ~minus_features; > + env->cpuid_ext_features &= ~minus_ext_features; > + env->cpuid_ext2_features &= ~minus_ext2_features; > +} > + > +static int read_one_cpu_info(CPUX86State *env, FILE *f) > +{ > + char line[300], *val; > + char vendor_id[13]; > + int family = -1, model = -1, stepping = -1; > + int features = 0, ext_features = 0, ext2_features = 0; > + > + while (fgets(line, sizeof(line), f)) { > + if (!strtok(line, "\t") || line[0]=='\n') { > + break; > + } > + val = strtok(NULL, "\t:"); > + if (!val) > + val = ""; > + if (*val==' ') > + val++; > + > + if (!strcmp(line,"vendor_id")) { > + strncpy(vendor_id, val, sizeof(vendor_id) - 1); > + vendor_id[sizeof(vendor_id)-1] = 0; > + } > + if (!strcmp(line,"cpu family")) { > + family = atoi(val); > + } > + if (!strcmp(line,"model")) { > + model = atoi(val); > + } > + if (!strcmp(line,"stepping")) { > + stepping = atoi(val); > + } > + if (!strcmp(line,"flags")) { > + char *flagname; > + struct flagstorage { > + char *name; > + int bit; > + int *container; > + }; > + > + flagname = strtok(val, " "); > + while (flagname) { > + add_flagname_to_bitmaps(flagname, &features, &ext_features, > &ext2_features); > + flagname = strtok(NULL, " \n"); > + } > + features &= ~CPUID_ACPI; /* acpi causes guest kernel panic on > boot time, in cpu_identify */ > + ext_features &= ~CPUID_EXT_VMX; /* KVM is not recursive */ > + } > + } > + if (family==-1 || model==-1 || stepping==-1) > + return -1; > + env->cpuid_level = 2; > + env->cpuid_vendor1 = *(uint32_t *)&vendor_id[0]; > + env->cpuid_vendor2 = *(uint32_t *)&vendor_id[4]; > + env->cpuid_vendor3 = *(uint32_t *)&vendor_id[8]; > + env->cpuid_version = (family << 8) | (model << 4) | stepping; > + env->cpuid_features = features; > + env->cpuid_ext_features = ext_features; > + env->cpuid_ext2_features = ext2_features; > + env->pat = 0x0007040600070406ULL; > + /* TODO expose real cpuid extended level */ > +#ifdef TARGET_X86_64 > + env->cpuid_xlevel = 0x80000008; > +#else > + env->cpuid_xlevel = 0; > +#endif > + return 0; > +} > + > +static int set_guest_cpu_hostlike(CPUX86State *env) { > + FILE *f; > + f = fopen("/proc/cpuinfo", "r"); > + if (!f) { > + fprintf(stderr, "falling back to standard guest cpu\n"); > + return -1; > + } > + read_one_cpu_info(env, f); > + fclose(f); > + return 0; > +} > + > +static int set_guest_cpu_standard(CPUX86State *env) > +{ > + int family, model, stepping; > +#ifdef TARGET_X86_64 > + env->cpuid_vendor1 = 0x68747541; /* "Auth" */ > + env->cpuid_vendor2 = 0x69746e65; /* "enti" */ > + env->cpuid_vendor3 = 0x444d4163; /* "cAMD" */ > + family = 6; > + model = 2; > + stepping = 3; > +#else > + env->cpuid_vendor1 = 0x756e6547; /* "Genu" */ > + env->cpuid_vendor2 = 0x49656e69; /* "ineI" */ > + env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */ > +#if 0 > + /* pentium 75-200 */ > + family = 5; > + model = 2; > + stepping = 11; > +#else > + /* pentium pro */ > + family = 6; > + model = 3; > + stepping = 3; > +#endif > +#endif > + env->cpuid_level = 2; > + env->cpuid_version = (family << 8) | (model << 4) | stepping; > + env->cpuid_features = (CPUID_FP87 | CPUID_DE | CPUID_PSE | > + CPUID_TSC | CPUID_MSR | CPUID_MCE | > + CPUID_CX8 | CPUID_PGE | CPUID_CMOV | > + CPUID_PAT); > + env->pat = 0x0007040600070406ULL; > + env->cpuid_ext_features = CPUID_EXT_SSE3; > + env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | > CPUID_PAE | CPUID_SEP; > + env->cpuid_features |= CPUID_APIC; > + env->cpuid_xlevel = 0; > +#ifdef TARGET_X86_64 > + /* currently not enabled for std i386 because not fully tested */ > + env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF); > + env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | > CPUID_EXT2_NX; > + env->cpuid_xlevel = 0x80000008; > + > + /* these features are needed for Win64 and aren't fully implemented */ > + env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA; > + /* this feature is needed for Solaris and isn't fully implemented */ > + env->cpuid_features |= CPUID_PSE36; > +#endif > + return 0; > +} > + > CPUX86State *cpu_x86_init(void) > { > CPUX86State *env; > @@ -80,64 +346,23 @@ CPUX86State *cpu_x86_init(void) > } > #endif > { > - int family, model, stepping; > -#ifdef TARGET_X86_64 > - env->cpuid_vendor1 = 0x68747541; /* "Auth" */ > - env->cpuid_vendor2 = 0x69746e65; /* "enti" */ > - env->cpuid_vendor3 = 0x444d4163; /* "cAMD" */ > - family = 6; > - model = 2; > - stepping = 3; > -#else > - env->cpuid_vendor1 = 0x756e6547; /* "Genu" */ > - env->cpuid_vendor2 = 0x49656e69; /* "ineI" */ > - env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */ > -#if 0 > - /* pentium 75-200 */ > - family = 5; > - model = 2; > - stepping = 11; > -#else > - /* pentium pro */ > - family = 6; > - model = 3; > - stepping = 3; > -#endif > -#endif > - env->cpuid_level = 2; > - env->cpuid_version = (family << 8) | (model << 4) | stepping; > - env->cpuid_features = (CPUID_FP87 | CPUID_DE | CPUID_PSE | > - CPUID_TSC | CPUID_MSR | CPUID_MCE | > - CPUID_CX8 | CPUID_PGE | CPUID_CMOV | > - CPUID_PAT); > - env->pat = 0x0007040600070406ULL; > - env->cpuid_ext_features = CPUID_EXT_SSE3; > - env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | > CPUID_SSE2 | CPUID_PAE | CPUID_SEP; > - env->cpuid_features |= CPUID_APIC; > - env->cpuid_xlevel = 0; > - { > - const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION; > - int c, len, i; > - len = strlen(model_id); > - for(i = 0; i < 48; i++) { > - if (i >= len) > - c = '\0'; > - else > - c = model_id[i]; > - env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); > - } > + extern int use_hostlike_cpu; > + if (!use_hostlike_cpu || set_guest_cpu_hostlike(env)) > + set_guest_cpu_standard(env); > + apply_machine_flavor(env); > + } > + { > + const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION; > + int c, len, i; > + > + len = strlen(model_id); > + for(i = 0; i < 48; i++) { > + if (i >= len) > + c = '\0'; > + else > + c = model_id[i]; > + env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); > } > -#ifdef TARGET_X86_64 > - /* currently not enabled for std i386 because not fully tested */ > - env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF); > - env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | > CPUID_EXT2_NX; > - env->cpuid_xlevel = 0x80000008; > - > - /* these features are needed for Win64 and aren't fully implemented > */ > - env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA; > - /* this feature is needed for Solaris and isn't fully implemented */ > - env->cpuid_features |= CPUID_PSE36; > -#endif > } > cpu_reset(env); > #ifdef USE_KQEMU > > ------------------------------------------------------------------------- > This SF.net email is sponsored by: Splunk Inc. > Still grepping through log files to find problems? Stop. > Now Search log events and configuration files using AJAX and a browser. > Download your FREE copy of Splunk now >> http://get.splunk.com/ > _______________________________________________ > kvm-devel mailing list > [EMAIL PROTECTED] > https://lists.sourceforge.net/lists/listinfo/kvm-devel