aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.objs7
-rw-r--r--hw/core/qdev-properties.c51
-rw-r--r--hw/core/qdev.c7
-rw-r--r--hw/i386/pc_piix.c1
-rw-r--r--hw/i386/pc_q35.c1
-rw-r--r--hw/net/virtio-net.c4
-rw-r--r--include/hw/i386/pc.h4
-rw-r--r--include/hw/qdev-properties.h7
-rw-r--r--qapi-schema.json32
-rw-r--r--qdev-monitor.c6
-rw-r--r--qga/commands-posix.c123
-rw-r--r--qga/main.c2
-rw-r--r--target-i386/cpu-qom.h3
-rw-r--r--target-i386/cpu.c150
-rw-r--r--target-i386/cpu.h4
15 files changed, 336 insertions, 66 deletions
diff --git a/Makefile.objs b/Makefile.objs
index fcb303a839..286ce069b2 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -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
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index ca1739ec84..3a324fb0c3 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -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);
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 069ac9034c..6985ad870c 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -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);
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index fe52e5f94b..f7c80ad0a5 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -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);
}
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 52511e2b69..4160e2ba37 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -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);
}
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 908e7b809e..9f18d6ab09 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -114,8 +114,8 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
return;
}
- if (!!n->vhost_started == virtio_net_started(n, status) &&
- !nc->peer->link_down) {
+ if (!!n->vhost_started ==
+ (virtio_net_started(n, status) && !nc->peer->link_down)) {
return;
}
if (!n->vhost_started) {
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 41869e56e9..417afe4ef0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -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
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 25dd1bb39a..39448b716c 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -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);
diff --git a/qapi-schema.json b/qapi-schema.json
index 7797400666..199744a833 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -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' } }
diff --git a/qdev-monitor.c b/qdev-monitor.c
index 2cb5600d63..e54dbc2c5d 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -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;
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 3b5c536e41..04c69515e7 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -18,6 +18,9 @@
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
#include <inttypes.h>
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
@@ -237,9 +240,122 @@ static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err)
return NULL;
}
+typedef const char * const ccpc;
+
+/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
+static const struct {
+ ccpc *forms;
+ int oflag_base;
+} guest_file_open_modes[] = {
+ { (ccpc[]){ "r", "rb", NULL }, O_RDONLY },
+ { (ccpc[]){ "w", "wb", NULL }, O_WRONLY | O_CREAT | O_TRUNC },
+ { (ccpc[]){ "a", "ab", NULL }, O_WRONLY | O_CREAT | O_APPEND },
+ { (ccpc[]){ "r+", "rb+", "r+b", NULL }, O_RDWR },
+ { (ccpc[]){ "w+", "wb+", "w+b", NULL }, O_RDWR | O_CREAT | O_TRUNC },
+ { (ccpc[]){ "a+", "ab+", "a+b", NULL }, O_RDWR | O_CREAT | O_APPEND }
+};
+
+static int
+find_open_flag(const char *mode_str, Error **err)
+{
+ unsigned mode;
+
+ for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
+ ccpc *form;
+
+ form = guest_file_open_modes[mode].forms;
+ while (*form != NULL && strcmp(*form, mode_str) != 0) {
+ ++form;
+ }
+ if (*form != NULL) {
+ break;
+ }
+ }
+
+ if (mode == ARRAY_SIZE(guest_file_open_modes)) {
+ error_setg(err, "invalid file open mode '%s'", mode_str);
+ return -1;
+ }
+ return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
+}
+
+#define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
+ S_IRGRP | S_IWGRP | \
+ S_IROTH | S_IWOTH)
+
+static FILE *
+safe_open_or_create(const char *path, const char *mode, Error **err)
+{
+ Error *local_err = NULL;
+ int oflag;
+
+ oflag = find_open_flag(mode, &local_err);
+ if (local_err == NULL) {
+ int fd;
+
+ /* If the caller wants / allows creation of a new file, we implement it
+ * with a two step process: open() + (open() / fchmod()).
+ *
+ * First we insist on creating the file exclusively as a new file. If
+ * that succeeds, we're free to set any file-mode bits on it. (The
+ * motivation is that we want to set those file-mode bits independently
+ * of the current umask.)
+ *
+ * If the exclusive creation fails because the file already exists
+ * (EEXIST is not possible for any other reason), we just attempt to
+ * open the file, but in this case we won't be allowed to change the
+ * file-mode bits on the preexistent file.
+ *
+ * The pathname should never disappear between the two open()s in
+ * practice. If it happens, then someone very likely tried to race us.
+ * In this case just go ahead and report the ENOENT from the second
+ * open() to the caller.
+ *
+ * If the caller wants to open a preexistent file, then the first
+ * open() is decisive and its third argument is ignored, and the second
+ * open() and the fchmod() are never called.
+ */
+ fd = open(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
+ if (fd == -1 && errno == EEXIST) {
+ oflag &= ~(unsigned)O_CREAT;
+ fd = open(path, oflag);
+ }
+
+ if (fd == -1) {
+ error_setg_errno(&local_err, errno, "failed to open file '%s' "
+ "(mode: '%s')", path, mode);
+ } else {
+ qemu_set_cloexec(fd);
+
+ if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
+ error_setg_errno(&local_err, errno, "failed to set permission "
+ "0%03o on new file '%s' (mode: '%s')",
+ (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
+ } else {
+ FILE *f;
+
+ f = fdopen(fd, mode);
+ if (f == NULL) {
+ error_setg_errno(&local_err, errno, "failed to associate "
+ "stdio stream with file descriptor %d, "
+ "file '%s' (mode: '%s')", fd, path, mode);
+ } else {
+ return f;
+ }
+ }
+
+ close(fd);
+ }
+ }
+
+ error_propagate(err, local_err);
+ return NULL;
+}
+
int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
{
FILE *fh;
+ Error *local_err = NULL;
int fd;
int64_t ret = -1, handle;
@@ -247,10 +363,9 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, E
mode = "r";
}
slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
- fh = fopen(path, mode);
- if (!fh) {
- error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')",
- path, mode);
+ fh = safe_open_or_create(path, mode, &local_err);
+ if (local_err != NULL) {
+ error_propagate(err, local_err);
return -1;
}
diff --git a/qga/main.c b/qga/main.c
index 1841759db2..44a283686b 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -478,7 +478,7 @@ static void become_daemon(const char *pidfile)
}
}
- umask(0);
+ umask(S_IRWXG | S_IRWXO);
sid = setsid();
if (sid < 0) {
goto fail;
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index f890f1c912..849cedf94c 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -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)
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 9f2adad805..1a501d9d33 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -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);
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 3e2e9f6b72..058c57fc19 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -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);