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


Reply via email to