qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Anthony Liguori <aliguori@linux.vnet.ibm.com>
To: john cooper <john.cooper@redhat.com>
Cc: "Przywara, Andre" <Andre.Przywara@amd.com>,
	qemu-devel@nongnu.org, KVM list <kvm@vger.kernel.org>
Subject: Re: [Qemu-devel] [PATCH] Add cpu model configuration support.. (resend)
Date: Wed, 10 Feb 2010 14:00:09 -0600	[thread overview]
Message-ID: <4B731049.4050707@linux.vnet.ibm.com> (raw)
In-Reply-To: <4B672535.5050303@redhat.com>

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<name>,
> 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<name>), 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-<arch>.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<john.cooper@redhat.com>
> ---
>
> 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 {
>    

  parent reply	other threads:[~2010-02-10 20:00 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-02-01 19:02 [Qemu-devel] [PATCH] Add cpu model configuration support.. (resend) john cooper
2010-02-02 10:17 ` [Qemu-devel] " Andre Przywara
2010-02-02 11:07 ` Andre Przywara
2010-02-02 19:34   ` john cooper
2010-02-06 18:59 ` [Qemu-devel] [PATCH] Add assignment operation to config file parser john cooper
2010-02-07 16:24   ` Anthony Liguori
2010-02-08 13:21     ` Gerd Hoffmann
2010-02-08 16:00       ` john cooper
2010-06-09  8:05   ` [Qemu-devel] [PATCH] Add optional dump of default config file paths john cooper
2010-06-14 17:01     ` Anthony Liguori
2010-06-14 17:07       ` Daniel P. Berrange
2010-06-14 17:59       ` john cooper
2010-06-14 19:13         ` Anthony Liguori
2010-02-10 20:00 ` Anthony Liguori [this message]
2010-02-14  6:52   ` [Qemu-devel] [PATCH] Add cpu model configuration support.. (resend) john cooper

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4B731049.4050707@linux.vnet.ibm.com \
    --to=aliguori@linux.vnet.ibm.com \
    --cc=Andre.Przywara@amd.com \
    --cc=john.cooper@redhat.com \
    --cc=kvm@vger.kernel.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).