i386: Register versioned CPU models

Add support for registration of multiple versions of CPU models.

The existing CPU models will be registered with a "-v1" suffix.

The -noTSX, -IBRS, and -IBPB CPU model variants will become
versions of the original models in a separate patch, so
make sure we register no versions for them.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Message-Id: <20190628002844.24894-5-ehabkost@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
This commit is contained in:
Eduardo Habkost 2019-06-27 21:28:39 -03:00
parent 164e779ce1
commit dcafd1ef0a
4 changed files with 318 additions and 30 deletions

View File

@ -36,13 +36,7 @@
#define X86_CPU_GET_CLASS(obj) \
OBJECT_GET_CLASS(X86CPUClass, (obj), TYPE_X86_CPU)
/**
* X86CPUDefinition:
*
* CPU model definition data that was not converted to QOM per-subclass
* property defaults yet.
*/
typedef struct X86CPUDefinition X86CPUDefinition;
typedef struct X86CPUModel X86CPUModel;
/**
* X86CPUClass:
@ -64,7 +58,7 @@ typedef struct X86CPUClass {
/* CPU definition, automatically loaded by instance_init if not NULL.
* Should be eventually replaced by subclass-specific property defaults.
*/
X86CPUDefinition *cpu_def;
X86CPUModel *model;
bool host_cpuid_required;
int ordering;

View File

@ -1433,7 +1433,17 @@ static char *x86_cpu_class_get_model_name(X86CPUClass *cc)
strlen(class_name) - strlen(X86_CPU_TYPE_SUFFIX));
}
struct X86CPUDefinition {
typedef struct PropValue {
const char *prop, *value;
} PropValue;
typedef struct X86CPUVersionDefinition {
X86CPUVersion version;
PropValue *props;
} X86CPUVersionDefinition;
/* Base definition for a CPU model */
typedef struct X86CPUDefinition {
const char *name;
uint32_t level;
uint32_t xlevel;
@ -1445,8 +1455,41 @@ struct X86CPUDefinition {
FeatureWordArray features;
const char *model_id;
CPUCaches *cache_info;
/*
* Definitions for alternative versions of CPU model.
* List is terminated by item with version == 0.
* If NULL, version 1 will be registered automatically.
*/
const X86CPUVersionDefinition *versions;
} X86CPUDefinition;
/* Reference to a specific CPU model version */
struct X86CPUModel {
/* Base CPU definition */
X86CPUDefinition *cpudef;
/* CPU model version */
X86CPUVersion version;
};
/* Get full model name for CPU version */
static char *x86_cpu_versioned_model_name(X86CPUDefinition *cpudef,
X86CPUVersion version)
{
assert(version > 0);
return g_strdup_printf("%s-v%d", cpudef->name, (int)version);
}
static const X86CPUVersionDefinition *x86_cpu_def_get_versions(X86CPUDefinition *def)
{
/* When X86CPUDefinition::versions is NULL, we register only v1 */
static const X86CPUVersionDefinition default_version_list[] = {
{ 1 },
{ /* end of list */ }
};
return def->versions ?: default_version_list;
}
static CPUCaches epyc_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
@ -1833,6 +1876,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_EXT3_LAHF_LM,
.xlevel = 0x80000008,
.model_id = "Intel Core i7 9xx (Nehalem Core i7, IBRS update)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Westmere",
@ -1887,6 +1935,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Westmere E56xx/L56xx/X56xx (IBRS update)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "SandyBridge",
@ -1951,6 +2004,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Xeon E312xx (Sandy Bridge, IBRS update)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "IvyBridge",
@ -2021,6 +2079,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Xeon E3-12xx v2 (Ivy Bridge, IBRS)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Haswell-noTSX",
@ -2057,6 +2120,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Core Processor (Haswell, no TSX)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Haswell-noTSX-IBRS",
@ -2095,6 +2163,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Core Processor (Haswell, no TSX, IBRS)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Haswell",
@ -2171,6 +2244,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Core Processor (Haswell, IBRS)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Broadwell-noTSX",
@ -2209,6 +2287,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Core Processor (Broadwell, no TSX)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Broadwell-noTSX-IBRS",
@ -2249,6 +2332,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Core Processor (Broadwell, no TSX, IBRS)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Broadwell",
@ -2327,6 +2415,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Core Processor (Broadwell, IBRS)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Skylake-Client",
@ -2419,6 +2512,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Core Processor (Skylake, IBRS)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Skylake-Server",
@ -2521,6 +2619,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
CPUID_6_EAX_ARAT,
.xlevel = 0x80000008,
.model_id = "Intel Xeon Processor (Skylake, IBRS)",
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Cascadelake-Server",
@ -3029,6 +3132,11 @@ static X86CPUDefinition builtin_x86_defs[] = {
.xlevel = 0x8000001E,
.model_id = "AMD EPYC Processor (with IBPB)",
.cache_info = &epyc_cache_info,
/*
* This CPU model will eventually be replaced by an alias,
* don't register any versions for it.
*/
.versions = (X86CPUVersionDefinition[]) { { /* end of list */ } },
},
{
.name = "Dhyana",
@ -3082,10 +3190,6 @@ static X86CPUDefinition builtin_x86_defs[] = {
},
};
typedef struct PropValue {
const char *prop, *value;
} PropValue;
/* KVM-specific features that are automatically added/removed
* from all CPU models when KVM is enabled.
*/
@ -3111,6 +3215,28 @@ static PropValue tcg_default_props[] = {
};
static X86CPUVersion x86_cpu_model_last_version(const X86CPUModel *model)
{
int v = 0;
const X86CPUVersionDefinition *vdef =
x86_cpu_def_get_versions(model->cpudef);
while (vdef->version) {
v = vdef->version;
vdef++;
}
return v;
}
/* Return the actual version being used for a specific CPU model */
static X86CPUVersion x86_cpu_model_resolve_version(const X86CPUModel *model)
{
X86CPUVersion v = model->version;
if (v == CPU_VERSION_LATEST) {
return x86_cpu_model_last_version(model);
}
return v;
}
void x86_cpu_change_kvm_default(const char *prop, const char *value)
{
PropValue *pv;
@ -3188,8 +3314,6 @@ static void max_x86_cpu_class_init(ObjectClass *oc, void *data)
dc->props = max_x86_cpu_properties;
}
static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp);
static void max_x86_cpu_initfn(Object *obj)
{
X86CPU *cpu = X86_CPU(obj);
@ -3980,10 +4104,40 @@ static void x86_cpu_apply_props(X86CPU *cpu, PropValue *props)
}
}
/* Apply properties for the CPU model version specified in model */
static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model)
{
const X86CPUVersionDefinition *vdef;
X86CPUVersion version = x86_cpu_model_resolve_version(model);
if (version == CPU_VERSION_LEGACY) {
return;
}
for (vdef = x86_cpu_def_get_versions(model->cpudef); vdef->version; vdef++) {
PropValue *p;
for (p = vdef->props; p && p->prop; p++) {
object_property_parse(OBJECT(cpu), p->value, p->prop,
&error_abort);
}
if (vdef->version == version) {
break;
}
}
/*
* If we reached the end of the list, version number was invalid
*/
assert(vdef->version == version);
}
/* Load data from X86CPUDefinition into a X86CPU object
*/
static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp)
static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model, Error **errp)
{
X86CPUDefinition *def = model->cpudef;
CPUX86State *env = &cpu->env;
const char *vendor;
char host_vendor[CPUID_VENDOR_SZ + 1];
@ -4040,11 +4194,12 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp)
object_property_set_str(OBJECT(cpu), vendor, "vendor", errp);
x86_cpu_apply_version_props(cpu, model);
}
#ifndef CONFIG_USER_ONLY
/* Return a QDict containing keys for all properties that can be included
* in static expansion of CPU models. All properties set by x86_cpu_load_def()
* in static expansion of CPU models. All properties set by x86_cpu_load_model()
* must be included in the dictionary.
*/
static QDict *x86_cpu_static_props(void)
@ -4258,23 +4413,33 @@ static gchar *x86_gdb_arch_name(CPUState *cs)
static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data)
{
X86CPUDefinition *cpudef = data;
X86CPUModel *model = data;
X86CPUClass *xcc = X86_CPU_CLASS(oc);
xcc->cpu_def = cpudef;
xcc->model = model;
xcc->migration_safe = true;
}
static void x86_register_cpudef_type(X86CPUDefinition *def)
static void x86_register_cpu_model_type(const char *name, X86CPUModel *model)
{
char *typename = x86_cpu_type_name(def->name);
char *typename = x86_cpu_type_name(name);
TypeInfo ti = {
.name = typename,
.parent = TYPE_X86_CPU,
.class_init = x86_cpu_cpudef_class_init,
.class_data = def,
.class_data = model,
};
type_register(&ti);
g_free(typename);
}
static void x86_register_cpudef_types(X86CPUDefinition *def)
{
X86CPUModel *m;
const X86CPUVersionDefinition *vdef;
char *name;
/* AMD aliases are handled at runtime based on CPUID vendor, so
* they shouldn't be set on the CPU model table.
*/
@ -4282,9 +4447,23 @@ static void x86_register_cpudef_type(X86CPUDefinition *def)
/* catch mistakes instead of silently truncating model_id when too long */
assert(def->model_id && strlen(def->model_id) <= 48);
/* Unversioned model: */
m = g_new0(X86CPUModel, 1);
m->cpudef = def;
m->version = CPU_VERSION_LEGACY;
x86_register_cpu_model_type(def->name, m);
/* Versioned models: */
for (vdef = x86_cpu_def_get_versions(def); vdef->version; vdef++) {
X86CPUModel *m = g_new0(X86CPUModel, 1);
m->cpudef = def;
m->version = vdef->version;
name = x86_cpu_versioned_model_name(def, vdef->version);
x86_register_cpu_model_type(name, m);
g_free(name);
}
type_register(&ti);
g_free(typename);
}
#if !defined(CONFIG_USER_ONLY)
@ -5110,7 +5289,7 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu)
* involved in setting up CPUID data are:
*
* 1) Loading CPU model definition (X86CPUDefinition). This is
* implemented by x86_cpu_load_def() and should be completely
* implemented by x86_cpu_load_model() and should be completely
* transparent, as it is done automatically by instance_init.
* No code should need to look at X86CPUDefinition structs
* outside instance_init.
@ -5432,7 +5611,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
/* Cache information initialization */
if (!cpu->legacy_cache) {
if (!xcc->cpu_def || !xcc->cpu_def->cache_info) {
if (!xcc->model || !xcc->model->cpudef->cache_info) {
char *name = x86_cpu_class_get_model_name(xcc);
error_setg(errp,
"CPU model '%s' doesn't support legacy-cache=off", name);
@ -5440,7 +5619,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
return;
}
env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd =
*xcc->cpu_def->cache_info;
*xcc->model->cpudef->cache_info;
} else {
/* Build legacy cache information */
env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache;
@ -5799,8 +5978,8 @@ static void x86_cpu_initfn(Object *obj)
object_property_add_alias(obj, "sse4_1", obj, "sse4.1", &error_abort);
object_property_add_alias(obj, "sse4_2", obj, "sse4.2", &error_abort);
if (xcc->cpu_def) {
x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort);
if (xcc->model) {
x86_cpu_load_model(cpu, xcc->model, &error_abort);
}
}
@ -6139,7 +6318,7 @@ static void x86_cpu_register_types(void)
type_register_static(&x86_cpu_type_info);
for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
x86_register_cpudef_type(&builtin_x86_defs[i]);
x86_register_cpudef_types(&builtin_x86_defs[i]);
}
type_register_static(&max_x86_cpu_type_info);
type_register_static(&x86_base_cpu_type_info);

View File

@ -1929,6 +1929,16 @@ void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
*/
void x86_cpu_change_kvm_default(const char *prop, const char *value);
/* Special values for X86CPUVersion: */
/* Resolve to latest CPU version */
#define CPU_VERSION_LATEST -1
/* Don't resolve to any versioned CPU models, like old QEMU versions */
#define CPU_VERSION_LEGACY 0
typedef int X86CPUVersion;
/* Return name of 32-bit register, from a R_* constant */
const char *get_register_name_32(unsigned int reg);

View File

@ -0,0 +1,105 @@
#!/usr/bin/env python
#
# Basic validation of x86 versioned CPU models and CPU model aliases
#
# Copyright (c) 2019 Red Hat Inc
#
# Author:
# Eduardo Habkost <ehabkost@redhat.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
import avocado_qemu
import re
class X86CPUModelAliases(avocado_qemu.Test):
"""
Validation of PC CPU model versions and CPU model aliases
:avocado: tags=arch:x86_64
"""
def validate_aliases(self, cpus):
for c in cpus.values():
if 'alias-of' in c:
# all aliases must point to a valid CPU model name:
self.assertIn(c['alias-of'], cpus,
'%s.alias-of (%s) is not a valid CPU model name' % (c['name'], c['alias-of']))
# aliases must not point to aliases
self.assertNotIn('alias-of', cpus[c['alias-of']],
'%s.alias-of (%s) points to another alias' % (c['name'], c['alias-of']))
# aliases must not be static
self.assertFalse(c['static'])
def validate_variant_aliases(self, cpus):
# -noTSX, -IBRS and -IBPB variants of CPU models are special:
# they shouldn't have their own versions:
self.assertNotIn("Haswell-noTSX-v1", cpus,
"Haswell-noTSX shouldn't be versioned")
self.assertNotIn("Broadwell-noTSX-v1", cpus,
"Broadwell-noTSX shouldn't be versioned")
self.assertNotIn("Nehalem-IBRS-v1", cpus,
"Nehalem-IBRS shouldn't be versioned")
self.assertNotIn("Westmere-IBRS-v1", cpus,
"Westmere-IBRS shouldn't be versioned")
self.assertNotIn("SandyBridge-IBRS-v1", cpus,
"SandyBridge-IBRS shouldn't be versioned")
self.assertNotIn("IvyBridge-IBRS-v1", cpus,
"IvyBridge-IBRS shouldn't be versioned")
self.assertNotIn("Haswell-noTSX-IBRS-v1", cpus,
"Haswell-noTSX-IBRS shouldn't be versioned")
self.assertNotIn("Haswell-IBRS-v1", cpus,
"Haswell-IBRS shouldn't be versioned")
self.assertNotIn("Broadwell-noTSX-IBRS-v1", cpus,
"Broadwell-noTSX-IBRS shouldn't be versioned")
self.assertNotIn("Broadwell-IBRS-v1", cpus,
"Broadwell-IBRS shouldn't be versioned")
self.assertNotIn("Skylake-Client-IBRS-v1", cpus,
"Skylake-Client-IBRS shouldn't be versioned")
self.assertNotIn("Skylake-Server-IBRS-v1", cpus,
"Skylake-Server-IBRS shouldn't be versioned")
self.assertNotIn("EPYC-IBPB-v1", cpus,
"EPYC-IBPB shouldn't be versioned")
def test_4_0_alias_compatibility(self):
"""Check if pc-*-4.0 unversioned CPU model won't be reported as aliases"""
# pc-*-4.0 won't expose non-versioned CPU models as aliases
# We do this to help management software to keep compatibility
# with older QEMU versions that didn't have the versioned CPU model
self.vm.add_args('-S')
self.vm.set_machine('pc-i440fx-4.0')
self.vm.launch()
cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions'))
self.assertFalse(cpus['Cascadelake-Server']['static'],
'unversioned Cascadelake-Server CPU model must not be static')
self.assertNotIn('alias-of', cpus['Cascadelake-Server'],
'Cascadelake-Server must not be an alias')
self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
'Cascadelake-Server-v1 must not be an alias')
self.assertFalse(cpus['qemu64']['static'],
'unversioned qemu64 CPU model must not be static')
self.assertNotIn('alias-of', cpus['qemu64'],
'qemu64 must not be an alias')
self.assertNotIn('alias-of', cpus['qemu64-v1'],
'qemu64-v1 must not be an alias')
self.validate_variant_aliases(cpus)
# On pc-*-4.0, no CPU model should be reported as an alias:
for name,c in cpus.items():
self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name))