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. 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