diff options
-rw-r--r-- | xen/arch/x86/acpi/lib.c | 2 | ||||
-rw-r--r-- | xen/arch/x86/hvm/vmsi.c | 61 | ||||
-rw-r--r-- | xen/drivers/acpi/osl.c | 29 | ||||
-rw-r--r-- | xen/include/xen/acpi.h | 2 |
4 files changed, 68 insertions, 26 deletions
diff --git a/xen/arch/x86/acpi/lib.c b/xen/arch/x86/acpi/lib.c index e8e69d1cb4..1f98c316c3 100644 --- a/xen/arch/x86/acpi/lib.c +++ b/xen/arch/x86/acpi/lib.c @@ -39,7 +39,7 @@ u32 __read_mostly x86_acpiid_to_apicid[MAX_MADT_ENTRIES] = * from the fixed base. That's why we start at FIX_ACPI_END and * count idx down while incrementing the phys address. */ -char *__acpi_map_table(unsigned long phys, unsigned long size) +char *__acpi_map_table(paddr_t phys, unsigned long size) { unsigned long base, offset, mapped_size; int idx; diff --git a/xen/arch/x86/hvm/vmsi.c b/xen/arch/x86/hvm/vmsi.c index 36de312ef0..0244b133fe 100644 --- a/xen/arch/x86/hvm/vmsi.c +++ b/xen/arch/x86/hvm/vmsi.c @@ -169,6 +169,7 @@ struct msixtbl_entry uint32_t msi_ad[3]; /* Shadow of address low, high and data */ } gentries[MAX_MSIX_ACC_ENTRIES]; struct rcu_head rcu; + const struct pirq *pirq; }; static DEFINE_RCU_READ_LOCK(msixtbl_rcu_lock); @@ -254,6 +255,8 @@ static int msixtbl_write(struct vcpu *v, unsigned long address, void *virt; unsigned int nr_entry, index; int r = X86EMUL_UNHANDLEABLE; + unsigned long flags, orig; + struct irq_desc *desc; if ( len != 4 || (address & 3) ) return r; @@ -283,22 +286,49 @@ static int msixtbl_write(struct vcpu *v, unsigned long address, if ( !virt ) goto out; - /* Do not allow the mask bit to be changed. */ -#if 0 /* XXX - * As the mask bit is the only defined bit in the word, and as the - * host MSI-X code doesn't preserve the other bits anyway, doing - * this is pointless. So for now just discard the write (also - * saving us from having to determine the matching irq_desc). - */ - spin_lock_irqsave(&desc->lock, flags); + desc = pirq_spin_lock_irq_desc(entry->pirq, &flags); + if ( !desc ) + goto out; + + if ( !desc->msi_desc ) + goto unlock; + orig = readl(virt); - val &= ~PCI_MSIX_VECTOR_BITMASK; - val |= orig & PCI_MSIX_VECTOR_BITMASK; + + /* + * Do not allow guest to modify MSI-X control bit if it is masked + * by Xen. We'll only handle the case where Xen thinks that + * bit is unmasked, but hardware has silently masked the bit + * (in case of SR-IOV VF reset, etc). On the other hand, if Xen + * thinks that the bit is masked, but it's really not, + * we log a warning. + */ + if ( desc->msi_desc->msi_attrib.masked ) + { + if ( !(orig & PCI_MSIX_VECTOR_BITMASK) ) + printk(XENLOG_WARNING "MSI-X control bit is unmasked when" + " it is expected to be masked [%04x:%02x:%02x.%01x]\n", + entry->pdev->seg, entry->pdev->bus, + PCI_SLOT(entry->pdev->devfn), + PCI_FUNC(entry->pdev->devfn)); + + goto unlock; + } + + /* + * The mask bit is the only defined bit in the word. But we + * ought to preserve the reserved bits. Clearing the reserved + * bits can result in undefined behaviour (see PCI Local Bus + * Specification revision 2.3). + */ + val &= PCI_MSIX_VECTOR_BITMASK; + val |= (orig & ~PCI_MSIX_VECTOR_BITMASK); writel(val, virt); - spin_unlock_irqrestore(&desc->lock, flags); -#endif +unlock: + spin_unlock_irqrestore(&desc->lock, flags); r = X86EMUL_OKAY; + out: rcu_read_unlock(&msixtbl_rcu_lock); return r; @@ -328,7 +358,8 @@ const struct hvm_mmio_handler msixtbl_mmio_handler = { static void add_msixtbl_entry(struct domain *d, struct pci_dev *pdev, uint64_t gtable, - struct msixtbl_entry *entry) + struct msixtbl_entry *entry, + const struct pirq *pirq) { u32 len; @@ -342,6 +373,7 @@ static void add_msixtbl_entry(struct domain *d, entry->table_len = len; entry->pdev = pdev; entry->gtable = (unsigned long) gtable; + entry->pirq = pirq; list_add_rcu(&entry->list, &d->arch.hvm_domain.msixtbl_list); } @@ -404,9 +436,10 @@ int msixtbl_pt_register(struct domain *d, struct pirq *pirq, uint64_t gtable) entry = new_entry; new_entry = NULL; - add_msixtbl_entry(d, pdev, gtable, entry); + add_msixtbl_entry(d, pdev, gtable, entry, pirq); found: + ASSERT(entry->pirq == pirq); atomic_inc(&entry->refcnt); spin_unlock(&d->arch.hvm_domain.msixtbl_list_lock); r = 0; diff --git a/xen/drivers/acpi/osl.c b/xen/drivers/acpi/osl.c index 1b60be621c..93c983ca9d 100644 --- a/xen/drivers/acpi/osl.c +++ b/xen/drivers/acpi/osl.c @@ -38,6 +38,7 @@ #include <xen/spinlock.h> #include <xen/domain_page.h> #include <xen/efi.h> +#include <xen/vmap.h> #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME("osl") @@ -83,14 +84,25 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) } } -void __iomem *__init +void __iomem * acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { - return __acpi_map_table((unsigned long)phys, size); + if (system_state >= SYS_STATE_active) { + unsigned long pfn = PFN_DOWN(phys); + unsigned int offs = phys & (PAGE_SIZE - 1); + + /* The low first Mb is always mapped. */ + if ( !((phys + size - 1) >> 20) ) + return __va(phys); + return __vmap(&pfn, PFN_UP(offs + size), 1, 1, PAGE_HYPERVISOR_NOCACHE) + offs; + } + return __acpi_map_table(phys, size); } -void __init acpi_os_unmap_memory(void __iomem * virt, acpi_size size) +void acpi_os_unmap_memory(void __iomem * virt, acpi_size size) { + if (system_state >= SYS_STATE_active) + vunmap((void *)((unsigned long)virt & PAGE_MASK)); } acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width) @@ -133,9 +145,8 @@ acpi_status acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) { u32 dummy; - void __iomem *virt_addr; + void __iomem *virt_addr = acpi_os_map_memory(phys_addr, width >> 3); - virt_addr = map_domain_page(phys_addr>>PAGE_SHIFT); if (!value) value = &dummy; @@ -153,7 +164,7 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) BUG(); } - unmap_domain_page(virt_addr); + acpi_os_unmap_memory(virt_addr, width >> 3); return AE_OK; } @@ -161,9 +172,7 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) acpi_status acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) { - void __iomem *virt_addr; - - virt_addr = map_domain_page(phys_addr>>PAGE_SHIFT); + void __iomem *virt_addr = acpi_os_map_memory(phys_addr, width >> 3); switch (width) { case 8: @@ -179,7 +188,7 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) BUG(); } - unmap_domain_page(virt_addr); + acpi_os_unmap_memory(virt_addr, width >> 3); return AE_OK; } diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h index 8f3cdca314..aedec65a28 100644 --- a/xen/include/xen/acpi.h +++ b/xen/include/xen/acpi.h @@ -56,7 +56,7 @@ typedef int (*acpi_table_handler) (struct acpi_table_header *table); typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end); unsigned int acpi_get_processor_id (unsigned int cpu); -char * __acpi_map_table (unsigned long phys_addr, unsigned long size); +char * __acpi_map_table (paddr_t phys_addr, unsigned long size); int acpi_boot_init (void); int acpi_boot_table_init (void); int acpi_numa_init (void); |