Merge remote-tracking branch 'afaerber/qom-cpu' into staging

# By Eduardo Habkost (6) and others
# Via Andreas Färber
* afaerber/qom-cpu:
  target-i386: n270 can MOVBE
  target-i386: Introduce generic CPUID feature compat function
  target-i386: Change CPUID model of 486 to 8
  target-i386: Emulate X86CPU subclasses for global properties
  qdev: Introduce qdev_prop_set_globals_for_type()
  qdev: Let qdev_prop_parse() pass through Error
  target-i386: Add "filtered-features" property to X86CPU
  target-i386: Introduce X86CPU::filtered_features field
  target-i386: Add "feature-words" property to X86CPU
  target-i386: Use FeatureWord loop on filter_features_for_kvm()
  target-i386: Add ECX information to FeatureWordInfo
This commit is contained in:
Anthony Liguori 2013-05-07 06:32:00 -05:00
commit fd8192a5a8
12 changed files with 214 additions and 59 deletions

View File

@ -79,10 +79,15 @@ common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
###################################################################### ######################################################################
# qapi # qapi
common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o common-obj-y += qmp-marshal.o
common-obj-y += qmp.o hmp.o common-obj-y += qmp.o hmp.o
endif endif
######################################################################
# some qapi visitors are used by both system and user emulation:
common-obj-y += qapi-visit.o qapi-types.o
####################################################################### #######################################################################
# Target-independent parts used in system and user emulation # Target-independent parts used in system and user emulation
common-obj-y += qemu-log.o common-obj-y += qemu-log.o

View File

@ -986,25 +986,18 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev,
} }
} }
int qdev_prop_parse(DeviceState *dev, const char *name, const char *value) void qdev_prop_parse(DeviceState *dev, const char *name, const char *value,
Error **errp)
{ {
char *legacy_name; char *legacy_name;
Error *err = NULL;
legacy_name = g_strdup_printf("legacy-%s", name); legacy_name = g_strdup_printf("legacy-%s", name);
if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) { if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
object_property_parse(OBJECT(dev), value, legacy_name, &err); object_property_parse(OBJECT(dev), value, legacy_name, errp);
} else { } else {
object_property_parse(OBJECT(dev), value, name, &err); object_property_parse(OBJECT(dev), value, name, errp);
} }
g_free(legacy_name); g_free(legacy_name);
if (err) {
qerror_report_err(err);
error_free(err);
return -1;
}
return 0;
} }
void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value) void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
@ -1106,19 +1099,37 @@ void qdev_prop_register_global_list(GlobalProperty *props)
} }
} }
void qdev_prop_set_globals(DeviceState *dev) void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
Error **errp)
{
GlobalProperty *prop;
QTAILQ_FOREACH(prop, &global_props, next) {
Error *err = NULL;
if (strcmp(typename, prop->driver) != 0) {
continue;
}
qdev_prop_parse(dev, prop->property, prop->value, &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
}
}
void qdev_prop_set_globals(DeviceState *dev, Error **errp)
{ {
ObjectClass *class = object_get_class(OBJECT(dev)); ObjectClass *class = object_get_class(OBJECT(dev));
do { do {
GlobalProperty *prop; Error *err = NULL;
QTAILQ_FOREACH(prop, &global_props, next) {
if (strcmp(object_class_get_name(class), prop->driver) != 0) { qdev_prop_set_globals_for_type(dev, object_class_get_name(class),
continue; &err);
} if (err != NULL) {
if (qdev_prop_parse(dev, prop->property, prop->value) != 0) { error_propagate(errp, err);
exit(1); return;
}
} }
class = object_class_get_parent(class); class = object_class_get_parent(class);
} while (class); } while (class);

View File

@ -752,7 +752,12 @@ static void device_initfn(Object *obj)
} }
class = object_class_get_parent(class); class = object_class_get_parent(class);
} while (class != object_class_by_name(TYPE_DEVICE)); } while (class != object_class_by_name(TYPE_DEVICE));
qdev_prop_set_globals(dev); qdev_prop_set_globals(dev, &err);
if (err != NULL) {
qerror_report_err(err);
error_free(err);
exit(1);
}
object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS, object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
(Object **)&dev->parent_bus, &err); (Object **)&dev->parent_bus, &err);

View File

@ -250,6 +250,7 @@ static void pc_init_pci_1_4(QEMUMachineInitArgs *args)
{ {
pc_sysfw_flash_vs_rom_bug_compatible = true; pc_sysfw_flash_vs_rom_bug_compatible = true;
has_pvpanic = false; has_pvpanic = false;
x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
pc_init_pci(args); pc_init_pci(args);
} }

View File

@ -212,6 +212,7 @@ static void pc_q35_init_1_4(QEMUMachineInitArgs *args)
{ {
pc_sysfw_flash_vs_rom_bug_compatible = true; pc_sysfw_flash_vs_rom_bug_compatible = true;
has_pvpanic = false; has_pvpanic = false;
x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
pc_q35_init(args); pc_q35_init(args);
} }

View File

@ -242,6 +242,10 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
.driver = "pc-sysfw",\ .driver = "pc-sysfw",\
.property = "rom_only",\ .property = "rom_only",\
.value = stringify(0),\ .value = stringify(0),\
},{\
.driver = "486-" TYPE_X86_CPU,\
.property = "model",\
.value = stringify(0),\
} }
#endif #endif

View File

@ -148,7 +148,8 @@ extern PropertyInfo qdev_prop_arraylen;
/* Set properties between creation and init. */ /* Set properties between creation and init. */
void *qdev_get_prop_ptr(DeviceState *dev, Property *prop); void *qdev_get_prop_ptr(DeviceState *dev, Property *prop);
int qdev_prop_parse(DeviceState *dev, const char *name, const char *value); void qdev_prop_parse(DeviceState *dev, const char *name, const char *value,
Error **errp);
void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value); void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value);
void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value); void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value);
void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value); void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value);
@ -167,7 +168,9 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value);
void qdev_prop_register_global(GlobalProperty *prop); void qdev_prop_register_global(GlobalProperty *prop);
void qdev_prop_register_global_list(GlobalProperty *props); void qdev_prop_register_global_list(GlobalProperty *props);
void qdev_prop_set_globals(DeviceState *dev); void qdev_prop_set_globals(DeviceState *dev, Error **errp);
void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
Error **errp);
void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev,
Property *prop, const char *value); Property *prop, const char *value);

View File

@ -3587,3 +3587,35 @@
## ##
{'command': 'query-command-line-options', 'data': { '*option': 'str' }, {'command': 'query-command-line-options', 'data': { '*option': 'str' },
'returns': ['CommandLineOptionInfo'] } 'returns': ['CommandLineOptionInfo'] }
##
# @X86CPURegister32
#
# A X86 32-bit register
#
# Since: 1.5
##
{ 'enum': 'X86CPURegister32',
'data': [ 'EAX', 'EBX', 'ECX', 'EDX', 'ESP', 'EBP', 'ESI', 'EDI' ] }
##
# @X86CPUFeatureWordInfo
#
# Information about a X86 CPU feature word
#
# @cpuid-input-eax: Input EAX value for CPUID instruction for that feature word
#
# @cpuid-input-ecx: #optional Input ECX value for CPUID instruction for that
# feature word
#
# @cpuid-register: Output register containing the feature bits
#
# @features: value of output register, containing the feature bits
#
# Since: 1.5
##
{ 'type': 'X86CPUFeatureWordInfo',
'data': { 'cpuid-input-eax': 'int',
'*cpuid-input-ecx': 'int',
'cpuid-register': 'X86CPURegister32',
'features': 'int' } }

View File

@ -105,13 +105,17 @@ static void qdev_print_devinfo(ObjectClass *klass, void *opaque)
static int set_property(const char *name, const char *value, void *opaque) static int set_property(const char *name, const char *value, void *opaque)
{ {
DeviceState *dev = opaque; DeviceState *dev = opaque;
Error *err = NULL;
if (strcmp(name, "driver") == 0) if (strcmp(name, "driver") == 0)
return 0; return 0;
if (strcmp(name, "bus") == 0) if (strcmp(name, "bus") == 0)
return 0; return 0;
if (qdev_prop_parse(dev, name, value) == -1) { qdev_prop_parse(dev, name, value, &err);
if (err != NULL) {
qerror_report_err(err);
error_free(err);
return -1; return -1;
} }
return 0; return 0;

View File

@ -65,6 +65,9 @@ typedef struct X86CPU {
/*< public >*/ /*< public >*/
CPUX86State env; CPUX86State env;
/* Features that were filtered out because of missing host capabilities */
uint32_t filtered_features[FEATURE_WORDS];
} X86CPU; } X86CPU;
static inline X86CPU *x86_env_get_cpu(CPUX86State *env) static inline X86CPU *x86_env_get_cpu(CPUX86State *env)

View File

@ -30,6 +30,8 @@
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qapi-types.h"
#include "qapi-visit.h"
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "sysemu/arch_init.h" #include "sysemu/arch_init.h"
@ -152,8 +154,10 @@ static const char *cpuid_7_0_ebx_feature_name[] = {
typedef struct FeatureWordInfo { typedef struct FeatureWordInfo {
const char **feat_names; const char **feat_names;
uint32_t cpuid_eax; /* Input EAX for CPUID */ uint32_t cpuid_eax; /* Input EAX for CPUID */
int cpuid_reg; /* R_* register constant */ bool cpuid_needs_ecx; /* CPUID instruction uses ECX as input */
uint32_t cpuid_ecx; /* Input ECX value for CPUID */
int cpuid_reg; /* output register (R_* constant) */
} FeatureWordInfo; } FeatureWordInfo;
static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
@ -187,27 +191,40 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
}, },
[FEAT_7_0_EBX] = { [FEAT_7_0_EBX] = {
.feat_names = cpuid_7_0_ebx_feature_name, .feat_names = cpuid_7_0_ebx_feature_name,
.cpuid_eax = 7, .cpuid_reg = R_EBX, .cpuid_eax = 7,
.cpuid_needs_ecx = true, .cpuid_ecx = 0,
.cpuid_reg = R_EBX,
}, },
}; };
typedef struct X86RegisterInfo32 {
/* Name of register */
const char *name;
/* QAPI enum value register */
X86CPURegister32 qapi_enum;
} X86RegisterInfo32;
#define REGISTER(reg) \
[R_##reg] = { .name = #reg, .qapi_enum = X86_C_P_U_REGISTER32_##reg }
X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
REGISTER(EAX),
REGISTER(ECX),
REGISTER(EDX),
REGISTER(EBX),
REGISTER(ESP),
REGISTER(EBP),
REGISTER(ESI),
REGISTER(EDI),
};
#undef REGISTER
const char *get_register_name_32(unsigned int reg) const char *get_register_name_32(unsigned int reg)
{ {
static const char *reg_names[CPU_NB_REGS32] = {
[R_EAX] = "EAX",
[R_ECX] = "ECX",
[R_EDX] = "EDX",
[R_EBX] = "EBX",
[R_ESP] = "ESP",
[R_EBP] = "EBP",
[R_ESI] = "ESI",
[R_EDI] = "EDI",
};
if (reg > CPU_NB_REGS32) { if (reg > CPU_NB_REGS32) {
return NULL; return NULL;
} }
return reg_names[reg]; return x86_reg_info_32[reg].name;
} }
/* collects per-function cpuid data /* collects per-function cpuid data
@ -571,7 +588,7 @@ static x86_def_t builtin_x86_defs[] = {
.level = 1, .level = 1,
.vendor = CPUID_VENDOR_INTEL, .vendor = CPUID_VENDOR_INTEL,
.family = 4, .family = 4,
.model = 0, .model = 8,
.stepping = 0, .stepping = 0,
.features[FEAT_1_EDX] = .features[FEAT_1_EDX] =
I486_FEATURES, I486_FEATURES,
@ -640,7 +657,8 @@ static x86_def_t builtin_x86_defs[] = {
/* Some CPUs got no CPUID_SEP */ /* Some CPUs got no CPUID_SEP */
.features[FEAT_1_ECX] = .features[FEAT_1_ECX] =
CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 |
CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR, CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR |
CPUID_EXT_MOVBE,
.features[FEAT_8000_0001_EDX] = .features[FEAT_8000_0001_EDX] =
(PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) |
CPUID_EXT2_NX, CPUID_EXT2_NX,
@ -954,6 +972,32 @@ static x86_def_t builtin_x86_defs[] = {
}, },
}; };
/**
* x86_cpu_compat_set_features:
* @cpu_model: CPU model name to be changed. If NULL, all CPU models are changed
* @w: Identifies the feature word to be changed.
* @feat_add: Feature bits to be added to feature word
* @feat_remove: Feature bits to be removed from feature word
*
* Change CPU model feature bits for compatibility.
*
* This function may be used by machine-type compatibility functions
* to enable or disable feature bits on specific CPU models.
*/
void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w,
uint32_t feat_add, uint32_t feat_remove)
{
x86_def_t *def;
int i;
for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
def = &builtin_x86_defs[i];
if (!cpu_model || !strcmp(cpu_model, def->name)) {
def->features[w] |= feat_add;
def->features[w] &= ~feat_remove;
}
}
}
#ifdef CONFIG_KVM #ifdef CONFIG_KVM
static int cpu_x86_fill_model_id(char *str) static int cpu_x86_fill_model_id(char *str)
{ {
@ -1401,6 +1445,36 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque,
cpu->env.cpuid_apic_id = value; cpu->env.cpuid_apic_id = value;
} }
/* Generic getter for "feature-words" and "filtered-features" properties */
static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
uint32_t *array = (uint32_t *)opaque;
FeatureWord w;
Error *err = NULL;
X86CPUFeatureWordInfo word_infos[FEATURE_WORDS] = { };
X86CPUFeatureWordInfoList list_entries[FEATURE_WORDS] = { };
X86CPUFeatureWordInfoList *list = NULL;
for (w = 0; w < FEATURE_WORDS; w++) {
FeatureWordInfo *wi = &feature_word_info[w];
X86CPUFeatureWordInfo *qwi = &word_infos[w];
qwi->cpuid_input_eax = wi->cpuid_eax;
qwi->has_cpuid_input_ecx = wi->cpuid_needs_ecx;
qwi->cpuid_input_ecx = wi->cpuid_ecx;
qwi->cpuid_register = x86_reg_info_32[wi->cpuid_reg].qapi_enum;
qwi->features = array[w];
/* List will be in reverse order, but order shouldn't matter */
list_entries[w].next = list;
list_entries[w].value = &word_infos[w];
list = &list_entries[w];
}
visit_type_X86CPUFeatureWordInfoList(v, &list, "feature-words", &err);
error_propagate(errp, err);
}
static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *name) static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *name)
{ {
x86_def_t *def; x86_def_t *def;
@ -1647,24 +1721,17 @@ static void filter_features_for_kvm(X86CPU *cpu)
{ {
CPUX86State *env = &cpu->env; CPUX86State *env = &cpu->env;
KVMState *s = kvm_state; KVMState *s = kvm_state;
FeatureWord w;
env->features[FEAT_1_EDX] &= for (w = 0; w < FEATURE_WORDS; w++) {
kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); FeatureWordInfo *wi = &feature_word_info[w];
env->features[FEAT_1_ECX] &= uint32_t host_feat = kvm_arch_get_supported_cpuid(s, wi->cpuid_eax,
kvm_arch_get_supported_cpuid(s, 1, 0, R_ECX); wi->cpuid_ecx,
env->features[FEAT_8000_0001_EDX] &= wi->cpuid_reg);
kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX); uint32_t requested_features = env->features[w];
env->features[FEAT_8000_0001_ECX] &= env->features[w] &= host_feat;
kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX); cpu->filtered_features[w] = requested_features & ~env->features[w];
env->features[FEAT_SVM] &= }
kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX);
env->features[FEAT_7_0_EBX] &=
kvm_arch_get_supported_cpuid(s, 7, 0, R_EBX);
env->features[FEAT_KVM] &=
kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX);
env->features[FEAT_C000_0001_EDX] &=
kvm_arch_get_supported_cpuid(s, 0xC0000001, 0, R_EDX);
} }
#endif #endif
@ -1711,6 +1778,7 @@ X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge,
CPUX86State *env; CPUX86State *env;
gchar **model_pieces; gchar **model_pieces;
char *name, *features; char *name, *features;
char *typename;
Error *error = NULL; Error *error = NULL;
model_pieces = g_strsplit(cpu_model, ",", 2); model_pieces = g_strsplit(cpu_model, ",", 2);
@ -1738,6 +1806,14 @@ X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge,
goto out; goto out;
} }
/* Emulate per-model subclasses for global properties */
typename = g_strdup_printf("%s-" TYPE_X86_CPU, name);
qdev_prop_set_globals_for_type(DEVICE(cpu), typename, &error);
g_free(typename);
if (error) {
goto out;
}
cpu_x86_parse_featurestr(cpu, features, &error); cpu_x86_parse_featurestr(cpu, features, &error);
if (error) { if (error) {
goto out; goto out;
@ -2402,6 +2478,12 @@ static void x86_cpu_initfn(Object *obj)
object_property_add(obj, "apic-id", "int", object_property_add(obj, "apic-id", "int",
x86_cpuid_get_apic_id, x86_cpuid_get_apic_id,
x86_cpuid_set_apic_id, NULL, NULL, NULL); x86_cpuid_set_apic_id, NULL, NULL, NULL);
object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo",
x86_cpu_get_feature_words,
NULL, NULL, (void *)env->features, NULL);
object_property_add(obj, "filtered-features", "X86CPUFeatureWordInfo",
x86_cpu_get_feature_words,
NULL, NULL, (void *)cpu->filtered_features, NULL);
env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index); env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index);

View File

@ -1255,6 +1255,10 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access);
void disable_kvm_pv_eoi(void); void disable_kvm_pv_eoi(void);
void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w,
uint32_t feat_add, uint32_t feat_remove);
/* Return name of 32-bit register, from a R_* constant */ /* Return name of 32-bit register, from a R_* constant */
const char *get_register_name_32(unsigned int reg); const char *get_register_name_32(unsigned int reg);