x86 queue, 2016-10-17
-----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJYBQ+LAAoJECgHk2+YTcWmaF0QAISVeb39vJyyXNhxXsy1Y5iM WSYA8Dym0TWCXTd7Fq7Ck4VS9ZC7hAREKNSBs2hgVPutecL56iB/IjWrB0AyFAMK u5y4H1pI6l9TiH+6GcDWwQjthM/0v2pEHzQ2udLWBSpKJGjDPTSQIafZUgrW2uu0 J/Drxg17FJ6KixqCg3FemPBXucbuU1PSW2qEWIgVElwj843j3d/Wc5l1wNb24irN jnOcvJd9WQsuT2fUDXezrCRVQle92tHR1cHtu5bZvC1aMFbGuHfSA4pm7pXw3l5N 8H0fhrCoj6JGKRY/pzHGmLgwMTWJL4qASxr6sEKkMAyu59DdjQ0+U8EhOwoAHYhp gSrNgpwPKRr2OKrSUJXil7w1cQ+hsokgEo44SDEgsV4k9Rgbz8VVVct+LwOxwfwW l9sC9L5ONheFODfB3rgVFiyAbspYxzwOvGZ29VoeMyb4CS1CUBrsvka8DledFi+m By26W6IMtXBa4NZoYqp49zHqUZ5Wu62I32LCaWDKscUQfaEJKrf1DtFQ9FlWhy5F 4NeSzTo4eAp3WPRDscbvXIyEJfYqzf7gs8KQA9QD+aTceDOIPiZeMz6oMokFukE8 Lt1fWzzppFJ6ZyPLO1YI/T91fOskl45r8b3T242fovOKGAlujkunpRcTAYaybN5C qUv5Qrq6ZujcRecupLfE =KM/h -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ehabkost/tags/x86-pull-request' into staging x86 queue, 2016-10-17 # gpg: Signature made Mon 17 Oct 2016 18:51:07 BST # gpg: using RSA key 0x2807936F984DC5A6 # gpg: Good signature from "Eduardo Habkost <ehabkost@redhat.com>" # Primary key fingerprint: 5A32 2FD5 ABC4 D3DB ACCF D1AA 2807 936F 984D C5A6 * remotes/ehabkost/tags/x86-pull-request: (21 commits) target-i386: Don't use cpu->migratable when filtering features target-i386: Return runnability information on query-cpu-definitions target-i386: x86_cpu_load_features() function target-i386: Unset cannot_destroy_with_object_finalize_yet target-i386/kvm: cache the return value of kvm_enable_x2apic() intel_iommu: reject broken EIM intel_iommu: add OnOffAuto intr_eim as "eim" property intel_iommu: redo configuraton check in realize intel_iommu: pass whole remapped addresses to apic apic: add send_msi() to APICCommonClass apic: add global apic_get_class() target-i386: Move warning code outside x86_cpu_filter_features() qmp: Add runnability information to query-cpu-definitions target-i386: xsave: Add FP and SSE bits to x86_ext_save_areas target-i386: Register properties for feature aliases manually target-i386: Remove underscores from feat_names arrays target-i386: Make plus_features/minus_features QOM-based target-i386: Register aliases for feature names with underscores target-i386: Disable VME by default with TCG target-i386: List CPU models using subclass list ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
		
						commit
						e8ddc2eae5
					
				| @ -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) | ||||
|  | ||||
| @ -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 = { | ||||
|  | ||||
| @ -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 = { | ||||
|  | ||||
| @ -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 = { | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -14,6 +14,10 @@ | ||||
|         .driver   = "ioapic",\ | ||||
|         .property = "version",\ | ||||
|         .value    = "0x11",\ | ||||
|     },{\ | ||||
|         .driver   = "intel-iommu",\ | ||||
|         .property = "x-buggy-eim",\ | ||||
|         .value    = "true",\ | ||||
|     }, | ||||
| 
 | ||||
| #define HW_COMPAT_2_6 \ | ||||
|  | ||||
| @ -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 */ | ||||
|  | ||||
| @ -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,
 | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void x86_cpu_definition_entry(gpointer data, gpointer user_data) | ||||
| { | ||||
|     ObjectClass *oc = data; | ||||
|     X86CPUClass *cc = X86_CPU_CLASS(oc); | ||||
|     CpuDefinitionInfoList **cpu_list = user_data; | ||||
|     CpuDefinitionInfoList *entry; | ||||
|     CpuDefinitionInfo *info; | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) | ||||
| { | ||||
|     CpuDefinitionInfoList *cpu_list = NULL; | ||||
|     X86CPUDefinition *def; | ||||
|     int i; | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|         entry = g_malloc0(sizeof(*entry)); | ||||
|         entry->value = info; | ||||
|         entry->next = cpu_list; | ||||
|         cpu_list = entry; | ||||
|     } | ||||
| 
 | ||||
|     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 = { | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell