diff options
Diffstat (limited to 'xen/arch/x86/hvm/vmsi.c')
-rw-r--r-- | xen/arch/x86/hvm/vmsi.c | 38 |
1 files changed, 24 insertions, 14 deletions
diff --git a/xen/arch/x86/hvm/vmsi.c b/xen/arch/x86/hvm/vmsi.c index d9ec9fb0a4..87f3bd1396 100644 --- a/xen/arch/x86/hvm/vmsi.c +++ b/xen/arch/x86/hvm/vmsi.c @@ -157,7 +157,7 @@ struct msixtbl_entry struct pci_dev *pdev; unsigned long gtable; /* gpa of msix table */ unsigned long table_len; - unsigned long table_flags[MAX_MSIX_TABLE_ENTRIES / BITS_PER_LONG + 1]; + unsigned long table_flags[BITS_TO_LONGS(MAX_MSIX_TABLE_ENTRIES)]; struct rcu_head rcu; }; @@ -179,17 +179,14 @@ static struct msixtbl_entry *msixtbl_find_entry( static void __iomem *msixtbl_addr_to_virt( struct msixtbl_entry *entry, unsigned long addr) { - int idx, nr_page; + unsigned int idx, nr_page; - if ( !entry ) + if ( !entry || !entry->pdev ) return NULL; nr_page = (addr >> PAGE_SHIFT) - (entry->gtable >> PAGE_SHIFT); - if ( !entry->pdev ) - return NULL; - idx = entry->pdev->msix_table_idx[nr_page]; if ( !idx ) return NULL; @@ -207,10 +204,10 @@ static int msixtbl_read( void *virt; int r = X86EMUL_UNHANDLEABLE; - rcu_read_lock(); + if ( len != 4 || (address & 3) ) + return r; - if ( len != 4 ) - goto out; + rcu_read_lock(); offset = address & (PCI_MSIX_ENTRY_SIZE - 1); if ( offset != PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET) @@ -235,13 +232,13 @@ static int msixtbl_write(struct vcpu *v, unsigned long address, unsigned long offset; struct msixtbl_entry *entry; void *virt; - int nr_entry; + unsigned int nr_entry; int r = X86EMUL_UNHANDLEABLE; - rcu_read_lock(); + if ( len != 4 || (address & 3) ) + return r; - if ( len != 4 ) - goto out; + rcu_read_lock(); entry = msixtbl_find_entry(v, address); nr_entry = (address - entry->gtable) / PCI_MSIX_ENTRY_SIZE; @@ -261,9 +258,22 @@ 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); + orig = readl(virt); + val &= ~PCI_MSIX_VECTOR_BITMASK; + val |= orig & PCI_MSIX_VECTOR_BITMASK; writel(val, virt); - r = X86EMUL_OKAY; + spin_unlock_irqrestore(&desc->lock, flags); +#endif + r = X86EMUL_OKAY; out: rcu_read_unlock(); return r; |