xref: /onnv-gate/usr/src/uts/i86pc/io/immu_intrmap.c (revision 13050:515b1e9bea30)
111600SVikram.Hegde@Sun.COM /*
211600SVikram.Hegde@Sun.COM  * CDDL HEADER START
311600SVikram.Hegde@Sun.COM  *
411600SVikram.Hegde@Sun.COM  * The contents of this file are subject to the terms of the
511600SVikram.Hegde@Sun.COM  * Common Development and Distribution License (the "License").
611600SVikram.Hegde@Sun.COM  * You may not use this file except in compliance with the License.
711600SVikram.Hegde@Sun.COM  *
811600SVikram.Hegde@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911600SVikram.Hegde@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011600SVikram.Hegde@Sun.COM  * See the License for the specific language governing permissions
1111600SVikram.Hegde@Sun.COM  * and limitations under the License.
1211600SVikram.Hegde@Sun.COM  *
1311600SVikram.Hegde@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411600SVikram.Hegde@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511600SVikram.Hegde@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611600SVikram.Hegde@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711600SVikram.Hegde@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811600SVikram.Hegde@Sun.COM  *
1911600SVikram.Hegde@Sun.COM  * CDDL HEADER END
2011600SVikram.Hegde@Sun.COM  */
2111600SVikram.Hegde@Sun.COM 
2211600SVikram.Hegde@Sun.COM /*
2312683SJimmy.Vetayases@oracle.com  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2411600SVikram.Hegde@Sun.COM  */
2511600SVikram.Hegde@Sun.COM 
2611600SVikram.Hegde@Sun.COM /*
2711600SVikram.Hegde@Sun.COM  * Copyright (c) 2009, Intel Corporation.
2811600SVikram.Hegde@Sun.COM  * All rights reserved.
2911600SVikram.Hegde@Sun.COM  */
3011600SVikram.Hegde@Sun.COM 
3111600SVikram.Hegde@Sun.COM 
3211600SVikram.Hegde@Sun.COM #include <sys/apic.h>
3311600SVikram.Hegde@Sun.COM #include <vm/hat_i86.h>
3411600SVikram.Hegde@Sun.COM #include <sys/sysmacros.h>
3511600SVikram.Hegde@Sun.COM #include <sys/smp_impldefs.h>
3611600SVikram.Hegde@Sun.COM #include <sys/immu.h>
3711600SVikram.Hegde@Sun.COM 
3811600SVikram.Hegde@Sun.COM 
3911600SVikram.Hegde@Sun.COM typedef struct intrmap_private {
4011600SVikram.Hegde@Sun.COM 	immu_t		*ir_immu;
41*13050Sfrank.van.der.linden@oracle.com 	immu_inv_wait_t	ir_inv_wait;
4211600SVikram.Hegde@Sun.COM 	uint16_t	ir_idx;
4311600SVikram.Hegde@Sun.COM 	uint32_t	ir_sid_svt_sq;
4411600SVikram.Hegde@Sun.COM } intrmap_private_t;
4511600SVikram.Hegde@Sun.COM 
4612683SJimmy.Vetayases@oracle.com #define	INTRMAP_PRIVATE(intrmap) ((intrmap_private_t *)intrmap)
4711600SVikram.Hegde@Sun.COM 
4811600SVikram.Hegde@Sun.COM /* interrupt remapping table entry */
4911600SVikram.Hegde@Sun.COM typedef struct intrmap_rte {
5011600SVikram.Hegde@Sun.COM 	uint64_t	lo;
5111600SVikram.Hegde@Sun.COM 	uint64_t	hi;
5211600SVikram.Hegde@Sun.COM } intrmap_rte_t;
5311600SVikram.Hegde@Sun.COM 
5411600SVikram.Hegde@Sun.COM #define	IRTE_HIGH(sid_svt_sq) (sid_svt_sq)
5511600SVikram.Hegde@Sun.COM #define	IRTE_LOW(dst, vector, dlm, tm, rh, dm, fpd, p)	\
5611600SVikram.Hegde@Sun.COM 	    (((uint64_t)(dst) << 32) |  \
5711600SVikram.Hegde@Sun.COM 	    ((uint64_t)(vector) << 16) | \
5811600SVikram.Hegde@Sun.COM 	    ((uint64_t)(dlm) << 5) | \
5911600SVikram.Hegde@Sun.COM 	    ((uint64_t)(tm) << 4) | \
6011600SVikram.Hegde@Sun.COM 	    ((uint64_t)(rh) << 3) | \
6111600SVikram.Hegde@Sun.COM 	    ((uint64_t)(dm) << 2) | \
6211600SVikram.Hegde@Sun.COM 	    ((uint64_t)(fpd) << 1) | \
6311600SVikram.Hegde@Sun.COM 	    (p))
6411600SVikram.Hegde@Sun.COM 
6511600SVikram.Hegde@Sun.COM typedef enum {
6611600SVikram.Hegde@Sun.COM 	SVT_NO_VERIFY = 0, 	/* no verification */
6711600SVikram.Hegde@Sun.COM 	SVT_ALL_VERIFY,		/* using sid and sq to verify */
6811600SVikram.Hegde@Sun.COM 	SVT_BUS_VERIFY,		/* verify #startbus and #endbus */
6911600SVikram.Hegde@Sun.COM 	SVT_RSVD
7011600SVikram.Hegde@Sun.COM } intrmap_svt_t;
7111600SVikram.Hegde@Sun.COM 
7211600SVikram.Hegde@Sun.COM typedef enum {
7311600SVikram.Hegde@Sun.COM 	SQ_VERIFY_ALL = 0,	/* verify all 16 bits */
7411600SVikram.Hegde@Sun.COM 	SQ_VERIFY_IGR_1,	/* ignore bit 3 */
7511600SVikram.Hegde@Sun.COM 	SQ_VERIFY_IGR_2,	/* ignore bit 2-3 */
7611600SVikram.Hegde@Sun.COM 	SQ_VERIFY_IGR_3		/* ignore bit 1-3 */
7711600SVikram.Hegde@Sun.COM } intrmap_sq_t;
7811600SVikram.Hegde@Sun.COM 
7911600SVikram.Hegde@Sun.COM /*
8011600SVikram.Hegde@Sun.COM  * S field of the Interrupt Remapping Table Address Register
8111600SVikram.Hegde@Sun.COM  * the size of the interrupt remapping table is 1 << (immu_intrmap_irta_s + 1)
8211600SVikram.Hegde@Sun.COM  */
8311600SVikram.Hegde@Sun.COM static uint_t intrmap_irta_s = INTRMAP_MAX_IRTA_SIZE;
8411600SVikram.Hegde@Sun.COM 
8511600SVikram.Hegde@Sun.COM /*
8611600SVikram.Hegde@Sun.COM  * If true, arrange to suppress broadcast EOI by setting edge-triggered mode
8711600SVikram.Hegde@Sun.COM  * even for level-triggered interrupts in the interrupt-remapping engine.
8811600SVikram.Hegde@Sun.COM  * If false, broadcast EOI can still be suppressed if the CPU supports the
8911600SVikram.Hegde@Sun.COM  * APIC_SVR_SUPPRESS_BROADCAST_EOI bit.  In both cases, the IOAPIC is still
9011600SVikram.Hegde@Sun.COM  * programmed with the correct trigger mode, and pcplusmp must send an EOI
9111600SVikram.Hegde@Sun.COM  * to the IOAPIC by writing to the IOAPIC's EOI register to make up for the
9211600SVikram.Hegde@Sun.COM  * missing broadcast EOI.
9311600SVikram.Hegde@Sun.COM  */
9411600SVikram.Hegde@Sun.COM static int intrmap_suppress_brdcst_eoi = 0;
9511600SVikram.Hegde@Sun.COM 
9611600SVikram.Hegde@Sun.COM /*
9711600SVikram.Hegde@Sun.COM  * whether verify the source id of interrupt request
9811600SVikram.Hegde@Sun.COM  */
9911600SVikram.Hegde@Sun.COM static int intrmap_enable_sid_verify = 0;
10011600SVikram.Hegde@Sun.COM 
10111600SVikram.Hegde@Sun.COM /* fault types for DVMA remapping */
10211600SVikram.Hegde@Sun.COM static char *immu_dvma_faults[] = {
10311600SVikram.Hegde@Sun.COM 	"Reserved",
10411600SVikram.Hegde@Sun.COM 	"The present field in root-entry is Clear",
10511600SVikram.Hegde@Sun.COM 	"The present field in context-entry is Clear",
10611600SVikram.Hegde@Sun.COM 	"Hardware detected invalid programming of a context-entry",
10711600SVikram.Hegde@Sun.COM 	"The DMA request attempted to access an address beyond max support",
10811600SVikram.Hegde@Sun.COM 	"The Write field in a page-table entry is Clear when DMA write",
10911600SVikram.Hegde@Sun.COM 	"The Read field in a page-table entry is Clear when DMA read",
11011600SVikram.Hegde@Sun.COM 	"Access the next level page table resulted in error",
11111600SVikram.Hegde@Sun.COM 	"Access the root-entry table resulted in error",
11211600SVikram.Hegde@Sun.COM 	"Access the context-entry table resulted in error",
11311600SVikram.Hegde@Sun.COM 	"Reserved field not initialized to zero in a present root-entry",
11411600SVikram.Hegde@Sun.COM 	"Reserved field not initialized to zero in a present context-entry",
11511600SVikram.Hegde@Sun.COM 	"Reserved field not initialized to zero in a present page-table entry",
11611600SVikram.Hegde@Sun.COM 	"DMA blocked due to the Translation Type field in context-entry",
11711600SVikram.Hegde@Sun.COM 	"Incorrect fault event reason number",
11811600SVikram.Hegde@Sun.COM };
11911600SVikram.Hegde@Sun.COM #define	DVMA_MAX_FAULTS (sizeof (immu_dvma_faults)/(sizeof (char *))) - 1
12011600SVikram.Hegde@Sun.COM 
12111600SVikram.Hegde@Sun.COM /* fault types for interrupt remapping */
12211600SVikram.Hegde@Sun.COM static char *immu_intrmap_faults[] = {
12311600SVikram.Hegde@Sun.COM 	"reserved field set in IRTE",
12411600SVikram.Hegde@Sun.COM 	"interrupt_index exceed the intr-remap table size",
12511600SVikram.Hegde@Sun.COM 	"present field in IRTE is clear",
12611600SVikram.Hegde@Sun.COM 	"hardware access intr-remap table address resulted in error",
12711600SVikram.Hegde@Sun.COM 	"reserved field set in IRTE, include various conditional",
12811600SVikram.Hegde@Sun.COM 	"hardware blocked an interrupt request in Compatibility format",
12911600SVikram.Hegde@Sun.COM 	"remappable interrupt request blocked due to verification failure"
13011600SVikram.Hegde@Sun.COM };
13111600SVikram.Hegde@Sun.COM #define	INTRMAP_MAX_FAULTS \
13211600SVikram.Hegde@Sun.COM 	(sizeof (immu_intrmap_faults) / (sizeof (char *))) - 1
13311600SVikram.Hegde@Sun.COM 
13411600SVikram.Hegde@Sun.COM /* Function prototypes */
13511600SVikram.Hegde@Sun.COM static int immu_intrmap_init(int apic_mode);
13611600SVikram.Hegde@Sun.COM static void immu_intrmap_switchon(int suppress_brdcst_eoi);
13712683SJimmy.Vetayases@oracle.com static void immu_intrmap_alloc(void **intrmap_private_tbl, dev_info_t *dip,
13812683SJimmy.Vetayases@oracle.com     uint16_t type, int count, uchar_t ioapic_index);
13912683SJimmy.Vetayases@oracle.com static void immu_intrmap_map(void *intrmap_private, void *intrmap_data,
14012683SJimmy.Vetayases@oracle.com     uint16_t type, int count);
14112683SJimmy.Vetayases@oracle.com static void immu_intrmap_free(void **intrmap_privatep);
14212683SJimmy.Vetayases@oracle.com static void immu_intrmap_rdt(void *intrmap_private, ioapic_rdt_t *irdt);
14312683SJimmy.Vetayases@oracle.com static void immu_intrmap_msi(void *intrmap_private, msi_regs_t *mregs);
14411600SVikram.Hegde@Sun.COM 
14511600SVikram.Hegde@Sun.COM static struct apic_intrmap_ops intrmap_ops = {
14611600SVikram.Hegde@Sun.COM 	immu_intrmap_init,
14711600SVikram.Hegde@Sun.COM 	immu_intrmap_switchon,
14811600SVikram.Hegde@Sun.COM 	immu_intrmap_alloc,
14911600SVikram.Hegde@Sun.COM 	immu_intrmap_map,
15011600SVikram.Hegde@Sun.COM 	immu_intrmap_free,
15111600SVikram.Hegde@Sun.COM 	immu_intrmap_rdt,
15211600SVikram.Hegde@Sun.COM 	immu_intrmap_msi,
15311600SVikram.Hegde@Sun.COM };
15411600SVikram.Hegde@Sun.COM 
15511600SVikram.Hegde@Sun.COM /* apic mode, APIC/X2APIC */
15611600SVikram.Hegde@Sun.COM static int intrmap_apic_mode = LOCAL_APIC;
15711600SVikram.Hegde@Sun.COM 
15811600SVikram.Hegde@Sun.COM 
15911600SVikram.Hegde@Sun.COM /*
16011600SVikram.Hegde@Sun.COM  * helper functions
16111600SVikram.Hegde@Sun.COM  */
16211600SVikram.Hegde@Sun.COM static uint_t
bitset_find_free(bitset_t * b,uint_t post)16311600SVikram.Hegde@Sun.COM bitset_find_free(bitset_t *b, uint_t post)
16411600SVikram.Hegde@Sun.COM {
16511600SVikram.Hegde@Sun.COM 	uint_t	i;
16611600SVikram.Hegde@Sun.COM 	uint_t	cap = bitset_capacity(b);
16711600SVikram.Hegde@Sun.COM 
16811600SVikram.Hegde@Sun.COM 	if (post == cap)
16911600SVikram.Hegde@Sun.COM 		post = 0;
17011600SVikram.Hegde@Sun.COM 
17111600SVikram.Hegde@Sun.COM 	ASSERT(post < cap);
17211600SVikram.Hegde@Sun.COM 
17311600SVikram.Hegde@Sun.COM 	for (i = post; i < cap; i++) {
17411600SVikram.Hegde@Sun.COM 		if (!bitset_in_set(b, i))
17511600SVikram.Hegde@Sun.COM 			return (i);
17611600SVikram.Hegde@Sun.COM 	}
17711600SVikram.Hegde@Sun.COM 
17811600SVikram.Hegde@Sun.COM 	for (i = 0; i < post; i++) {
17911600SVikram.Hegde@Sun.COM 		if (!bitset_in_set(b, i))
18011600SVikram.Hegde@Sun.COM 			return (i);
18111600SVikram.Hegde@Sun.COM 	}
18211600SVikram.Hegde@Sun.COM 
18311600SVikram.Hegde@Sun.COM 	return (INTRMAP_IDX_FULL);	/* no free index */
18411600SVikram.Hegde@Sun.COM }
18511600SVikram.Hegde@Sun.COM 
18611600SVikram.Hegde@Sun.COM /*
18711600SVikram.Hegde@Sun.COM  * helper function to find 'count' contigous free
18811600SVikram.Hegde@Sun.COM  * interrupt remapping table entries
18911600SVikram.Hegde@Sun.COM  */
19011600SVikram.Hegde@Sun.COM static uint_t
bitset_find_multi_free(bitset_t * b,uint_t post,uint_t count)19111600SVikram.Hegde@Sun.COM bitset_find_multi_free(bitset_t *b, uint_t post, uint_t count)
19211600SVikram.Hegde@Sun.COM {
19311600SVikram.Hegde@Sun.COM 	uint_t  i, j;
19411600SVikram.Hegde@Sun.COM 	uint_t	cap = bitset_capacity(b);
19511600SVikram.Hegde@Sun.COM 
19611600SVikram.Hegde@Sun.COM 	if (post == INTRMAP_IDX_FULL) {
19711600SVikram.Hegde@Sun.COM 		return (INTRMAP_IDX_FULL);
19811600SVikram.Hegde@Sun.COM 	}
19911600SVikram.Hegde@Sun.COM 
20011600SVikram.Hegde@Sun.COM 	if (count > cap)
20111600SVikram.Hegde@Sun.COM 		return (INTRMAP_IDX_FULL);
20211600SVikram.Hegde@Sun.COM 
20311600SVikram.Hegde@Sun.COM 	ASSERT(post < cap);
20411600SVikram.Hegde@Sun.COM 
20511600SVikram.Hegde@Sun.COM 	for (i = post; (i + count) <= cap; i++) {
20611600SVikram.Hegde@Sun.COM 		for (j = 0; j < count; j++) {
20711600SVikram.Hegde@Sun.COM 			if (bitset_in_set(b, (i + j))) {
20811600SVikram.Hegde@Sun.COM 				i = i + j;
20911600SVikram.Hegde@Sun.COM 				break;
21011600SVikram.Hegde@Sun.COM 			}
21111600SVikram.Hegde@Sun.COM 			if (j == count - 1)
21211600SVikram.Hegde@Sun.COM 				return (i);
21311600SVikram.Hegde@Sun.COM 		}
21411600SVikram.Hegde@Sun.COM 	}
21511600SVikram.Hegde@Sun.COM 
21611600SVikram.Hegde@Sun.COM 	for (i = 0; (i < post) && ((i + count) <= cap); i++) {
21711600SVikram.Hegde@Sun.COM 		for (j = 0; j < count; j++) {
21811600SVikram.Hegde@Sun.COM 			if (bitset_in_set(b, (i + j))) {
21911600SVikram.Hegde@Sun.COM 				i = i + j;
22011600SVikram.Hegde@Sun.COM 				break;
22111600SVikram.Hegde@Sun.COM 			}
22211600SVikram.Hegde@Sun.COM 			if (j == count - 1)
22311600SVikram.Hegde@Sun.COM 				return (i);
22411600SVikram.Hegde@Sun.COM 		}
22511600SVikram.Hegde@Sun.COM 	}
22611600SVikram.Hegde@Sun.COM 
22711600SVikram.Hegde@Sun.COM 	return (INTRMAP_IDX_FULL);  		/* no free index */
22811600SVikram.Hegde@Sun.COM }
22911600SVikram.Hegde@Sun.COM 
23011600SVikram.Hegde@Sun.COM /* alloc one interrupt remapping table entry */
23111600SVikram.Hegde@Sun.COM static int
alloc_tbl_entry(intrmap_t * intrmap)23211600SVikram.Hegde@Sun.COM alloc_tbl_entry(intrmap_t *intrmap)
23311600SVikram.Hegde@Sun.COM {
23411600SVikram.Hegde@Sun.COM 	uint32_t idx;
23511600SVikram.Hegde@Sun.COM 
23611600SVikram.Hegde@Sun.COM 	for (;;) {
23711600SVikram.Hegde@Sun.COM 		mutex_enter(&intrmap->intrmap_lock);
23811600SVikram.Hegde@Sun.COM 		idx = intrmap->intrmap_free;
23911600SVikram.Hegde@Sun.COM 		if (idx != INTRMAP_IDX_FULL) {
24011600SVikram.Hegde@Sun.COM 			bitset_add(&intrmap->intrmap_map, idx);
24111600SVikram.Hegde@Sun.COM 			intrmap->intrmap_free =
24211600SVikram.Hegde@Sun.COM 			    bitset_find_free(&intrmap->intrmap_map, idx + 1);
24311600SVikram.Hegde@Sun.COM 			mutex_exit(&intrmap->intrmap_lock);
24411600SVikram.Hegde@Sun.COM 			break;
24511600SVikram.Hegde@Sun.COM 		}
24611600SVikram.Hegde@Sun.COM 
24711600SVikram.Hegde@Sun.COM 		/* no free intr entry, use compatible format intr */
24811600SVikram.Hegde@Sun.COM 		mutex_exit(&intrmap->intrmap_lock);
24911600SVikram.Hegde@Sun.COM 
25011600SVikram.Hegde@Sun.COM 		if (intrmap_apic_mode != LOCAL_X2APIC) {
25111600SVikram.Hegde@Sun.COM 			break;
25211600SVikram.Hegde@Sun.COM 		}
25311600SVikram.Hegde@Sun.COM 
25411600SVikram.Hegde@Sun.COM 		/*
25511600SVikram.Hegde@Sun.COM 		 * x2apic mode not allowed compatible
25611600SVikram.Hegde@Sun.COM 		 * interrupt
25711600SVikram.Hegde@Sun.COM 		 */
25811600SVikram.Hegde@Sun.COM 		delay(IMMU_ALLOC_RESOURCE_DELAY);
25911600SVikram.Hegde@Sun.COM 	}
26011600SVikram.Hegde@Sun.COM 
26111600SVikram.Hegde@Sun.COM 	return (idx);
26211600SVikram.Hegde@Sun.COM }
26311600SVikram.Hegde@Sun.COM 
26411600SVikram.Hegde@Sun.COM /* alloc 'cnt' contigous interrupt remapping table entries */
26511600SVikram.Hegde@Sun.COM static int
alloc_tbl_multi_entries(intrmap_t * intrmap,uint_t cnt)26611600SVikram.Hegde@Sun.COM alloc_tbl_multi_entries(intrmap_t *intrmap, uint_t cnt)
26711600SVikram.Hegde@Sun.COM {
26811600SVikram.Hegde@Sun.COM 	uint_t idx, pos, i;
26911600SVikram.Hegde@Sun.COM 
27011600SVikram.Hegde@Sun.COM 	for (; ; ) {
27111600SVikram.Hegde@Sun.COM 		mutex_enter(&intrmap->intrmap_lock);
27211600SVikram.Hegde@Sun.COM 		pos = intrmap->intrmap_free;
27311600SVikram.Hegde@Sun.COM 		idx = bitset_find_multi_free(&intrmap->intrmap_map, pos, cnt);
27411600SVikram.Hegde@Sun.COM 
27511600SVikram.Hegde@Sun.COM 		if (idx != INTRMAP_IDX_FULL) {
27611600SVikram.Hegde@Sun.COM 			if (idx <= pos && pos < (idx + cnt)) {
27711600SVikram.Hegde@Sun.COM 				intrmap->intrmap_free = bitset_find_free(
27811600SVikram.Hegde@Sun.COM 				    &intrmap->intrmap_map, idx + cnt);
27911600SVikram.Hegde@Sun.COM 			}
28011600SVikram.Hegde@Sun.COM 			for (i = 0; i < cnt; i++) {
28111600SVikram.Hegde@Sun.COM 				bitset_add(&intrmap->intrmap_map, idx + i);
28211600SVikram.Hegde@Sun.COM 			}
28311600SVikram.Hegde@Sun.COM 			mutex_exit(&intrmap->intrmap_lock);
28412716Sfrank.van.der.linden@oracle.com 			break;
28511600SVikram.Hegde@Sun.COM 		}
28611600SVikram.Hegde@Sun.COM 
28711600SVikram.Hegde@Sun.COM 		mutex_exit(&intrmap->intrmap_lock);
28811600SVikram.Hegde@Sun.COM 
28911600SVikram.Hegde@Sun.COM 		if (intrmap_apic_mode != LOCAL_X2APIC) {
29011600SVikram.Hegde@Sun.COM 			break;
29111600SVikram.Hegde@Sun.COM 		}
29211600SVikram.Hegde@Sun.COM 
29311600SVikram.Hegde@Sun.COM 		/* x2apic mode not allowed comapitible interrupt */
29411600SVikram.Hegde@Sun.COM 		delay(IMMU_ALLOC_RESOURCE_DELAY);
29511600SVikram.Hegde@Sun.COM 	}
29611600SVikram.Hegde@Sun.COM 
29711600SVikram.Hegde@Sun.COM 	return (idx);
29811600SVikram.Hegde@Sun.COM }
29911600SVikram.Hegde@Sun.COM 
30011600SVikram.Hegde@Sun.COM /* init interrupt remapping table */
30111600SVikram.Hegde@Sun.COM static int
init_unit(immu_t * immu)30211600SVikram.Hegde@Sun.COM init_unit(immu_t *immu)
30311600SVikram.Hegde@Sun.COM {
30411600SVikram.Hegde@Sun.COM 	intrmap_t *intrmap;
30511600SVikram.Hegde@Sun.COM 	size_t size;
30611600SVikram.Hegde@Sun.COM 
30711600SVikram.Hegde@Sun.COM 	ddi_dma_attr_t intrmap_dma_attr = {
30811600SVikram.Hegde@Sun.COM 		DMA_ATTR_V0,
30911600SVikram.Hegde@Sun.COM 		0U,
31012716Sfrank.van.der.linden@oracle.com 		0xffffffffffffffffULL,
31111600SVikram.Hegde@Sun.COM 		0xffffffffU,
31211600SVikram.Hegde@Sun.COM 		MMU_PAGESIZE,	/* page aligned */
31311600SVikram.Hegde@Sun.COM 		0x1,
31411600SVikram.Hegde@Sun.COM 		0x1,
31511600SVikram.Hegde@Sun.COM 		0xffffffffU,
31612716Sfrank.van.der.linden@oracle.com 		0xffffffffffffffffULL,
31711600SVikram.Hegde@Sun.COM 		1,
31811600SVikram.Hegde@Sun.COM 		4,
31911600SVikram.Hegde@Sun.COM 		0
32011600SVikram.Hegde@Sun.COM 	};
32111600SVikram.Hegde@Sun.COM 
32211600SVikram.Hegde@Sun.COM 	ddi_device_acc_attr_t intrmap_acc_attr = {
32311600SVikram.Hegde@Sun.COM 		DDI_DEVICE_ATTR_V0,
32411600SVikram.Hegde@Sun.COM 		DDI_NEVERSWAP_ACC,
32511600SVikram.Hegde@Sun.COM 		DDI_STRICTORDER_ACC
32611600SVikram.Hegde@Sun.COM 	};
32711600SVikram.Hegde@Sun.COM 
32812513Sfrank.van.der.linden@oracle.com 	/*
32912513Sfrank.van.der.linden@oracle.com 	 * Using interrupt remapping implies using the queue
33012513Sfrank.van.der.linden@oracle.com 	 * invalidation interface. According to Intel,
33112513Sfrank.van.der.linden@oracle.com 	 * hardware that supports interrupt remapping should
33212513Sfrank.van.der.linden@oracle.com 	 * also support QI.
33312513Sfrank.van.der.linden@oracle.com 	 */
33412513Sfrank.van.der.linden@oracle.com 	ASSERT(IMMU_ECAP_GET_QI(immu->immu_regs_excap));
33512513Sfrank.van.der.linden@oracle.com 
33611600SVikram.Hegde@Sun.COM 	if (intrmap_apic_mode == LOCAL_X2APIC) {
33711600SVikram.Hegde@Sun.COM 		if (!IMMU_ECAP_GET_EIM(immu->immu_regs_excap)) {
33811600SVikram.Hegde@Sun.COM 			return (DDI_FAILURE);
33911600SVikram.Hegde@Sun.COM 		}
34011600SVikram.Hegde@Sun.COM 	}
34111600SVikram.Hegde@Sun.COM 
34211600SVikram.Hegde@Sun.COM 	if (intrmap_irta_s > INTRMAP_MAX_IRTA_SIZE) {
34311600SVikram.Hegde@Sun.COM 		intrmap_irta_s = INTRMAP_MAX_IRTA_SIZE;
34411600SVikram.Hegde@Sun.COM 	}
34511600SVikram.Hegde@Sun.COM 
34611600SVikram.Hegde@Sun.COM 	intrmap =  kmem_zalloc(sizeof (intrmap_t), KM_SLEEP);
34711600SVikram.Hegde@Sun.COM 
34811600SVikram.Hegde@Sun.COM 	if (ddi_dma_alloc_handle(immu->immu_dip,
34911600SVikram.Hegde@Sun.COM 	    &intrmap_dma_attr,
35011600SVikram.Hegde@Sun.COM 	    DDI_DMA_SLEEP,
35111600SVikram.Hegde@Sun.COM 	    NULL,
35211600SVikram.Hegde@Sun.COM 	    &(intrmap->intrmap_dma_hdl)) != DDI_SUCCESS) {
35311600SVikram.Hegde@Sun.COM 		kmem_free(intrmap, sizeof (intrmap_t));
35411600SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
35511600SVikram.Hegde@Sun.COM 	}
35611600SVikram.Hegde@Sun.COM 
35711600SVikram.Hegde@Sun.COM 	intrmap->intrmap_size = 1 << (intrmap_irta_s + 1);
35811600SVikram.Hegde@Sun.COM 	size = intrmap->intrmap_size * INTRMAP_RTE_SIZE;
35911600SVikram.Hegde@Sun.COM 	if (ddi_dma_mem_alloc(intrmap->intrmap_dma_hdl,
36011600SVikram.Hegde@Sun.COM 	    size,
36111600SVikram.Hegde@Sun.COM 	    &intrmap_acc_attr,
36211600SVikram.Hegde@Sun.COM 	    DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
36311600SVikram.Hegde@Sun.COM 	    DDI_DMA_SLEEP,
36411600SVikram.Hegde@Sun.COM 	    NULL,
36511600SVikram.Hegde@Sun.COM 	    &(intrmap->intrmap_vaddr),
36611600SVikram.Hegde@Sun.COM 	    &size,
36711600SVikram.Hegde@Sun.COM 	    &(intrmap->intrmap_acc_hdl)) != DDI_SUCCESS) {
36811600SVikram.Hegde@Sun.COM 		ddi_dma_free_handle(&(intrmap->intrmap_dma_hdl));
36911600SVikram.Hegde@Sun.COM 		kmem_free(intrmap, sizeof (intrmap_t));
37011600SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
37111600SVikram.Hegde@Sun.COM 	}
37211600SVikram.Hegde@Sun.COM 
37311600SVikram.Hegde@Sun.COM 	ASSERT(!((uintptr_t)intrmap->intrmap_vaddr & MMU_PAGEOFFSET));
37411600SVikram.Hegde@Sun.COM 	bzero(intrmap->intrmap_vaddr, size);
37511600SVikram.Hegde@Sun.COM 	intrmap->intrmap_paddr = pfn_to_pa(
37611600SVikram.Hegde@Sun.COM 	    hat_getpfnum(kas.a_hat, intrmap->intrmap_vaddr));
37711600SVikram.Hegde@Sun.COM 
37811600SVikram.Hegde@Sun.COM 	mutex_init(&(intrmap->intrmap_lock), NULL, MUTEX_DRIVER, NULL);
37911600SVikram.Hegde@Sun.COM 	bitset_init(&intrmap->intrmap_map);
38011600SVikram.Hegde@Sun.COM 	bitset_resize(&intrmap->intrmap_map, intrmap->intrmap_size);
38111600SVikram.Hegde@Sun.COM 	intrmap->intrmap_free = 0;
38211600SVikram.Hegde@Sun.COM 
38311600SVikram.Hegde@Sun.COM 	immu->immu_intrmap = intrmap;
38411600SVikram.Hegde@Sun.COM 
38511600SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
38611600SVikram.Hegde@Sun.COM }
38711600SVikram.Hegde@Sun.COM 
38812683SJimmy.Vetayases@oracle.com static immu_t *
get_immu(dev_info_t * dip,uint16_t type,uchar_t ioapic_index)38912683SJimmy.Vetayases@oracle.com get_immu(dev_info_t *dip, uint16_t type, uchar_t ioapic_index)
39011600SVikram.Hegde@Sun.COM {
39111600SVikram.Hegde@Sun.COM 	immu_t	*immu = NULL;
39211600SVikram.Hegde@Sun.COM 
39312683SJimmy.Vetayases@oracle.com 	if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
39412683SJimmy.Vetayases@oracle.com 		immu = immu_dmar_ioapic_immu(ioapic_index);
39511600SVikram.Hegde@Sun.COM 	} else {
39612683SJimmy.Vetayases@oracle.com 		if (dip != NULL)
39712683SJimmy.Vetayases@oracle.com 			immu = immu_dmar_get_immu(dip);
39811600SVikram.Hegde@Sun.COM 	}
39911600SVikram.Hegde@Sun.COM 
40012683SJimmy.Vetayases@oracle.com 	return (immu);
40111600SVikram.Hegde@Sun.COM }
40211600SVikram.Hegde@Sun.COM 
40311600SVikram.Hegde@Sun.COM static int
get_top_pcibridge(dev_info_t * dip,void * arg)40411600SVikram.Hegde@Sun.COM get_top_pcibridge(dev_info_t *dip, void *arg)
40511600SVikram.Hegde@Sun.COM {
40611600SVikram.Hegde@Sun.COM 	dev_info_t **topdipp = arg;
40711600SVikram.Hegde@Sun.COM 	immu_devi_t *immu_devi;
40811600SVikram.Hegde@Sun.COM 
40911600SVikram.Hegde@Sun.COM 	mutex_enter(&(DEVI(dip)->devi_lock));
41011600SVikram.Hegde@Sun.COM 	immu_devi = DEVI(dip)->devi_iommu;
41111600SVikram.Hegde@Sun.COM 	mutex_exit(&(DEVI(dip)->devi_lock));
41211600SVikram.Hegde@Sun.COM 
41311600SVikram.Hegde@Sun.COM 	if (immu_devi == NULL || immu_devi->imd_pcib_type == IMMU_PCIB_BAD ||
41411600SVikram.Hegde@Sun.COM 	    immu_devi->imd_pcib_type == IMMU_PCIB_ENDPOINT) {
41511600SVikram.Hegde@Sun.COM 		return (DDI_WALK_CONTINUE);
41611600SVikram.Hegde@Sun.COM 	}
41711600SVikram.Hegde@Sun.COM 
41811600SVikram.Hegde@Sun.COM 	*topdipp = dip;
41911600SVikram.Hegde@Sun.COM 
42011600SVikram.Hegde@Sun.COM 	return (DDI_WALK_CONTINUE);
42111600SVikram.Hegde@Sun.COM }
42211600SVikram.Hegde@Sun.COM 
42311600SVikram.Hegde@Sun.COM static dev_info_t *
intrmap_top_pcibridge(dev_info_t * rdip)42411600SVikram.Hegde@Sun.COM intrmap_top_pcibridge(dev_info_t *rdip)
42511600SVikram.Hegde@Sun.COM {
42611600SVikram.Hegde@Sun.COM 	dev_info_t *top_pcibridge = NULL;
42711600SVikram.Hegde@Sun.COM 
42811600SVikram.Hegde@Sun.COM 	if (immu_walk_ancestor(rdip, NULL, get_top_pcibridge,
42911600SVikram.Hegde@Sun.COM 	    &top_pcibridge, NULL, 0) != DDI_SUCCESS) {
43011600SVikram.Hegde@Sun.COM 		return (NULL);
43111600SVikram.Hegde@Sun.COM 	}
43211600SVikram.Hegde@Sun.COM 
43311600SVikram.Hegde@Sun.COM 	return (top_pcibridge);
43411600SVikram.Hegde@Sun.COM }
43511600SVikram.Hegde@Sun.COM 
43611600SVikram.Hegde@Sun.COM /* function to get interrupt request source id */
43712683SJimmy.Vetayases@oracle.com static uint32_t
get_sid(dev_info_t * dip,uint16_t type,uchar_t ioapic_index)43812683SJimmy.Vetayases@oracle.com get_sid(dev_info_t *dip, uint16_t type, uchar_t ioapic_index)
43911600SVikram.Hegde@Sun.COM {
44012683SJimmy.Vetayases@oracle.com 	dev_info_t	*pdip;
44111600SVikram.Hegde@Sun.COM 	immu_devi_t	*immu_devi;
44211600SVikram.Hegde@Sun.COM 	uint16_t	sid;
44311600SVikram.Hegde@Sun.COM 	uchar_t		svt, sq;
44411600SVikram.Hegde@Sun.COM 
44511600SVikram.Hegde@Sun.COM 	if (!intrmap_enable_sid_verify) {
44612683SJimmy.Vetayases@oracle.com 		return (0);
44711600SVikram.Hegde@Sun.COM 	}
44811600SVikram.Hegde@Sun.COM 
44912683SJimmy.Vetayases@oracle.com 	if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
45011600SVikram.Hegde@Sun.COM 		/* for interrupt through I/O APIC */
45112683SJimmy.Vetayases@oracle.com 		sid = immu_dmar_ioapic_sid(ioapic_index);
45211600SVikram.Hegde@Sun.COM 		svt = SVT_ALL_VERIFY;
45311600SVikram.Hegde@Sun.COM 		sq = SQ_VERIFY_ALL;
45411600SVikram.Hegde@Sun.COM 	} else {
45511600SVikram.Hegde@Sun.COM 		/* MSI/MSI-X interrupt */
45611600SVikram.Hegde@Sun.COM 		ASSERT(dip);
45711600SVikram.Hegde@Sun.COM 		pdip = intrmap_top_pcibridge(dip);
45811600SVikram.Hegde@Sun.COM 		ASSERT(pdip);
45911600SVikram.Hegde@Sun.COM 		immu_devi = DEVI(pdip)->devi_iommu;
46011600SVikram.Hegde@Sun.COM 		ASSERT(immu_devi);
46111600SVikram.Hegde@Sun.COM 		if (immu_devi->imd_pcib_type == IMMU_PCIB_PCIE_PCI) {
46211600SVikram.Hegde@Sun.COM 			/* device behind pcie to pci bridge */
46311600SVikram.Hegde@Sun.COM 			sid = (immu_devi->imd_bus << 8) | immu_devi->imd_sec;
46411600SVikram.Hegde@Sun.COM 			svt = SVT_BUS_VERIFY;
46511600SVikram.Hegde@Sun.COM 			sq = SQ_VERIFY_ALL;
46611600SVikram.Hegde@Sun.COM 		} else {
46711600SVikram.Hegde@Sun.COM 			/* pcie device or device behind pci to pci bridge */
46811600SVikram.Hegde@Sun.COM 			sid = (immu_devi->imd_bus << 8) |
46911600SVikram.Hegde@Sun.COM 			    immu_devi->imd_devfunc;
47011600SVikram.Hegde@Sun.COM 			svt = SVT_ALL_VERIFY;
47111600SVikram.Hegde@Sun.COM 			sq = SQ_VERIFY_ALL;
47211600SVikram.Hegde@Sun.COM 		}
47311600SVikram.Hegde@Sun.COM 	}
47411600SVikram.Hegde@Sun.COM 
47512683SJimmy.Vetayases@oracle.com 	return (sid | (svt << 18) | (sq << 16));
47611600SVikram.Hegde@Sun.COM }
47711600SVikram.Hegde@Sun.COM 
47811600SVikram.Hegde@Sun.COM static void
intrmap_enable(immu_t * immu)47911600SVikram.Hegde@Sun.COM intrmap_enable(immu_t *immu)
48011600SVikram.Hegde@Sun.COM {
48111600SVikram.Hegde@Sun.COM 	intrmap_t *intrmap;
48211600SVikram.Hegde@Sun.COM 	uint64_t irta_reg;
48311600SVikram.Hegde@Sun.COM 
48411600SVikram.Hegde@Sun.COM 	intrmap = immu->immu_intrmap;
48511600SVikram.Hegde@Sun.COM 
48611600SVikram.Hegde@Sun.COM 	irta_reg = intrmap->intrmap_paddr | intrmap_irta_s;
48711600SVikram.Hegde@Sun.COM 	if (intrmap_apic_mode == LOCAL_X2APIC) {
48811600SVikram.Hegde@Sun.COM 		irta_reg |= (0x1 << 11);
48911600SVikram.Hegde@Sun.COM 	}
49011600SVikram.Hegde@Sun.COM 
49111600SVikram.Hegde@Sun.COM 	immu_regs_intrmap_enable(immu, irta_reg);
49211600SVikram.Hegde@Sun.COM }
49311600SVikram.Hegde@Sun.COM 
49411600SVikram.Hegde@Sun.COM /* ####################################################################### */
49511600SVikram.Hegde@Sun.COM 
49611600SVikram.Hegde@Sun.COM /*
49711600SVikram.Hegde@Sun.COM  * immu_intr_handler()
49811600SVikram.Hegde@Sun.COM  * 	the fault event handler for a single immu unit
49911600SVikram.Hegde@Sun.COM  */
50011600SVikram.Hegde@Sun.COM int
immu_intr_handler(immu_t * immu)50111600SVikram.Hegde@Sun.COM immu_intr_handler(immu_t *immu)
50211600SVikram.Hegde@Sun.COM {
50311600SVikram.Hegde@Sun.COM 	uint32_t status;
50411600SVikram.Hegde@Sun.COM 	int index, fault_reg_offset;
50511600SVikram.Hegde@Sun.COM 	int max_fault_index;
50611600SVikram.Hegde@Sun.COM 	boolean_t found_fault;
50711600SVikram.Hegde@Sun.COM 	dev_info_t *idip;
50811600SVikram.Hegde@Sun.COM 
50911600SVikram.Hegde@Sun.COM 	mutex_enter(&(immu->immu_intr_lock));
51011600SVikram.Hegde@Sun.COM 	mutex_enter(&(immu->immu_regs_lock));
51111600SVikram.Hegde@Sun.COM 
51211600SVikram.Hegde@Sun.COM 	/* read the fault status */
51311600SVikram.Hegde@Sun.COM 	status = immu_regs_get32(immu, IMMU_REG_FAULT_STS);
51411600SVikram.Hegde@Sun.COM 
51511600SVikram.Hegde@Sun.COM 	idip = immu->immu_dip;
51611600SVikram.Hegde@Sun.COM 	ASSERT(idip);
51711600SVikram.Hegde@Sun.COM 
51811600SVikram.Hegde@Sun.COM 	/* check if we have a pending fault for this immu unit */
51911600SVikram.Hegde@Sun.COM 	if ((status & IMMU_FAULT_STS_PPF) == 0) {
52011600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_regs_lock));
52111600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_intr_lock));
52211600SVikram.Hegde@Sun.COM 		return (DDI_INTR_UNCLAIMED);
52311600SVikram.Hegde@Sun.COM 	}
52411600SVikram.Hegde@Sun.COM 
52511600SVikram.Hegde@Sun.COM 	/*
52611600SVikram.Hegde@Sun.COM 	 * handle all primary pending faults
52711600SVikram.Hegde@Sun.COM 	 */
52811600SVikram.Hegde@Sun.COM 	index = IMMU_FAULT_GET_INDEX(status);
52911600SVikram.Hegde@Sun.COM 	max_fault_index =  IMMU_CAP_GET_NFR(immu->immu_regs_cap) - 1;
53011600SVikram.Hegde@Sun.COM 	fault_reg_offset = IMMU_CAP_GET_FRO(immu->immu_regs_cap);
53111600SVikram.Hegde@Sun.COM 
53211600SVikram.Hegde@Sun.COM 	found_fault = B_FALSE;
53311600SVikram.Hegde@Sun.COM 	_NOTE(CONSTCOND)
53411600SVikram.Hegde@Sun.COM 	while (1) {
53511600SVikram.Hegde@Sun.COM 		uint64_t val;
53611600SVikram.Hegde@Sun.COM 		uint8_t fault_reason;
53711600SVikram.Hegde@Sun.COM 		uint8_t fault_type;
53811600SVikram.Hegde@Sun.COM 		uint16_t sid;
53911600SVikram.Hegde@Sun.COM 		uint64_t pg_addr;
54011600SVikram.Hegde@Sun.COM 		uint64_t idx;
54111600SVikram.Hegde@Sun.COM 
54211600SVikram.Hegde@Sun.COM 		/* read the higher 64bits */
54311600SVikram.Hegde@Sun.COM 		val = immu_regs_get64(immu, fault_reg_offset + index * 16 + 8);
54411600SVikram.Hegde@Sun.COM 
54511600SVikram.Hegde@Sun.COM 		/* check if this fault register has pending fault */
54611600SVikram.Hegde@Sun.COM 		if (!IMMU_FRR_GET_F(val)) {
54711600SVikram.Hegde@Sun.COM 			break;
54811600SVikram.Hegde@Sun.COM 		}
54911600SVikram.Hegde@Sun.COM 
55011600SVikram.Hegde@Sun.COM 		found_fault = B_TRUE;
55111600SVikram.Hegde@Sun.COM 
55211600SVikram.Hegde@Sun.COM 		/* get the fault reason, fault type and sid */
55311600SVikram.Hegde@Sun.COM 		fault_reason = IMMU_FRR_GET_FR(val);
55411600SVikram.Hegde@Sun.COM 		fault_type = IMMU_FRR_GET_FT(val);
55511600SVikram.Hegde@Sun.COM 		sid = IMMU_FRR_GET_SID(val);
55611600SVikram.Hegde@Sun.COM 
55711600SVikram.Hegde@Sun.COM 		/* read the first 64bits */
55811600SVikram.Hegde@Sun.COM 		val = immu_regs_get64(immu, fault_reg_offset + index * 16);
55911600SVikram.Hegde@Sun.COM 		pg_addr = val & IMMU_PAGEMASK;
56011600SVikram.Hegde@Sun.COM 		idx = val >> 48;
56111600SVikram.Hegde@Sun.COM 
56211600SVikram.Hegde@Sun.COM 		/* clear the fault */
56311600SVikram.Hegde@Sun.COM 		immu_regs_put32(immu, fault_reg_offset + index * 16 + 12,
56411600SVikram.Hegde@Sun.COM 		    (((uint32_t)1) << 31));
56511600SVikram.Hegde@Sun.COM 
56611600SVikram.Hegde@Sun.COM 		/* report the fault info */
56711600SVikram.Hegde@Sun.COM 		if (fault_reason < 0x20) {
56811600SVikram.Hegde@Sun.COM 			/* immu-remapping fault */
56911600SVikram.Hegde@Sun.COM 			ddi_err(DER_WARN, idip,
57011600SVikram.Hegde@Sun.COM 			    "generated a fault event when translating DMA %s\n"
57111600SVikram.Hegde@Sun.COM 			    "\t on address 0x%" PRIx64 " for PCI(%d, %d, %d), "
57211600SVikram.Hegde@Sun.COM 			    "the reason is:\n\t %s",
57311600SVikram.Hegde@Sun.COM 			    fault_type ? "read" : "write", pg_addr,
57411600SVikram.Hegde@Sun.COM 			    (sid >> 8) & 0xff, (sid >> 3) & 0x1f, sid & 0x7,
57511600SVikram.Hegde@Sun.COM 			    immu_dvma_faults[MIN(fault_reason,
57611600SVikram.Hegde@Sun.COM 			    DVMA_MAX_FAULTS)]);
577*13050Sfrank.van.der.linden@oracle.com 			immu_print_fault_info(sid, pg_addr);
57811600SVikram.Hegde@Sun.COM 		} else if (fault_reason < 0x27) {
57911600SVikram.Hegde@Sun.COM 			/* intr-remapping fault */
58011600SVikram.Hegde@Sun.COM 			ddi_err(DER_WARN, idip,
58111600SVikram.Hegde@Sun.COM 			    "generated a fault event when translating "
58211600SVikram.Hegde@Sun.COM 			    "interrupt request\n"
58311600SVikram.Hegde@Sun.COM 			    "\t on index 0x%" PRIx64 " for PCI(%d, %d, %d), "
58411600SVikram.Hegde@Sun.COM 			    "the reason is:\n\t %s",
58511600SVikram.Hegde@Sun.COM 			    idx,
58611600SVikram.Hegde@Sun.COM 			    (sid >> 8) & 0xff, (sid >> 3) & 0x1f, sid & 0x7,
58711600SVikram.Hegde@Sun.COM 			    immu_intrmap_faults[MIN((fault_reason - 0x20),
58811600SVikram.Hegde@Sun.COM 			    INTRMAP_MAX_FAULTS)]);
58911600SVikram.Hegde@Sun.COM 		} else {
59011600SVikram.Hegde@Sun.COM 			ddi_err(DER_WARN, idip, "Unknown fault reason: 0x%x",
59111600SVikram.Hegde@Sun.COM 			    fault_reason);
59211600SVikram.Hegde@Sun.COM 		}
59311600SVikram.Hegde@Sun.COM 
59411600SVikram.Hegde@Sun.COM 		index++;
59511600SVikram.Hegde@Sun.COM 		if (index > max_fault_index)
59611600SVikram.Hegde@Sun.COM 			index = 0;
59711600SVikram.Hegde@Sun.COM 	}
59811600SVikram.Hegde@Sun.COM 
59911600SVikram.Hegde@Sun.COM 	/* Clear the fault */
60011600SVikram.Hegde@Sun.COM 	if (!found_fault) {
60111600SVikram.Hegde@Sun.COM 		ddi_err(DER_MODE, idip,
60211600SVikram.Hegde@Sun.COM 		    "Fault register set but no fault present");
60311600SVikram.Hegde@Sun.COM 	}
60411600SVikram.Hegde@Sun.COM 	immu_regs_put32(immu, IMMU_REG_FAULT_STS, 1);
60511600SVikram.Hegde@Sun.COM 	mutex_exit(&(immu->immu_regs_lock));
60611600SVikram.Hegde@Sun.COM 	mutex_exit(&(immu->immu_intr_lock));
60711600SVikram.Hegde@Sun.COM 	return (DDI_INTR_CLAIMED);
60811600SVikram.Hegde@Sun.COM }
60911600SVikram.Hegde@Sun.COM /* ######################################################################### */
61011600SVikram.Hegde@Sun.COM 
61111600SVikram.Hegde@Sun.COM /*
61211600SVikram.Hegde@Sun.COM  * Interrupt remap entry points
61311600SVikram.Hegde@Sun.COM  */
61411600SVikram.Hegde@Sun.COM 
61511600SVikram.Hegde@Sun.COM /* initialize interrupt remapping */
61611600SVikram.Hegde@Sun.COM static int
immu_intrmap_init(int apic_mode)61711600SVikram.Hegde@Sun.COM immu_intrmap_init(int apic_mode)
61811600SVikram.Hegde@Sun.COM {
61911600SVikram.Hegde@Sun.COM 	immu_t *immu;
62011600SVikram.Hegde@Sun.COM 	int error = DDI_FAILURE;
62111600SVikram.Hegde@Sun.COM 
62211600SVikram.Hegde@Sun.COM 	if (immu_intrmap_enable == B_FALSE) {
62311600SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
62411600SVikram.Hegde@Sun.COM 	}
62511600SVikram.Hegde@Sun.COM 
62611600SVikram.Hegde@Sun.COM 	intrmap_apic_mode = apic_mode;
62711600SVikram.Hegde@Sun.COM 
62811600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
62911600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
63011600SVikram.Hegde@Sun.COM 		if ((immu->immu_intrmap_running == B_TRUE) &&
63111600SVikram.Hegde@Sun.COM 		    IMMU_ECAP_GET_IR(immu->immu_regs_excap)) {
63211600SVikram.Hegde@Sun.COM 			if (init_unit(immu) == DDI_SUCCESS) {
63311600SVikram.Hegde@Sun.COM 				error = DDI_SUCCESS;
63411600SVikram.Hegde@Sun.COM 			}
63511600SVikram.Hegde@Sun.COM 		}
63611600SVikram.Hegde@Sun.COM 	}
63711600SVikram.Hegde@Sun.COM 
63811600SVikram.Hegde@Sun.COM 	/*
63911600SVikram.Hegde@Sun.COM 	 * if all IOMMU units disable intr remapping,
64011600SVikram.Hegde@Sun.COM 	 * return FAILURE
64111600SVikram.Hegde@Sun.COM 	 */
64211600SVikram.Hegde@Sun.COM 	return (error);
64311600SVikram.Hegde@Sun.COM }
64411600SVikram.Hegde@Sun.COM 
64511600SVikram.Hegde@Sun.COM 
64611600SVikram.Hegde@Sun.COM 
64711600SVikram.Hegde@Sun.COM /* enable interrupt remapping */
64811600SVikram.Hegde@Sun.COM static void
immu_intrmap_switchon(int suppress_brdcst_eoi)64911600SVikram.Hegde@Sun.COM immu_intrmap_switchon(int suppress_brdcst_eoi)
65011600SVikram.Hegde@Sun.COM {
65111600SVikram.Hegde@Sun.COM 	immu_t *immu;
65211600SVikram.Hegde@Sun.COM 
65311600SVikram.Hegde@Sun.COM 
65411600SVikram.Hegde@Sun.COM 	intrmap_suppress_brdcst_eoi = suppress_brdcst_eoi;
65511600SVikram.Hegde@Sun.COM 
65611600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
65711600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
65811600SVikram.Hegde@Sun.COM 		if (immu->immu_intrmap_setup == B_TRUE) {
65911600SVikram.Hegde@Sun.COM 			intrmap_enable(immu);
66011600SVikram.Hegde@Sun.COM 		}
66111600SVikram.Hegde@Sun.COM 	}
66211600SVikram.Hegde@Sun.COM }
66311600SVikram.Hegde@Sun.COM 
66411600SVikram.Hegde@Sun.COM /* alloc remapping entry for the interrupt */
66511600SVikram.Hegde@Sun.COM static void
immu_intrmap_alloc(void ** intrmap_private_tbl,dev_info_t * dip,uint16_t type,int count,uchar_t ioapic_index)66612683SJimmy.Vetayases@oracle.com immu_intrmap_alloc(void **intrmap_private_tbl, dev_info_t *dip,
66712683SJimmy.Vetayases@oracle.com     uint16_t type, int count, uchar_t ioapic_index)
66811600SVikram.Hegde@Sun.COM {
66911600SVikram.Hegde@Sun.COM 	immu_t	*immu;
67011600SVikram.Hegde@Sun.COM 	intrmap_t *intrmap;
671*13050Sfrank.van.der.linden@oracle.com 	immu_inv_wait_t *iwp;
67212683SJimmy.Vetayases@oracle.com 	uint32_t		idx, i;
67311600SVikram.Hegde@Sun.COM 	uint32_t		sid_svt_sq;
67412683SJimmy.Vetayases@oracle.com 	intrmap_private_t	*intrmap_private;
67511600SVikram.Hegde@Sun.COM 
67612683SJimmy.Vetayases@oracle.com 	if (intrmap_private_tbl[0] == INTRMAP_DISABLE ||
67712683SJimmy.Vetayases@oracle.com 	    intrmap_private_tbl[0] != NULL) {
67811600SVikram.Hegde@Sun.COM 		return;
67911600SVikram.Hegde@Sun.COM 	}
68011600SVikram.Hegde@Sun.COM 
68112683SJimmy.Vetayases@oracle.com 	intrmap_private_tbl[0] =
68211600SVikram.Hegde@Sun.COM 	    kmem_zalloc(sizeof (intrmap_private_t), KM_SLEEP);
68312683SJimmy.Vetayases@oracle.com 	intrmap_private = INTRMAP_PRIVATE(intrmap_private_tbl[0]);
68411600SVikram.Hegde@Sun.COM 
68512683SJimmy.Vetayases@oracle.com 	immu = get_immu(dip, type, ioapic_index);
68612683SJimmy.Vetayases@oracle.com 	if ((immu != NULL) && (immu->immu_intrmap_running == B_TRUE)) {
68712683SJimmy.Vetayases@oracle.com 		intrmap_private->ir_immu = immu;
68812683SJimmy.Vetayases@oracle.com 	} else {
68911600SVikram.Hegde@Sun.COM 		goto intrmap_disable;
69011600SVikram.Hegde@Sun.COM 	}
69111600SVikram.Hegde@Sun.COM 
69211600SVikram.Hegde@Sun.COM 	intrmap = immu->immu_intrmap;
69311600SVikram.Hegde@Sun.COM 
69412683SJimmy.Vetayases@oracle.com 	if (count == 1) {
69511600SVikram.Hegde@Sun.COM 		idx = alloc_tbl_entry(intrmap);
69611600SVikram.Hegde@Sun.COM 	} else {
69712683SJimmy.Vetayases@oracle.com 		idx = alloc_tbl_multi_entries(intrmap, count);
69811600SVikram.Hegde@Sun.COM 	}
69911600SVikram.Hegde@Sun.COM 
70011600SVikram.Hegde@Sun.COM 	if (idx == INTRMAP_IDX_FULL) {
70111600SVikram.Hegde@Sun.COM 		goto intrmap_disable;
70211600SVikram.Hegde@Sun.COM 	}
70311600SVikram.Hegde@Sun.COM 
70412683SJimmy.Vetayases@oracle.com 	intrmap_private->ir_idx = idx;
70511600SVikram.Hegde@Sun.COM 
70612683SJimmy.Vetayases@oracle.com 	sid_svt_sq = intrmap_private->ir_sid_svt_sq =
70712683SJimmy.Vetayases@oracle.com 	    get_sid(dip, type, ioapic_index);
708*13050Sfrank.van.der.linden@oracle.com 	iwp = &intrmap_private->ir_inv_wait;
709*13050Sfrank.van.der.linden@oracle.com 	immu_init_inv_wait(iwp, "intrmaplocal", B_TRUE);
71011600SVikram.Hegde@Sun.COM 
71112683SJimmy.Vetayases@oracle.com 	if (count == 1) {
71211600SVikram.Hegde@Sun.COM 		if (IMMU_CAP_GET_CM(immu->immu_regs_cap)) {
713*13050Sfrank.van.der.linden@oracle.com 			immu_qinv_intr_one_cache(immu, idx, iwp);
71411600SVikram.Hegde@Sun.COM 		} else {
71511600SVikram.Hegde@Sun.COM 			immu_regs_wbf_flush(immu);
71611600SVikram.Hegde@Sun.COM 		}
71711600SVikram.Hegde@Sun.COM 		return;
71811600SVikram.Hegde@Sun.COM 	}
71911600SVikram.Hegde@Sun.COM 
72012683SJimmy.Vetayases@oracle.com 	for (i = 1; i < count; i++) {
72112683SJimmy.Vetayases@oracle.com 		intrmap_private_tbl[i] =
72211600SVikram.Hegde@Sun.COM 		    kmem_zalloc(sizeof (intrmap_private_t), KM_SLEEP);
72311600SVikram.Hegde@Sun.COM 
72412683SJimmy.Vetayases@oracle.com 		INTRMAP_PRIVATE(intrmap_private_tbl[i])->ir_immu = immu;
72512683SJimmy.Vetayases@oracle.com 		INTRMAP_PRIVATE(intrmap_private_tbl[i])->ir_sid_svt_sq =
72612683SJimmy.Vetayases@oracle.com 		    sid_svt_sq;
72712683SJimmy.Vetayases@oracle.com 		INTRMAP_PRIVATE(intrmap_private_tbl[i])->ir_idx = idx + i;
72811600SVikram.Hegde@Sun.COM 	}
72911600SVikram.Hegde@Sun.COM 
73011600SVikram.Hegde@Sun.COM 	if (IMMU_CAP_GET_CM(immu->immu_regs_cap)) {
731*13050Sfrank.van.der.linden@oracle.com 		immu_qinv_intr_caches(immu, idx, count, iwp);
73211600SVikram.Hegde@Sun.COM 	} else {
73311600SVikram.Hegde@Sun.COM 		immu_regs_wbf_flush(immu);
73411600SVikram.Hegde@Sun.COM 	}
73511600SVikram.Hegde@Sun.COM 
73611600SVikram.Hegde@Sun.COM 	return;
73711600SVikram.Hegde@Sun.COM 
73811600SVikram.Hegde@Sun.COM intrmap_disable:
73912683SJimmy.Vetayases@oracle.com 	kmem_free(intrmap_private_tbl[0], sizeof (intrmap_private_t));
74012683SJimmy.Vetayases@oracle.com 	intrmap_private_tbl[0] = INTRMAP_DISABLE;
74111600SVikram.Hegde@Sun.COM }
74211600SVikram.Hegde@Sun.COM 
74311600SVikram.Hegde@Sun.COM 
74411600SVikram.Hegde@Sun.COM /* remapping the interrupt */
74511600SVikram.Hegde@Sun.COM static void
immu_intrmap_map(void * intrmap_private,void * intrmap_data,uint16_t type,int count)74612683SJimmy.Vetayases@oracle.com immu_intrmap_map(void *intrmap_private, void *intrmap_data, uint16_t type,
74712683SJimmy.Vetayases@oracle.com     int count)
74811600SVikram.Hegde@Sun.COM {
74911600SVikram.Hegde@Sun.COM 	immu_t	*immu;
750*13050Sfrank.van.der.linden@oracle.com 	immu_inv_wait_t	*iwp;
75111600SVikram.Hegde@Sun.COM 	intrmap_t	*intrmap;
75211600SVikram.Hegde@Sun.COM 	ioapic_rdt_t	*irdt = (ioapic_rdt_t *)intrmap_data;
75311600SVikram.Hegde@Sun.COM 	msi_regs_t	*mregs = (msi_regs_t *)intrmap_data;
75411600SVikram.Hegde@Sun.COM 	intrmap_rte_t	irte;
75512683SJimmy.Vetayases@oracle.com 	uint_t		idx, i;
75611600SVikram.Hegde@Sun.COM 	uint32_t	dst, sid_svt_sq;
75711600SVikram.Hegde@Sun.COM 	uchar_t		vector, dlm, tm, rh, dm;
75811600SVikram.Hegde@Sun.COM 
75912683SJimmy.Vetayases@oracle.com 	if (intrmap_private == INTRMAP_DISABLE)
76011600SVikram.Hegde@Sun.COM 		return;
76111600SVikram.Hegde@Sun.COM 
76212683SJimmy.Vetayases@oracle.com 	idx = INTRMAP_PRIVATE(intrmap_private)->ir_idx;
76312683SJimmy.Vetayases@oracle.com 	immu = INTRMAP_PRIVATE(intrmap_private)->ir_immu;
764*13050Sfrank.van.der.linden@oracle.com 	iwp = &INTRMAP_PRIVATE(intrmap_private)->ir_inv_wait;
76512683SJimmy.Vetayases@oracle.com 	intrmap = immu->immu_intrmap;
76612683SJimmy.Vetayases@oracle.com 	sid_svt_sq = INTRMAP_PRIVATE(intrmap_private)->ir_sid_svt_sq;
76711600SVikram.Hegde@Sun.COM 
76812683SJimmy.Vetayases@oracle.com 	if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
76911600SVikram.Hegde@Sun.COM 		dm = RDT_DM(irdt->ir_lo);
77011600SVikram.Hegde@Sun.COM 		rh = 0;
77111600SVikram.Hegde@Sun.COM 		tm = RDT_TM(irdt->ir_lo);
77211600SVikram.Hegde@Sun.COM 		dlm = RDT_DLM(irdt->ir_lo);
77311600SVikram.Hegde@Sun.COM 		dst = irdt->ir_hi;
77411600SVikram.Hegde@Sun.COM 
77511600SVikram.Hegde@Sun.COM 		/*
77611600SVikram.Hegde@Sun.COM 		 * Mark the IRTE's TM as Edge to suppress broadcast EOI.
77711600SVikram.Hegde@Sun.COM 		 */
77811600SVikram.Hegde@Sun.COM 		if (intrmap_suppress_brdcst_eoi) {
77911600SVikram.Hegde@Sun.COM 			tm = TRIGGER_MODE_EDGE;
78011600SVikram.Hegde@Sun.COM 		}
78112683SJimmy.Vetayases@oracle.com 
78212683SJimmy.Vetayases@oracle.com 		vector = RDT_VECTOR(irdt->ir_lo);
78311600SVikram.Hegde@Sun.COM 	} else {
78411600SVikram.Hegde@Sun.COM 		dm = MSI_ADDR_DM_PHYSICAL;
78511600SVikram.Hegde@Sun.COM 		rh = MSI_ADDR_RH_FIXED;
78611600SVikram.Hegde@Sun.COM 		tm = TRIGGER_MODE_EDGE;
78711600SVikram.Hegde@Sun.COM 		dlm = 0;
78811600SVikram.Hegde@Sun.COM 		dst = mregs->mr_addr;
78912683SJimmy.Vetayases@oracle.com 
79012683SJimmy.Vetayases@oracle.com 		vector = mregs->mr_data & 0xff;
79111600SVikram.Hegde@Sun.COM 	}
79211600SVikram.Hegde@Sun.COM 
79311600SVikram.Hegde@Sun.COM 	if (intrmap_apic_mode == LOCAL_APIC)
79411600SVikram.Hegde@Sun.COM 		dst = (dst & 0xFF) << 8;
79511600SVikram.Hegde@Sun.COM 
79612683SJimmy.Vetayases@oracle.com 	if (count == 1) {
79711600SVikram.Hegde@Sun.COM 		irte.lo = IRTE_LOW(dst, vector, dlm, tm, rh, dm, 0, 1);
79811600SVikram.Hegde@Sun.COM 		irte.hi = IRTE_HIGH(sid_svt_sq);
79911600SVikram.Hegde@Sun.COM 
80011600SVikram.Hegde@Sun.COM 		/* set interrupt remapping table entry */
80111600SVikram.Hegde@Sun.COM 		bcopy(&irte, intrmap->intrmap_vaddr +
80211600SVikram.Hegde@Sun.COM 		    idx * INTRMAP_RTE_SIZE,
80311600SVikram.Hegde@Sun.COM 		    INTRMAP_RTE_SIZE);
80411600SVikram.Hegde@Sun.COM 
805*13050Sfrank.van.der.linden@oracle.com 		immu_qinv_intr_one_cache(immu, idx, iwp);
80611600SVikram.Hegde@Sun.COM 
80711600SVikram.Hegde@Sun.COM 	} else {
80812683SJimmy.Vetayases@oracle.com 		for (i = 0; i < count; i++) {
80911600SVikram.Hegde@Sun.COM 			irte.lo = IRTE_LOW(dst, vector, dlm, tm, rh, dm, 0, 1);
81011600SVikram.Hegde@Sun.COM 			irte.hi = IRTE_HIGH(sid_svt_sq);
81111600SVikram.Hegde@Sun.COM 
81211600SVikram.Hegde@Sun.COM 			/* set interrupt remapping table entry */
81311600SVikram.Hegde@Sun.COM 			bcopy(&irte, intrmap->intrmap_vaddr +
81411600SVikram.Hegde@Sun.COM 			    idx * INTRMAP_RTE_SIZE,
81511600SVikram.Hegde@Sun.COM 			    INTRMAP_RTE_SIZE);
81611600SVikram.Hegde@Sun.COM 			vector++;
81711600SVikram.Hegde@Sun.COM 			idx++;
81811600SVikram.Hegde@Sun.COM 		}
81911600SVikram.Hegde@Sun.COM 
820*13050Sfrank.van.der.linden@oracle.com 		immu_qinv_intr_caches(immu, idx, count, iwp);
82111600SVikram.Hegde@Sun.COM 	}
82211600SVikram.Hegde@Sun.COM }
82311600SVikram.Hegde@Sun.COM 
82411600SVikram.Hegde@Sun.COM /* free the remapping entry */
82511600SVikram.Hegde@Sun.COM static void
immu_intrmap_free(void ** intrmap_privatep)82612683SJimmy.Vetayases@oracle.com immu_intrmap_free(void **intrmap_privatep)
82711600SVikram.Hegde@Sun.COM {
82811600SVikram.Hegde@Sun.COM 	immu_t *immu;
829*13050Sfrank.van.der.linden@oracle.com 	immu_inv_wait_t *iwp;
83011600SVikram.Hegde@Sun.COM 	intrmap_t *intrmap;
83111600SVikram.Hegde@Sun.COM 	uint32_t idx;
83211600SVikram.Hegde@Sun.COM 
83312683SJimmy.Vetayases@oracle.com 	if (*intrmap_privatep == INTRMAP_DISABLE || *intrmap_privatep == NULL) {
83412683SJimmy.Vetayases@oracle.com 		*intrmap_privatep = NULL;
83511600SVikram.Hegde@Sun.COM 		return;
83611600SVikram.Hegde@Sun.COM 	}
83711600SVikram.Hegde@Sun.COM 
83812683SJimmy.Vetayases@oracle.com 	immu = INTRMAP_PRIVATE(*intrmap_privatep)->ir_immu;
839*13050Sfrank.van.der.linden@oracle.com 	iwp = &INTRMAP_PRIVATE(*intrmap_privatep)->ir_inv_wait;
84011600SVikram.Hegde@Sun.COM 	intrmap = immu->immu_intrmap;
84112683SJimmy.Vetayases@oracle.com 	idx = INTRMAP_PRIVATE(*intrmap_privatep)->ir_idx;
84211600SVikram.Hegde@Sun.COM 
84311600SVikram.Hegde@Sun.COM 	bzero(intrmap->intrmap_vaddr + idx * INTRMAP_RTE_SIZE,
84411600SVikram.Hegde@Sun.COM 	    INTRMAP_RTE_SIZE);
84511600SVikram.Hegde@Sun.COM 
846*13050Sfrank.van.der.linden@oracle.com 	immu_qinv_intr_one_cache(immu, idx, iwp);
84711600SVikram.Hegde@Sun.COM 
84811600SVikram.Hegde@Sun.COM 	mutex_enter(&intrmap->intrmap_lock);
84911600SVikram.Hegde@Sun.COM 	bitset_del(&intrmap->intrmap_map, idx);
85011600SVikram.Hegde@Sun.COM 	if (intrmap->intrmap_free == INTRMAP_IDX_FULL) {
85111600SVikram.Hegde@Sun.COM 		intrmap->intrmap_free = idx;
85211600SVikram.Hegde@Sun.COM 	}
85311600SVikram.Hegde@Sun.COM 	mutex_exit(&intrmap->intrmap_lock);
85411600SVikram.Hegde@Sun.COM 
85512683SJimmy.Vetayases@oracle.com 	kmem_free(*intrmap_privatep, sizeof (intrmap_private_t));
85612683SJimmy.Vetayases@oracle.com 	*intrmap_privatep = NULL;
85711600SVikram.Hegde@Sun.COM }
85811600SVikram.Hegde@Sun.COM 
85911600SVikram.Hegde@Sun.COM /* record the ioapic rdt entry */
86011600SVikram.Hegde@Sun.COM static void
immu_intrmap_rdt(void * intrmap_private,ioapic_rdt_t * irdt)86112683SJimmy.Vetayases@oracle.com immu_intrmap_rdt(void *intrmap_private, ioapic_rdt_t *irdt)
86211600SVikram.Hegde@Sun.COM {
86311600SVikram.Hegde@Sun.COM 	uint32_t rdt_entry, tm, pol, idx, vector;
86411600SVikram.Hegde@Sun.COM 
86511600SVikram.Hegde@Sun.COM 	rdt_entry = irdt->ir_lo;
86611600SVikram.Hegde@Sun.COM 
86712683SJimmy.Vetayases@oracle.com 	if (intrmap_private != INTRMAP_DISABLE && intrmap_private != NULL) {
86812683SJimmy.Vetayases@oracle.com 		idx = INTRMAP_PRIVATE(intrmap_private)->ir_idx;
86911600SVikram.Hegde@Sun.COM 		tm = RDT_TM(rdt_entry);
87011600SVikram.Hegde@Sun.COM 		pol = RDT_POL(rdt_entry);
87112683SJimmy.Vetayases@oracle.com 		vector = RDT_VECTOR(rdt_entry);
87211600SVikram.Hegde@Sun.COM 		irdt->ir_lo = (tm << INTRMAP_IOAPIC_TM_SHIFT) |
87311600SVikram.Hegde@Sun.COM 		    (pol << INTRMAP_IOAPIC_POL_SHIFT) |
87411600SVikram.Hegde@Sun.COM 		    ((idx >> 15) << INTRMAP_IOAPIC_IDX15_SHIFT) |
87511600SVikram.Hegde@Sun.COM 		    vector;
87611600SVikram.Hegde@Sun.COM 		irdt->ir_hi = (idx << INTRMAP_IOAPIC_IDX_SHIFT) |
87711600SVikram.Hegde@Sun.COM 		    (1 << INTRMAP_IOAPIC_FORMAT_SHIFT);
87811600SVikram.Hegde@Sun.COM 	} else {
87911600SVikram.Hegde@Sun.COM 		irdt->ir_hi <<= APIC_ID_BIT_OFFSET;
88011600SVikram.Hegde@Sun.COM 	}
88111600SVikram.Hegde@Sun.COM }
88211600SVikram.Hegde@Sun.COM 
88311600SVikram.Hegde@Sun.COM /* record the msi interrupt structure */
88411600SVikram.Hegde@Sun.COM /*ARGSUSED*/
88511600SVikram.Hegde@Sun.COM static void
immu_intrmap_msi(void * intrmap_private,msi_regs_t * mregs)88612683SJimmy.Vetayases@oracle.com immu_intrmap_msi(void *intrmap_private, msi_regs_t *mregs)
88711600SVikram.Hegde@Sun.COM {
88811600SVikram.Hegde@Sun.COM 	uint_t	idx;
88911600SVikram.Hegde@Sun.COM 
89012683SJimmy.Vetayases@oracle.com 	if (intrmap_private != INTRMAP_DISABLE && intrmap_private != NULL) {
89112683SJimmy.Vetayases@oracle.com 		idx = INTRMAP_PRIVATE(intrmap_private)->ir_idx;
89211600SVikram.Hegde@Sun.COM 
89311600SVikram.Hegde@Sun.COM 		mregs->mr_data = 0;
89411600SVikram.Hegde@Sun.COM 		mregs->mr_addr = MSI_ADDR_HDR |
89511600SVikram.Hegde@Sun.COM 		    ((idx & 0x7fff) << INTRMAP_MSI_IDX_SHIFT) |
89611600SVikram.Hegde@Sun.COM 		    (1 << INTRMAP_MSI_FORMAT_SHIFT) |
89711600SVikram.Hegde@Sun.COM 		    (1 << INTRMAP_MSI_SHV_SHIFT) |
89811600SVikram.Hegde@Sun.COM 		    ((idx >> 15) << INTRMAP_MSI_IDX15_SHIFT);
89911600SVikram.Hegde@Sun.COM 	} else {
90011600SVikram.Hegde@Sun.COM 		mregs->mr_addr = MSI_ADDR_HDR |
90111600SVikram.Hegde@Sun.COM 		    (MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
90211600SVikram.Hegde@Sun.COM 		    (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT) |
90311600SVikram.Hegde@Sun.COM 		    (mregs->mr_addr << MSI_ADDR_DEST_SHIFT);
90411600SVikram.Hegde@Sun.COM 		mregs->mr_data = (MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) |
90511600SVikram.Hegde@Sun.COM 		    mregs->mr_data;
90611600SVikram.Hegde@Sun.COM 	}
90711600SVikram.Hegde@Sun.COM }
90811600SVikram.Hegde@Sun.COM 
90911600SVikram.Hegde@Sun.COM /* ######################################################################### */
91011600SVikram.Hegde@Sun.COM /*
91111600SVikram.Hegde@Sun.COM  * Functions exported by immu_intr.c
91211600SVikram.Hegde@Sun.COM  */
91311600SVikram.Hegde@Sun.COM void
immu_intrmap_setup(list_t * listp)91411600SVikram.Hegde@Sun.COM immu_intrmap_setup(list_t *listp)
91511600SVikram.Hegde@Sun.COM {
91611600SVikram.Hegde@Sun.COM 	immu_t *immu;
91711600SVikram.Hegde@Sun.COM 
91811600SVikram.Hegde@Sun.COM 	/*
91911600SVikram.Hegde@Sun.COM 	 * Check if ACPI DMAR tables say that
92011600SVikram.Hegde@Sun.COM 	 * interrupt remapping is supported
92111600SVikram.Hegde@Sun.COM 	 */
92211600SVikram.Hegde@Sun.COM 	if (immu_dmar_intrmap_supported() == B_FALSE) {
92311600SVikram.Hegde@Sun.COM 		return;
92411600SVikram.Hegde@Sun.COM 	}
92511600SVikram.Hegde@Sun.COM 
92611600SVikram.Hegde@Sun.COM 	/*
92711600SVikram.Hegde@Sun.COM 	 * Check if interrupt remapping is disabled.
92811600SVikram.Hegde@Sun.COM 	 */
92911600SVikram.Hegde@Sun.COM 	if (immu_intrmap_enable == B_FALSE) {
93011600SVikram.Hegde@Sun.COM 		return;
93111600SVikram.Hegde@Sun.COM 	}
93211600SVikram.Hegde@Sun.COM 
93311600SVikram.Hegde@Sun.COM 	psm_vt_ops = &intrmap_ops;
93411600SVikram.Hegde@Sun.COM 
93511600SVikram.Hegde@Sun.COM 	immu = list_head(listp);
93611600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(listp, immu)) {
93711600SVikram.Hegde@Sun.COM 		mutex_init(&(immu->immu_intrmap_lock), NULL,
93811600SVikram.Hegde@Sun.COM 		    MUTEX_DEFAULT, NULL);
93911600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_intrmap_lock));
940*13050Sfrank.van.der.linden@oracle.com 		immu_init_inv_wait(&immu->immu_intrmap_inv_wait,
941*13050Sfrank.van.der.linden@oracle.com 		    "intrmapglobal", B_TRUE);
94211600SVikram.Hegde@Sun.COM 		immu->immu_intrmap_setup = B_TRUE;
94311600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_intrmap_lock));
94411600SVikram.Hegde@Sun.COM 	}
94511600SVikram.Hegde@Sun.COM }
94611600SVikram.Hegde@Sun.COM 
94711600SVikram.Hegde@Sun.COM void
immu_intrmap_startup(immu_t * immu)94811600SVikram.Hegde@Sun.COM immu_intrmap_startup(immu_t *immu)
94911600SVikram.Hegde@Sun.COM {
95011600SVikram.Hegde@Sun.COM 	/* do nothing */
95111600SVikram.Hegde@Sun.COM 	mutex_enter(&(immu->immu_intrmap_lock));
95211600SVikram.Hegde@Sun.COM 	if (immu->immu_intrmap_setup == B_TRUE) {
95311600SVikram.Hegde@Sun.COM 		immu->immu_intrmap_running = B_TRUE;
95411600SVikram.Hegde@Sun.COM 	}
95511600SVikram.Hegde@Sun.COM 	mutex_exit(&(immu->immu_intrmap_lock));
95611600SVikram.Hegde@Sun.COM }
95711600SVikram.Hegde@Sun.COM 
95811600SVikram.Hegde@Sun.COM /*
95911600SVikram.Hegde@Sun.COM  * Register a Intel IOMMU unit (i.e. DMAR unit's)
96011600SVikram.Hegde@Sun.COM  * interrupt handler
96111600SVikram.Hegde@Sun.COM  */
96211600SVikram.Hegde@Sun.COM void
immu_intr_register(immu_t * immu)96311600SVikram.Hegde@Sun.COM immu_intr_register(immu_t *immu)
96411600SVikram.Hegde@Sun.COM {
96511600SVikram.Hegde@Sun.COM 	int irq, vect;
96611600SVikram.Hegde@Sun.COM 	char intr_handler_name[IMMU_MAXNAMELEN];
96711600SVikram.Hegde@Sun.COM 	uint32_t msi_data;
96811600SVikram.Hegde@Sun.COM 	uint32_t uaddr;
96911600SVikram.Hegde@Sun.COM 	uint32_t msi_addr;
97012683SJimmy.Vetayases@oracle.com 	uint32_t localapic_id = 0;
97112683SJimmy.Vetayases@oracle.com 
97212683SJimmy.Vetayases@oracle.com 	if (psm_get_localapicid)
97312683SJimmy.Vetayases@oracle.com 		localapic_id = psm_get_localapicid(0);
97411600SVikram.Hegde@Sun.COM 
97511600SVikram.Hegde@Sun.COM 	msi_addr = (MSI_ADDR_HDR |
97612683SJimmy.Vetayases@oracle.com 	    ((localapic_id & 0xFF) << MSI_ADDR_DEST_SHIFT) |
97711600SVikram.Hegde@Sun.COM 	    (MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
97811600SVikram.Hegde@Sun.COM 	    (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT));
97911600SVikram.Hegde@Sun.COM 
98011600SVikram.Hegde@Sun.COM 	if (intrmap_apic_mode == LOCAL_X2APIC) {
98112683SJimmy.Vetayases@oracle.com 		uaddr = localapic_id & 0xFFFFFF00;
98211600SVikram.Hegde@Sun.COM 	} else {
98311600SVikram.Hegde@Sun.COM 		uaddr = 0;
98411600SVikram.Hegde@Sun.COM 	}
98511600SVikram.Hegde@Sun.COM 
98611600SVikram.Hegde@Sun.COM 	/* Dont need to hold immu_intr_lock since we are in boot */
98712683SJimmy.Vetayases@oracle.com 	irq = vect = psm_get_ipivect(IMMU_INTR_IPL, -1);
98812683SJimmy.Vetayases@oracle.com 	if (psm_xlate_vector_by_irq != NULL)
98912683SJimmy.Vetayases@oracle.com 		vect = psm_xlate_vector_by_irq(irq);
99012683SJimmy.Vetayases@oracle.com 
99111600SVikram.Hegde@Sun.COM 	msi_data = ((MSI_DATA_DELIVERY_FIXED <<
99211600SVikram.Hegde@Sun.COM 	    MSI_DATA_DELIVERY_SHIFT) | vect);
99311600SVikram.Hegde@Sun.COM 
99411600SVikram.Hegde@Sun.COM 	(void) snprintf(intr_handler_name, sizeof (intr_handler_name),
99511600SVikram.Hegde@Sun.COM 	    "%s-intr-handler", immu->immu_name);
99611600SVikram.Hegde@Sun.COM 
99711600SVikram.Hegde@Sun.COM 	(void) add_avintr((void *)NULL, IMMU_INTR_IPL,
99811600SVikram.Hegde@Sun.COM 	    (avfunc)(immu_intr_handler), intr_handler_name, irq,
99911600SVikram.Hegde@Sun.COM 	    (caddr_t)immu, NULL, NULL, NULL);
100011600SVikram.Hegde@Sun.COM 
100111600SVikram.Hegde@Sun.COM 	immu_regs_intr_enable(immu, msi_addr, msi_data, uaddr);
100211600SVikram.Hegde@Sun.COM 
100311600SVikram.Hegde@Sun.COM 	(void) immu_intr_handler(immu);
100411600SVikram.Hegde@Sun.COM }
1005