From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NfIjp-0006ih-IW for qemu-devel@nongnu.org; Wed, 10 Feb 2010 15:00:41 -0500 Received: from [199.232.76.173] (port=57457 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NfIjo-0006hW-II for qemu-devel@nongnu.org; Wed, 10 Feb 2010 15:00:40 -0500 Received: from Debian-exim by monty-python.gnu.org with spam-scanned (Exim 4.60) (envelope-from ) id 1NfIjj-0005fk-JZ for qemu-devel@nongnu.org; Wed, 10 Feb 2010 15:00:40 -0500 Received: from e2.ny.us.ibm.com ([32.97.182.142]:35037) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1NfIji-0005da-0N for qemu-devel@nongnu.org; Wed, 10 Feb 2010 15:00:34 -0500 Received: from d01relay03.pok.ibm.com (d01relay03.pok.ibm.com [9.56.227.235]) by e2.ny.us.ibm.com (8.14.3/8.13.1) with ESMTP id o1AJoNR6029265 for ; Wed, 10 Feb 2010 14:50:23 -0500 Received: from d01av03.pok.ibm.com (d01av03.pok.ibm.com [9.56.224.217]) by d01relay03.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id o1AK0BQw145666 for ; Wed, 10 Feb 2010 15:00:15 -0500 Received: from d01av03.pok.ibm.com (loopback [127.0.0.1]) by d01av03.pok.ibm.com (8.14.3/8.13.1/NCO v10.0 AVout) with ESMTP id o1AK0B66015463 for ; Wed, 10 Feb 2010 18:00:11 -0200 Message-ID: <4B731049.4050707@linux.vnet.ibm.com> Date: Wed, 10 Feb 2010 14:00:09 -0600 From: Anthony Liguori MIME-Version: 1.0 Subject: Re: [Qemu-devel] [PATCH] Add cpu model configuration support.. (resend) References: <4B672535.5050303@redhat.com> In-Reply-To: <4B672535.5050303@redhat.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: john cooper Cc: "Przywara, Andre" , qemu-devel@nongnu.org, KVM list On 02/01/2010 01:02 PM, john cooper wrote: > [target-x86_64.conf was unintentionally omitted from the earlier patch] > > This is a reimplementation of prior versions which adds > the ability to define cpu models for contemporary processors. > The added models are likewise selected via -cpu, > and are intended to displace the existing convention > of "-cpu qemu64" augmented with a series of feature flags > This breaks the arm-softmmu build. Regards, Anthony Liguori > A 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. > > This version of the patch replaces the prior hard wired > definitions with a configuration file approach for new > models. Existing models are thus far left as-is but may > easily be transitioned to (or may be overridden by) the > configuration file representation. > > Proposed new model definitions are provided here for current > AMD and Intel processors. Each model consists of a name > used to select it on the command line (-cpu), and a > model_id which corresponds to a least common denominator > commercial instance of the processor class. > > A table of names/model_ids may be queried via "-cpu ?model": > > : > x86 Opteron_G3 AMD Opteron 23xx (Gen 3 Class Opteron) > x86 Opteron_G2 AMD Opteron 22xx (Gen 2 Class Opteron) > x86 Opteron_G1 AMD Opteron 240 (Gen 1 Class Opteron) > x86 Nehalem Intel Core i7 9xx (Nehalem Class Core i7) > x86 Penryn Intel Core 2 Duo P9xxx (Penryn Class Core 2) > x86 Conroe Intel Celeron_4x0 (Conroe/Merom Class Core 2) > : > > Also added is "-cpu ?dump" which exhaustively outputs all config > data for all defined models, and "-cpu ?cpuid" which enumerates > all qemu recognized CPUID feature flags. > > The pseudo cpuid flag 'check' when added to the feature flag list > will warn 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|sse4_2' [0x00100000] > warning: host cpuid 0000_0001 lacks requested flag 'popcnt' [0x00800000] > > A similar 'enforce' pseudo flag exists which in addition > to the above causes qemu to error exit if requested flags are > unavailable. > > Configuration data for a cpu model resides in the target config > file which by default will be installed as: > > /usr/local/etc/qemu/target-.conf > > The format of this file should be self explanatory given the > definitions for the above six models and essentially mimics > the structure of the static x86_def_t x86_defs. > > Encoding of cpuid flags names now allows aliases for both the > configuration file and the command line which reconciles some > Intel/AMD/Linux/Qemu naming differences. > > This patch was tested relative to qemu.git. > > Signed-off-by: john cooper > --- > > diff --git a/Makefile b/Makefile > index 3848627..b7fa6ef 100644 > --- a/Makefile > +++ b/Makefile > @@ -191,7 +191,11 @@ ifdef CONFIG_POSIX > $(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" > endif > > -install: all $(if $(BUILD_DOCS),install-doc) > +install-sysconfig: > + $(INSTALL_DIR) "$(sysconfdir)/qemu" > + $(INSTALL_DATA) sysconfigs/target/target-x86_64.conf "$(sysconfdir)/qemu" > + > +install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig > $(INSTALL_DIR) "$(DESTDIR)$(bindir)" > ifneq ($(TOOLS),) > $(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)" > diff --git a/qemu-config.c b/qemu-config.c > index c3203c8..246fae6 100644 > --- a/qemu-config.c > +++ b/qemu-config.c > @@ -242,6 +242,54 @@ QemuOptsList qemu_mon_opts = { > }, > }; > > +QemuOptsList qemu_cpudef_opts = { > + .name = "cpudef", > + .head = QTAILQ_HEAD_INITIALIZER(qemu_cpudef_opts.head), > + .desc = { > + { > + .name = "name", > + .type = QEMU_OPT_STRING, > + },{ > + .name = "level", > + .type = QEMU_OPT_NUMBER, > + },{ > + .name = "vendor", > + .type = QEMU_OPT_STRING, > + },{ > + .name = "family", > + .type = QEMU_OPT_NUMBER, > + },{ > + .name = "model", > + .type = QEMU_OPT_NUMBER, > + },{ > + .name = "stepping", > + .type = QEMU_OPT_NUMBER, > + },{ > + .name = "feature_edx", /* cpuid 0000_0001.edx */ > + .type = QEMU_OPT_STRING, > + },{ > + .name = "feature_ecx", /* cpuid 0000_0001.ecx */ > + .type = QEMU_OPT_STRING, > + },{ > + .name = "extfeature_edx", /* cpuid 8000_0001.edx */ > + .type = QEMU_OPT_STRING, > + },{ > + .name = "extfeature_ecx", /* cpuid 8000_0001.ecx */ > + .type = QEMU_OPT_STRING, > + },{ > + .name = "xlevel", > + .type = QEMU_OPT_NUMBER, > + },{ > + .name = "model_id", > + .type = QEMU_OPT_STRING, > + },{ > + .name = "vendor_override", > + .type = QEMU_OPT_NUMBER, > + }, > + { /* end of list */ } > + }, > +}; > + > static QemuOptsList *lists[] = { > &qemu_drive_opts, > &qemu_chardev_opts, > @@ -251,6 +299,7 @@ static QemuOptsList *lists[] = { > &qemu_rtc_opts, > &qemu_global_opts, > &qemu_mon_opts, > +&qemu_cpudef_opts, > NULL, > }; > > diff --git a/qemu-config.h b/qemu-config.h > index dd89ae4..b335c42 100644 > --- a/qemu-config.h > +++ b/qemu-config.h > @@ -9,6 +9,7 @@ extern QemuOptsList qemu_net_opts; > extern QemuOptsList qemu_rtc_opts; > extern QemuOptsList qemu_global_opts; > extern QemuOptsList qemu_mon_opts; > +extern QemuOptsList qemu_cpudef_opts; > > int qemu_set_option(const char *str); > int qemu_global_option(const char *str); > diff --git a/sysconfigs/target/target-x86_64.conf b/sysconfigs/target/target-x86_64.conf > new file mode 100644 > index 0000000..43ad282 > --- /dev/null > +++ b/sysconfigs/target/target-x86_64.conf > @@ -0,0 +1,86 @@ > +# x86 CPU MODELS > + > +[cpudef] > + name = "Conroe" > + level = "2" > + vendor = "GenuineIntel" > + family = "6" > + model = "2" > + stepping = "3" > + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" > + feature_ecx = "sse3 ssse3" > + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" > + extfeature_ecx = "lahf_lm" > + xlevel = "0x8000000A" > + model_id = "Intel Celeron_4x0 (Conroe/Merom Class Core 2)" > + > +[cpudef] > + name = "Penryn" > + level = "2" > + vendor = "GenuineIntel" > + family = "6" > + model = "2" > + stepping = "3" > + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" > + feature_ecx = "sse3 cx16 ssse3 sse4.1" > + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" > + extfeature_ecx = "lahf_lm" > + xlevel = "0x8000000A" > + model_id = "Intel Core 2 Duo P9xxx (Penryn Class Core 2)" > + > +[cpudef] > + name = "Nehalem" > + level = "2" > + vendor = "GenuineIntel" > + family = "6" > + model = "2" > + stepping = "3" > + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" > + feature_ecx = "sse3 cx16 ssse3 sse4.1 sse4.2 popcnt" > + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" > + extfeature_ecx = "lahf_lm" > + xlevel = "0x8000000A" > + model_id = "Intel Core i7 9xx (Nehalem Class Core i7)" > + > +[cpudef] > + name = "Opteron_G1" > + level = "5" > + vendor = "AuthenticAMD" > + family = "15" > + model = "6" > + stepping = "1" > + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" > + feature_ecx = "sse3" > + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" > +# extfeature_ecx = "" > + xlevel = "0x80000008" > + model_id = "AMD Opteron 240 (Gen 1 Class Opteron)" > + > +[cpudef] > + name = "Opteron_G2" > + level = "5" > + vendor = "AuthenticAMD" > + family = "15" > + model = "6" > + stepping = "1" > + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" > + feature_ecx = "sse3 cx16" > + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp" > + extfeature_ecx = "svm lahf_lm" > + xlevel = "0x80000008" > + model_id = "AMD Opteron 22xx (Gen 2 Class Opteron)" > + > +[cpudef] > + name = "Opteron_G3" > + level = "5" > + vendor = "AuthenticAMD" > + family = "15" > + model = "6" > + stepping = "1" > + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" > + feature_ecx = "sse3 cx16 monitor popcnt" > + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp" > + extfeature_ecx = "svm sse4a abm misalignsse lahf_lm" > + xlevel = "0x80000008" > + model_id = "AMD Opteron 23xx (Gen 3 Class Opteron)" > + > diff --git a/target-i386/cpu.h b/target-i386/cpu.h > index 216b00e..c1a5256 100644 > --- a/target-i386/cpu.h > +++ b/target-i386/cpu.h > @@ -723,8 +723,10 @@ 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); > +void cpudef_setup(void); > + > int cpu_get_pic_interrupt(CPUX86State *s); > /* MSDOS compatibility mode FPU exception support */ > void cpu_set_ferr(CPUX86State *s); > @@ -876,7 +878,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 70762bb..37dd2c6 100644 > --- a/target-i386/helper.c > +++ b/target-i386/helper.c > @@ -29,33 +29,52 @@ > #include "kvm.h" > > //#define DEBUG_MMU > +#include "qemu-option.h" > +#include "qemu-config.h" > > /* feature flags taken from "Intel Processor Identification and the CPUID > - * Instruction" and AMD's "CPUID Specification". In cases of disagreement > - * about feature names, the Linux name is used. */ > + * Instruction" and AMD's "CPUID Specification". In cases of disagreement > + * between feature naming conventions, aliases may be added. > + */ > static const char *feature_name[] = { > - "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", > - "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov", > - "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, NULL, "ds" /* Intel dts */, "acpi", "mmx", > - "fxsr", "sse", "sse2", "ss", "ht" /* Intel htt */, "tm", "ia64", "pbe", > + "fpu", "vme", "de", "pse", > + "tsc", "msr", "pae", "mce", > + "cx8", "apic", NULL, "sep", > + "mtrr", "pge", "mca", "cmov", > + "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, > + NULL, "ds" /* Intel dts */, "acpi", "mmx", > + "fxsr", "sse", "sse2", "ss", > + "ht" /* Intel htt */, "tm", "ia64", "pbe", > }; > 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, NULL, NULL, NULL, NULL, NULL, "hypervisor", > + "pni|sse3" /* Intel,AMD sse3 */, NULL, NULL, "monitor", > + "ds_cpl", "vmx", NULL /* Linux smx */, "est", > + "tm2", "ssse3", "cid", NULL, > + NULL, "cx16", "xtpr", NULL, > + NULL, NULL, "dca", "sse4.1|sse4_1", > + "sse4.2|sse4_2", "x2apic", NULL, "popcnt", > + NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, "hypervisor", > }; > static const char *ext2_feature_name[] = { > - "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", > - "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", "mtrr", "pge", "mca", "cmov", > - "pat", "pse36", NULL, NULL /* Linux mp */, "nx" /* Intel xd */, NULL, "mmxext", "mmx", > - "fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, "rdtscp", NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow", > + "fpu", "vme", "de", "pse", > + "tsc", "msr", "pae", "mce", > + "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", > + "mtrr", "pge", "mca", "cmov", > + "pat", "pse36", NULL, NULL /* Linux mp */, > + "nx" /* Intel xd */, NULL, "mmxext", "mmx", > + "fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, "rdtscp", > + NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow", > }; > static const char *ext3_feature_name[] = { > - "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse", > - "3dnowprefetch", "osvw", NULL /* Linux ibs */, NULL, "skinit", "wdt", NULL, NULL, > - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, > + "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, > + "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse", > + "3dnowprefetch", "osvw", NULL /* Linux ibs */, NULL, > + "skinit", "wdt", NULL, NULL, > + NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, > + NULL, NULL, NULL, NULL, > }; > > static const char *kvm_feature_name[] = { > @@ -65,47 +84,99 @@ static const char *kvm_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; > +int enforce_cpuid = 0; > + > +static void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, > + uint32_t *ebx, uint32_t *ecx, uint32_t *edx); > + > +#define iswhite(c) ((c)&& ((c)<= ' ' || '~'< (c))) > + > +/* general substring compare of *[s1..e1) and *[s2..e2). sx is start of > + * a substring. ex if !NULL points to the first char after a substring, > + * otherwise the string is assumed to sized by a terminating nul. > + * Return lexical ordering of *s1:*s2. > + */ > +static int sstrcmp(const char *s1, const char *e1, const char *s2, > + const char *e2) > +{ > + for (;;) { > + if (!*s1 || !*s2 || *s1 != *s2) > + return (*s1 - *s2); > + ++s1, ++s2; > + if (s1 == e1&& s2 == e2) > + return (0); > + else if (s1 == e1) > + return (*s2); > + else if (s2 == e2) > + return (*s1); > + } > +} > + > +/* compare *[s..e) to *altstr. *altstr may be a simple string or multiple > + * '|' delimited (possibly empty) strings in which case search for a match > + * within the alternatives proceeds left to right. Return 0 for success, > + * non-zero otherwise. > + */ > +static int altcmp(const char *s, const char *e, const char *altstr) > +{ > + const char *p, *q; > + > + for (q = p = altstr; ; ) { > + while (*p&& *p != '|') > + ++p; > + if ((q == p&& !*s) || (q != p&& !sstrcmp(s, e, q, p))) > + return (0); > + if (!*p) > + return (1); > + else > + q = ++p; > + } > +} > + > +/* search featureset for flag *[s..e), if found set corresponding bit in > + * *pval and return success, otherwise return zero > + */ > +static int lookup_feature(uint32_t *pval, const char *s, const char *e, > + const char **featureset) > +{ > + uint32_t mask; > + const char **ppc; > + > + for (mask = 1, ppc = featureset; mask; mask<<= 1, ++ppc) > + if (*ppc&& !altcmp(s, e, *ppc)) { > + *pval |= mask; > + break; > + } > + return (mask ? 1 : 0); > +} > + > static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, > uint32_t *ext_features, > uint32_t *ext2_features, > uint32_t *ext3_features, > uint32_t *kvm_features) > { > - int i; > - int found = 0; > - > - for ( i = 0 ; i< 32 ; i++ ) > - if (feature_name[i]&& !strcmp (flagname, feature_name[i])) { > - *features |= 1<< i; > - found = 1; > - } > - for ( i = 0 ; i< 32 ; i++ ) > - if (ext_feature_name[i]&& !strcmp (flagname, ext_feature_name[i])) { > - *ext_features |= 1<< i; > - found = 1; > - } > - for ( i = 0 ; i< 32 ; i++ ) > - if (ext2_feature_name[i]&& !strcmp (flagname, ext2_feature_name[i])) { > - *ext2_features |= 1<< i; > - found = 1; > - } > - for ( i = 0 ; i< 32 ; i++ ) > - if (ext3_feature_name[i]&& !strcmp (flagname, ext3_feature_name[i])) { > - *ext3_features |= 1<< i; > - found = 1; > - } > - for ( i = 0 ; i< 32 ; i++ ) > - if (kvm_feature_name[i]&& !strcmp (flagname, kvm_feature_name[i])) { > - *kvm_features |= 1<< i; > - found = 1; > - } > - > - if (!found) { > - fprintf(stderr, "CPU feature %s not found\n", flagname); > - } > + if (!lookup_feature(features, flagname, NULL, feature_name)&& > + !lookup_feature(ext_features, flagname, NULL, ext_feature_name)&& > + !lookup_feature(ext2_features, flagname, NULL, ext2_feature_name)&& > + !lookup_feature(ext3_features, flagname, NULL, ext3_feature_name)&& > + !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name)) > + fprintf(stderr, "CPU feature %s not found\n", flagname); > } > > typedef struct x86_def_t { > + struct x86_def_t *next; > const char *name; > uint32_t level; > uint32_t vendor1, vendor2, vendor3; > @@ -116,6 +187,7 @@ typedef struct x86_def_t { > uint32_t xlevel; > char model_id[48]; > int vendor_override; > + uint32_t flags; > } x86_def_t; > > #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) > @@ -129,7 +201,14 @@ typedef struct x86_def_t { > 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) > -static x86_def_t x86_defs[] = { > + > +/* maintains list of cpu model definitions > + */ > +static x86_def_t *x86_defs = {NULL}; > + > +/* built-in cpu model definitions (deprecated) > + */ > +static x86_def_t builtin_x86_defs[] = { > #ifdef TARGET_X86_64 > { > .name = "qemu64", > @@ -334,9 +413,6 @@ static x86_def_t x86_defs[] = { > }, > }; > > -static void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, > - uint32_t *ebx, uint32_t *ecx, uint32_t *edx); > - > static int cpu_x86_fill_model_id(char *str) > { > uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; > @@ -382,6 +458,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; > @@ -393,13 +514,9 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) > uint32_t minus_features = 0, minus_ext_features = 0, minus_ext2_features = 0, minus_ext3_features = 0, minus_kvm_features = 0; > uint32_t numvalue; > > - def = NULL; > - for (i = 0; i< ARRAY_SIZE(x86_defs); i++) { > - if (strcmp(name, x86_defs[i].name) == 0) { > - def =&x86_defs[i]; > + for (def = x86_defs; def; def = def->next) > + if (!strcmp(name, def->name)) > break; > - } > - } > if (kvm_enabled()&& strcmp(name, "host") == 0) { > cpu_x86_fill_host(x86_cpu_def); > } else if (!def) { > @@ -488,6 +605,10 @@ 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 if (!strcmp(featurestr, "enforce")) { > + check_cpuid = enforce_cpuid = 1; > } else { > fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr); > goto error; > @@ -504,6 +625,10 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) > x86_cpu_def->ext2_features&= ~minus_ext2_features; > x86_cpu_def->ext3_features&= ~minus_ext3_features; > x86_cpu_def->kvm_features&= ~minus_kvm_features; > + if (check_cpuid) { > + if (check_features_against_host(x86_cpu_def)&& enforce_cpuid) > + goto error; > + } > free(s); > return 0; > > @@ -512,12 +637,97 @@ error: > return -1; > } > > -void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) > +/* generate a composite string into buf of all cpuid names in featureset > + * selected by fbits. indicate truncation at bufsize in the event of overflow. > + * if flags, suppress names undefined in featureset. > + */ > +static void listflags(char *buf, int bufsize, uint32_t fbits, > + const char **featureset, uint32_t flags) > { > - unsigned int i; > + const char **p =&featureset[31]; > + char *q, *b, bit; > + int nc; > + > + b = 4<= bufsize ? buf + (bufsize -= 3) - 1 : NULL; > + *buf = '\0'; > + for (q = buf, bit = 31; fbits&& bufsize; --p, fbits&= ~(1<< bit), --bit) > + if (fbits& 1<< bit&& (*p || !flags)) { > + if (*p) > + nc = snprintf(q, bufsize, "%s%s", q == buf ? "" : " ", *p); > + else > + nc = snprintf(q, bufsize, "%s[%d]", q == buf ? "" : " ", bit); > + if (bufsize<= nc) { > + if (b) > + sprintf(b, "..."); > + return; > + } > + q += nc; > + bufsize -= nc; > + } > +} > > - for (i = 0; i< ARRAY_SIZE(x86_defs); i++) > - (*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name); > +/* generate CPU information: > + * -? list model names > + * -?model list model names/IDs > + * -?dump output all model (x86_def_t) data > + * -?cpuid list all recognized cpuid flag names > + */ > +void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...), > + const char *optarg) > +{ > + unsigned char model = !strcmp("?model", optarg); > + unsigned char dump = !strcmp("?dump", optarg); > + unsigned char cpuid = !strcmp("?cpuid", optarg); > + x86_def_t *def; > + char buf[256]; > + > + if (cpuid) { > + (*cpu_fprintf)(f, "Recognized CPUID flags:\n"); > + listflags(buf, sizeof (buf), (uint32_t)~0, feature_name, 1); > + (*cpu_fprintf)(f, " f_edx: %s\n", buf); > + listflags(buf, sizeof (buf), (uint32_t)~0, ext_feature_name, 1); > + (*cpu_fprintf)(f, " f_ecx: %s\n", buf); > + listflags(buf, sizeof (buf), (uint32_t)~0, ext2_feature_name, 1); > + (*cpu_fprintf)(f, " extf_edx: %s\n", buf); > + listflags(buf, sizeof (buf), (uint32_t)~0, ext3_feature_name, 1); > + (*cpu_fprintf)(f, " extf_ecx: %s\n", buf); > + return; > + } > + for (def = x86_defs; def; def = def->next) { > + snprintf(buf, sizeof (buf), def->flags ? "[%s]": "%s", def->name); > + if (model || dump) { > + (*cpu_fprintf)(f, "x86 %16s %-48s\n", buf, def->model_id); > + } else { > + (*cpu_fprintf)(f, "x86 %16s\n", buf); > + } > + if (dump) { > + memcpy(buf,&def->vendor1, sizeof (def->vendor1)); > + memcpy(buf + 4,&def->vendor2, sizeof (def->vendor2)); > + memcpy(buf + 8,&def->vendor3, sizeof (def->vendor3)); > + buf[12] = '\0'; > + (*cpu_fprintf)(f, > + " family %d model %d stepping %d level %d xlevel 0x%x" > + " vendor \"%s\"\n", > + def->family, def->model, def->stepping, def->level, > + def->xlevel, buf); > + listflags(buf, sizeof (buf), def->features, feature_name, 0); > + (*cpu_fprintf)(f, " feature_edx %08x (%s)\n", def->features, > + buf); > + listflags(buf, sizeof (buf), def->ext_features, ext_feature_name, > + 0); > + (*cpu_fprintf)(f, " feature_ecx %08x (%s)\n", def->ext_features, > + buf); > + listflags(buf, sizeof (buf), def->ext2_features, ext2_feature_name, > + 0); > + (*cpu_fprintf)(f, " extfeature_edx %08x (%s)\n", > + def->ext2_features, buf); > + listflags(buf, sizeof (buf), def->ext3_features, ext3_feature_name, > + 0); > + (*cpu_fprintf)(f, " extfeature_ecx %08x (%s)\n", > + def->ext3_features, buf); > + (*cpu_fprintf)(f, "\n"); > + } > + } > } > > static int cpu_x86_register (CPUX86State *env, const char *cpu_model) > @@ -566,6 +776,124 @@ static int cpu_x86_register (CPUX86State *env, const char *cpu_model) > return 0; > } > > +/* copy vendor id string to 32 bit register, nul pad as needed > + */ > +static void cpyid(const char *s, uint32_t *id) > +{ > + char *d = (char *)id; > + char i; > + > + for (i = sizeof (*id); i--; ) > + *d++ = *s ? *s++ : '\0'; > +} > + > +/* interpret radix and convert from string to arbitrary scalar, > + * otherwise flag failure > + */ > +#define setscalar(pval, str, perr) \ > +{ \ > + char *pend; \ > + unsigned long ul; \ > + \ > + ul = strtoul(str,&pend, 0); \ > + *str&& !*pend ? (*pval = ul) : (*perr = 1); \ > +} > + > +/* map cpuid options to feature bits, otherwise return failure > + * (option tags in *str are delimited by whitespace) > + */ > +static void setfeatures(uint32_t *pval, const char *str, > + const char **featureset, int *perr) > +{ > + const char *p, *q; > + > + for (q = p = str; *p || *q; q = p) { > + while (iswhite(*p)) > + q = ++p; > + while (*p&& !iswhite(*p)) > + ++p; > + if (!*q&& !*p) > + return; > + if (!lookup_feature(pval, q, p, featureset)) { > + fprintf(stderr, "error: feature \"%.*s\" not available in set\n", > + (int)(p - q), q); > + *perr = 1; > + return; > + } > + } > +} > + > +/* map config file options to x86_def_t form > + */ > +static int cpudef_setfield(const char *name, const char *str, void *opaque) > +{ > + x86_def_t *def = opaque; > + int err = 0; > + > + if (!strcmp(name, "name")) { > + def->name = strdup(str); > + } else if (!strcmp(name, "model_id")) { > + strncpy(def->model_id, str, sizeof (def->model_id)); > + } else if (!strcmp(name, "level")) { > + setscalar(&def->level, str,&err) > + } else if (!strcmp(name, "vendor")) { > + cpyid(&str[0],&def->vendor1); > + cpyid(&str[4],&def->vendor2); > + cpyid(&str[8],&def->vendor3); > + } else if (!strcmp(name, "family")) { > + setscalar(&def->family, str,&err) > + } else if (!strcmp(name, "model")) { > + setscalar(&def->model, str,&err) > + } else if (!strcmp(name, "stepping")) { > + setscalar(&def->stepping, str,&err) > + } else if (!strcmp(name, "feature_edx")) { > + setfeatures(&def->features, str, feature_name,&err); > + } else if (!strcmp(name, "feature_ecx")) { > + setfeatures(&def->ext_features, str, ext_feature_name,&err); > + } else if (!strcmp(name, "extfeature_edx")) { > + setfeatures(&def->ext2_features, str, ext2_feature_name,&err); > + } else if (!strcmp(name, "extfeature_ecx")) { > + setfeatures(&def->ext3_features, str, ext3_feature_name,&err); > + } else if (!strcmp(name, "xlevel")) { > + setscalar(&def->xlevel, str,&err) > + } else { > + fprintf(stderr, "error: unknown option [%s = %s]\n", name, str); > + return (1); > + } > + if (err) { > + fprintf(stderr, "error: bad option value [%s = %s]\n", name, str); > + return (1); > + } > + return (0); > +} > + > +/* register config file entry as x86_def_t > + */ > +static int cpudef_register(QemuOpts *opts, void *opaque) > +{ > + x86_def_t *def = qemu_mallocz(sizeof (x86_def_t)); > + > + qemu_opt_foreach(opts, cpudef_setfield, def, 1); > + def->next = x86_defs; > + x86_defs = def; > + return (0); > +} > + > +/* register "cpudef" models defined in configuration file after preloading > + * built-in definitions > + */ > +void cpudef_setup(void) > +{ > + int i; > + > + for (i = 0; i< ARRAY_SIZE(builtin_x86_defs); ++i) { > + builtin_x86_defs[i].next = x86_defs; > + builtin_x86_defs[i].flags = 1; > + x86_defs =&builtin_x86_defs[i]; > + } > + qemu_opts_foreach(&qemu_cpudef_opts, cpudef_register, NULL, 0); > +} > + > /* NOTE: must be called outside the CPU execute loop */ > void cpu_reset(CPUX86State *env) > { > diff --git a/vl.c b/vl.c > index 6f1e1ab..d6cd62c 100644 > --- a/vl.c > +++ b/vl.c > @@ -4851,6 +4851,7 @@ int main(int argc, char **argv, char **envp) > fclose(fp); > } > } > + cpudef_setup(); > > /* second pass of option parsing */ > optind = 1; > @@ -4884,8 +4885,10 @@ 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) > - cpu_list(stdout,&fprintf); > +#if defined(cpu_list_id) > + cpu_list_id(stdout,&fprintf, optarg); > +#elif defined(cpu_list) > + cpu_list(stdout,&fprintf); /* deprecated */ > #endif > exit(0); > } else { >