Module Name:    src
Committed By:   knakahara
Date:           Mon Apr 27 07:03:58 UTC 2015

Modified Files:
        src/distrib/sets/lists/comp: mi
        src/share/man/man9: Makefile
        src/sys/arch/amd64/amd64: mainbus.c
        src/sys/arch/amd64/include: types.h
        src/sys/arch/i386/i386: mainbus.c
        src/sys/arch/i386/include: types.h
        src/sys/arch/x86/conf: files.x86
        src/sys/arch/x86/include: i82093var.h pci_machdep.h
            pci_machdep_common.h pic.h
        src/sys/arch/x86/pci: pci_intr_machdep.c pci_machdep.c
        src/sys/arch/x86/x86: intr.c ioapic.c
        src/sys/arch/xen/include: intr.h
        src/sys/dev/pci: pci.c pcireg.h pcivar.h
Added Files:
        src/share/man/man9: pci_msi.9
        src/sys/arch/x86/pci: msipic.c msipic.h pci_msi_machdep.c

Log Message:
add x86 MD MSI/MSI-X support code.


To generate a diff of this commit:
cvs rdiff -u -r1.1955 -r1.1956 src/distrib/sets/lists/comp/mi
cvs rdiff -u -r1.385 -r1.386 src/share/man/man9/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man9/pci_msi.9
cvs rdiff -u -r1.34 -r1.35 src/sys/arch/amd64/amd64/mainbus.c
cvs rdiff -u -r1.45 -r1.46 src/sys/arch/amd64/include/types.h
cvs rdiff -u -r1.98 -r1.99 src/sys/arch/i386/i386/mainbus.c
cvs rdiff -u -r1.80 -r1.81 src/sys/arch/i386/include/types.h
cvs rdiff -u -r1.83 -r1.84 src/sys/arch/x86/conf/files.x86
cvs rdiff -u -r1.13 -r1.14 src/sys/arch/x86/include/i82093var.h
cvs rdiff -u -r1.17 -r1.18 src/sys/arch/x86/include/pci_machdep.h
cvs rdiff -u -r1.14 -r1.15 src/sys/arch/x86/include/pci_machdep_common.h
cvs rdiff -u -r1.7 -r1.8 src/sys/arch/x86/include/pic.h
cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/msipic.c \
    src/sys/arch/x86/pci/msipic.h src/sys/arch/x86/pci/pci_msi_machdep.c
cvs rdiff -u -r1.29 -r1.30 src/sys/arch/x86/pci/pci_intr_machdep.c
cvs rdiff -u -r1.69 -r1.70 src/sys/arch/x86/pci/pci_machdep.c
cvs rdiff -u -r1.80 -r1.81 src/sys/arch/x86/x86/intr.c
cvs rdiff -u -r1.49 -r1.50 src/sys/arch/x86/x86/ioapic.c
cvs rdiff -u -r1.35 -r1.36 src/sys/arch/xen/include/intr.h
cvs rdiff -u -r1.145 -r1.146 src/sys/dev/pci/pci.c
cvs rdiff -u -r1.101 -r1.102 src/sys/dev/pci/pcireg.h \
    src/sys/dev/pci/pcivar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/comp/mi
diff -u src/distrib/sets/lists/comp/mi:1.1955 src/distrib/sets/lists/comp/mi:1.1956
--- src/distrib/sets/lists/comp/mi:1.1955	Mon Apr 27 06:42:52 2015
+++ src/distrib/sets/lists/comp/mi	Mon Apr 27 07:03:57 2015
@@ -1,4 +1,4 @@
-#	$NetBSD: mi,v 1.1955 2015/04/27 06:42:52 knakahara Exp $
+#	$NetBSD: mi,v 1.1956 2015/04/27 07:03:57 knakahara Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -17367,10 +17367,27 @@
 ./usr/share/man/html9/pci_intr_evcnt.html	comp-sys-htmlman	html
 ./usr/share/man/html9/pci_intr_map.html		comp-sys-htmlman	html
 ./usr/share/man/html9/pci_intr_string.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_intx_alloc.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_intx_release.html	comp-sys-htmlman	html
 ./usr/share/man/html9/pci_make_tag.html		comp-sys-htmlman	html
 ./usr/share/man/html9/pci_mapreg_info.html	comp-sys-htmlman	html
 ./usr/share/man/html9/pci_mapreg_map.html	comp-sys-htmlman	html
 ./usr/share/man/html9/pci_mapreg_type.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msi.html		comp-sys-htmlman	html
+./usr/share/man/html9/pci_msi_alloc.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msi_alloc_exact.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msi_count.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msi_disestablish.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msi_establish.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msi_release.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix.html		comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix_alloc.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix_alloc_exact.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix_alloc_map.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix_count.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix_disestablish.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix_establish.html	comp-sys-htmlman	html
+./usr/share/man/html9/pci_msix_release.html	comp-sys-htmlman	html
 ./usr/share/man/html9/pci_set_powerstate.html	comp-sys-htmlman	html
 ./usr/share/man/html9/pci_vpd_read.html		comp-sys-htmlman	html
 ./usr/share/man/html9/pci_vpd_write.html	comp-sys-htmlman	html
@@ -24306,10 +24323,27 @@
 ./usr/share/man/man9/pci_intr_evcnt.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_intr_map.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_intr_string.9		comp-sys-man		.man
+./usr/share/man/man9/pci_intx_alloc.9		comp-sys-man		.man
+./usr/share/man/man9/pci_intx_release.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_make_tag.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_mapreg_info.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_mapreg_map.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_mapreg_type.9		comp-sys-man		.man
+./usr/share/man/man9/pci_msi.9			comp-sys-man		.man
+./usr/share/man/man9/pci_msi_alloc.9		comp-sys-man		.man
+./usr/share/man/man9/pci_msi_alloc_exact.9	comp-sys-man		.man
+./usr/share/man/man9/pci_msi_count.9		comp-sys-man		.man
+./usr/share/man/man9/pci_msi_disestablish.9	comp-sys-man		.man
+./usr/share/man/man9/pci_msi_establish.9	comp-sys-man		.man
+./usr/share/man/man9/pci_msi_release.9		comp-sys-man		.man
+./usr/share/man/man9/pci_msix.9			comp-sys-man		.man
+./usr/share/man/man9/pci_msix_alloc.9		comp-sys-man		.man
+./usr/share/man/man9/pci_msix_alloc_exact.9	comp-sys-man		.man
+./usr/share/man/man9/pci_msix_alloc_map.9	comp-sys-man		.man
+./usr/share/man/man9/pci_msix_count.9		comp-sys-man		.man
+./usr/share/man/man9/pci_msix_disestablish.9	comp-sys-man		.man
+./usr/share/man/man9/pci_msix_establish.9	comp-sys-man		.man
+./usr/share/man/man9/pci_msix_release.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_set_powerstate.9	comp-sys-man		.man
 ./usr/share/man/man9/pci_vpd_read.9		comp-sys-man		.man
 ./usr/share/man/man9/pci_vpd_write.9		comp-sys-man		.man

Index: src/share/man/man9/Makefile
diff -u src/share/man/man9/Makefile:1.385 src/share/man/man9/Makefile:1.386
--- src/share/man/man9/Makefile:1.385	Mon Apr 27 06:42:52 2015
+++ src/share/man/man9/Makefile	Mon Apr 27 07:03:57 2015
@@ -1,4 +1,4 @@
-#       $NetBSD: Makefile,v 1.385 2015/04/27 06:42:52 knakahara Exp $
+#       $NetBSD: Makefile,v 1.386 2015/04/27 07:03:57 knakahara Exp $
 
 #	Makefile for section 9 (kernel function and variable) manual pages.
 
@@ -40,7 +40,7 @@ MAN=	accept_filter.9 accf_data.9 accf_ht
 	mstohz.9 mutex.9 m_tag.9 namecache.9 \
 	namei.9 nullop.9 opencrypto.9 optstr.9 \
 	panic.9 pathbuf.9 pci.9 pci_configure_bus.9 pci_intr.9 \
-	pci_intr_distribute.9 pckbport.9 pcmcia.9 pcq.9 pcu.9 \
+	pci_intr_distribute.9 pci_msi.9 pckbport.9 pcmcia.9 pcq.9 pcu.9 \
 	percpu.9 pfil.9 physio.9 pmap.9 pmatch.9 pmc.9 pmf.9 pool.9 \
 	pool_cache.9 powerhook_establish.9 ppi.9 ppsratecheck.9 preempt.9 \
 	proc_find.9 pserialize.9 putter.9 \
@@ -572,6 +572,22 @@ MLINKS+=pci.9 pci_conf_read.9 \
 	pci.9 PCI_VENDOR.9 \
 	pci.9 PCI_PRODUCT.9 \
 	pci.9 PCI_REVISION.9
+MLINKS+=pci_msi.9 pci_msix.9 \
+	pci_msi.9 pci_intx_alloc.9 \
+	pci_msi.9 pci_intx_release.9 \
+	pci_msi.9 pci_msi_count.9 \
+	pci_msi.9 pci_msi_alloc.9 \
+	pci_msi.9 pci_msi_alloc_exact.9 \
+	pci_msi.9 pci_msi_release.9 \
+	pci_msi.9 pci_msi_establish.9 \
+	pci_msi.9 pci_msi_disestablish.9 \
+	pci_msi.9 pci_msix_count.9 \
+	pci_msi.9 pci_msix_alloc.9 \
+	pci_msi.9 pci_msix_alloc_exact.9 \
+	pci_msi.9 pci_msix_alloc_map.9 \
+	pci_msi.9 pci_msix_release.9 \
+	pci_msi.9 pci_msix_establish.9 \
+	pci_msi.9 pci_msix_disestablish.9
 MLINKS+=pci_configure_bus.9 pci_conf_hook.9 \
 	pci_configure_bus.9 pci_conf_interrupt.9
 MLINKS+=pckbport.9 pckbport_attach.9 \

Index: src/sys/arch/amd64/amd64/mainbus.c
diff -u src/sys/arch/amd64/amd64/mainbus.c:1.34 src/sys/arch/amd64/amd64/mainbus.c:1.35
--- src/sys/arch/amd64/amd64/mainbus.c:1.34	Wed Jul 31 14:05:33 2013
+++ src/sys/arch/amd64/amd64/mainbus.c	Mon Apr 27 07:03:57 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: mainbus.c,v 1.34 2013/07/31 14:05:33 soren Exp $	*/
+/*	$NetBSD: mainbus.c,v 1.35 2015/04/27 07:03:57 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.34 2013/07/31 14:05:33 soren Exp $");
+__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.35 2015/04/27 07:03:57 knakahara Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -74,6 +74,7 @@ __KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 
 #include <arch/x86/pci/pci_addr_fixup.h>
 #endif
 #endif
+#include <arch/x86/pci/msipic.h>
 #endif
 
 /*
@@ -175,6 +176,8 @@ mainbus_attach(device_t parent, device_t
 #endif
 
 #if NPCI > 0
+	msipic_init();
+
 	/*
 	 * ACPI needs to be able to access PCI configuration space.
 	 */

Index: src/sys/arch/amd64/include/types.h
diff -u src/sys/arch/amd64/include/types.h:1.45 src/sys/arch/amd64/include/types.h:1.46
--- src/sys/arch/amd64/include/types.h:1.45	Thu Apr  3 15:22:36 2014
+++ src/sys/arch/amd64/include/types.h	Mon Apr 27 07:03:57 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: types.h,v 1.45 2014/04/03 15:22:36 christos Exp $	*/
+/*	$NetBSD: types.h,v 1.46 2015/04/27 07:03:57 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 1990 The Regents of the University of California.
@@ -103,6 +103,7 @@ typedef	volatile unsigned char		__cpu_si
 #define	__HAVE_MM_MD_DIRECT_MAPPED_IO
 #define	__HAVE_MM_MD_DIRECT_MAPPED_PHYS
 #define	__HAVE_CPU_UAREA_ROUTINES
+#define	__HAVE_PCI_MSI_MSIX
 #endif
 #endif
 

Index: src/sys/arch/i386/i386/mainbus.c
diff -u src/sys/arch/i386/i386/mainbus.c:1.98 src/sys/arch/i386/i386/mainbus.c:1.99
--- src/sys/arch/i386/i386/mainbus.c:1.98	Fri Nov  8 03:12:48 2013
+++ src/sys/arch/i386/i386/mainbus.c	Mon Apr 27 07:03:57 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: mainbus.c,v 1.98 2013/11/08 03:12:48 christos Exp $	*/
+/*	$NetBSD: mainbus.c,v 1.99 2015/04/27 07:03:57 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.98 2013/11/08 03:12:48 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.99 2015/04/27 07:03:57 knakahara Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -86,6 +86,7 @@ __KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 
 #include <arch/x86/pci/pci_addr_fixup.h>
 #endif
 #endif
+#include <archx86/pci/msipic.h>
 #endif
 
 void	mainbus_childdetached(device_t, device_t);
@@ -227,6 +228,8 @@ mainbus_attach(device_t parent, device_t
 #endif
 
 #if NPCI > 0
+	msipic_init();
+
 	/*
 	 * ACPI needs to be able to access PCI configuration space.
 	 */

Index: src/sys/arch/i386/include/types.h
diff -u src/sys/arch/i386/include/types.h:1.80 src/sys/arch/i386/include/types.h:1.81
--- src/sys/arch/i386/include/types.h:1.80	Wed Apr 22 19:48:01 2015
+++ src/sys/arch/i386/include/types.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: types.h,v 1.80 2015/04/22 19:48:01 pooka Exp $	*/
+/*	$NetBSD: types.h,v 1.81 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 1990 The Regents of the University of California.
@@ -129,6 +129,10 @@ typedef	volatile unsigned char		__cpu_si
 
 #if defined(_KERNEL)
 #define	__HAVE_RAS
+
+#if !defined(XEN)
+#define __HAVE_PCI_MSI_MSIX
+#endif
 #endif
 
 #endif	/* _I386_MACHTYPES_H_ */

Index: src/sys/arch/x86/conf/files.x86
diff -u src/sys/arch/x86/conf/files.x86:1.83 src/sys/arch/x86/conf/files.x86:1.84
--- src/sys/arch/x86/conf/files.x86:1.83	Fri Oct 10 17:44:17 2014
+++ src/sys/arch/x86/conf/files.x86	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-#	$NetBSD: files.x86,v 1.83 2014/10/10 17:44:17 uebayasi Exp $
+#	$NetBSD: files.x86,v 1.84 2015/04/27 07:03:58 knakahara Exp $
 
 # options for MP configuration through the MP spec
 defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI
@@ -141,6 +141,8 @@ file	arch/x86/x86/tprof_amdpmi.c	tprof_a
 file	arch/x86/pci/pci_machdep.c	pci
 #file	arch/x86/pci/pci_ranges.c	pci
 file	arch/x86/pci/pci_intr_machdep.c	pci
+file	arch/x86/pci/pci_msi_machdep.c	pci
+file	arch/x86/pci/msipic.c		pci
 
 file	arch/x86/pci/pciide_machdep.c	pciide_common
 

Index: src/sys/arch/x86/include/i82093var.h
diff -u src/sys/arch/x86/include/i82093var.h:1.13 src/sys/arch/x86/include/i82093var.h:1.14
--- src/sys/arch/x86/include/i82093var.h:1.13	Mon Apr 27 06:51:40 2015
+++ src/sys/arch/x86/include/i82093var.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: i82093var.h,v 1.13 2015/04/27 06:51:40 knakahara Exp $ */
+/* $NetBSD: i82093var.h,v 1.14 2015/04/27 07:03:58 knakahara Exp $ */
 
 /*-
  * Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -68,10 +68,13 @@ struct ioapic_softc {
  * (ih&0xff0000)>>16 -> ioapic id.
  * (ih&0x00ff00)>>8 -> ioapic pin.
  *
- * 0x80000000 is used by pci_intr_machdep.c for MPSAFE_MASK
+ * MSI/MSI-X:
+ * (ih&0x000ff80000000000)>>43 -> MSI/MSI-X device id.
+ * (ih&0x000007ff00000000)>>32 -> MSI/MSI-X vector id in a device.
  */
-
+#define	MPSAFE_MASK		0x80000000ULL
 #define APIC_INT_VIA_APIC	0x10000000ULL
+#define APIC_INT_VIA_MSI	0x20000000ULL
 #define APIC_INT_APIC_MASK	0x00ff0000ULL
 #define APIC_INT_APIC_SHIFT	16
 #define APIC_INT_PIN_MASK	0x0000ff00ULL
@@ -82,9 +85,21 @@ struct ioapic_softc {
 #define APIC_IRQ_ISLEGACY(x) (bool)(!((x) & APIC_INT_VIA_APIC))
 #define APIC_IRQ_LEGACY_IRQ(x) (int)((x) & 0xff)
 
+#define INT_VIA_MSI(x) (bool)(((x) & APIC_INT_VIA_MSI) != 0)
+
+#define MSI_INT_MSIX		0x1000000000000000ULL
+#define MSI_INT_DEV_MASK	0x000ff80000000000ULL
+#define MSI_INT_VEC_MASK	0x000007ff00000000ULL
+
+#define MSI_INT_IS_MSIX(x) (bool)((((x) & MSI_INT_MSIX) != 0))
+#define MSI_INT_MAKE_MSI(x) ((x) &= ~MSI_INT_MSIX)
+#define MSI_INT_MAKE_MSIX(x) ((x) |= MSI_INT_MSIX)
+#define MSI_INT_DEV(x) __SHIFTOUT((x), MSI_INT_DEV_MASK)
+#define MSI_INT_VEC(x) __SHIFTOUT((x), MSI_INT_VEC_MASK)
+
 void ioapic_print_redir(struct ioapic_softc *, const char *, int);
 void ioapic_format_redir(char *, const char *, int, uint32_t, uint32_t);
-struct ioapic_softc *ioapic_find(intr_handle_t);
+struct ioapic_softc *ioapic_find(int);
 struct ioapic_softc *ioapic_find_bybase(int);
 
 void ioapic_enable(void);

Index: src/sys/arch/x86/include/pci_machdep.h
diff -u src/sys/arch/x86/include/pci_machdep.h:1.17 src/sys/arch/x86/include/pci_machdep.h:1.18
--- src/sys/arch/x86/include/pci_machdep.h:1.17	Mon Apr 27 06:51:40 2015
+++ src/sys/arch/x86/include/pci_machdep.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci_machdep.h,v 1.17 2015/04/27 06:51:40 knakahara Exp $	*/
+/*	$NetBSD: pci_machdep.h,v 1.18 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
@@ -41,6 +41,7 @@
  * Types provided to machine-independent PCI code
  * See also i82093var.h to find out pci_intr_handle_t's bitfield.
  */
+
 typedef intr_handle_t pci_intr_handle_t;
 
 #include <x86/pci_machdep_common.h>

Index: src/sys/arch/x86/include/pci_machdep_common.h
diff -u src/sys/arch/x86/include/pci_machdep_common.h:1.14 src/sys/arch/x86/include/pci_machdep_common.h:1.15
--- src/sys/arch/x86/include/pci_machdep_common.h:1.14	Mon Apr 27 06:42:52 2015
+++ src/sys/arch/x86/include/pci_machdep_common.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci_machdep_common.h,v 1.14 2015/04/27 06:42:52 knakahara Exp $	*/
+/*	$NetBSD: pci_machdep_common.h,v 1.15 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
@@ -120,9 +120,34 @@ void		*pci_intr_establish(pci_chipset_ta
 void		pci_intr_disestablish(pci_chipset_tag_t, void *);
 int		pci_intr_distribute(void *, const kcpuset_t *, kcpuset_t *);
 
+/*
+ * If device drivers use MSI/MSI-X, they should use these API for INTx
+ * instead of pci_intr_map(), because of conforming the pci_intr_handle
+ * ownership to MSI/MSI-X.
+ */
+int		pci_intx_alloc(const struct pci_attach_args *,
+		    pci_intr_handle_t **);
+void		pci_intx_release(pci_chipset_tag_t, pci_intr_handle_t *);
+
 /* experimental MSI support */
-void *pci_msi_establish(struct pci_attach_args *, int, int (*)(void *), void *);
-void pci_msi_disestablish(void *);
+const char	*pci_msi_string(pci_chipset_tag_t, pci_intr_handle_t, char *, size_t);
+int		pci_msi_count(struct pci_attach_args *);
+int		pci_msi_alloc(struct pci_attach_args *, pci_intr_handle_t **, int *);
+int		pci_msi_alloc_exact(struct pci_attach_args *, pci_intr_handle_t **, int);
+void		pci_msi_release(pci_chipset_tag_t, pci_intr_handle_t **, int);
+void		*pci_msi_establish(pci_chipset_tag_t, pci_intr_handle_t,
+		    int, int (*)(void *), void *);
+void		pci_msi_disestablish(pci_chipset_tag_t, void *);
+
+/* experimental MSI-X support */
+int		pci_msix_count(struct pci_attach_args *);
+int		pci_msix_alloc(struct pci_attach_args *, pci_intr_handle_t **, int *);
+int		pci_msix_alloc_exact(struct pci_attach_args *, pci_intr_handle_t **, int);
+int		pci_msix_alloc_map(struct pci_attach_args *, pci_intr_handle_t **, u_int *, int);
+void		pci_msix_release(pci_chipset_tag_t, pci_intr_handle_t **, int);
+void		*pci_msix_establish(pci_chipset_tag_t, pci_intr_handle_t,
+		    int, int (*)(void *), void *);
+void		pci_msix_disestablish(pci_chipset_tag_t, void *);
 
 /*
  * ALL OF THE FOLLOWING ARE MACHINE-DEPENDENT, AND SHOULD NOT BE USED

Index: src/sys/arch/x86/include/pic.h
diff -u src/sys/arch/x86/include/pic.h:1.7 src/sys/arch/x86/include/pic.h:1.8
--- src/sys/arch/x86/include/pic.h:1.7	Sun Apr 19 14:11:37 2009
+++ src/sys/arch/x86/include/pic.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pic.h,v 1.7 2009/04/19 14:11:37 ad Exp $	*/
+/*	$NetBSD: pic.h,v 1.8 2015/04/27 07:03:58 knakahara Exp $	*/
 
 #ifndef _X86_PIC_H
 #define _X86_PIC_H
@@ -22,6 +22,7 @@ struct pic {
 	struct intrstub *pic_level_stubs;
 	struct intrstub *pic_edge_stubs;
 	struct ioapic_softc *pic_ioapic; /* if pic_type == PIC_IOAPIC */
+	struct msipic *pic_msipic; /* if (pic_type == PIC_MSI) || (pic_type == PIC_MSIX) */
 };
 
 /*
@@ -30,7 +31,9 @@ struct pic {
 #define PIC_I8259	0
 #define PIC_IOAPIC	1
 #define PIC_LAPIC	2
-#define PIC_SOFT	3
+#define PIC_MSI		3
+#define PIC_MSIX	4
+#define PIC_SOFT	5
 
 extern struct pic i8259_pic;
 extern struct pic local_pic;

Index: src/sys/arch/x86/pci/pci_intr_machdep.c
diff -u src/sys/arch/x86/pci/pci_intr_machdep.c:1.29 src/sys/arch/x86/pci/pci_intr_machdep.c:1.30
--- src/sys/arch/x86/pci/pci_intr_machdep.c:1.29	Mon Apr 27 06:51:40 2015
+++ src/sys/arch/x86/pci/pci_intr_machdep.c	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci_intr_machdep.c,v 1.29 2015/04/27 06:51:40 knakahara Exp $	*/
+/*	$NetBSD: pci_intr_machdep.c,v 1.30 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 1997, 1998, 2009 The NetBSD Foundation, Inc.
@@ -73,7 +73,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.29 2015/04/27 06:51:40 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.30 2015/04/27 07:03:58 knakahara Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -112,8 +112,6 @@ __KERNEL_RCSID(0, "$NetBSD: pci_intr_mac
 #include <machine/mpacpi.h>
 #endif
 
-#define	MPSAFE_MASK	0x80000000
-
 int
 pci_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp)
 {
@@ -227,6 +225,9 @@ pci_intr_string(pci_chipset_tag_t pc, pc
 {
 	pci_chipset_tag_t ipc;
 
+	if (INT_VIA_MSI(ih))
+		return pci_msi_string(pc, ih, buf, len);
+
 	for (ipc = pc; ipc != NULL; ipc = ipc->pc_super) {
 		if ((ipc->pc_present & PCI_OVERRIDE_INTR_STRING) == 0)
 			continue;
@@ -340,105 +341,60 @@ pci_intr_distribute(void *cookie, const 
 }
 
 #if NIOAPIC > 0
-/*
- * experimental support for MSI, does support a single vector,
- * no MSI-X, 8-bit APIC IDs
- * (while it doesn't need the ioapic technically, it borrows
- * from its kernel support)
- */
-
-/* dummies, needed by common intr_establish code */
-static void
-msipic_hwmask(struct pic *pic, int pin)
-{
-}
-static void
-msipic_addroute(struct pic *pic, struct cpu_info *ci,
-		int pin, int vec, int type)
+int
+pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **pih)
 {
-}
+	struct intrsource *isp;
+	pci_intr_handle_t *handle;
+	int error;
+	char intrstr_buf[INTRIDBUF];
+	const char *intrstr;
+
+	handle = kmem_zalloc(sizeof(*handle), KM_SLEEP);
+	if (handle == NULL) {
+		aprint_normal("cannot allocate pci_intr_handle_t\n");
+		return ENOMEM;
+	}
+
+	if (pci_intr_map(pa, handle) != 0) {
+		aprint_normal("cannot set up pci_intr_handle_t\n");
+		error = EINVAL;
+		goto error;
+	}
+
+	intrstr = pci_intr_string(pa->pa_pc, *handle,
+	    intrstr_buf, sizeof(intrstr_buf));
+	mutex_enter(&cpu_lock);
+	isp = intr_allocate_io_intrsource(intrstr);
+	mutex_exit(&cpu_lock);
+	if (isp == NULL) {
+		aprint_normal("can't allocate io_intersource\n");
+		error = ENOMEM;
+		goto error;
+	}
 
-static struct pic msi_pic = {
-	.pic_name = "msi",
-	.pic_type = PIC_SOFT,
-	.pic_vecbase = 0,
-	.pic_apicid = 0,
-	.pic_lock = __SIMPLELOCK_UNLOCKED,
-	.pic_hwmask = msipic_hwmask,
-	.pic_hwunmask = msipic_hwmask,
-	.pic_addroute = msipic_addroute,
-	.pic_delroute = msipic_addroute,
-	.pic_edge_stubs = ioapic_edge_stubs,
-};
-
-struct msi_hdl {
-	struct intrhand *ih;
-	pci_chipset_tag_t pc;
-	pcitag_t tag;
-	int co;
-};
+	*pih = handle;
+	return 0;
 
-void *
-pci_msi_establish(struct pci_attach_args *pa, int level,
-		  int (*func)(void *), void *arg)
-{
-	int co;
-	struct intrhand *ih;
-	struct msi_hdl *msih;
-	struct cpu_info *ci;
-	struct intrsource *is;
-	pcireg_t reg;
-
-	if (!pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSI, &co, 0))
-		return NULL;
-
-	ih = intr_establish(-1, &msi_pic, -1, IST_EDGE, level, func, arg, 0);
-	if (ih == NULL)
-		return NULL;
-
-	msih = malloc(sizeof(*msih), M_DEVBUF, M_WAITOK);
-	msih->ih = ih;
-	msih->pc = pa->pa_pc;
-	msih->tag = pa->pa_tag;
-	msih->co = co;
-
-	ci = ih->ih_cpu;
-	is = ci->ci_isources[ih->ih_slot];
-	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, co + PCI_MSI_CTL);
-	pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MADDR64_LO,
-		       LAPIC_MSIADDR_BASE |
-		       __SHIFTIN(ci->ci_cpuid, LAPIC_MSIADDR_DSTID_MASK));
-	if (reg & PCI_MSI_CTL_64BIT_ADDR) {
-		pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MADDR64_HI,
-		    0);
-		/* XXX according to the manual, ASSERT is unnecessary if
-		 * EDGE
-		 */
-		pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MDATA64,
-		    __SHIFTIN(is->is_idtvec, LAPIC_MSIDATA_VECTOR_MASK) |
-		    LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_LEVEL_ASSERT |
-		    LAPIC_MSIDATA_DM_FIXED);
-	} else {
-		/* XXX according to the manual, ASSERT is unnecessary if
-		 * EDGE
-		 */
-		pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MDATA,
-		    __SHIFTIN(is->is_idtvec, LAPIC_MSIDATA_VECTOR_MASK) |
-		    LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_LEVEL_ASSERT |
-		    LAPIC_MSIDATA_DM_FIXED);
-	}
-	pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_CTL,
-	    PCI_MSI_CTL_MSI_ENABLE);
-	return msih;
+error:
+	kmem_free(handle, sizeof(*handle));
+	return error;
 }
 
 void
-pci_msi_disestablish(void *ih)
+pci_intx_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih)
 {
-	struct msi_hdl *msih = ih;
+	char intrstr_buf[INTRIDBUF];
+	const char *intrstr;
+
+	if (pih == NULL)
+		return;
+
+	intrstr = pci_intr_string(NULL, *pih, intrstr_buf, sizeof(intrstr_buf));
+	mutex_enter(&cpu_lock);
+	intr_free_io_intrsource(intrstr);
+	mutex_exit(&cpu_lock);
 
-	pci_conf_write(msih->pc, msih->tag, msih->co + PCI_MSI_CTL, 0);
-	intr_disestablish(msih->ih);
-	free(msih, M_DEVBUF);
+	kmem_free(pih, sizeof(*pih));
 }
 #endif

Index: src/sys/arch/x86/pci/pci_machdep.c
diff -u src/sys/arch/x86/pci/pci_machdep.c:1.69 src/sys/arch/x86/pci/pci_machdep.c:1.70
--- src/sys/arch/x86/pci/pci_machdep.c:1.69	Fri Nov  7 12:48:21 2014
+++ src/sys/arch/x86/pci/pci_machdep.c	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci_machdep.c,v 1.69 2014/11/07 12:48:21 christos Exp $	*/
+/*	$NetBSD: pci_machdep.c,v 1.70 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@@ -73,7 +73,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pci_machdep.c,v 1.69 2014/11/07 12:48:21 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pci_machdep.c,v 1.70 2015/04/27 07:03:58 knakahara Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -126,6 +126,8 @@ __KERNEL_RCSID(0, "$NetBSD: pci_machdep.
 #include <x86/vga_post.h>
 #endif
 
+#include <x86/cpuvar.h>
+
 #include <machine/autoconf.h>
 #include <machine/bootinfo.h>
 
@@ -210,6 +212,58 @@ const struct {
 #undef _tag
 #undef _qe
 
+/* arch/xen does not support MSI/MSI-X yet. */
+#ifdef __HAVE_PCI_MSI_MSIX
+#define PCI_QUIRK_DISABLE_MSI	1 /* Neigher MSI nor MSI-X work */
+#define PCI_QUIRK_DISABLE_MSIX	2 /* MSI-X does not work */
+#define PCI_QUIRK_ENABLE_MSI_VM	3 /* Older chipset in VM where MSI and MSI-X works */
+
+#define _dme(vend, prod) \
+	{ PCI_QUIRK_DISABLE_MSI, PCI_ID_CODE(vend, prod) }
+#define _dmxe(vend, prod) \
+	{ PCI_QUIRK_DISABLE_MSIX, PCI_ID_CODE(vend, prod) }
+#define _emve(vend, prod) \
+	{ PCI_QUIRK_ENABLE_MSI_VM, PCI_ID_CODE(vend, prod) }
+const struct {
+	int type;
+	pcireg_t id;
+} pci_msi_quirk_tbl[] = {
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PCMC),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82437FX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82437MX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82437VX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82439HX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82439TX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443GX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443GX_AGP),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82441FX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX_AGP),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX_NOAGP),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443GX_NOAGP),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443LX),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443LX_AGP),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82810_MCH),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82810E_MCH),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82815_FULL_HUB),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82820_MCH),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82830MP_IO_1),
+	_dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82840_HB),
+	_dme(PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE_PCHB),
+	_dme(PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_PCHB),
+	_dme(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_SC751_SC),
+	_dme(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_SC761_SC),
+	_dme(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_SC762_NB),
+
+	_emve(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82441FX), /* QEMU */
+	_emve(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX), /* VMWare */
+};
+#undef _dme
+#undef _dmxe
+#undef _emve
+#endif /* __HAVE_PCI_MSI_MSIX */
+
 /*
  * PCI doesn't have any special needs; just use the generic versions
  * of these functions.
@@ -370,9 +424,30 @@ pci_conf_select(uint32_t sel)
 	}
 }
 
+#ifdef __HAVE_PCI_MSI_MSIX
+static int
+pci_has_msi_quirk(pcireg_t id, int type)
+{
+	int i;
+
+	for (i = 0; i < __arraycount(pci_msi_quirk_tbl); i++) {
+		if (id == pci_msi_quirk_tbl[i].id &&
+		    type == pci_msi_quirk_tbl[i].type)
+			return 1;
+	}
+
+	return 0;
+}
+#endif
+
 void
 pci_attach_hook(device_t parent, device_t self, struct pcibus_attach_args *pba)
 {
+#ifdef __HAVE_PCI_MSI_MSIX
+	pci_chipset_tag_t pc = pba->pba_pc;
+	pcitag_t tag;
+	pcireg_t id, class;
+#endif
 
 	if (pba->pba_bus == 0)
 		aprint_normal(": configuration mode %d", pci_mode);
@@ -382,6 +457,58 @@ pci_attach_hook(device_t parent, device_
 #if NACPICA > 0
 	mpacpi_pci_attach_hook(parent, self, pba);
 #endif
+
+#ifdef __HAVE_PCI_MSI_MSIX
+	/*
+	 * In order to decide whether the system supports MSI we look
+	 * at the host bridge, which should be device 0 function 0 on
+	 * bus 0.  It is better to not enable MSI on systems that
+	 * support it than the other way around, so be conservative
+	 * here.  So we don't enable MSI if we don't find a host
+	 * bridge there.  We also deliberately don't enable MSI on
+	 * chipsets from low-end manifacturers like VIA and SiS.
+	 */
+	tag = pci_make_tag(pc, 0, 0, 0);
+	id = pci_conf_read(pc, tag, PCI_ID_REG);
+	class = pci_conf_read(pc, tag, PCI_CLASS_REG);
+
+	if (PCI_CLASS(class) != PCI_CLASS_BRIDGE ||
+	    PCI_SUBCLASS(class) != PCI_SUBCLASS_BRIDGE_HOST)
+		return;
+
+	if (pci_has_msi_quirk(id, PCI_QUIRK_DISABLE_MSI)) {
+		pba->pba_flags &= ~PCI_FLAGS_MSI_OKAY;
+		pba->pba_flags &= ~PCI_FLAGS_MSIX_OKAY;
+	} else if (pci_has_msi_quirk(id, PCI_QUIRK_DISABLE_MSIX)) {
+		pba->pba_flags |= PCI_FLAGS_MSI_OKAY;
+		pba->pba_flags &= ~PCI_FLAGS_MSIX_OKAY;
+	} else {
+		pba->pba_flags |= PCI_FLAGS_MSI_OKAY;
+		pba->pba_flags |= PCI_FLAGS_MSIX_OKAY;
+	}
+
+	/* VMware and KVM use old chipset, but they can use MSI/MSI-X */
+	if (cpu_feature[1] & CPUID2_RAZ) {
+		if (pci_has_msi_quirk(id, PCI_QUIRK_ENABLE_MSI_VM)) {
+			pba->pba_flags |= PCI_FLAGS_MSI_OKAY;
+			pba->pba_flags |= PCI_FLAGS_MSIX_OKAY;
+		}
+	}
+
+	/*
+	 * Don't enable MSI on a HyperTransport bus.  In order to
+	 * determine that bus 0 is a HyperTransport bus, we look at
+	 * device 24 function 0, which is the HyperTransport
+	 * host/primary interface integrated on most 64-bit AMD CPUs.
+	 * If that device has a HyperTransport capability, bus 0 must
+	 * be a HyperTransport bus and we disable MSI.
+	 */
+	tag = pci_make_tag(pc, 0, 24, 0);
+	if (pci_get_capability(pc, tag, PCI_CAP_LDT, NULL, NULL)) {
+		pba->pba_flags &= ~PCI_FLAGS_MSI_OKAY;
+		pba->pba_flags &= ~PCI_FLAGS_MSIX_OKAY;
+	}
+#endif /* __HAVE_PCI_MSI_MSIX */
 }
 
 int

Index: src/sys/arch/x86/x86/intr.c
diff -u src/sys/arch/x86/x86/intr.c:1.80 src/sys/arch/x86/x86/intr.c:1.81
--- src/sys/arch/x86/x86/intr.c:1.80	Mon Apr 27 06:51:40 2015
+++ src/sys/arch/x86/x86/intr.c	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: intr.c,v 1.80 2015/04/27 06:51:40 knakahara Exp $	*/
+/*	$NetBSD: intr.c,v 1.81 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc.
@@ -133,7 +133,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.80 2015/04/27 06:51:40 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.81 2015/04/27 07:03:58 knakahara Exp $");
 
 #include "opt_intrdebug.h"
 #include "opt_multiprocessor.h"
@@ -176,6 +176,8 @@ __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.8
 #include <dev/pci/ppbreg.h>
 #endif
 
+#include <x86/pci/msipic.h>
+
 #ifdef DDB
 #include <ddb/db_output.h>
 #endif
@@ -225,6 +227,12 @@ static void intr_redistribute_xc_s1(void
 static void intr_redistribute_xc_s2(void *, void *);
 static bool intr_redistribute(struct cpu_info *);
 
+static const char *create_intrid(int, struct pic *, int, char *, size_t);
+
+static struct intrsource *intr_get_io_intrsource(const char *);
+static void intr_free_io_intrsource_direct(struct intrsource *);
+static int intr_num_handlers(struct intrsource *);
+
 static const char *legacy_intr_string(int, char *, size_t, struct pic *);
 
 static int intr_find_unused_slot(struct cpu_info *, int *);
@@ -460,6 +468,23 @@ create_intrid(int legacy_irq, struct pic
 {
 	int ih;
 
+	if ((pic->pic_type == PIC_MSI) || (pic->pic_type == PIC_MSIX)) {
+		uint64_t pih;
+		int dev, vec;
+
+		dev = msipic_get_devid(pic);
+		vec = pin;
+		pih = __SHIFTIN((uint64_t)dev, MSI_INT_DEV_MASK)
+			| __SHIFTIN((uint64_t)vec, MSI_INT_VEC_MASK)
+			| APIC_INT_VIA_MSI;
+		if (pic->pic_type == PIC_MSI)
+			MSI_INT_MAKE_MSI(pih);
+		else if (pic->pic_type == PIC_MSIX)
+			MSI_INT_MAKE_MSIX(pih);
+
+		return pci_msi_string(NULL, pih, buf, len);
+	}
+
 	/*
 	 * If the device is pci, "legacy_irq" is alway -1. Least 8 bit of "ih"
 	 * is only used in intr_string() to show the irq number.
@@ -469,15 +494,15 @@ create_intrid(int legacy_irq, struct pic
 	if (pic->pic_type == PIC_I8259) {
 		ih = legacy_irq;
 		return legacy_intr_string(ih, buf, len, pic);
-	} else {
-		ih = ((pic->pic_apicid << APIC_INT_APIC_SHIFT) & APIC_INT_APIC_MASK) |
-			((pin << APIC_INT_PIN_SHIFT) & APIC_INT_PIN_MASK);
-		if (pic->pic_type == PIC_IOAPIC) {
-			ih |= APIC_INT_VIA_APIC;
-		}
-		ih |= pin;
-		return intr_string(ih, buf, len);
 	}
+
+	ih = ((pic->pic_apicid << APIC_INT_APIC_SHIFT) & APIC_INT_APIC_MASK)
+	    | ((pin << APIC_INT_PIN_SHIFT) & APIC_INT_PIN_MASK);
+	if (pic->pic_type == PIC_IOAPIC) {
+		ih |= APIC_INT_VIA_APIC;
+	}
+	ih |= pin;
+	return intr_string(ih, buf, len);
 }
 
 /*
@@ -492,9 +517,8 @@ intr_get_io_intrsource(const char *intri
 
 	SIMPLEQ_FOREACH(isp, &io_interrupt_sources, is_list) {
 		KASSERT(isp->is_intrid != NULL);
-		if (strncmp(intrid, isp->is_intrid, INTRIDBUF - 1) == 0) {
+		if (strncmp(intrid, isp->is_intrid, INTRIDBUF - 1) == 0)
 			return isp;
-		}
 	}
 	return NULL;
 }
@@ -553,6 +577,7 @@ intr_free_io_intrsource_direct(struct in
 
 	kmem_free(isp->is_saved_evcnt,
 	    sizeof(*(isp->is_saved_evcnt)) * ncpu);
+
 	kmem_free(isp, sizeof(*isp));
 }
 
@@ -590,13 +615,17 @@ intr_allocate_slot_cpu(struct cpu_info *
 		KASSERT(CPU_IS_PRIMARY(ci));
 		slot = pin;
 	} else {
+		int start = 0;
 		slot = -1;
 
+		/* avoid reserved slots for legacy interrupts. */
+		if (CPU_IS_PRIMARY(ci) && msipic_is_msi_pic(pic))
+			start = NUM_LEGACY_IRQS;
 		/*
 		 * intr_allocate_slot has checked for an existing mapping.
 		 * Now look for a free slot.
 		 */
-		for (i = 0; i < MAX_INTR_SOURCES ; i++) {
+		for (i = start; i < MAX_INTR_SOURCES ; i++) {
 			if (ci->ci_isources[i] == NULL) {
 				slot = i;
 				break;
@@ -609,10 +638,16 @@ intr_allocate_slot_cpu(struct cpu_info *
 
 	isp = ci->ci_isources[slot];
 	if (isp == NULL) {
+		const char *via;
+
 		isp = chained;
 		KASSERT(isp != NULL);
+		if (pic->pic_type == PIC_MSI || pic->pic_type == PIC_MSIX)
+			via = "vec";
+		else
+			via = "pin";
 		snprintf(isp->is_evname, sizeof (isp->is_evname),
-		    "pin %d", pin);
+		    "%s %d", via, pin);
 		evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL,
 		    pic->pic_name, isp->is_evname);
 		isp->is_active_cpu = ci->ci_cpuid;
@@ -877,6 +912,11 @@ intr_establish(int legacy_irq, struct pi
 	/* allocate intrsource pool, if not yet. */
 	chained = intr_get_io_intrsource(intrstr);
 	if (chained == NULL) {
+		if (msipic_is_msi_pic(pic)) {
+			mutex_exit(&cpu_lock);
+			printf("%s: %s has no intrsource\n", __func__, intrstr);
+			return NULL;
+		}
 		chained = intr_allocate_io_intrsource(intrstr);
 		if (chained == NULL) {
 			mutex_exit(&cpu_lock);
@@ -1104,13 +1144,10 @@ intr_disestablish(struct intrhand *ih)
 		where = xc_unicast(0, intr_disestablish_xcall, ih, NULL, ci);
 		xc_wait(where);
 	}	
-
-	if (intr_num_handlers(isp) < 1) {
+	if (!msipic_is_msi_pic(isp->is_pic) && intr_num_handlers(isp) < 1) {
 		intr_free_io_intrsource_direct(isp);
 	}
-
 	mutex_exit(&cpu_lock);
-
 	kmem_free(ih, sizeof(*ih));
 }
 

Index: src/sys/arch/x86/x86/ioapic.c
diff -u src/sys/arch/x86/x86/ioapic.c:1.49 src/sys/arch/x86/x86/ioapic.c:1.50
--- src/sys/arch/x86/x86/ioapic.c:1.49	Mon Apr 27 06:51:40 2015
+++ src/sys/arch/x86/x86/ioapic.c	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/* 	$NetBSD: ioapic.c,v 1.49 2015/04/27 06:51:40 knakahara Exp $	*/
+/* 	$NetBSD: ioapic.c,v 1.50 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 2000, 2009 The NetBSD Foundation, Inc.
@@ -64,7 +64,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ioapic.c,v 1.49 2015/04/27 06:51:40 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ioapic.c,v 1.50 2015/04/27 07:03:58 knakahara Exp $");
 
 #include "opt_ddb.h"
 
@@ -183,7 +183,7 @@ ioapic_write(struct ioapic_softc *sc,int
 }
 
 struct ioapic_softc *
-ioapic_find(intr_handle_t apicid)
+ioapic_find(int apicid)
 {
 	struct ioapic_softc *sc;
 

Index: src/sys/arch/xen/include/intr.h
diff -u src/sys/arch/xen/include/intr.h:1.35 src/sys/arch/xen/include/intr.h:1.36
--- src/sys/arch/xen/include/intr.h:1.35	Thu Dec 27 06:42:14 2012
+++ src/sys/arch/xen/include/intr.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: intr.h,v 1.35 2012/12/27 06:42:14 cherry Exp $	*/
+/*	$NetBSD: intr.h,v 1.36 2015/04/27 07:03:58 knakahara Exp $	*/
 /*	NetBSD intr.h,v 1.15 2004/10/31 10:39:34 yamt Exp	*/
 
 /*-
@@ -154,6 +154,8 @@ splraiseipl(ipl_cookie_t icookie)
 
 struct pcibus_attach_args;
 
+typedef int intr_handle_t;
+
 #ifdef MULTIPROCESSOR
 int intr_biglock_wrapper(void *);
 #endif
@@ -163,7 +165,7 @@ int x86_nmi(void);
 
 void *intr_establish(int, struct pic *, int, int, int, int (*)(void *), void *, bool);
 void intr_disestablish(struct intrhand *);
-const char *intr_string(int);
+const char *intr_string(intr_handle_t);
 void cpu_intr_init(struct cpu_info *);
 int xen_intr_map(int *, int);
 #ifdef INTRDEBUG

Index: src/sys/dev/pci/pci.c
diff -u src/sys/dev/pci/pci.c:1.145 src/sys/dev/pci/pci.c:1.146
--- src/sys/dev/pci/pci.c:1.145	Fri Sep  5 05:29:16 2014
+++ src/sys/dev/pci/pci.c	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci.c,v 1.145 2014/09/05 05:29:16 matt Exp $	*/
+/*	$NetBSD: pci.c,v 1.146 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1995, 1996, 1997, 1998
@@ -36,7 +36,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pci.c,v 1.145 2014/09/05 05:29:16 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pci.c,v 1.146 2015/04/27 07:03:58 knakahara Exp $");
 
 #include "opt_pci.h"
 
@@ -275,6 +275,10 @@ pci_probe_device(struct pci_softc *sc, p
 	pci_chipset_tag_t pc = sc->sc_pc;
 	struct pci_attach_args pa;
 	pcireg_t id, /* csr, */ pciclass, intr, bhlcr, bar, endbar;
+#ifdef __HAVE_PCI_MSI_MSIX
+	pcireg_t cap;
+	int off;
+#endif
 	int ret, pin, bus, device, function, i, width;
 	int locs[PCICF_NLOCS];
 
@@ -406,6 +410,34 @@ pci_probe_device(struct pci_softc *sc, p
 	}
 	pa.pa_intrline = PCI_INTERRUPT_LINE(intr);
 
+#ifdef __HAVE_PCI_MSI_MSIX
+	if (pci_get_ht_capability(pc, tag, PCI_HT_CAP_MSIMAP, &off, &cap)) {
+		/*
+		 * XXX Should we enable MSI mapping ourselves on
+		 * systems that have it disabled?
+		 */
+		if (cap & PCI_HT_MSI_ENABLED) {
+			uint64_t addr;
+			if ((cap & PCI_HT_MSI_FIXED) == 0) {
+				addr = pci_conf_read(pc, tag,
+				    off + PCI_HT_MSI_ADDR_LO);
+				addr |= (uint64_t)pci_conf_read(pc, tag,
+				    off + PCI_HT_MSI_ADDR_HI) << 32;
+			} else
+				addr = PCI_HT_MSI_FIXED_ADDR;
+
+			/*
+			 * XXX This will fail to enable MSI on systems
+			 * that don't use the canonical address.
+			 */
+			if (addr == PCI_HT_MSI_FIXED_ADDR) {
+				pa.pa_flags |= PCI_FLAGS_MSI_OKAY;
+				pa.pa_flags |= PCI_FLAGS_MSIX_OKAY;
+			}
+		}
+	}
+#endif
+
 	if (match != NULL) {
 		ret = (*match)(&pa);
 		if (ret != 0 && pap != NULL)
@@ -508,6 +540,35 @@ pci_get_capability(pci_chipset_tag_t pc,
 }
 
 int
+pci_get_ht_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
+    int *offset, pcireg_t *value)
+{
+	pcireg_t reg;
+	unsigned int ofs;
+
+	if (pci_get_capability(pc, tag, PCI_CAP_LDT, &ofs, NULL) == 0)
+		return 0;
+
+	while (ofs != 0) {
+#ifdef DIAGNOSTIC
+		if ((ofs & 3) || (ofs < 0x40))
+			panic("pci_get_ht_capability");
+#endif
+		reg = pci_conf_read(pc, tag, ofs);
+		if (PCI_HT_CAP(reg) == capid) {
+			if (offset)
+				*offset = ofs;
+			if (value)
+				*value = reg;
+			return 1;
+		}
+		ofs = PCI_CAPLIST_NEXT(reg);
+	}
+
+	return 0;
+}
+
+int
 pci_find_device(struct pci_attach_args *pa,
 		int (*match)(const struct pci_attach_args *))
 {

Index: src/sys/dev/pci/pcireg.h
diff -u src/sys/dev/pci/pcireg.h:1.101 src/sys/dev/pci/pcireg.h:1.102
--- src/sys/dev/pci/pcireg.h:1.101	Mon Feb 23 04:16:17 2015
+++ src/sys/dev/pci/pcireg.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pcireg.h,v 1.101 2015/02/23 04:16:17 knakahara Exp $	*/
+/*	$NetBSD: pcireg.h,v 1.102 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1995, 1996, 1999, 2000
@@ -653,6 +653,9 @@ typedef u_int8_t pci_revision_t;
  * MSI Pending Bits (32 bit field)
  */
 
+ /* Max number of MSI vectors. See PCI-SIG specification. */
+#define	PCI_MSI_MAX_VECTORS	32
+
 /*
  * Capability ID: 0x07
  * PCI-X capability.
@@ -1069,6 +1072,9 @@ struct pci_msix_table_entry {
 };
 #define	PCI_MSIX_VECTCTL_HWMASK_MASK	0x00000001
 
+ /* Max number of MSI-X vectors. See PCI-SIG specification. */
+#define	PCI_MSIX_MAX_VECTORS		2048
+
 /*
  * Capability ID: 0x12
  * SATA
Index: src/sys/dev/pci/pcivar.h
diff -u src/sys/dev/pci/pcivar.h:1.101 src/sys/dev/pci/pcivar.h:1.102
--- src/sys/dev/pci/pcivar.h:1.101	Fri Dec 26 05:09:03 2014
+++ src/sys/dev/pci/pcivar.h	Mon Apr 27 07:03:58 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pcivar.h,v 1.101 2014/12/26 05:09:03 msaitoh Exp $	*/
+/*	$NetBSD: pcivar.h,v 1.102 2015/04/27 07:03:58 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1996, 1997 Christopher G. Demetriou.  All rights reserved.
@@ -279,6 +279,7 @@ int pci_find_rom(const struct pci_attach
 	    int, bus_space_handle_t *, bus_size_t *);
 
 int pci_get_capability(pci_chipset_tag_t, pcitag_t, int, int *, pcireg_t *);
+int pci_get_ht_capability(pci_chipset_tag_t, pcitag_t, int, int *, pcireg_t *);
 
 /*
  * Helper functions for autoconfiguration.

Added files:

Index: src/share/man/man9/pci_msi.9
diff -u /dev/null src/share/man/man9/pci_msi.9:1.1
--- /dev/null	Mon Apr 27 07:03:58 2015
+++ src/share/man/man9/pci_msi.9	Mon Apr 27 07:03:57 2015
@@ -0,0 +1,210 @@
+.\" $NetBSD: pci_msi.9,v 1.1 2015/04/27 07:03:57 knakahara Exp $
+.\"
+.\" Copyright (c) 2015 Internet Initiative Japan Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 8, 2015
+.Dt PCI_MSI 9 (DRAFT)
+.Os
+.Sh NAME (DRAFT)
+.Nm pci_msi ,
+.Nm pci_msix,
+.Nm pci_msi_count ,
+.Nm pci_msi_alloc ,
+.Nm pci_msi_alloc_exact ,
+.Nm pci_msi_release ,
+.Nm pci_msi_establish ,
+.Nm pci_msi_disestablish ,
+.Nm pci_msi_string
+.Nm pci_msix_count ,
+.Nm pci_msix_alloc ,
+.Nm pci_msix_alloc_exact ,
+.Nm pci_msix_alloc_map ,
+.Nm pci_msix_release ,
+.Nm pci_msix_establish ,
+.Nm pci_msix_disestablish ,
+.Nm pci_intx_alloc ,
+.Nm pci_intx_release
+.Nd PCI MSI{,-X} manipulation functions
+.Sh SYNOPSIS
+.In NOTYET
+.Ft int
+.Fn pci_msi_count "struct pci_attach_args *pa"
+.Ft int
+.Fn pci_msi_alloc  "struct pci_attach_args *pa" \
+"pci_intr_handle_t **ihps" "int *count"
+.Ft int
+.Fn pci_msi_alloc_exect "struct pci_attach_args *pa" \
+"pci_intr_handle_t **ihps" "int count"
+.Ft void
+.Fn pci_msi_release "pci_intr_handle_t **pihs" "int count"
+.Ft void *
+.Fn pci_msi_establish "pci_chipset_tag_t pc" "pci_intr_handle_t ih" \
+"int level" "int (*func)(void *)" "void *arg"
+.Ft void
+.Fn pci_msi_disestablish "pci_chipset_tag_t pc" "void *cookie"
+.Ft const char *
+.Ft pci_msi_string "pci_chipset_tag_t pc" \
+"pci_intr_handle_t, char *buf" "size_t len"
+.Ft int
+.Fn pci_msix_count "struct pci_attach_args *pa"
+.Ft int
+.Fn pci_msix_alloc  "struct pci_attach_args *pa" \
+"pci_intr_handle_t **ihps" "int *count"
+.Ft int
+.Fn pci_msix_alloc_exect "struct pci_attach_args *pa" \
+"pci_intr_handle_t **ihps" "int count"
+.Ft int
+.Fn pci_msix_alloc_map "struct pci_attach_args *pa" \
+"pci_intr_handle_t **ihps" "u_int *table_indexes" "int count"
+.Ft void
+.Fn pci_msix_release "pci_intr_handle_t **pihs" "int count"
+.Ft void *
+.Fn pci_msix_establish "pci_chipset_tag_t pc" "pci_intr_handle_t ih" \
+"int level" "int (*func)(void *)" "void *arg"
+.Fn pci_msix_disestablish "pci_chipset_tag_t pc" "void *cookie"
+.Ft int
+.Fn pci_intx_alloc  "struct pci_attach_args *pa" \
+"pci_intr_handle_t **ihp"
+.Ft void
+.Fn pci_intx_release "pci_intr_handle_t *pih"
+.Sh DESCRIPTION
+XXX This decument describes draft APIs. These APIs may change later.
+.Pp
+The
+.Nm
+functions exist to allow device drivers to use MSI/MSI-X.
+When the system use MSI/MSI-X, it must define a __HAVE_PCI_MSI_MSIX
+build option.
+.Pp
+Each driver has an
+.Fn attach
+function which has a bus-specific
+.Ft attach_args
+structure.
+Each driver for a PCI device is passed a pointer to an object of type
+.Ft struct pci_attach_args
+which contains, among other things, information about the location
+of the device in the PCI bus topology sufficient to allow interrupts
+from the device to be handled.
+.Pp
+If a driver wishes to establish an MSI handler for the device,
+it should pass the
+.Ft struct pci_attach_args *
+and
+.Ft count
+.Fn pci_msi_alloc
+or
+.Fn pci_msi_alloc_exact
+functions, which returns zero on success, and nonzero on failure.
+When the functions successed, set the pointer to allocated handle
+array to
+.Ft pihs
+whose size is
+.Ft count
+or less. The difference between
+.Fn pci_msi_alloc
+and
+.Fn pci_msi_alloc_exact
+is
+.Ft count
+can be decremented or not.
+.Fn pci_msi_alloc
+can decrement
+.Ft count ,
+and
+which is similar to FreeBSD's
+.Fn pci_alloc_msi .
+In contrast,
+.Fn pci_msi_alloc_exact
+can not decrement
+.Ft count .
+.Pp
+If the driver wishes to refer to the MSI source in an attach or
+error message, it should use the value returned by
+.Fn pci_msi_string .
+The buffer passed to
+.Fn pci_msi_string
+should be at least
+.Dv PCI_INTRSTR_LEN
+bytes.
+.Pp
+Subsequently, when the driver is prepared to receive MSIs, it
+should call
+.Fn pci_msi_establish
+to actually establish the handler; when the device interrupts,
+.Fa intrhand
+will be called with a single argument
+.Fa intrarg ,
+and will run at the interrupt priority level
+.Fa ipl .
+This is the same as
+.Fn pci_intr_establish .
+.Pp
+The return value of
+.Fn pci_msi_establish
+may be saved and passed to
+.Fn pci_msi_disestablish
+to disable the interrupt handler
+when the driver is no longer interested in MSIs from the device.
+After that, the driver should also
+.Fn pci_msi_release
+to free resources about MSI.
+.Pp
+If a driver wishes to establish an MSI-X handler for the device,
+it is alomost the same as MSI.
+The only differences is
+.Fn pci_msix_alloc_map .
+This function can assign each handles to MSI-X table entries.
+e.g. If the driver want assign each handler to
+.Bd -literal
+	msix_handler0 => MSI-X table index: 4
+	msix_handler1 => MSI-X table index: 5
+	msix_handler2 => MSI-X table index: 0
+.Ed
+, the driver should set
+.Bd -literal
+	table_indexes[0] = 4;
+	table_indexes[1] = 5;
+	table_indexes[2] = 0;
+.Ed
+to
+.Ft table_indexes .
+.Pp
+If the driver want to fallback to INTx, the driver should use
+.Fn pci_intx_alloc
+and
+.Fn pci_intx_release
+instead of
+.Fn pci_intr_map
+to resolve contradiction of the interrupt handler ownership.
+i.e.
+.Fn pci_intr_map
+does not have the ownership (the function just calcurates value),
+in contrast,
+.Fn pci_msi_alloc
+and
+.Fn pci_msix_alloc
+has the owneship (the functions allocate memory for interrupt
+handlers).

Index: src/sys/arch/x86/pci/msipic.c
diff -u /dev/null src/sys/arch/x86/pci/msipic.c:1.1
--- /dev/null	Mon Apr 27 07:03:58 2015
+++ src/sys/arch/x86/pci/msipic.c	Mon Apr 27 07:03:58 2015
@@ -0,0 +1,737 @@
+/*	$NetBSD: msipic.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $	*/
+
+/*
+ * Copyright (c) 2015 Internet Initiative Japan Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msipic.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+
+#include <dev/pci/pcivar.h>
+
+#include <machine/i82489reg.h>
+#include <machine/i82093reg.h>
+#include <machine/i82093var.h>
+#include <machine/pic.h>
+#include <machine/lock.h>
+
+#include <x86/pci/msipic.h>
+
+#ifdef INTRDEBUG
+#define MSIPICDEBUG
+#endif
+
+#ifdef MSIPICDEBUG
+#define DPRINTF(msg) printf msg
+#else
+#define DPRINTF(msg)
+#endif
+
+#define BUS_SPACE_WRITE_FLUSH(pc, tag) (void)bus_space_read_4(pc, tag, 0)
+
+#define MSIPICNAMEBUF 16
+
+/*
+ * A Pseudo pic for single MSI/MSI-X device.
+ * The pic and MSI/MSI-X device are distinbuished by "devid". The "devid"
+ * is managed by below "dev_seqs".
+ */
+struct msipic {
+	int mp_bus;
+	int mp_dev;
+	int mp_fun;
+
+	int mp_devid; /* The device id for the MSI/MSI-X device. */
+	int mp_veccnt; /* The number of MSI/MSI-X vectors. */
+
+	char mp_pic_name[MSIPICNAMEBUF]; /* The MSI/MSI-X device's name. */
+
+	struct pci_attach_args mp_pa;
+	bus_space_tag_t mp_bstag;
+	bus_space_handle_t mp_bshandle;
+	bus_size_t mp_bssize;
+	struct pic *mp_pic;
+
+	LIST_ENTRY(msipic) mp_list;
+};
+
+static kmutex_t msipic_list_lock;
+
+static LIST_HEAD(, msipic) msipic_list =
+	LIST_HEAD_INITIALIZER(msipic_list);
+
+/*
+ * This struct managements "devid" to use the same "devid" for the device
+ * re-attached. If the device's bus number and device numer and function
+ * number are equal, it is assumed re-attached.
+ */
+struct dev_last_used_seq {
+	bool ds_using;
+	int ds_bus;
+	int ds_dev;
+	int ds_fun;
+};
+/* The number of MSI/MSI-X devices supported by system. */
+#define NUM_MSI_DEVS 256
+/* Record devids to use the same devid when the device is re-attached. */
+static struct dev_last_used_seq dev_seqs[NUM_MSI_DEVS];
+
+static int msipic_allocate_common_msi_devid(struct pci_attach_args *);
+static void msipic_release_common_msi_devid(int);
+
+static struct pic *msipic_find_msi_pic_locked(int);
+static struct pic *msipic_construct_common_msi_pic(struct pci_attach_args *,
+						   struct pic *);
+static void msipic_destruct_common_msi_pic(struct pic *);
+
+static void msi_set_msictl_enablebit(struct pic *, int, int);
+static void msi_hwmask(struct pic *, int);
+static void msi_hwunmask(struct pic *, int);
+static void msi_addroute(struct pic *, struct cpu_info *, int, int, int);
+static void msi_delroute(struct pic *, struct cpu_info *, int, int, int);
+
+static void msix_set_vecctl_mask(struct pic *, int, int);
+static void msix_hwmask(struct pic *, int);
+static void msix_hwunmask(struct pic *, int);
+static void msix_addroute(struct pic *, struct cpu_info *, int, int, int);
+static void msix_delroute(struct pic *, struct cpu_info *, int, int, int);
+
+/*
+ * Return new "devid" for the device attached first.
+ * Return the same "devid" for the device re-attached after dettached once.
+ * Return -1 if the number of attached MSI/MSI-X devices is over NUM_MSI_DEVS.
+ */
+static int
+msipic_allocate_common_msi_devid(struct pci_attach_args *pa)
+{
+	pci_chipset_tag_t pc;
+	pcitag_t tag;
+	int bus, dev, fun, i;
+
+	KASSERT(mutex_owned(&msipic_list_lock));
+
+	pc = pa->pa_pc;
+	tag = pa->pa_tag;
+	pci_decompose_tag(pc, tag, &bus, &dev, &fun);
+
+	/* if the device was once attached, use same devid */
+	for (i = 0; i < NUM_MSI_DEVS; i++) {
+		/* skip host bridge */
+		if (dev_seqs[i].ds_bus == 0
+		    && dev_seqs[i].ds_dev == 0
+		    && dev_seqs[i].ds_fun == 0)
+			break;
+
+		if (dev_seqs[i].ds_bus == bus
+		    && dev_seqs[i].ds_dev == dev
+		    && dev_seqs[i].ds_fun == fun) {
+			dev_seqs[i].ds_using = true;
+			return i;
+		}
+	}
+
+	for (i = 0; i < NUM_MSI_DEVS; i++) {
+		if (dev_seqs[i].ds_using == 0) {
+			dev_seqs[i].ds_using = true;
+			dev_seqs[i].ds_bus = bus;
+			dev_seqs[i].ds_dev = dev;
+			dev_seqs[i].ds_fun = fun;
+			return i;
+		}
+	}
+
+	DPRINTF(("too many MSI devices.\n"));
+	return -1;
+}
+
+/*
+ * Set the "devid" unused, but keep reserving the "devid" to reuse when
+ * the device is re-attached.
+ */
+static void
+msipic_release_common_msi_devid(int devid)
+{
+
+	KASSERT(mutex_owned(&msipic_list_lock));
+
+	if (devid < 0 || NUM_MSI_DEVS <= devid) {
+		DPRINTF(("%s: invalid devid.\n", __func__));
+		return;
+	}
+
+	dev_seqs[devid].ds_using = false;
+	/* Keep ds_* to reuse the same devid for the same device. */
+}
+
+static struct pic *
+msipic_find_msi_pic_locked(int devid)
+{
+	struct msipic *mpp;
+
+	KASSERT(mutex_owned(&msipic_list_lock));
+
+	LIST_FOREACH(mpp, &msipic_list, mp_list) {
+		if(mpp->mp_devid == devid)
+			return mpp->mp_pic;
+	}
+	return NULL;
+}
+
+/*
+ * Return the msi_pic whose device is already registered.
+ * If the device is not registered yet, return NULL.
+ */
+struct pic *
+msipic_find_msi_pic(int devid)
+{
+	struct pic *msipic;
+
+	mutex_enter(&msipic_list_lock);
+	msipic = msipic_find_msi_pic_locked(devid);
+	mutex_exit(&msipic_list_lock);
+
+	return msipic;
+}
+
+/*
+ * A common construct process of MSI and MSI-X.
+ */
+static struct pic *
+msipic_construct_common_msi_pic(struct pci_attach_args *pa,
+    struct pic *pic_tmpl)
+{
+	struct pic *pic;
+	struct msipic *msipic;
+	int devid;
+
+	pic = kmem_alloc(sizeof(*pic), KM_SLEEP);
+	if (pic == NULL)
+		return NULL;
+
+	msipic = kmem_zalloc(sizeof(*msipic), KM_SLEEP);
+	if (msipic == NULL) {
+		kmem_free(pic, sizeof(*pic));
+		return NULL;
+	}
+
+	mutex_enter(&msipic_list_lock);
+
+	devid = msipic_allocate_common_msi_devid(pa);
+	if (devid == -1) {
+		mutex_exit(&msipic_list_lock);
+		kmem_free(pic, sizeof(*pic));
+		kmem_free(msipic, sizeof(*msipic));
+		return NULL;
+	}
+
+	memcpy(pic, pic_tmpl, sizeof(*pic));
+	pic->pic_msipic = msipic;
+	msipic->mp_pic = pic;
+	pci_decompose_tag(pa->pa_pc, pa->pa_tag,
+	    &msipic->mp_bus, &msipic->mp_dev, &msipic->mp_fun);
+	memcpy(&msipic->mp_pa, pa, sizeof(msipic->mp_pa));
+	msipic->mp_devid = devid;
+	/*
+	 * pci_msi{,x}_alloc() must be called only once in the device driver.
+	 */
+	KASSERT(msipic_find_msi_pic_locked(msipic->mp_devid) == NULL);
+
+	LIST_INSERT_HEAD(&msipic_list, msipic, mp_list);
+
+	mutex_exit(&msipic_list_lock);
+
+	return pic;
+}
+
+static void
+msipic_destruct_common_msi_pic(struct pic *msi_pic)
+{
+	struct msipic *msipic;
+
+	if (msi_pic == NULL)
+		return;
+
+	msipic = msi_pic->pic_msipic;
+	mutex_enter(&msipic_list_lock);
+	LIST_REMOVE(msipic, mp_list);
+	msipic_release_common_msi_devid(msipic->mp_devid);
+	mutex_exit(&msipic_list_lock);
+
+	kmem_free(msipic, sizeof(*msipic));
+	kmem_free(msi_pic, sizeof(*msi_pic));
+}
+
+/*
+ * The pic is MSI/MSI-X pic or not.
+ */
+bool
+msipic_is_msi_pic(struct pic *pic)
+{
+
+	return (pic->pic_msipic != NULL);
+}
+
+/*
+ * Return the MSI/MSI-X devid which is unique for each devices.
+ */
+int
+msipic_get_devid(struct pic *pic)
+{
+
+	KASSERT(msipic_is_msi_pic(pic));
+
+	return pic->pic_msipic->mp_devid;
+}
+
+#define MSI_MSICTL_ENABLE 1
+#define MSI_MSICTL_DISABLE 0
+static void
+msi_set_msictl_enablebit(struct pic *pic, int msi_vec, int flag)
+{
+	pci_chipset_tag_t pc;
+	struct pci_attach_args *pa;
+	pcitag_t tag;
+	pcireg_t ctl;
+	int off;
+
+	pc = NULL;
+	pa = &pic->pic_msipic->mp_pa;
+	tag = pa->pa_tag;
+	KASSERT(pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0);
+
+	/*
+	 * MSI can establish only one vector at once.
+	 * So, use whole device mask bit instead of a vector mask bit.
+	 */
+	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
+	if (flag == MSI_MSICTL_ENABLE)
+		ctl |= PCI_MSI_CTL_MSI_ENABLE;
+	else
+		ctl &= ~PCI_MSI_CTL_MSI_ENABLE;
+
+	pci_conf_write(pc, tag, off, ctl);
+}
+
+static void
+msi_hwmask(struct pic *pic, int msi_vec)
+{
+
+	msi_set_msictl_enablebit(pic, msi_vec, MSI_MSICTL_DISABLE);
+}
+
+/*
+ * Do not use pic->hwunmask() immediately after pic->delroute().
+ * It is required to use pic->addroute() before pic->hwunmask().
+ */
+static void
+msi_hwunmask(struct pic *pic, int msi_vec)
+{
+
+	msi_set_msictl_enablebit(pic, msi_vec, MSI_MSICTL_ENABLE);
+}
+
+static void
+msi_addroute(struct pic *pic, struct cpu_info *ci,
+	     int unused, int idt_vec, int type)
+{
+	pci_chipset_tag_t pc;
+	struct pci_attach_args *pa;
+	pcitag_t tag;
+	pcireg_t addr, data, ctl;
+	int off;
+
+	pc = NULL;
+	pa = &pic->pic_msipic->mp_pa;
+	tag = pa->pa_tag;
+	KASSERT(pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0);
+
+	/*
+	 * See Intel 64 and IA-32 Architectures Software Developer's Manual
+	 * Volume 3 10.11 Message Signalled Interrupts.
+	 */
+	/*
+	 * "cpuid" for MSI address is local APIC ID. In NetBSD, the ID is
+	 * the same as ci->ci_cpuid.
+	 */
+	addr = LAPIC_MSIADDR_BASE | __SHIFTIN(ci->ci_cpuid,
+	    LAPIC_MSIADDR_DSTID_MASK);
+	/* If trigger mode is edge, it don't care level for trigger mode. */
+	data = __SHIFTIN(idt_vec, LAPIC_MSIDATA_VECTOR_MASK)
+		| LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_DM_FIXED;
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
+	if (ctl & PCI_MSI_CTL_64BIT_ADDR) {
+		pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_LO, addr);
+		pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_HI, 0);
+		pci_conf_write(pc, tag, off + PCI_MSI_MDATA64, data);
+	} else {
+		pci_conf_write(pc, tag, off + PCI_MSI_MADDR, addr);
+		pci_conf_write(pc, tag, off + PCI_MSI_MDATA, data);
+	}
+	ctl |= PCI_MSI_CTL_MSI_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl);
+}
+
+/*
+ * Do not use pic->hwunmask() immediately after pic->delroute().
+ * It is required to use pic->addroute() before pic->hwunmask().
+ */
+static void
+msi_delroute(struct pic *pic, struct cpu_info *ci,
+	     int msi_vec, int idt_vec, int type)
+{
+
+	msi_hwmask(pic, msi_vec);
+}
+
+/*
+ * Template for MSI pic.
+ * .pic_msipic is set later in construct_msi_pic().
+ */
+static struct pic msi_pic_tmpl = {
+	.pic_type = PIC_MSI,
+	.pic_vecbase = 0,
+	.pic_apicid = 0,
+	.pic_lock = __SIMPLELOCK_UNLOCKED, /* not used for msi_pic */
+	.pic_hwmask = msi_hwmask,
+	.pic_hwunmask = msi_hwunmask,
+	.pic_addroute = msi_addroute,
+	.pic_delroute = msi_delroute,
+	.pic_edge_stubs = ioapic_edge_stubs,
+	.pic_ioapic = NULL,
+};
+
+/*
+ * Create pseudo pic for a MSI device.
+ */
+struct pic *
+msipic_construct_msi_pic(struct pci_attach_args *pa)
+{
+	struct pic *msi_pic;
+	char pic_name_buf[MSIPICNAMEBUF];
+
+	msi_pic = msipic_construct_common_msi_pic(pa, &msi_pic_tmpl);
+	if (msi_pic == NULL) {
+		DPRINTF(("cannot allocate MSI pic.\n"));
+		return NULL;
+	}
+
+	memset(pic_name_buf, 0, MSIPICNAMEBUF);
+	snprintf(pic_name_buf, MSIPICNAMEBUF, "msi%d",
+	    msi_pic->pic_msipic->mp_devid);
+	strncpy(msi_pic->pic_msipic->mp_pic_name, pic_name_buf,
+	    MSIPICNAMEBUF - 1);
+	msi_pic->pic_name = msi_pic->pic_msipic->mp_pic_name;
+
+	return msi_pic;
+}
+
+/*
+ * Delete pseudo pic for a MSI device.
+ */
+void
+msipic_destruct_msi_pic(struct pic *msi_pic)
+{
+
+	msipic_destruct_common_msi_pic(msi_pic);
+}
+
+#define MSIX_VECCTL_HWMASK 1
+#define MSIX_VECCTL_HWUNMASK 0
+static void
+msix_set_vecctl_mask(struct pic *pic, int msix_vec, int flag)
+{
+	bus_space_tag_t bstag;
+	bus_space_handle_t bshandle;
+	uint64_t entry_base;
+	uint32_t vecctl;
+
+	if (msix_vec < 0) {
+		DPRINTF(("%s: invalid MSI-X table index, devid=%d vecid=%d",
+			__func__, msi_get_devid(pic), msix_vec));
+		return;
+	}
+
+	entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec;
+
+	bstag = pic->pic_msipic->mp_bstag;
+	bshandle = pic->pic_msipic->mp_bshandle;
+	vecctl = bus_space_read_4(bstag, bshandle,
+	    entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL);
+	if (flag == MSIX_VECCTL_HWMASK)
+		vecctl |= PCI_MSIX_VECTCTL_HWMASK_MASK;
+	else
+		vecctl &= ~PCI_MSIX_VECTCTL_HWMASK_MASK;
+
+	bus_space_write_4(bstag, bshandle,
+	    entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL, vecctl);
+	BUS_SPACE_WRITE_FLUSH(bstag, bshandle);
+}
+
+static void
+msix_hwmask(struct pic *pic, int msix_vec)
+{
+
+	msix_set_vecctl_mask(pic, msix_vec, MSIX_VECCTL_HWMASK);
+}
+
+/*
+ * Do not use pic->hwunmask() immediately after pic->delroute().
+ * It is required to use pic->addroute() before pic->hwunmask().
+ */
+static void
+msix_hwunmask(struct pic *pic, int msix_vec)
+{
+
+	msix_set_vecctl_mask(pic, msix_vec, MSIX_VECCTL_HWUNMASK);
+}
+
+static void
+msix_addroute(struct pic *pic, struct cpu_info *ci,
+	     int msix_vec, int idt_vec, int type)
+{
+	pci_chipset_tag_t pc;
+	struct pci_attach_args *pa;
+	pcitag_t tag;
+	bus_space_tag_t bstag;
+	bus_space_handle_t bshandle;
+	uint64_t entry_base;
+	pcireg_t addr, data, ctl;
+	int off;
+
+	if (msix_vec < 0) {
+		DPRINTF(("%s: invalid MSI-X table index, devid=%d vecid=%d",
+			__func__, msi_get_devid(pic), msix_vec));
+		return;
+	}
+
+	pa = &pic->pic_msipic->mp_pa;
+	pc = pa->pa_pc;
+	tag = pa->pa_tag;
+	KASSERT(pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) != 0);
+
+	entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec;
+
+	/*
+	 * See Intel 64 and IA-32 Architectures Software Developer's Manual
+	 * Volume 3 10.11 Message Signalled Interrupts.
+	 */
+	/*
+	 * "cpuid" for MSI-X address is local APIC ID. In NetBSD, the ID is
+	 * the same as ci->ci_cpuid.
+	 */
+	addr = LAPIC_MSIADDR_BASE | __SHIFTIN(ci->ci_cpuid,
+	    LAPIC_MSIADDR_DSTID_MASK);
+	/* If trigger mode is edge, it don't care level for trigger mode. */
+	data = __SHIFTIN(idt_vec, LAPIC_MSIDATA_VECTOR_MASK)
+		| LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_DM_FIXED;
+
+	bstag = pic->pic_msipic->mp_bstag;
+	bshandle = pic->pic_msipic->mp_bshandle;
+	bus_space_write_4(bstag, bshandle,
+	    entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_LO, addr);
+	bus_space_write_4(bstag, bshandle,
+	    entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_HI, 0);
+	bus_space_write_4(bstag, bshandle,
+	    entry_base + PCI_MSIX_TABLE_ENTRY_DATA, data);
+	bus_space_write_4(bstag, bshandle,
+	    entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL, 0);
+	BUS_SPACE_WRITE_FLUSH(bstag, bshandle);
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL);
+	ctl |= PCI_MSIX_CTL_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl);
+}
+
+/*
+ * Do not use pic->hwunmask() immediately after pic->delroute().
+ * It is required to use pic->addroute() before pic->hwunmask().
+ */
+static void
+msix_delroute(struct pic *pic, struct cpu_info *ci,
+	     int msix_vec, int vec, int type)
+{
+
+	msix_hwmask(pic, msix_vec);
+}
+
+/*
+ * Template for MSI-X pic.
+ * .pic_msipic is set later in construct_msix_pic().
+ */
+static struct pic msix_pic_tmpl = {
+	.pic_type = PIC_MSIX,
+	.pic_vecbase = 0,
+	.pic_apicid = 0,
+	.pic_lock = __SIMPLELOCK_UNLOCKED, /* not used for msix_pic */
+	.pic_hwmask = msix_hwmask,
+	.pic_hwunmask = msix_hwunmask,
+	.pic_addroute = msix_addroute,
+	.pic_delroute = msix_delroute,
+	.pic_edge_stubs = ioapic_edge_stubs,
+};
+
+struct pic *
+msipic_construct_msix_pic(struct pci_attach_args *pa)
+{
+	struct pic *msix_pic;
+	pci_chipset_tag_t pc;
+	pcitag_t tag;
+	pcireg_t tbl;
+	bus_space_tag_t bstag;
+	bus_space_handle_t bshandle;
+	bus_size_t bssize;
+	size_t table_size;
+	uint32_t table_offset;
+	u_int memtype;
+	int bir, bar, err, off, table_nentry;
+	char pic_name_buf[MSIPICNAMEBUF];
+
+	table_nentry = pci_msix_count(pa);
+	if (table_nentry == 0) {
+		DPRINTF(("MSI-X table entry is 0.\n"));
+		return NULL;
+	}
+
+	pc = pa->pa_pc;
+	tag = pa->pa_tag;
+	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) == 0) {
+		DPRINTF(("%s: no msix capability", __func__));
+		return NULL;
+	}
+
+	msix_pic = msipic_construct_common_msi_pic(pa, &msix_pic_tmpl);
+	if (msix_pic == NULL) {
+		DPRINTF(("cannot allocate MSI-X pic.\n"));
+		return NULL;
+	}
+
+	memset(pic_name_buf, 0, MSIPICNAMEBUF);
+	snprintf(pic_name_buf, MSIPICNAMEBUF, "msix%d",
+	    msix_pic->pic_msipic->mp_devid);
+	strncpy(msix_pic->pic_msipic->mp_pic_name, pic_name_buf,
+	    MSIPICNAMEBUF - 1);
+	msix_pic->pic_name = msix_pic->pic_msipic->mp_pic_name;
+
+	tbl = pci_conf_read(pc, tag, off + PCI_MSIX_TBLOFFSET);
+	table_offset = tbl & PCI_MSIX_TBLOFFSET_MASK;
+	bir = tbl & PCI_MSIX_PBABIR_MASK;
+	switch(bir) {
+	case 0:
+		bar = PCI_BAR0;
+		break;
+	case 1:
+		bar = PCI_BAR1;
+		break;
+	case 2:
+		bar = PCI_BAR2;
+		break;
+	case 3:
+		bar = PCI_BAR3;
+		break;
+	case 4:
+		bar = PCI_BAR4;
+		break;
+	case 5:
+		bar = PCI_BAR5;
+		break;
+	default:
+		aprint_error("detect an illegal device! The device use reserved BIR values.\n");
+		msipic_destruct_common_msi_pic(msix_pic);
+		return NULL;
+	}
+	memtype = pci_mapreg_type(pc, tag, bar);
+	 /*
+	  * PCI_MSIX_TABLE_ENTRY_SIZE consists below
+	  *     - Vector Control (32bit)
+	  *     - Message Data (32bit)
+	  *     - Message Upper Address (32bit)
+	  *     - Message Lower Address (32bit)
+	  */
+	table_size = table_nentry * PCI_MSIX_TABLE_ENTRY_SIZE;
+	err = pci_mapreg_submap(pa, bar, memtype, BUS_SPACE_MAP_LINEAR,
+	    roundup(table_size, PAGE_SIZE), table_offset,
+	    &bstag, &bshandle, NULL, &bssize);
+	if (err) {
+		DPRINTF(("cannot map msix table.\n"));
+		msipic_destruct_common_msi_pic(msix_pic);
+		return NULL;
+	}
+	msix_pic->pic_msipic->mp_bstag = bstag;
+	msix_pic->pic_msipic->mp_bshandle = bshandle;
+	msix_pic->pic_msipic->mp_bssize = bssize;
+
+	return msix_pic;
+}
+
+/*
+ * Delete pseudo pic for a MSI-X device.
+ */
+void
+msipic_destruct_msix_pic(struct pic *msix_pic)
+{
+	struct msipic *msipic;
+
+	KASSERT(msipic_is_msi_pic(msix_pic));
+	KASSERT(msix_pic->pic_type == PIC_MSIX);
+
+	msipic = msix_pic->pic_msipic;
+	bus_space_unmap(msipic->mp_bstag, msipic->mp_bshandle,
+	    msipic->mp_bssize);
+
+	msipic_destruct_common_msi_pic(msix_pic);
+}
+
+/*
+ * Set the number of MSI vectors for pseudo MSI pic.
+ */
+int
+msipic_set_msi_vectors(struct pic *msi_pic, pci_intr_handle_t *pihs,
+    int count)
+{
+
+	KASSERT(msipic_is_msi_pic(msi_pic));
+
+	msi_pic->pic_msipic->mp_veccnt = count;
+	return 0;
+}
+
+/*
+ * Initialize the system to use MSI/MSI-X.
+ */
+void
+msipic_init(void)
+{
+
+	mutex_init(&msipic_list_lock, MUTEX_DEFAULT, IPL_NONE);
+}
Index: src/sys/arch/x86/pci/msipic.h
diff -u /dev/null src/sys/arch/x86/pci/msipic.h:1.1
--- /dev/null	Mon Apr 27 07:03:58 2015
+++ src/sys/arch/x86/pci/msipic.h	Mon Apr 27 07:03:58 2015
@@ -0,0 +1,46 @@
+/*	$NetBSD: msipic.h,v 1.1 2015/04/27 07:03:58 knakahara Exp $	*/
+
+/*
+ * Copyright (c) 2015 Internet Initiative Japan Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _X86_PCI_MSIPIC_H_
+#define _X86_PCI_MSIPIC_H_
+
+#include <dev/pci/pcivar.h>
+
+struct pic	*msipic_construct_msi_pic(struct pci_attach_args *);
+void		msipic_destruct_msi_pic(struct pic *);
+struct pic	*msipic_construct_msix_pic(struct pci_attach_args *);
+void		msipic_destruct_msix_pic(struct pic *);
+struct pic	*msipic_find_msi_pic(int);
+int		msipic_set_msi_vectors(struct pic *, pci_intr_handle_t *, int);
+
+bool		msipic_is_msi_pic(struct pic *);
+int		msipic_get_devid(struct pic *);
+
+void		msipic_init(void);
+
+#endif /* _X86_PCI_MSIPIC_H_ */
Index: src/sys/arch/x86/pci/pci_msi_machdep.c
diff -u /dev/null src/sys/arch/x86/pci/pci_msi_machdep.c:1.1
--- /dev/null	Mon Apr 27 07:03:58 2015
+++ src/sys/arch/x86/pci/pci_msi_machdep.c	Mon Apr 27 07:03:58 2015
@@ -0,0 +1,686 @@
+/*	$NetBSD: pci_msi_machdep.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $	*/
+
+/*
+ * Copyright (c) 2015 Internet Initiative Japan Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * TODO
+ *
+ *     - PBA (Pending Bit Array) support
+ *     - HyperTransport mapping support
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: pci_msi_machdep.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <sys/cpu.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/kmem.h>
+#include <sys/malloc.h>
+
+#include <dev/pci/pcivar.h>
+
+#include <machine/i82093var.h>
+#include <machine/pic.h>
+
+#include <x86/pci/msipic.h>
+
+#ifdef INTRDEBUG
+#define MSIDEBUG
+#endif
+
+#ifdef MSIDEBUG
+#define DPRINTF(msg) printf msg
+#else
+#define DPRINTF(msg)
+#endif
+
+/*
+ * Return intrid for a MSI/MSI-X device.
+ * "buf" must be allocated by caller.
+ */
+const char *
+pci_msi_string(pci_chipset_tag_t pc, pci_intr_handle_t ih, char *buf,
+    size_t len)
+{
+	int dev, vec;
+
+	KASSERT(INT_VIA_MSI(ih));
+
+	dev = MSI_INT_DEV(ih);
+	vec = MSI_INT_VEC(ih);
+	if (MSI_INT_IS_MSIX(ih))
+		snprintf(buf, len, "msix%d vec %d", dev, vec);
+	else
+		snprintf(buf, len, "msi%d vec %d", dev, vec);
+
+	return buf;
+}
+
+static pci_intr_handle_t
+pci_msi_calculate_handle(struct pic *msi_pic, int vector)
+{
+	pci_intr_handle_t pih;
+
+	KASSERT(msipic_is_msi_pic(msi_pic));
+
+	pih = __SHIFTIN((uint64_t)msipic_get_devid(msi_pic), MSI_INT_DEV_MASK)
+	    | __SHIFTIN((uint64_t)vector, MSI_INT_VEC_MASK)
+	    | APIC_INT_VIA_MSI;
+	if (msi_pic->pic_type == PIC_MSI)
+		MSI_INT_MAKE_MSI(pih);
+	else if (msi_pic->pic_type == PIC_MSIX)
+		MSI_INT_MAKE_MSIX(pih);
+	else
+		panic("%s: Unexpected pic_type: %d\n", __func__,
+		    msi_pic->pic_type);
+
+	return pih;
+}
+
+static pci_intr_handle_t *
+pci_msi_alloc_vectors(struct pic *msi_pic, uint *table_indexes, int *count)
+{
+	struct intrsource *isp;
+	pci_intr_handle_t *vectors, pih;
+	int i;
+	const char *intrstr;
+	char intrstr_buf[INTRIDBUF];
+
+	vectors = kmem_zalloc(sizeof(vectors[0]) * (*count), KM_SLEEP);
+	if (vectors == NULL) {
+		DPRINTF(("cannot allocate vectors\n"));
+		return NULL;
+	}
+
+	mutex_enter(&cpu_lock);
+	for (i = 0; i < *count; i++) {
+		u_int table_index;
+
+		if (table_indexes == NULL)
+			table_index = i;
+		else
+			table_index = table_indexes[i];
+
+		pih = pci_msi_calculate_handle(msi_pic, table_index);
+
+		intrstr = pci_msi_string(NULL, pih, intrstr_buf,
+		    sizeof(intrstr_buf));
+		isp = intr_allocate_io_intrsource(intrstr);
+		if (isp == NULL) {
+			mutex_exit(&cpu_lock);
+			DPRINTF(("can't allocate io_intersource\n"));
+			kmem_free(vectors, sizeof(vectors[0]) * (*count));
+			return NULL;
+		}
+
+		vectors[i] = pih;
+	}
+	mutex_exit(&cpu_lock);
+
+	return vectors;
+}
+
+static void
+pci_msi_free_vectors(struct pic *msi_pic, pci_intr_handle_t *pihs, int count)
+{
+	pci_intr_handle_t pih;
+	int i;
+	const char *intrstr;
+	char intrstr_buf[INTRIDBUF];
+
+	mutex_enter(&cpu_lock);
+	for (i = 0; i < count; i++) {
+		pih = pci_msi_calculate_handle(msi_pic, i);
+		intrstr = pci_msi_string(NULL, pih, intrstr_buf,
+		    sizeof(intrstr_buf));
+		intr_free_io_intrsource(intrstr);
+	}
+	mutex_exit(&cpu_lock);
+
+	kmem_free(pihs, sizeof(pihs[0]) * count);
+}
+
+static int
+pci_msi_alloc_md_common(pci_intr_handle_t **ihps, int *count,
+    struct pci_attach_args *pa, bool exact)
+{
+	struct pic *msi_pic;
+	pci_intr_handle_t *vectors;
+	int error, i;
+
+	if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0) {
+		DPRINTF(("PCI host bridge does not support MSI.\n"));
+		return ENODEV;
+	}
+
+	msi_pic = msipic_construct_msi_pic(pa);
+	if (msi_pic == NULL) {
+		DPRINTF(("cannot allocate MSI pic.\n"));
+		return EINVAL;
+	}
+
+	vectors = NULL;
+	while (*count > 0) {
+		vectors = pci_msi_alloc_vectors(msi_pic, NULL, count);
+		if (vectors != NULL)
+			break;
+
+		if (exact) {
+			DPRINTF(("cannot allocate MSI vectors.\n"));
+			msipic_destruct_msi_pic(msi_pic);
+			return ENOMEM;
+		} else {
+			(*count) >>= 1; /* must be power of 2. */
+			continue;
+		}
+	}
+	if (vectors == NULL) {
+		DPRINTF(("cannot allocate MSI vectors.\n"));
+		msipic_destruct_msi_pic(msi_pic);
+		return ENOMEM;
+	}
+
+	for (i = 0; i < *count; i++) {
+		MSI_INT_MAKE_MSI(vectors[i]);
+	}
+
+	error = msipic_set_msi_vectors(msi_pic, NULL, *count);
+	if (error) {
+		pci_msi_free_vectors(msi_pic, vectors, *count);
+		msipic_destruct_msi_pic(msi_pic);
+		return error;
+	}
+
+	*ihps = vectors;
+	return 0;
+}
+
+static int
+pci_msi_alloc_md(pci_intr_handle_t **ihps, int *count,
+    struct pci_attach_args *pa)
+{
+
+	return pci_msi_alloc_md_common(ihps, count, pa, false);
+}
+
+static int
+pci_msi_alloc_exact_md(pci_intr_handle_t **ihps, int count,
+    struct pci_attach_args *pa)
+{
+
+	return pci_msi_alloc_md_common(ihps, &count, pa, true);
+}
+
+static void
+pci_msi_release_md(pci_intr_handle_t **pihs, int count)
+{
+	struct pic *pic;
+	pci_intr_handle_t *vectors;
+
+	vectors = *pihs;
+	pic = msipic_find_msi_pic(MSI_INT_DEV(vectors[0]));
+	if (pic == NULL)
+		return;
+
+	pci_msi_free_vectors(pic, vectors, count);
+	msipic_destruct_msi_pic(pic);
+}
+
+static void *
+pci_msi_common_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
+    int level, int (*func)(void *), void *arg, struct pic *pic)
+{
+	int irq, pin;
+	bool mpsafe;
+
+	KASSERT(INT_VIA_MSI(ih));
+
+	irq = -1;
+	pin = MSI_INT_VEC(ih);
+	mpsafe = ((ih & MPSAFE_MASK) != 0);
+
+	return intr_establish(irq, pic, pin, IST_EDGE, level, func, arg,
+	    mpsafe);
+}
+
+static void
+pci_msi_common_disestablish(pci_chipset_tag_t pc, void *cookie)
+{
+
+	intr_disestablish(cookie);
+}
+
+static int
+pci_msix_alloc_md_common(pci_intr_handle_t **ihps, u_int *table_indexes,
+    int *count, struct pci_attach_args *pa, bool exact)
+{
+	struct pic *msix_pic;
+	pci_intr_handle_t *vectors;
+	int error, i;
+
+	if (((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0)
+	    || ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0)) {
+		DPRINTF(("PCI host bridge does not support MSI-X.\n"));
+		return ENODEV;
+	}
+
+	msix_pic = msipic_construct_msix_pic(pa);
+	if (msix_pic == NULL)
+		return EINVAL;
+
+	vectors = NULL;
+	while (*count > 0) {
+		vectors = pci_msi_alloc_vectors(msix_pic, table_indexes, count);
+		if (vectors != NULL)
+			break;
+
+		if (exact) {
+			DPRINTF(("cannot allocate MSI-X vectors.\n"));
+			msipic_destruct_msix_pic(msix_pic);
+			return ENOMEM;
+		} else {
+			(*count)--;
+			continue;
+		}
+	}
+	if (vectors == NULL) {
+		DPRINTF(("cannot allocate MSI-X vectors.\n"));
+		msipic_destruct_msix_pic(msix_pic);
+		return ENOMEM;
+	}
+
+	for (i = 0; i < *count; i++) {
+		MSI_INT_MAKE_MSIX(vectors[i]);
+	}
+
+	error = msipic_set_msi_vectors(msix_pic, vectors, *count);
+	if (error) {
+		pci_msi_free_vectors(msix_pic, vectors, *count);
+		msipic_destruct_msix_pic(msix_pic);
+		return error;
+	}
+
+	*ihps = vectors;
+	return 0;
+}
+
+static int
+pci_msix_alloc_md(pci_intr_handle_t **ihps, int *count,
+    struct pci_attach_args *pa)
+{
+
+	return pci_msix_alloc_md_common(ihps, NULL, count, pa, false);
+}
+
+static int
+pci_msix_alloc_exact_md(pci_intr_handle_t **ihps, int count,
+    struct pci_attach_args *pa)
+{
+
+	return pci_msix_alloc_md_common(ihps, NULL, &count, pa, true);
+}
+
+static int
+pci_msix_alloc_map_md(pci_intr_handle_t **ihps, u_int *table_indexes, int count,
+    struct pci_attach_args *pa)
+{
+
+	return pci_msix_alloc_md_common(ihps, table_indexes, &count, pa, true);
+}
+
+static void
+pci_msix_release_md(pci_intr_handle_t **pihs, int count)
+{
+	struct pic *pic;
+	pci_intr_handle_t *vectors;
+
+	vectors = *pihs;
+	pic = msipic_find_msi_pic(MSI_INT_DEV(vectors[0]));
+	if (pic == NULL)
+		return;
+
+	pci_msi_free_vectors(pic, vectors, count);
+	msipic_destruct_msix_pic(pic);
+}
+
+/*****************************************************************************/
+/*
+ * these APIs may be MI code.
+ */
+
+/*
+ * return number of the devices's MSI vectors
+ * return 0 if the device does not support MSI
+ */
+int
+pci_msi_count(struct pci_attach_args *pa)
+{
+	pci_chipset_tag_t pc;
+	pcitag_t tag;
+	pcireg_t reg;
+	uint32_t mmc;
+	int count, offset;
+
+	pc = pa->pa_pc;
+	tag = pa->pa_tag;
+	if (pci_get_capability(pc, tag, PCI_CAP_MSI, &offset, NULL) == 0)
+		return 0;
+
+	reg = pci_conf_read(pc, tag, offset + PCI_MSI_CTL);
+	mmc = PCI_MSI_CTL_MMC(reg);
+	count = 1 << mmc;
+	if (count > PCI_MSI_MAX_VECTORS) {
+		aprint_error("detect an illegal device! The device use reserved MMC values.\n");
+		return 0;
+	}
+
+	return count;
+}
+
+/*
+ * This function is used by device drivers like pci_intr_map().
+ *
+ * "ihps" is the array  of vector numbers which MSI used instead of IRQ number.
+ * "count" must be power of 2.
+ * "count" can decrease if struct intrsource cannot be allocated.
+ * if count == 0, return non-zero value.
+ */
+int
+pci_msi_alloc(struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
+{
+	int hw_max;
+
+	/* MSI vector count must be power of 2. */
+	KASSERT(*count > 0);
+	KASSERT(((*count - 1) & *count) == 0);
+
+	hw_max = pci_msi_count(pa);
+	if (hw_max == 0)
+		return ENODEV;
+
+	if (*count > hw_max) {
+		DPRINTF(("cut off MSI count to %d\n", hw_max));
+		*count = hw_max; /* cut off hw_max */
+	}
+
+	return pci_msi_alloc_md(ihps, count, pa);
+}
+
+/*
+ * This function is used by device drivers like pci_intr_map().
+ *
+ * "ihps" is the array  of vector numbers which MSI used instead of IRQ number.
+ * "count" must be power of 2.
+ * "count" can not decrease.
+ * If "count" struct intrsources cannot be allocated, return non-zero value.
+ */
+int
+pci_msi_alloc_exact(struct pci_attach_args *pa, pci_intr_handle_t **ihps,
+    int count)
+{
+	int hw_max;
+
+	/* MSI vector count must be power of 2. */
+	KASSERT(count > 0);
+	KASSERT(((count - 1) & count) == 0);
+
+	hw_max = pci_msi_count(pa);
+	if (hw_max == 0)
+		return ENODEV;
+
+	if (count > hw_max) {
+		DPRINTF(("over hardware max MSI count %d\n", hw_max));
+		return EINVAL;
+	}
+
+	return pci_msi_alloc_exact_md(ihps, count, pa);
+}
+
+/*
+ * Release MSI handles.
+ */
+void
+pci_msi_release(pci_chipset_tag_t pc, pci_intr_handle_t **pihs, int count)
+{
+
+	if (count < 1)
+		return;
+
+	return pci_msi_release_md(pihs, count);
+}
+
+/*
+ * Establish a MSI handle.
+ * If multiple MSI handle is requied to establish, device driver must call
+ * this function for each handle.
+ */
+void *
+pci_msi_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
+    int level, int (*func)(void *), void *arg)
+{
+	struct pic *pic;
+
+	pic = msipic_find_msi_pic(MSI_INT_DEV(ih));
+	if (pic == NULL) {
+		DPRINTF(("pci_intr_handler has no msi_pic\n"));
+		return NULL;
+	}
+
+	return pci_msi_common_establish(pc, ih, level, func, arg, pic);
+}
+
+/*
+ * Disestablish a MSI handle.
+ * If multiple MSI handle is requied to disestablish, device driver must call
+ * this function for each handle.
+ */
+void
+pci_msi_disestablish(pci_chipset_tag_t pc, void *cookie)
+{
+
+	pci_msi_common_disestablish(pc, cookie);
+}
+
+/*
+ * return number of the devices's MSI-X vectors
+ * return 0 if the device does not support MSI-X
+ */
+int
+pci_msix_count(struct pci_attach_args *pa)
+{
+	pci_chipset_tag_t pc;
+	pcitag_t tag;
+	pcireg_t reg;
+	int offset;
+
+	pc = pa->pa_pc;
+	tag = pa->pa_tag;
+	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &offset, NULL) == 0)
+		return 0;
+
+	reg = pci_conf_read(pc, tag, offset + PCI_MSIX_CTL);
+
+	return PCI_MSIX_CTL_TBLSIZE(reg);
+}
+
+/*
+ * This function is used by device drivers like pci_intr_map().
+ *
+ * "ihps" is the array  of vector numbers which MSI-X used instead of IRQ number.
+ * "count" can decrease if enough struct intrsources cannot be allocated.
+ * if count == 0, return non-zero value.
+ */
+int
+pci_msix_alloc(struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
+{
+	int hw_max;
+
+	KASSERT(*count > 0);
+
+	hw_max = pci_msix_count(pa);
+	if (hw_max == 0)
+		return ENODEV;
+
+	if (*count > hw_max) {
+		DPRINTF(("cut off MSI-X count to %d\n", hw_max));
+		*count = hw_max; /* cut off hw_max */
+	}
+
+	return pci_msix_alloc_md(ihps, count, pa);
+}
+
+/*
+ * This function is used by device drivers like pci_intr_map().
+ *
+ * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number.
+ * "count" can not decrease.
+ * If "count" struct intrsource cannot be allocated, return non-zero value.
+ */
+int
+pci_msix_alloc_exact(struct pci_attach_args *pa, pci_intr_handle_t **ihps,
+    int count)
+{
+	int hw_max;
+
+	KASSERT(count > 0);
+
+	hw_max = pci_msix_count(pa);
+	if (hw_max == 0)
+		return ENODEV;
+
+	if (count > hw_max) {
+		DPRINTF(("over hardware max MSI-X count %d\n", hw_max));
+		return EINVAL;
+	}
+
+	return pci_msix_alloc_exact_md(ihps, count, pa);
+}
+
+
+/*
+ * This function is used by device drivers like pci_intr_map().
+ * Futhermore, this function can map each handle to a MSI-X table index.
+ *
+ * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number.
+ * "count" can not decrease.
+ * "map" size must be equal to "count".
+ * If "count" struct intrsource cannot be allocated, return non-zero value.
+ * e.g.
+ *     If "map" = { 1, 4, 0 },
+ *     1st handle is bound to MSI-X index 1
+ *     2nd handle is bound to MSI-X index 4
+ *     3rd handle is bound to MSI-X index 0
+ */
+int
+pci_msix_alloc_map(struct pci_attach_args *pa, pci_intr_handle_t **ihps,
+    u_int *table_indexes, int count)
+{
+	int hw_max, i, j;
+
+	KASSERT(count > 0);
+
+	hw_max = pci_msix_count(pa);
+	if (hw_max == 0)
+		return ENODEV;
+
+	if (count > hw_max) {
+		DPRINTF(("over hardware max MSI-X count %d\n", hw_max));
+		return EINVAL;
+	}
+
+	/* check not to duplicate table_index */
+	for (i = 0; i < count; i++) {
+		u_int basing = table_indexes[i];
+
+		KASSERT(table_indexes[i] < PCI_MSIX_MAX_VECTORS);
+		if (basing >= hw_max) {
+			DPRINTF(("table index is over hardware max MSI-X index %d\n",
+				hw_max - 1));
+			return EINVAL;
+		}
+
+		for (j = i + 1; j < count; j++) {
+			if (basing == table_indexes[j]) {
+				DPRINTF(("MSI-X table index duplicated\n"));
+				return EINVAL;
+			}
+		}
+	}
+
+	return pci_msix_alloc_map_md(ihps, table_indexes, count, pa);
+}
+
+/*
+ * Release MSI-X handles.
+ */
+void
+pci_msix_release(pci_chipset_tag_t pc, pci_intr_handle_t **pihs, int count)
+{
+
+	if (count < 1)
+		return;
+
+	return pci_msix_release_md(pihs, count);
+}
+
+/*
+ * Establish a MSI-X handle.
+ * If multiple MSI-X handle is requied to establish, device driver must call
+ * this function for each handle.
+ */
+void *
+pci_msix_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
+    int level, int (*func)(void *), void *arg)
+{
+	struct pic *pic;
+
+	pic = msipic_find_msi_pic(MSI_INT_DEV(ih));
+	if (pic == NULL) {
+		DPRINTF(("pci_intr_handler has no msi_pic\n"));
+		return NULL;
+	}
+
+	return pci_msi_common_establish(pc, ih, level, func, arg, pic);
+}
+
+/*
+ * Disestablish a MSI-X handle.
+ * If multiple MSI-X handle is requied to disestablish, device driver must call
+ * this function for each handle.
+ */
+void
+pci_msix_disestablish(pci_chipset_tag_t pc, void *cookie)
+{
+
+	pci_msi_common_disestablish(pc, cookie);
+}

Reply via email to