summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/i386/intel_iommu.c81
-rw-r--r--hw/i386/kvm/apic.c19
-rw-r--r--hw/i386/xen/xen_apic.c6
-rw-r--r--hw/intc/apic.c8
-rw-r--r--hw/intc/apic_common.c1
-rw-r--r--include/hw/compat.h4
-rw-r--r--include/hw/i386/apic_internal.h6
-rw-r--r--include/hw/i386/intel_iommu.h2
-rw-r--r--qapi-schema.json23
-rw-r--r--target-i386/cpu-qom.h4
-rw-r--r--target-i386/cpu.c519
-rw-r--r--target-i386/kvm-stub.c5
-rw-r--r--target-i386/kvm.c26
-rw-r--r--target-i386/kvm_i386.h1
-rw-r--r--tests/test-x86-cpuid-compat.c44
15 files changed, 526 insertions, 223 deletions
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 2efd69bbd7..1655a65bce 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "intel_iommu_internal.h"
@@ -32,6 +33,8 @@
#include "hw/i386/x86-iommu.h"
#include "hw/pci-host/q35.h"
#include "sysemu/kvm.h"
+#include "hw/i386/apic_internal.h"
+#include "kvm_i386.h"
/*#define DEBUG_INTEL_IOMMU*/
#ifdef DEBUG_INTEL_IOMMU
@@ -280,18 +283,17 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
static void vtd_generate_interrupt(IntelIOMMUState *s, hwaddr mesg_addr_reg,
hwaddr mesg_data_reg)
{
- hwaddr addr;
- uint32_t data;
+ MSIMessage msi;
assert(mesg_data_reg < DMAR_REG_SIZE);
assert(mesg_addr_reg < DMAR_REG_SIZE);
- addr = vtd_get_long_raw(s, mesg_addr_reg);
- data = vtd_get_long_raw(s, mesg_data_reg);
+ msi.address = vtd_get_long_raw(s, mesg_addr_reg);
+ msi.data = vtd_get_long_raw(s, mesg_data_reg);
- VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32, addr, data);
- address_space_stl_le(&address_space_memory, addr, data,
- MEMTXATTRS_UNSPECIFIED, NULL);
+ VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32,
+ msi.address, msi.data);
+ apic_get_class()->send_msi(&msi);
}
/* Generate a fault event to software via MSI if conditions are met.
@@ -2012,6 +2014,9 @@ static const MemoryRegionOps vtd_mem_ops = {
static Property vtd_properties[] = {
DEFINE_PROP_UINT32("version", IntelIOMMUState, version, 0),
+ DEFINE_PROP_ON_OFF_AUTO("eim", IntelIOMMUState, intr_eim,
+ ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_BOOL("x-buggy-eim", IntelIOMMUState, buggy_eim, false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2134,6 +2139,7 @@ static void vtd_generate_msi_message(VTDIrq *irq, MSIMessage *msg_out)
msg.dest_mode = irq->dest_mode;
msg.redir_hint = irq->redir_hint;
msg.dest = irq->dest;
+ msg.__addr_hi = irq->dest & 0xffffff00;
msg.__addr_head = cpu_to_le32(0xfee);
/* Keep this from original MSI address bits */
msg.__not_used = irq->msi_addr_last_bits;
@@ -2293,11 +2299,7 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr,
" for device sid 0x%04x",
to.address, to.data, sid);
- if (dma_memory_write(&address_space_memory, to.address,
- &to.data, size)) {
- VTD_DPRINTF(GENERAL, "error: fail to write 0x%"PRIx64
- " value 0x%"PRIx32, to.address, to.data);
- }
+ apic_get_class()->send_msi(&to);
return MEMTX_OK;
}
@@ -2382,7 +2384,11 @@ static void vtd_init(IntelIOMMUState *s)
s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO;
if (x86_iommu->intr_supported) {
- s->ecap |= VTD_ECAP_IR | VTD_ECAP_EIM | VTD_ECAP_MHMV;
+ s->ecap |= VTD_ECAP_IR | VTD_ECAP_MHMV;
+ if (s->intr_eim == ON_OFF_AUTO_ON) {
+ s->ecap |= VTD_ECAP_EIM;
+ }
+ assert(s->intr_eim != ON_OFF_AUTO_AUTO);
}
vtd_reset_context_cache(s);
@@ -2463,6 +2469,42 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
return &vtd_as->as;
}
+static bool vtd_decide_config(IntelIOMMUState *s, Error **errp)
+{
+ X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
+
+ /* Currently Intel IOMMU IR only support "kernel-irqchip={off|split}" */
+ if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() &&
+ !kvm_irqchip_is_split()) {
+ error_setg(errp, "Intel Interrupt Remapping cannot work with "
+ "kernel-irqchip=on, please use 'split|off'.");
+ return false;
+ }
+ if (s->intr_eim == ON_OFF_AUTO_ON && !x86_iommu->intr_supported) {
+ error_setg(errp, "eim=on cannot be selected without intremap=on");
+ return false;
+ }
+
+ if (s->intr_eim == ON_OFF_AUTO_AUTO) {
+ s->intr_eim = (kvm_irqchip_in_kernel() || s->buggy_eim)
+ && x86_iommu->intr_supported ?
+ ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
+ }
+ if (s->intr_eim == ON_OFF_AUTO_ON && !s->buggy_eim) {
+ if (!kvm_irqchip_in_kernel()) {
+ error_setg(errp, "eim=on requires accel=kvm,kernel-irqchip=split");
+ return false;
+ }
+ if (!kvm_enable_x2apic()) {
+ error_setg(errp, "eim=on requires support on the KVM side"
+ "(X2APIC_API, first shipped in v4.7)");
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void vtd_realize(DeviceState *dev, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
@@ -2472,6 +2514,11 @@ static void vtd_realize(DeviceState *dev, Error **errp)
VTD_DPRINTF(GENERAL, "");
x86_iommu->type = TYPE_INTEL;
+
+ if (!vtd_decide_config(s, errp)) {
+ return;
+ }
+
memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
"intel_iommu", DMAR_REG_SIZE);
@@ -2486,14 +2533,6 @@ static void vtd_realize(DeviceState *dev, Error **errp)
pci_setup_iommu(bus, vtd_host_dma_iommu, dev);
/* Pseudo address space under root PCI bus. */
pcms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC);
-
- /* Currently Intel IOMMU IR only support "kernel-irqchip={off|split}" */
- if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() &&
- !kvm_irqchip_is_split()) {
- error_report("Intel Interrupt Remapping cannot work with "
- "kernel-irqchip=on, please use 'split|off'.");
- exit(1);
- }
}
static void vtd_class_init(ObjectClass *klass, void *data)
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index c016e63fc2..be55102c00 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -169,6 +169,17 @@ static void kvm_apic_external_nmi(APICCommonState *s)
run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s);
}
+static void kvm_send_msi(MSIMessage *msg)
+{
+ int ret;
+
+ ret = kvm_irqchip_send_msi(kvm_state, *msg);
+ if (ret < 0) {
+ fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
+ strerror(-ret));
+ }
+}
+
static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -179,13 +190,8 @@ static void kvm_apic_mem_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
MSIMessage msg = { .address = addr, .data = data };
- int ret;
- ret = kvm_irqchip_send_msi(kvm_state, msg);
- if (ret < 0) {
- fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
- strerror(-ret));
- }
+ kvm_send_msi(&msg);
}
static const MemoryRegionOps kvm_apic_io_ops = {
@@ -232,6 +238,7 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data)
k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting;
k->vapic_base_update = kvm_apic_vapic_base_update;
k->external_nmi = kvm_apic_external_nmi;
+ k->send_msi = kvm_send_msi;
}
static const TypeInfo kvm_apic_info = {
diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c
index 21d68ee04b..55769eba7e 100644
--- a/hw/i386/xen/xen_apic.c
+++ b/hw/i386/xen/xen_apic.c
@@ -68,6 +68,11 @@ static void xen_apic_external_nmi(APICCommonState *s)
{
}
+static void xen_send_msi(MSIMessage *msi)
+{
+ xen_hvm_inject_msi(msi->address, msi->data);
+}
+
static void xen_apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
@@ -78,6 +83,7 @@ static void xen_apic_class_init(ObjectClass *klass, void *data)
k->get_tpr = xen_apic_get_tpr;
k->vapic_base_update = xen_apic_vapic_base_update;
k->external_nmi = xen_apic_external_nmi;
+ k->send_msi = xen_send_msi;
}
static const TypeInfo xen_apic_info = {
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 7bd1d279c4..fe15fb6024 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -740,8 +740,10 @@ static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
return val;
}
-static void apic_send_msi(hwaddr addr, uint32_t data)
+static void apic_send_msi(MSIMessage *msi)
{
+ uint64_t addr = msi->address;
+ uint32_t data = msi->data;
uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
@@ -762,7 +764,8 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
* APIC is connected directly to the CPU.
* Mapping them on the global bus happens to work because
* MSI registers are reserved in APIC MMIO and vice versa. */
- apic_send_msi(addr, val);
+ MSIMessage msi = { .address = addr, .data = val };
+ apic_send_msi(&msi);
return;
}
@@ -913,6 +916,7 @@ static void apic_class_init(ObjectClass *klass, void *data)
k->external_nmi = apic_external_nmi;
k->pre_save = apic_pre_save;
k->post_load = apic_post_load;
+ k->send_msi = apic_send_msi;
}
static const TypeInfo apic_info = {
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 14ac43c186..8d01c9c875 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -18,6 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qemu-common.h"
#include "cpu.h"
diff --git a/include/hw/compat.h b/include/hw/compat.h
index ef3fae3e1b..0f06e113be 100644
--- a/include/hw/compat.h
+++ b/include/hw/compat.h
@@ -14,6 +14,10 @@
.driver = "ioapic",\
.property = "version",\
.value = "0x11",\
+ },{\
+ .driver = "intel-iommu",\
+ .property = "x-buggy-eim",\
+ .value = "true",\
},
#define HW_COMPAT_2_6 \
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 06c4e9f6f9..cdd11fb093 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -146,6 +146,10 @@ typedef struct APICCommonClass
void (*pre_save)(APICCommonState *s);
void (*post_load)(APICCommonState *s);
void (*reset)(APICCommonState *s);
+ /* send_msi emulates an APIC bus and its proper place would be in a new
+ * device, but it's convenient to have it here for now.
+ */
+ void (*send_msi)(MSIMessage *msi);
} APICCommonClass;
struct APICCommonState {
@@ -222,4 +226,6 @@ static inline int apic_get_bit(uint32_t *tab, int index)
return !!(tab[i] & mask);
}
+APICCommonClass *apic_get_class(void);
+
#endif /* QEMU_APIC_INTERNAL_H */
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index a42dbd745a..1989c1eec1 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -289,6 +289,8 @@ struct IntelIOMMUState {
dma_addr_t intr_root; /* Interrupt remapping table pointer */
uint32_t intr_size; /* Number of IR table entries */
bool intr_eime; /* Extended interrupt mode enabled */
+ OnOffAuto intr_eim; /* Toggle for EIM cabability */
+ bool buggy_eim; /* Force buggy EIM unless eim=off */
};
/* Find the VTD Address space associated with the given bus pointer,
diff --git a/qapi-schema.json b/qapi-schema.json
index ded1179f06..5a8ec38e10 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3101,10 +3101,31 @@
# QEMU version, machine type, machine options and accelerator options.
# A static model is always migration-safe. (since 2.8)
#
+# @unavailable-features: #optional List of properties that prevent
+# the CPU model from running in the current
+# host. (since 2.8)
+#
+# @unavailable-features is a list of QOM property names that
+# represent CPU model attributes that prevent the CPU from running.
+# If the QOM property is read-only, that means there's no known
+# way to make the CPU model run in the current host. Implementations
+# that choose not to provide specific information return the
+# property name "type".
+# If the property is read-write, it means that it MAY be possible
+# to run the CPU model in the current host if that property is
+# changed. Management software can use it as hints to suggest or
+# choose an alternative for the user, or just to generate meaningful
+# error messages explaining why the CPU model can't be used.
+# If @unavailable-features is an empty list, the CPU model is
+# runnable using the current host and machine-type.
+# If @unavailable-features is not present, runnability
+# information for the CPU is not available.
+#
# Since: 1.2.0
##
{ 'struct': 'CpuDefinitionInfo',
- 'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool' } }
+ 'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool',
+ '*unavailable-features': [ 'str' ] } }
##
# @query-cpu-definitions:
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index 5dde658ac4..e724004a78 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -63,6 +63,10 @@ typedef struct X86CPUClass {
bool kvm_required;
+ /* Optional description of CPU model.
+ * If unavailable, cpu_def->model_id is used */
+ const char *model_description;
+
DeviceRealize parent_realize;
void (*parent_reset)(CPUState *cpu);
} X86CPUClass;
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 1c57fce81b..d95514c7dd 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -278,12 +278,12 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_1_ECX] = {
.feat_names = {
- "pni|sse3" /* Intel,AMD sse3 */, "pclmulqdq|pclmuldq", "dtes64", "monitor",
- "ds_cpl", "vmx", "smx", "est",
+ "pni" /* Intel,AMD sse3 */, "pclmulqdq", "dtes64", "monitor",
+ "ds-cpl", "vmx", "smx", "est",
"tm2", "ssse3", "cid", NULL,
"fma", "cx16", "xtpr", "pdcm",
- NULL, "pcid", "dca", "sse4.1|sse4_1",
- "sse4.2|sse4_2", "x2apic", "movbe", "popcnt",
+ NULL, "pcid", "dca", "sse4.1",
+ "sse4.2", "x2apic", "movbe", "popcnt",
"tsc-deadline", "aes", "xsave", "osxsave",
"avx", "f16c", "rdrand", "hypervisor",
},
@@ -302,22 +302,22 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
NULL /* cx8 */, NULL /* apic */, NULL, "syscall",
NULL /* mtrr */, NULL /* pge */, NULL /* mca */, NULL /* cmov */,
NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */,
- "nx|xd", NULL, "mmxext", NULL /* mmx */,
- NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb", "rdtscp",
- NULL, "lm|i64", "3dnowext", "3dnow",
+ "nx", NULL, "mmxext", NULL /* mmx */,
+ NULL /* fxsr */, "fxsr-opt", "pdpe1gb", "rdtscp",
+ NULL, "lm", "3dnowext", "3dnow",
},
.cpuid_eax = 0x80000001, .cpuid_reg = R_EDX,
.tcg_features = TCG_EXT2_FEATURES,
},
[FEAT_8000_0001_ECX] = {
.feat_names = {
- "lahf_lm", "cmp_legacy", "svm", "extapic",
+ "lahf-lm", "cmp-legacy", "svm", "extapic",
"cr8legacy", "abm", "sse4a", "misalignsse",
"3dnowprefetch", "osvw", "ibs", "xop",
"skinit", "wdt", NULL, "lwp",
- "fma4", "tce", NULL, "nodeid_msr",
- NULL, "tbm", "topoext", "perfctr_core",
- "perfctr_nb", NULL, NULL, NULL,
+ "fma4", "tce", NULL, "nodeid-msr",
+ NULL, "tbm", "topoext", "perfctr-core",
+ "perfctr-nb", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
},
.cpuid_eax = 0x80000001, .cpuid_reg = R_ECX,
@@ -339,8 +339,8 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_KVM] = {
.feat_names = {
- "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock",
- "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", "kvm_pv_unhalt",
+ "kvmclock", "kvm-nopiodelay", "kvm-mmu", "kvmclock",
+ "kvm-asyncpf", "kvm-steal-time", "kvm-pv-eoi", "kvm-pv-unhalt",
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
@@ -400,9 +400,9 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_SVM] = {
.feat_names = {
- "npt", "lbrv", "svm_lock", "nrip_save",
- "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
- NULL, NULL, "pause_filter", NULL,
+ "npt", "lbrv", "svm-lock", "nrip-save",
+ "tsc-scale", "vmcb-clean", "flushbyasid", "decodeassists",
+ NULL, NULL, "pause-filter", NULL,
"pfthreshold", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
@@ -414,7 +414,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_7_0_EBX] = {
.feat_names = {
- "fsgsbase", "tsc_adjust", NULL, "bmi1",
+ "fsgsbase", "tsc-adjust", NULL, "bmi1",
"hle", "avx2", NULL, "smep",
"bmi2", "erms", "invpcid", "rtm",
NULL, NULL, "mpx", NULL,
@@ -535,6 +535,20 @@ typedef struct ExtSaveArea {
} ExtSaveArea;
static const ExtSaveArea x86_ext_save_areas[] = {
+ [XSTATE_FP_BIT] = {
+ /* x87 FP state component is always enabled if XSAVE is supported */
+ .feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE,
+ /* x87 state is in the legacy region of the XSAVE area */
+ .offset = 0,
+ .size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader),
+ },
+ [XSTATE_SSE_BIT] = {
+ /* SSE state component is always enabled if XSAVE is supported */
+ .feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE,
+ /* SSE state is in the legacy region of the XSAVE area */
+ .offset = 0,
+ .size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader),
+ },
[XSTATE_YMM_BIT] =
{ .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX,
.offset = offsetof(X86XSaveArea, avx_state),
@@ -568,9 +582,9 @@ static const ExtSaveArea x86_ext_save_areas[] = {
static uint32_t xsave_area_size(uint64_t mask)
{
int i;
- uint64_t ret = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader);
+ uint64_t ret = 0;
- for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
+ for (i = 0; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
const ExtSaveArea *esa = &x86_ext_save_areas[i];
if ((mask >> i) & 1) {
ret = MAX(ret, esa->offset + esa->size);
@@ -650,85 +664,6 @@ void host_cpuid(uint32_t function, uint32_t count,
*edx = vec[3];
}
-#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 true, otherwise return false
- */
-static bool lookup_feature(uint32_t *pval, const char *s, const char *e,
- const char **featureset)
-{
- uint32_t mask;
- const char **ppc;
- bool found = false;
-
- for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc) {
- if (*ppc && !altcmp(s, e, *ppc)) {
- *pval |= mask;
- found = true;
- }
- }
- return found;
-}
-
-static void add_flagname_to_bitmaps(const char *flagname,
- FeatureWordArray words,
- Error **errp)
-{
- FeatureWord w;
- for (w = 0; w < FEATURE_WORDS; w++) {
- FeatureWordInfo *wi = &feature_word_info[w];
- if (lookup_feature(&words[w], flagname, NULL, wi->feat_names)) {
- break;
- }
- }
- if (w == FEATURE_WORDS) {
- error_setg(errp, "CPU feature %s not found", flagname);
- }
-}
-
/* CPU class name definitions: */
#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU
@@ -1550,6 +1485,14 @@ static PropValue kvm_default_props[] = {
{ NULL, NULL },
};
+/* TCG-specific defaults that override all CPU models when using TCG
+ */
+static PropValue tcg_default_props[] = {
+ { "vme", "off" },
+ { NULL, NULL },
+};
+
+
void x86_cpu_change_kvm_default(const char *prop, const char *value)
{
PropValue *pv;
@@ -1628,6 +1571,9 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data)
cpu_x86_fill_model_id(host_cpudef.model_id);
xcc->cpu_def = &host_cpudef;
+ xcc->model_description =
+ "KVM processor with all supported host features "
+ "(only available in KVM mode)";
/* level, xlevel, xlevel2, and the feature words are initialized on
* instance_init, because they require KVM to be initialized.
@@ -1999,13 +1945,33 @@ static inline void feat2prop(char *s)
}
}
+/* Return the feature property name for a feature flag bit */
+static const char *x86_cpu_feature_name(FeatureWord w, int bitnr)
+{
+ /* XSAVE components are automatically enabled by other features,
+ * so return the original feature name instead
+ */
+ if (w == FEAT_XSAVE_COMP_LO || w == FEAT_XSAVE_COMP_HI) {
+ int comp = (w == FEAT_XSAVE_COMP_HI) ? bitnr + 32 : bitnr;
+
+ if (comp < ARRAY_SIZE(x86_ext_save_areas) &&
+ x86_ext_save_areas[comp].bits) {
+ w = x86_ext_save_areas[comp].feature;
+ bitnr = ctz32(x86_ext_save_areas[comp].bits);
+ }
+ }
+
+ assert(bitnr < 32);
+ assert(w < FEATURE_WORDS);
+ return feature_word_info[w].feat_names[bitnr];
+}
+
/* Compatibily hack to maintain legacy +-feat semantic,
* where +-feat overwrites any feature set by
* feat=on|feat even if the later is parsed after +-feat
* (i.e. "-x2apic,x2apic=on" will result in x2apic disabled)
*/
-static FeatureWordArray plus_features = { 0 };
-static FeatureWordArray minus_features = { 0 };
+static GList *plus_features, *minus_features;
/* Parse "+feature,-feature,feature=foo" CPU feature string
*/
@@ -2036,10 +2002,12 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features,
/* Compatibility syntax: */
if (featurestr[0] == '+') {
- add_flagname_to_bitmaps(featurestr + 1, plus_features, &local_err);
+ plus_features = g_list_append(plus_features,
+ g_strdup(featurestr + 1));
continue;
} else if (featurestr[0] == '-') {
- add_flagname_to_bitmaps(featurestr + 1, minus_features, &local_err);
+ minus_features = g_list_append(minus_features,
+ g_strdup(featurestr + 1));
continue;
}
@@ -2083,6 +2051,59 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features,
}
}
+static void x86_cpu_load_features(X86CPU *cpu, Error **errp);
+static int x86_cpu_filter_features(X86CPU *cpu);
+
+/* Check for missing features that may prevent the CPU class from
+ * running using the current machine and accelerator.
+ */
+static void x86_cpu_class_check_missing_features(X86CPUClass *xcc,
+ strList **missing_feats)
+{
+ X86CPU *xc;
+ FeatureWord w;
+ Error *err = NULL;
+ strList **next = missing_feats;
+
+ if (xcc->kvm_required && !kvm_enabled()) {
+ strList *new = g_new0(strList, 1);
+ new->value = g_strdup("kvm");;
+ *missing_feats = new;
+ return;
+ }
+
+ xc = X86_CPU(object_new(object_class_get_name(OBJECT_CLASS(xcc))));
+
+ x86_cpu_load_features(xc, &err);
+ if (err) {
+ /* Errors at x86_cpu_load_features should never happen,
+ * but in case it does, just report the model as not
+ * runnable at all using the "type" property.
+ */
+ strList *new = g_new0(strList, 1);
+ new->value = g_strdup("type");
+ *next = new;
+ next = &new->next;
+ }
+
+ x86_cpu_filter_features(xc);
+
+ for (w = 0; w < FEATURE_WORDS; w++) {
+ uint32_t filtered = xc->filtered_features[w];
+ int i;
+ for (i = 0; i < 32; i++) {
+ if (filtered & (1UL << i)) {
+ strList *new = g_new0(strList, 1);
+ new->value = g_strdup(x86_cpu_feature_name(w, i));
+ *next = new;
+ next = &new->next;
+ }
+ }
+ }
+
+ object_unref(OBJECT(xc));
+}
+
/* Print all cpuid feature names in featureset
*/
static void listflags(FILE *f, fprintf_function print, const char **featureset)
@@ -2098,23 +2119,62 @@ static void listflags(FILE *f, fprintf_function print, const char **featureset)
}
}
-/* generate CPU information. */
+/* Sort alphabetically by type name, listing kvm_required models last. */
+static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b)
+{
+ ObjectClass *class_a = (ObjectClass *)a;
+ ObjectClass *class_b = (ObjectClass *)b;
+ X86CPUClass *cc_a = X86_CPU_CLASS(class_a);
+ X86CPUClass *cc_b = X86_CPU_CLASS(class_b);
+ const char *name_a, *name_b;
+
+ if (cc_a->kvm_required != cc_b->kvm_required) {
+ /* kvm_required items go last */
+ return cc_a->kvm_required ? 1 : -1;
+ } else {
+ name_a = object_class_get_name(class_a);
+ name_b = object_class_get_name(class_b);
+ return strcmp(name_a, name_b);
+ }
+}
+
+static GSList *get_sorted_cpu_model_list(void)
+{
+ GSList *list = object_class_get_list(TYPE_X86_CPU, false);
+ list = g_slist_sort(list, x86_cpu_list_compare);
+ return list;
+}
+
+static void x86_cpu_list_entry(gpointer data, gpointer user_data)
+{
+ ObjectClass *oc = data;
+ X86CPUClass *cc = X86_CPU_CLASS(oc);
+ CPUListState *s = user_data;
+ char *name = x86_cpu_class_get_model_name(cc);
+ const char *desc = cc->model_description;
+ if (!desc) {
+ desc = cc->cpu_def->model_id;
+ }
+
+ (*s->cpu_fprintf)(s->file, "x86 %16s %-48s\n",
+ name, desc);
+ g_free(name);
+}
+
+/* list available CPU models and flags */
void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf)
{
- X86CPUDefinition *def;
- char buf[256];
int i;
+ CPUListState s = {
+ .file = f,
+ .cpu_fprintf = cpu_fprintf,
+ };
+ GSList *list;
- for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
- def = &builtin_x86_defs[i];
- snprintf(buf, sizeof(buf), "%s", def->name);
- (*cpu_fprintf)(f, "x86 %16s %-48s\n", buf, def->model_id);
- }
-#ifdef CONFIG_KVM
- (*cpu_fprintf)(f, "x86 %16s %-48s\n", "host",
- "KVM processor with all supported host features "
- "(only available in KVM mode)");
-#endif
+ (*cpu_fprintf)(f, "Available CPUs:\n");
+ list = get_sorted_cpu_model_list();
+ g_slist_foreach(list, x86_cpu_list_entry, &s);
+ g_slist_free(list);
(*cpu_fprintf)(f, "\nRecognized CPUID flags:\n");
for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) {
@@ -2126,26 +2186,31 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf)
}
}
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+static void x86_cpu_definition_entry(gpointer data, gpointer user_data)
{
- CpuDefinitionInfoList *cpu_list = NULL;
- X86CPUDefinition *def;
- int i;
+ ObjectClass *oc = data;
+ X86CPUClass *cc = X86_CPU_CLASS(oc);
+ CpuDefinitionInfoList **cpu_list = user_data;
+ CpuDefinitionInfoList *entry;
+ CpuDefinitionInfo *info;
- for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
- CpuDefinitionInfoList *entry;
- CpuDefinitionInfo *info;
-
- def = &builtin_x86_defs[i];
- info = g_malloc0(sizeof(*info));
- info->name = g_strdup(def->name);
+ info = g_malloc0(sizeof(*info));
+ info->name = x86_cpu_class_get_model_name(cc);
+ x86_cpu_class_check_missing_features(cc, &info->unavailable_features);
+ info->has_unavailable_features = true;
- entry = g_malloc0(sizeof(*entry));
- entry->value = info;
- entry->next = cpu_list;
- cpu_list = entry;
- }
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = info;
+ entry->next = *cpu_list;
+ *cpu_list = entry;
+}
+CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+{
+ CpuDefinitionInfoList *cpu_list = NULL;
+ GSList *list = get_sorted_cpu_model_list();
+ g_slist_foreach(list, x86_cpu_definition_entry, &cpu_list);
+ g_slist_free(list);
return cpu_list;
}
@@ -2183,14 +2248,11 @@ static int x86_cpu_filter_features(X86CPU *cpu)
for (w = 0; w < FEATURE_WORDS; w++) {
uint32_t host_feat =
- x86_cpu_get_supported_feature_word(w, cpu->migratable);
+ x86_cpu_get_supported_feature_word(w, false);
uint32_t requested_features = env->features[w];
env->features[w] &= host_feat;
cpu->filtered_features[w] = requested_features & ~env->features[w];
if (cpu->filtered_features[w]) {
- if (cpu->check_cpuid || cpu->enforce_cpuid) {
- report_unavailable_features(w, cpu->filtered_features[w]);
- }
rv = 1;
}
}
@@ -2198,6 +2260,15 @@ static int x86_cpu_filter_features(X86CPU *cpu)
return rv;
}
+static void x86_cpu_report_filtered_features(X86CPU *cpu)
+{
+ FeatureWord w;
+
+ for (w = 0; w < FEATURE_WORDS; w++) {
+ report_unavailable_features(w, cpu->filtered_features[w]);
+ }
+}
+
static void x86_cpu_apply_props(X86CPU *cpu, PropValue *props)
{
PropValue *pv;
@@ -2238,6 +2309,8 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp)
}
x86_cpu_apply_props(cpu, kvm_default_props);
+ } else if (tcg_enabled()) {
+ x86_cpu_apply_props(cpu, tcg_default_props);
}
env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR;
@@ -2848,9 +2921,8 @@ static void mce_init(X86CPU *cpu)
}
#ifndef CONFIG_USER_ONLY
-static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
+APICCommonClass *apic_get_class(void)
{
- APICCommonState *apic;
const char *apic_type = "apic";
if (kvm_apic_in_kernel()) {
@@ -2859,7 +2931,15 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
apic_type = "xen-apic";
}
- cpu->apic_state = DEVICE(object_new(apic_type));
+ return APIC_COMMON_CLASS(object_class_by_name(apic_type));
+}
+
+static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
+{
+ APICCommonState *apic;
+ ObjectClass *apic_class = OBJECT_CLASS(apic_get_class());
+
+ cpu->apic_state = DEVICE(object_new(object_class_get_name(apic_class)));
object_property_add_child(OBJECT(cpu), "lapic",
OBJECT(cpu->apic_state), &error_abort);
@@ -2984,8 +3064,8 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu)
return;
}
- mask = (XSTATE_FP_MASK | XSTATE_SSE_MASK);
- for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
+ mask = 0;
+ for (i = 0; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
const ExtSaveArea *esa = &x86_ext_save_areas[i];
if (env->features[esa->feature] & esa->bits) {
mask |= (1ULL << i);
@@ -2996,33 +3076,13 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu)
env->features[FEAT_XSAVE_COMP_HI] = mask >> 32;
}
-#define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \
- (env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \
- (env)->cpuid_vendor3 == CPUID_VENDOR_INTEL_3)
-#define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \
- (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \
- (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3)
-static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
+/* Load CPUID data based on configured features */
+static void x86_cpu_load_features(X86CPU *cpu, Error **errp)
{
- CPUState *cs = CPU(dev);
- X86CPU *cpu = X86_CPU(dev);
- X86CPUClass *xcc = X86_CPU_GET_CLASS(dev);
CPUX86State *env = &cpu->env;
- Error *local_err = NULL;
- static bool ht_warned;
FeatureWord w;
-
- if (xcc->kvm_required && !kvm_enabled()) {
- char *name = x86_cpu_class_get_model_name(xcc);
- error_setg(&local_err, "CPU model '%s' requires KVM", name);
- g_free(name);
- goto out;
- }
-
- if (cpu->apic_id == UNASSIGNED_APIC_ID) {
- error_setg(errp, "apic-id property was not initialized properly");
- return;
- }
+ GList *l;
+ Error *local_err = NULL;
/*TODO: cpu->host_features incorrectly overwrites features
* set using "feat=on|off". Once we fix this, we can convert
@@ -3036,9 +3096,20 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
}
}
- for (w = 0; w < FEATURE_WORDS; w++) {
- cpu->env.features[w] |= plus_features[w];
- cpu->env.features[w] &= ~minus_features[w];
+ for (l = plus_features; l; l = l->next) {
+ const char *prop = l->data;
+ object_property_set_bool(OBJECT(cpu), true, prop, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ }
+
+ for (l = minus_features; l; l = l->next) {
+ const char *prop = l->data;
+ object_property_set_bool(OBJECT(cpu), false, prop, &local_err);
+ if (local_err) {
+ goto out;
+ }
}
if (!kvm_enabled() || !cpu->expose_kvm) {
@@ -3077,14 +3148,56 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
env->cpuid_xlevel2 = env->cpuid_min_xlevel2;
}
- if (x86_cpu_filter_features(cpu) && cpu->enforce_cpuid) {
- error_setg(&local_err,
- kvm_enabled() ?
- "Host doesn't support requested features" :
- "TCG doesn't support requested features");
+out:
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ }
+}
+
+#define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \
+ (env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \
+ (env)->cpuid_vendor3 == CPUID_VENDOR_INTEL_3)
+#define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \
+ (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \
+ (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3)
+static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+ CPUState *cs = CPU(dev);
+ X86CPU *cpu = X86_CPU(dev);
+ X86CPUClass *xcc = X86_CPU_GET_CLASS(dev);
+ CPUX86State *env = &cpu->env;
+ Error *local_err = NULL;
+ static bool ht_warned;
+
+ if (xcc->kvm_required && !kvm_enabled()) {
+ char *name = x86_cpu_class_get_model_name(xcc);
+ error_setg(&local_err, "CPU model '%s' requires KVM", name);
+ g_free(name);
+ goto out;
+ }
+
+ if (cpu->apic_id == UNASSIGNED_APIC_ID) {
+ error_setg(errp, "apic-id property was not initialized properly");
+ return;
+ }
+
+ x86_cpu_load_features(cpu, &local_err);
+ if (local_err) {
goto out;
}
+ if (x86_cpu_filter_features(cpu) &&
+ (cpu->check_cpuid || cpu->enforce_cpuid)) {
+ x86_cpu_report_filtered_features(cpu);
+ if (cpu->enforce_cpuid) {
+ error_setg(&local_err,
+ kvm_enabled() ?
+ "Host doesn't support requested features" :
+ "TCG doesn't support requested features");
+ goto out;
+ }
+ }
+
/* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on
* CPUID[1].EDX.
*/
@@ -3332,27 +3445,22 @@ static void x86_cpu_register_feature_bit_props(X86CPU *cpu,
FeatureWord w,
int bitnr)
{
- Object *obj = OBJECT(cpu);
- int i;
- char **names;
FeatureWordInfo *fi = &feature_word_info[w];
+ const char *name = fi->feat_names[bitnr];
- if (!fi->feat_names[bitnr]) {
+ if (!name) {
return;
}
- names = g_strsplit(fi->feat_names[bitnr], "|", 0);
-
- feat2prop(names[0]);
- x86_cpu_register_bit_prop(cpu, names[0], &cpu->env.features[w], bitnr);
-
- for (i = 1; names[i]; i++) {
- feat2prop(names[i]);
- object_property_add_alias(obj, names[i], obj, names[0],
- &error_abort);
- }
-
- g_strfreev(names);
+ /* Property names should use "-" instead of "_".
+ * Old names containing underscores are registered as aliases
+ * using object_property_add_alias()
+ */
+ assert(!strchr(name, '_'));
+ /* aliases don't use "|" delimiters anymore, they are registered
+ * manually using object_property_add_alias() */
+ assert(!strchr(name, '|'));
+ x86_cpu_register_bit_prop(cpu, name, &cpu->env.features[w], bitnr);
}
static void x86_cpu_initfn(Object *obj)
@@ -3400,6 +3508,36 @@ static void x86_cpu_initfn(Object *obj)
}
}
+ object_property_add_alias(obj, "sse3", obj, "pni", &error_abort);
+ object_property_add_alias(obj, "pclmuldq", obj, "pclmulqdq", &error_abort);
+ object_property_add_alias(obj, "sse4-1", obj, "sse4.1", &error_abort);
+ object_property_add_alias(obj, "sse4-2", obj, "sse4.2", &error_abort);
+ object_property_add_alias(obj, "xd", obj, "nx", &error_abort);
+ object_property_add_alias(obj, "ffxsr", obj, "fxsr-opt", &error_abort);
+ object_property_add_alias(obj, "i64", obj, "lm", &error_abort);
+
+ object_property_add_alias(obj, "ds_cpl", obj, "ds-cpl", &error_abort);
+ object_property_add_alias(obj, "tsc_adjust", obj, "tsc-adjust", &error_abort);
+ object_property_add_alias(obj, "fxsr_opt", obj, "fxsr-opt", &error_abort);
+ object_property_add_alias(obj, "lahf_lm", obj, "lahf-lm", &error_abort);
+ object_property_add_alias(obj, "cmp_legacy", obj, "cmp-legacy", &error_abort);
+ object_property_add_alias(obj, "nodeid_msr", obj, "nodeid-msr", &error_abort);
+ object_property_add_alias(obj, "perfctr_core", obj, "perfctr-core", &error_abort);
+ object_property_add_alias(obj, "perfctr_nb", obj, "perfctr-nb", &error_abort);
+ object_property_add_alias(obj, "kvm_nopiodelay", obj, "kvm-nopiodelay", &error_abort);
+ object_property_add_alias(obj, "kvm_mmu", obj, "kvm-mmu", &error_abort);
+ object_property_add_alias(obj, "kvm_asyncpf", obj, "kvm-asyncpf", &error_abort);
+ object_property_add_alias(obj, "kvm_steal_time", obj, "kvm-steal-time", &error_abort);
+ object_property_add_alias(obj, "kvm_pv_eoi", obj, "kvm-pv-eoi", &error_abort);
+ object_property_add_alias(obj, "kvm_pv_unhalt", obj, "kvm-pv-unhalt", &error_abort);
+ object_property_add_alias(obj, "svm_lock", obj, "svm-lock", &error_abort);
+ object_property_add_alias(obj, "nrip_save", obj, "nrip-save", &error_abort);
+ object_property_add_alias(obj, "tsc_scale", obj, "tsc-scale", &error_abort);
+ object_property_add_alias(obj, "vmcb_clean", obj, "vmcb-clean", &error_abort);
+ object_property_add_alias(obj, "pause_filter", obj, "pause-filter", &error_abort);
+ object_property_add_alias(obj, "sse4_1", obj, "sse4.1", &error_abort);
+ object_property_add_alias(obj, "sse4_2", obj, "sse4.2", &error_abort);
+
x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort);
}
@@ -3537,11 +3675,6 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
cc->cpu_exec_exit = x86_cpu_exec_exit;
dc->cannot_instantiate_with_device_add_yet = false;
- /*
- * Reason: x86_cpu_initfn() calls cpu_exec_init(), which saves the
- * object in cpus -> dangling pointer after final object_unref().
- */
- dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo x86_cpu_type_info = {
diff --git a/target-i386/kvm-stub.c b/target-i386/kvm-stub.c
index cdf1506109..bda4dc2f0c 100644
--- a/target-i386/kvm-stub.c
+++ b/target-i386/kvm-stub.c
@@ -25,6 +25,11 @@ bool kvm_has_smm(void)
return 1;
}
+bool kvm_enable_x2apic(void)
+{
+ return false;
+}
+
/* This function is only called inside conditionals which we
* rely on the compiler to optimize out when CONFIG_KVM is not
* defined.
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index ee1f53e569..0472f45fd0 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -122,6 +122,32 @@ bool kvm_allows_irq0_override(void)
return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
}
+static bool kvm_x2apic_api_set_flags(uint64_t flags)
+{
+ KVMState *s = KVM_STATE(current_machine->accelerator);
+
+ return !kvm_vm_enable_cap(s, KVM_CAP_X2APIC_API, 0, flags);
+}
+
+#define MEMORIZE(fn) \
+ ({ \
+ static typeof(fn) _result; \
+ static bool _memorized; \
+ \
+ if (_memorized) { \
+ return _result; \
+ } \
+ _memorized = true; \
+ _result = fn; \
+ })
+
+bool kvm_enable_x2apic(void)
+{
+ return MEMORIZE(
+ kvm_x2apic_api_set_flags(KVM_X2APIC_API_USE_32BIT_IDS |
+ KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK));
+}
+
static int kvm_get_tsc(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
diff --git a/target-i386/kvm_i386.h b/target-i386/kvm_i386.h
index 36407e0a5d..5c369b1c5b 100644
--- a/target-i386/kvm_i386.h
+++ b/target-i386/kvm_i386.h
@@ -43,4 +43,5 @@ int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id);
void kvm_put_apicbase(X86CPU *cpu, uint64_t value);
+bool kvm_enable_x2apic(void);
#endif
diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c
index 83162a44c0..260dd2795c 100644
--- a/tests/test-x86-cpuid-compat.c
+++ b/tests/test-x86-cpuid-compat.c
@@ -3,6 +3,7 @@
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qint.h"
+#include "qapi/qmp/qbool.h"
#include "libqtest.h"
static char *get_cpu0_qom_path(void)
@@ -34,6 +35,15 @@ static QObject *qom_get(const char *path, const char *prop)
return ret;
}
+static bool qom_get_bool(const char *path, const char *prop)
+{
+ QBool *value = qobject_to_qbool(qom_get(path, prop));
+ bool b = qbool_get_bool(value);
+
+ QDECREF(value);
+ return b;
+}
+
typedef struct CpuidTestArgs {
const char *cmdline;
const char *property;
@@ -66,10 +76,44 @@ static void add_cpuid_test(const char *name, const char *cmdline,
qtest_add_data_func(name, args, test_cpuid_prop);
}
+static void test_plus_minus(void)
+{
+ char *path;
+
+ /* Rules:
+ * 1)"-foo" overrides "+foo"
+ * 2) "[+-]foo" overrides "foo=..."
+ * 3) Old feature names with underscores (e.g. "sse4_2")
+ * should keep working
+ *
+ * Note: rules 1 and 2 are planned to be removed soon, but we
+ * need to keep compatibility for a while until we start
+ * warning users about it.
+ */
+ qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on");
+ path = get_cpu0_qom_path();
+
+ g_assert_false(qom_get_bool(path, "fpu"));
+ g_assert_false(qom_get_bool(path, "mce"));
+ g_assert_true(qom_get_bool(path, "cx8"));
+
+ /* Test both the original and the alias feature names: */
+ g_assert_true(qom_get_bool(path, "sse4-1"));
+ g_assert_true(qom_get_bool(path, "sse4.1"));
+
+ g_assert_true(qom_get_bool(path, "sse4-2"));
+ g_assert_true(qom_get_bool(path, "sse4.2"));
+
+ qtest_end();
+ g_free(path);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
+ qtest_add_func("x86/cpuid/parsing-plus-minus", test_plus_minus);
+
/* Original level values for CPU models: */
add_cpuid_test("x86/cpuid/phenom/level",
"-cpu phenom", "level", 5);