This is a rework of the prior version which adds definitions for contemporary processors selected via -cpu <model>, as an alternative to the existing use of "-cpu qemu64" augmented with a series of feature flags.
The primary motivation was determination of a least common denominator within a given processor class to simplify guest migration. It is still possible to modify an arbitrary model via additional feature flags however the goal here was to make doing so unnecessary in typical usage. The other consideration was providing models names reflective of current processors. Both AMD and Intel have reviewed the models in terms of balancing generality of migration vs. excessive feature downgrade relative to released silicon. Concerning the prior version of the patch, the proposed name used for a given model drew a fair amount of debate, the main concern being use of names as mnemonic as possible to the wisest group of users. Another suggestion was to use the vendor name of released silicon corresponding to a least common denominator CPU within the class, rational being doing so is more definitive of the intended functionality. However something like: -cpu "Intel Core 2 Duo P9xxx" probably isn't all that easy to remember nor type when selecting a Penryn class cpu. So I struck what I believe to be a reasonable compromise where the original x86_def_t.name was for the most part retained with the x86_def_t.model_id capturing the marketing name of the cpu being used as the least common denominator for the class. To make it easier for a user to associate a *.name with *.model_id, "-cpu ?" invoked rather as "-cpu ??" will append *.model_id to the generated table: : x86 Conroe Intel Celeron_4x0 (Conroe/Merom Class Core 2) x86 Penryn Intel Core 2 Duo P9xxx (Penryn Class Core 2) x86 Nehalem Intel Core i7 9xx (Nehalem Class Core i7) x86 Opteron_G1 AMD Opteron 240 (Gen 1 Class Opteron) x86 Opteron_G2 AMD Opteron 22xx (Gen 2 Class Opteron) x86 Opteron_G3 AMD Opteron 23xx (Gen 3 Class Opteron) : As before a cpu feature 'check' option is added which warns when feature flags (either implicit in a cpu model or explicit on the command line) would have otherwise been quietly unavailable to a guest: # qemu-system-x86_64 ... -cpu Nehalem,check warning: host cpuid 0000_0001 lacks requested flag 'sse4.2' [0x00100000] warning: host cpuid 0000_0001 lacks requested flag 'popcnt' [0x00800000] This patch was tested relative to qemu.git. Signed-off-by: john cooper <john.coo...@redhat.com> --- diff --git a/target-i386/cpu.h b/target-i386/cpu.h index f3834b3..58400ab 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -722,8 +722,8 @@ typedef struct CPUX86State { CPUX86State *cpu_x86_init(const char *cpu_model); int cpu_x86_exec(CPUX86State *s); void cpu_x86_close(CPUX86State *s); -void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, - ...)); +void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + const char *optarg); int cpu_get_pic_interrupt(CPUX86State *s); /* MSDOS compatibility mode FPU exception support */ void cpu_set_ferr(CPUX86State *s); @@ -875,7 +875,7 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define cpu_exec cpu_x86_exec #define cpu_gen_code cpu_x86_gen_code #define cpu_signal_handler cpu_x86_signal_handler -#define cpu_list x86_cpu_list +#define cpu_list_id x86_cpu_list #define CPU_SAVE_VERSION 11 diff --git a/target-i386/helper.c b/target-i386/helper.c index 730e396..34f4936 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -42,7 +42,7 @@ static const char *feature_name[] = { static const char *ext_feature_name[] = { "pni" /* Intel,AMD sse3 */, NULL, NULL, "monitor", "ds_cpl", "vmx", NULL /* Linux smx */, "est", "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL, - NULL, NULL, "dca", NULL, NULL, NULL, NULL, "popcnt", + NULL, NULL, "dca", "sse4.1", "sse4.2", "x2apic", NULL, "popcnt", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "hypervisor", }; static const char *ext2_feature_name[] = { @@ -58,6 +58,18 @@ static const char *ext3_feature_name[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; +/* collects per-function cpuid data + */ +typedef struct model_features_t { + uint32_t *guest_feat; + uint32_t *host_feat; + uint32_t check_feat; + const char **flag_names; + uint32_t cpuid; + } model_features_t; + +int check_cpuid = 0; + static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, uint32_t *ext_features, uint32_t *ext2_features, @@ -111,10 +123,25 @@ typedef struct x86_def_t { CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ CPUID_PSE36 | CPUID_FXSR) #define PENTIUM3_FEATURES (PENTIUM2_FEATURES | CPUID_SSE) -#define PPRO_FEATURES (CPUID_FP87 | CPUID_DE | CPUID_PSE | CPUID_TSC | \ - CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \ - CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \ - CPUID_PAE | CPUID_SEP | CPUID_APIC) + +#define PPRO_FEATURES (\ + 0|CPUID_SSE2|CPUID_SSE|CPUID_FXSR| /* 7 */ \ + CPUID_MMX|0 << 22|0 << 21|0 << 20| /* 8 */ \ + 0 << 19|0 << 18|0 << 17|CPUID_PAT| /* 1 */ \ + CPUID_CMOV|0 << 14|CPUID_PGE|0 << 12| /* a */ \ + CPUID_SEP|0 << 10|CPUID_APIC|CPUID_CX8| /* b */ \ + CPUID_MCE|CPUID_PAE|CPUID_MSR|CPUID_TSC| /* f */ \ + CPUID_PSE|CPUID_DE|0 << 1|CPUID_FP87) /* d */ + +#define CPUID_EXT2_MASK (\ + 0 << 27|0 << 26|0 << 25|CPUID_FXSR| /* 1 */ \ + CPUID_MMX|0 << 22|0 << 21|0 << 20| /* 8 */ \ + 0 << 19|0 << 18|CPUID_PSE36|CPUID_PAT| /* 3 */ \ + CPUID_CMOV|CPUID_MCA|CPUID_PGE|CPUID_MTRR| /* f */ \ + 0 << 11|0 << 10|CPUID_APIC|CPUID_CX8| /* 3 */ \ + CPUID_MCE|CPUID_PAE|CPUID_MSR|CPUID_TSC| /* f */ \ + CPUID_PSE|CPUID_DE|CPUID_VME|CPUID_FP87) /* f */ + static x86_def_t x86_defs[] = { #ifdef TARGET_X86_64 { @@ -127,12 +154,10 @@ static x86_def_t x86_defs[] = { .model = 2, .stepping = 3, .features = PPRO_FEATURES | - /* these features are needed for Win64 and aren't fully implemented */ - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | - /* this feature is needed for Solaris and isn't fully implemented */ - CPUID_PSE36, + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | /* note 1 */ + CPUID_PSE36, /* note 2 */ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, @@ -155,7 +180,7 @@ static x86_def_t x86_defs[] = { .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_MMXEXT | CPUID_EXT2_FFXSR, @@ -169,6 +194,126 @@ static x86_def_t x86_defs[] = { .model_id = "AMD Phenom(tm) 9550 Quad-Core Processor" }, { + .name = "Conroe", + .level = 2, + .vendor1 = CPUID_VENDOR_INTEL_1, + .vendor2 = CPUID_VENDOR_INTEL_2, + .vendor3 = CPUID_VENDOR_INTEL_3, + .family = 6, /* P6 */ + .model = 2, + .stepping = 3, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | /* note 1 */ + CPUID_PSE36, /* note 2 */ + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_SSSE3, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + .ext3_features = CPUID_EXT3_LAHF_LM, + .xlevel = 0x8000000A, + .model_id = "Intel Celeron_4x0 (Conroe/Merom Class Core 2)", + }, + { + .name = "Penryn", + .level = 2, + .vendor1 = CPUID_VENDOR_INTEL_1, + .vendor2 = CPUID_VENDOR_INTEL_2, + .vendor3 = CPUID_VENDOR_INTEL_3, + .family = 6, /* P6 */ + .model = 2, + .stepping = 3, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | /* note 1 */ + CPUID_PSE36, /* note 2 */ + .ext_features = CPUID_EXT_SSE3 | + CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE41, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + .ext3_features = CPUID_EXT3_LAHF_LM, + .xlevel = 0x8000000A, + .model_id = "Intel Core 2 Duo P9xxx (Penryn Class Core 2)", + }, + { + .name = "Nehalem", + .level = 2, + .vendor1 = CPUID_VENDOR_INTEL_1, + .vendor2 = CPUID_VENDOR_INTEL_2, + .vendor3 = CPUID_VENDOR_INTEL_3, + .family = 6, /* P6 */ + .model = 2, + .stepping = 3, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | /* note 1 */ + CPUID_PSE36, /* note 2 */ + .ext_features = CPUID_EXT_SSE3 | + CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE41 | + CPUID_EXT_SSE42 | CPUID_EXT_POPCNT, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + .ext3_features = CPUID_EXT3_LAHF_LM, + .xlevel = 0x8000000A, + .model_id = "Intel Core i7 9xx (Nehalem Class Core i7)", + }, + { + .name = "Opteron_G1", + .level = 5, + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, + .family = 15, + .model = 6, + .stepping = 1, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | /* note 1 */ + CPUID_PSE36, /* note 2 */ + .ext_features = CPUID_EXT_SSE3, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + .xlevel = 0x80000008, + .model_id = "AMD Opteron 240 (Gen 1 Class Opteron)", + }, + { + .name = "Opteron_G2", + .level = 5, + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, + .family = 15, + .model = 6, + .stepping = 1, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | /* note 1 */ + CPUID_PSE36, /* note 2 */ + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | + CPUID_EXT2_RDTSCP, + .ext3_features = CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + .xlevel = 0x80000008, + .model_id = "AMD Opteron 22xx (Gen 2 Class Opteron)", + }, + { + .name = "Opteron_G3", + .level = 5, + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, + .family = 15, + .model = 6, + .stepping = 1, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | /* note 1 */ + CPUID_PSE36, /* note 2 */ + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | + CPUID_EXT_POPCNT | CPUID_EXT_CX16, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | + CPUID_EXT2_RDTSCP, + .ext3_features = CPUID_EXT3_SVM | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | + CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_LAHF_LM, + .xlevel = 0x80000008, + .model_id = "AMD Opteron 23xx (Gen 3 Class Opteron)", + }, + { .name = "core2duo", .level = 10, .family = 6, @@ -205,7 +350,7 @@ static x86_def_t x86_defs[] = { /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16, /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, /* Missing: CPUID_EXT3_LAHF_LM, CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, CPUID_EXT3_CR8LEG, CPUID_EXT3_ABM, CPUID_EXT3_SSE4A, @@ -292,7 +437,7 @@ static x86_def_t x86_defs[] = { .model = 2, .stepping = 3, .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA, - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, .xlevel = 0x80000008, /* XXX: put another string ? */ .model_id = "QEMU Virtual CPU version " QEMU_VERSION, @@ -313,12 +458,16 @@ static x86_def_t x86_defs[] = { CPUID_EXT_SSE3 /* PNI */ | CPUID_EXT_SSSE3, /* Missing: CPUID_EXT_DSCPL | CPUID_EXT_EST | * CPUID_EXT_TM2 | CPUID_EXT_XTPR */ - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | CPUID_EXT2_NX, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_MASK) | CPUID_EXT2_NX, /* Missing: .ext3_features = CPUID_EXT3_LAHF_LM */ .xlevel = 0x8000000A, .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz", - }, + } }; +/* notes for preceeding cpu models: + * 1: these features are needed for Win64 and aren't fully implemented + * 2: this feature is needed for Solaris and isn't fully implemented + */ static void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); @@ -368,6 +517,51 @@ static int cpu_x86_fill_host(x86_def_t *x86_cpu_def) return 0; } +static int unavailable_host_feature(struct model_features_t *f, uint32_t mask) +{ + int i; + + for (i = 0; i < 32; ++i) + if (1 << i & mask) { + fprintf(stderr, "warning: host cpuid %04x_%04x lacks requested" + " flag '%s' [0x%08x]\n", + f->cpuid >> 16, f->cpuid & 0xffff, + f->flag_names[i] ? f->flag_names[i] : "[reserved]", mask); + break; + } + return 0; +} + +/* best effort attempt to inform user requested cpu flags aren't making + * their way to the guest. Note: ft[].check_feat ideally should be + * specified via a guest_def field to suppress report of extraneous flags. + */ +static int check_features_against_host(x86_def_t *guest_def) +{ + x86_def_t host_def; + uint32_t mask; + int rv, i; + struct model_features_t ft[] = { + {&guest_def->features, &host_def.features, + ~0, feature_name, 0x00000000}, + {&guest_def->ext_features, &host_def.ext_features, + ~CPUID_EXT_HYPERVISOR, ext_feature_name, 0x00000001}, + {&guest_def->ext2_features, &host_def.ext2_features, + ~PPRO_FEATURES, ext2_feature_name, 0x80000000}, + {&guest_def->ext3_features, &host_def.ext3_features, + ~CPUID_EXT3_SVM, ext3_feature_name, 0x80000001}}; + + cpu_x86_fill_host(&host_def); + for (rv = 0, i = 0; i < sizeof (ft) / sizeof (ft[0]); ++i) + for (mask = 1; mask; mask <<= 1) + if (ft[i].check_feat & mask && *ft[i].guest_feat & mask && + !(*ft[i].host_feat & mask)) { + unavailable_host_feature(&ft[i], mask); + rv = 1; + } + return rv; +} + static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) { unsigned int i; @@ -471,6 +665,8 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) fprintf(stderr, "unrecognized feature %s\n", featurestr); goto error; } + } else if (!strcmp(featurestr, "check")) { + check_cpuid = 1; } else { fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr); goto error; @@ -485,6 +681,9 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) x86_cpu_def->ext_features &= ~minus_ext_features; x86_cpu_def->ext2_features &= ~minus_ext2_features; x86_cpu_def->ext3_features &= ~minus_ext3_features; + if (check_cpuid) { + check_features_against_host(x86_cpu_def); + } free(s); return 0; @@ -493,12 +692,19 @@ error: return -1; } -void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + const char *optarg) { unsigned int i; + unsigned char id = !strcmp("??", optarg); for (i = 0; i < ARRAY_SIZE(x86_defs); i++) - (*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name); + if (id) { + (*cpu_fprintf)(f, "x86 %16s %-48s\n", x86_defs[i].name, + x86_defs[i].model_id); + } else { + (*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name); + } } static int cpu_x86_register (CPUX86State *env, const char *cpu_model) diff --git a/vl.c b/vl.c index e606903..b1d8490 100644 --- a/vl.c +++ b/vl.c @@ -4982,8 +4982,12 @@ int main(int argc, char **argv, char **envp) /* hw initialization will check this */ if (*optarg == '?') { /* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) +#if defined(cpu_list_id) + cpu_list_id(stdout, &fprintf, optarg); +#elif defined(cpu_list) /* revert to previous func definition */ cpu_list(stdout, &fprintf); +#else +#error cpu_list_id() is undefined for this architecture. #endif exit(0); } else { -- john.coo...@redhat.com -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html