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:
commit
fd8192a5a8
|
@ -79,10 +79,15 @@ common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
|
|||
######################################################################
|
||||
# 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
|
||||
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
|
||||
common-obj-y += qemu-log.o
|
||||
|
|
|
@ -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;
|
||||
Error *err = NULL;
|
||||
|
||||
legacy_name = g_strdup_printf("legacy-%s", name);
|
||||
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 {
|
||||
object_property_parse(OBJECT(dev), value, name, &err);
|
||||
object_property_parse(OBJECT(dev), value, name, errp);
|
||||
}
|
||||
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)
|
||||
|
@ -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));
|
||||
|
||||
do {
|
||||
GlobalProperty *prop;
|
||||
QTAILQ_FOREACH(prop, &global_props, next) {
|
||||
if (strcmp(object_class_get_name(class), prop->driver) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (qdev_prop_parse(dev, prop->property, prop->value) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
Error *err = NULL;
|
||||
|
||||
qdev_prop_set_globals_for_type(dev, object_class_get_name(class),
|
||||
&err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
class = object_class_get_parent(class);
|
||||
} while (class);
|
||||
|
|
|
@ -752,7 +752,12 @@ static void device_initfn(Object *obj)
|
|||
}
|
||||
class = object_class_get_parent(class);
|
||||
} 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 **)&dev->parent_bus, &err);
|
||||
|
|
|
@ -250,6 +250,7 @@ static void pc_init_pci_1_4(QEMUMachineInitArgs *args)
|
|||
{
|
||||
pc_sysfw_flash_vs_rom_bug_compatible = true;
|
||||
has_pvpanic = false;
|
||||
x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
|
||||
pc_init_pci(args);
|
||||
}
|
||||
|
||||
|
|
|
@ -212,6 +212,7 @@ static void pc_q35_init_1_4(QEMUMachineInitArgs *args)
|
|||
{
|
||||
pc_sysfw_flash_vs_rom_bug_compatible = true;
|
||||
has_pvpanic = false;
|
||||
x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
|
||||
pc_q35_init(args);
|
||||
}
|
||||
|
||||
|
|
|
@ -242,6 +242,10 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
|
|||
.driver = "pc-sysfw",\
|
||||
.property = "rom_only",\
|
||||
.value = stringify(0),\
|
||||
},{\
|
||||
.driver = "486-" TYPE_X86_CPU,\
|
||||
.property = "model",\
|
||||
.value = stringify(0),\
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -148,7 +148,8 @@ extern PropertyInfo qdev_prop_arraylen;
|
|||
|
||||
/* Set properties between creation and init. */
|
||||
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_uint8(DeviceState *dev, const char *name, uint8_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_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,
|
||||
Property *prop, const char *value);
|
||||
|
||||
|
|
|
@ -3587,3 +3587,35 @@
|
|||
##
|
||||
{'command': 'query-command-line-options', 'data': { '*option': 'str' },
|
||||
'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' } }
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
DeviceState *dev = opaque;
|
||||
Error *err = NULL;
|
||||
|
||||
if (strcmp(name, "driver") == 0)
|
||||
return 0;
|
||||
if (strcmp(name, "bus") == 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 0;
|
||||
|
|
|
@ -65,6 +65,9 @@ typedef struct X86CPU {
|
|||
/*< public >*/
|
||||
|
||||
CPUX86State env;
|
||||
|
||||
/* Features that were filtered out because of missing host capabilities */
|
||||
uint32_t filtered_features[FEATURE_WORDS];
|
||||
} X86CPU;
|
||||
|
||||
static inline X86CPU *x86_env_get_cpu(CPUX86State *env)
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "qemu/config-file.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
||||
#include "qapi-types.h"
|
||||
#include "qapi-visit.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
|
||||
|
@ -152,8 +154,10 @@ static const char *cpuid_7_0_ebx_feature_name[] = {
|
|||
|
||||
typedef struct FeatureWordInfo {
|
||||
const char **feat_names;
|
||||
uint32_t cpuid_eax; /* Input EAX for CPUID */
|
||||
int cpuid_reg; /* R_* register constant */
|
||||
uint32_t cpuid_eax; /* Input EAX for CPUID */
|
||||
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;
|
||||
|
||||
static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
|
||||
|
@ -187,27 +191,40 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
|
|||
},
|
||||
[FEAT_7_0_EBX] = {
|
||||
.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)
|
||||
{
|
||||
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) {
|
||||
return NULL;
|
||||
}
|
||||
return reg_names[reg];
|
||||
return x86_reg_info_32[reg].name;
|
||||
}
|
||||
|
||||
/* collects per-function cpuid data
|
||||
|
@ -571,7 +588,7 @@ static x86_def_t builtin_x86_defs[] = {
|
|||
.level = 1,
|
||||
.vendor = CPUID_VENDOR_INTEL,
|
||||
.family = 4,
|
||||
.model = 0,
|
||||
.model = 8,
|
||||
.stepping = 0,
|
||||
.features[FEAT_1_EDX] =
|
||||
I486_FEATURES,
|
||||
|
@ -640,7 +657,8 @@ static x86_def_t builtin_x86_defs[] = {
|
|||
/* Some CPUs got no CPUID_SEP */
|
||||
.features[FEAT_1_ECX] =
|
||||
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] =
|
||||
(PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) |
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
x86_def_t *def;
|
||||
|
@ -1647,24 +1721,17 @@ static void filter_features_for_kvm(X86CPU *cpu)
|
|||
{
|
||||
CPUX86State *env = &cpu->env;
|
||||
KVMState *s = kvm_state;
|
||||
FeatureWord w;
|
||||
|
||||
env->features[FEAT_1_EDX] &=
|
||||
kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX);
|
||||
env->features[FEAT_1_ECX] &=
|
||||
kvm_arch_get_supported_cpuid(s, 1, 0, R_ECX);
|
||||
env->features[FEAT_8000_0001_EDX] &=
|
||||
kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX);
|
||||
env->features[FEAT_8000_0001_ECX] &=
|
||||
kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX);
|
||||
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);
|
||||
|
||||
for (w = 0; w < FEATURE_WORDS; w++) {
|
||||
FeatureWordInfo *wi = &feature_word_info[w];
|
||||
uint32_t host_feat = kvm_arch_get_supported_cpuid(s, wi->cpuid_eax,
|
||||
wi->cpuid_ecx,
|
||||
wi->cpuid_reg);
|
||||
uint32_t requested_features = env->features[w];
|
||||
env->features[w] &= host_feat;
|
||||
cpu->filtered_features[w] = requested_features & ~env->features[w];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1711,6 +1778,7 @@ X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge,
|
|||
CPUX86State *env;
|
||||
gchar **model_pieces;
|
||||
char *name, *features;
|
||||
char *typename;
|
||||
Error *error = NULL;
|
||||
|
||||
model_pieces = g_strsplit(cpu_model, ",", 2);
|
||||
|
@ -1738,6 +1806,14 @@ X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge,
|
|||
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);
|
||||
if (error) {
|
||||
goto out;
|
||||
|
@ -2402,6 +2478,12 @@ static void x86_cpu_initfn(Object *obj)
|
|||
object_property_add(obj, "apic-id", "int",
|
||||
x86_cpuid_get_apic_id,
|
||||
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);
|
||||
|
||||
|
|
|
@ -1255,6 +1255,10 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access);
|
|||
|
||||
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 */
|
||||
const char *get_register_name_32(unsigned int reg);
|
||||
|
||||
|
|
Loading…
Reference in a new issue