13a634bfcSVikram Hegde /* 23a634bfcSVikram Hegde * CDDL HEADER START 33a634bfcSVikram Hegde * 43a634bfcSVikram Hegde * The contents of this file are subject to the terms of the 53a634bfcSVikram Hegde * Common Development and Distribution License (the "License"). 63a634bfcSVikram Hegde * You may not use this file except in compliance with the License. 73a634bfcSVikram Hegde * 83a634bfcSVikram Hegde * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93a634bfcSVikram Hegde * or http://www.opensolaris.org/os/licensing. 103a634bfcSVikram Hegde * See the License for the specific language governing permissions 113a634bfcSVikram Hegde * and limitations under the License. 123a634bfcSVikram Hegde * 133a634bfcSVikram Hegde * When distributing Covered Code, include this CDDL HEADER in each 143a634bfcSVikram Hegde * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153a634bfcSVikram Hegde * If applicable, add the following below this CDDL HEADER, with the 163a634bfcSVikram Hegde * fields enclosed by brackets "[]" replaced with your own identifying 173a634bfcSVikram Hegde * information: Portions Copyright [yyyy] [name of copyright owner] 183a634bfcSVikram Hegde * 193a634bfcSVikram Hegde * CDDL HEADER END 203a634bfcSVikram Hegde */ 213a634bfcSVikram Hegde 223a634bfcSVikram Hegde /* 237ff178cdSJimmy Vetayases * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 243a634bfcSVikram Hegde */ 253a634bfcSVikram Hegde 263a634bfcSVikram Hegde /* 273a634bfcSVikram Hegde * Copyright (c) 2009, Intel Corporation. 283a634bfcSVikram Hegde * All rights reserved. 293a634bfcSVikram Hegde */ 303a634bfcSVikram Hegde 313a634bfcSVikram Hegde 323a634bfcSVikram Hegde #include <sys/apic.h> 333a634bfcSVikram Hegde #include <vm/hat_i86.h> 343a634bfcSVikram Hegde #include <sys/sysmacros.h> 353a634bfcSVikram Hegde #include <sys/smp_impldefs.h> 363a634bfcSVikram Hegde #include <sys/immu.h> 373a634bfcSVikram Hegde 383a634bfcSVikram Hegde 393a634bfcSVikram Hegde typedef struct intrmap_private { 403a634bfcSVikram Hegde immu_t *ir_immu; 41*50200e77SFrank Van Der Linden immu_inv_wait_t ir_inv_wait; 423a634bfcSVikram Hegde uint16_t ir_idx; 433a634bfcSVikram Hegde uint32_t ir_sid_svt_sq; 443a634bfcSVikram Hegde } intrmap_private_t; 453a634bfcSVikram Hegde 467ff178cdSJimmy Vetayases #define INTRMAP_PRIVATE(intrmap) ((intrmap_private_t *)intrmap) 473a634bfcSVikram Hegde 483a634bfcSVikram Hegde /* interrupt remapping table entry */ 493a634bfcSVikram Hegde typedef struct intrmap_rte { 503a634bfcSVikram Hegde uint64_t lo; 513a634bfcSVikram Hegde uint64_t hi; 523a634bfcSVikram Hegde } intrmap_rte_t; 533a634bfcSVikram Hegde 543a634bfcSVikram Hegde #define IRTE_HIGH(sid_svt_sq) (sid_svt_sq) 553a634bfcSVikram Hegde #define IRTE_LOW(dst, vector, dlm, tm, rh, dm, fpd, p) \ 563a634bfcSVikram Hegde (((uint64_t)(dst) << 32) | \ 573a634bfcSVikram Hegde ((uint64_t)(vector) << 16) | \ 583a634bfcSVikram Hegde ((uint64_t)(dlm) << 5) | \ 593a634bfcSVikram Hegde ((uint64_t)(tm) << 4) | \ 603a634bfcSVikram Hegde ((uint64_t)(rh) << 3) | \ 613a634bfcSVikram Hegde ((uint64_t)(dm) << 2) | \ 623a634bfcSVikram Hegde ((uint64_t)(fpd) << 1) | \ 633a634bfcSVikram Hegde (p)) 643a634bfcSVikram Hegde 653a634bfcSVikram Hegde typedef enum { 663a634bfcSVikram Hegde SVT_NO_VERIFY = 0, /* no verification */ 673a634bfcSVikram Hegde SVT_ALL_VERIFY, /* using sid and sq to verify */ 683a634bfcSVikram Hegde SVT_BUS_VERIFY, /* verify #startbus and #endbus */ 693a634bfcSVikram Hegde SVT_RSVD 703a634bfcSVikram Hegde } intrmap_svt_t; 713a634bfcSVikram Hegde 723a634bfcSVikram Hegde typedef enum { 733a634bfcSVikram Hegde SQ_VERIFY_ALL = 0, /* verify all 16 bits */ 743a634bfcSVikram Hegde SQ_VERIFY_IGR_1, /* ignore bit 3 */ 753a634bfcSVikram Hegde SQ_VERIFY_IGR_2, /* ignore bit 2-3 */ 763a634bfcSVikram Hegde SQ_VERIFY_IGR_3 /* ignore bit 1-3 */ 773a634bfcSVikram Hegde } intrmap_sq_t; 783a634bfcSVikram Hegde 793a634bfcSVikram Hegde /* 803a634bfcSVikram Hegde * S field of the Interrupt Remapping Table Address Register 813a634bfcSVikram Hegde * the size of the interrupt remapping table is 1 << (immu_intrmap_irta_s + 1) 823a634bfcSVikram Hegde */ 833a634bfcSVikram Hegde static uint_t intrmap_irta_s = INTRMAP_MAX_IRTA_SIZE; 843a634bfcSVikram Hegde 853a634bfcSVikram Hegde /* 863a634bfcSVikram Hegde * If true, arrange to suppress broadcast EOI by setting edge-triggered mode 873a634bfcSVikram Hegde * even for level-triggered interrupts in the interrupt-remapping engine. 883a634bfcSVikram Hegde * If false, broadcast EOI can still be suppressed if the CPU supports the 893a634bfcSVikram Hegde * APIC_SVR_SUPPRESS_BROADCAST_EOI bit. In both cases, the IOAPIC is still 903a634bfcSVikram Hegde * programmed with the correct trigger mode, and pcplusmp must send an EOI 913a634bfcSVikram Hegde * to the IOAPIC by writing to the IOAPIC's EOI register to make up for the 923a634bfcSVikram Hegde * missing broadcast EOI. 933a634bfcSVikram Hegde */ 943a634bfcSVikram Hegde static int intrmap_suppress_brdcst_eoi = 0; 953a634bfcSVikram Hegde 963a634bfcSVikram Hegde /* 973a634bfcSVikram Hegde * whether verify the source id of interrupt request 983a634bfcSVikram Hegde */ 993a634bfcSVikram Hegde static int intrmap_enable_sid_verify = 0; 1003a634bfcSVikram Hegde 1013a634bfcSVikram Hegde /* fault types for DVMA remapping */ 1023a634bfcSVikram Hegde static char *immu_dvma_faults[] = { 1033a634bfcSVikram Hegde "Reserved", 1043a634bfcSVikram Hegde "The present field in root-entry is Clear", 1053a634bfcSVikram Hegde "The present field in context-entry is Clear", 1063a634bfcSVikram Hegde "Hardware detected invalid programming of a context-entry", 1073a634bfcSVikram Hegde "The DMA request attempted to access an address beyond max support", 1083a634bfcSVikram Hegde "The Write field in a page-table entry is Clear when DMA write", 1093a634bfcSVikram Hegde "The Read field in a page-table entry is Clear when DMA read", 1103a634bfcSVikram Hegde "Access the next level page table resulted in error", 1113a634bfcSVikram Hegde "Access the root-entry table resulted in error", 1123a634bfcSVikram Hegde "Access the context-entry table resulted in error", 1133a634bfcSVikram Hegde "Reserved field not initialized to zero in a present root-entry", 1143a634bfcSVikram Hegde "Reserved field not initialized to zero in a present context-entry", 1153a634bfcSVikram Hegde "Reserved field not initialized to zero in a present page-table entry", 1163a634bfcSVikram Hegde "DMA blocked due to the Translation Type field in context-entry", 1173a634bfcSVikram Hegde "Incorrect fault event reason number", 1183a634bfcSVikram Hegde }; 1193a634bfcSVikram Hegde #define DVMA_MAX_FAULTS (sizeof (immu_dvma_faults)/(sizeof (char *))) - 1 1203a634bfcSVikram Hegde 1213a634bfcSVikram Hegde /* fault types for interrupt remapping */ 1223a634bfcSVikram Hegde static char *immu_intrmap_faults[] = { 1233a634bfcSVikram Hegde "reserved field set in IRTE", 1243a634bfcSVikram Hegde "interrupt_index exceed the intr-remap table size", 1253a634bfcSVikram Hegde "present field in IRTE is clear", 1263a634bfcSVikram Hegde "hardware access intr-remap table address resulted in error", 1273a634bfcSVikram Hegde "reserved field set in IRTE, include various conditional", 1283a634bfcSVikram Hegde "hardware blocked an interrupt request in Compatibility format", 1293a634bfcSVikram Hegde "remappable interrupt request blocked due to verification failure" 1303a634bfcSVikram Hegde }; 1313a634bfcSVikram Hegde #define INTRMAP_MAX_FAULTS \ 1323a634bfcSVikram Hegde (sizeof (immu_intrmap_faults) / (sizeof (char *))) - 1 1333a634bfcSVikram Hegde 1343a634bfcSVikram Hegde /* Function prototypes */ 1353a634bfcSVikram Hegde static int immu_intrmap_init(int apic_mode); 1363a634bfcSVikram Hegde static void immu_intrmap_switchon(int suppress_brdcst_eoi); 1377ff178cdSJimmy Vetayases static void immu_intrmap_alloc(void **intrmap_private_tbl, dev_info_t *dip, 1387ff178cdSJimmy Vetayases uint16_t type, int count, uchar_t ioapic_index); 1397ff178cdSJimmy Vetayases static void immu_intrmap_map(void *intrmap_private, void *intrmap_data, 1407ff178cdSJimmy Vetayases uint16_t type, int count); 1417ff178cdSJimmy Vetayases static void immu_intrmap_free(void **intrmap_privatep); 1427ff178cdSJimmy Vetayases static void immu_intrmap_rdt(void *intrmap_private, ioapic_rdt_t *irdt); 1437ff178cdSJimmy Vetayases static void immu_intrmap_msi(void *intrmap_private, msi_regs_t *mregs); 1443a634bfcSVikram Hegde 1453a634bfcSVikram Hegde static struct apic_intrmap_ops intrmap_ops = { 1463a634bfcSVikram Hegde immu_intrmap_init, 1473a634bfcSVikram Hegde immu_intrmap_switchon, 1483a634bfcSVikram Hegde immu_intrmap_alloc, 1493a634bfcSVikram Hegde immu_intrmap_map, 1503a634bfcSVikram Hegde immu_intrmap_free, 1513a634bfcSVikram Hegde immu_intrmap_rdt, 1523a634bfcSVikram Hegde immu_intrmap_msi, 1533a634bfcSVikram Hegde }; 1543a634bfcSVikram Hegde 1553a634bfcSVikram Hegde /* apic mode, APIC/X2APIC */ 1563a634bfcSVikram Hegde static int intrmap_apic_mode = LOCAL_APIC; 1573a634bfcSVikram Hegde 1583a634bfcSVikram Hegde 1593a634bfcSVikram Hegde /* 1603a634bfcSVikram Hegde * helper functions 1613a634bfcSVikram Hegde */ 1623a634bfcSVikram Hegde static uint_t 1633a634bfcSVikram Hegde bitset_find_free(bitset_t *b, uint_t post) 1643a634bfcSVikram Hegde { 1653a634bfcSVikram Hegde uint_t i; 1663a634bfcSVikram Hegde uint_t cap = bitset_capacity(b); 1673a634bfcSVikram Hegde 1683a634bfcSVikram Hegde if (post == cap) 1693a634bfcSVikram Hegde post = 0; 1703a634bfcSVikram Hegde 1713a634bfcSVikram Hegde ASSERT(post < cap); 1723a634bfcSVikram Hegde 1733a634bfcSVikram Hegde for (i = post; i < cap; i++) { 1743a634bfcSVikram Hegde if (!bitset_in_set(b, i)) 1753a634bfcSVikram Hegde return (i); 1763a634bfcSVikram Hegde } 1773a634bfcSVikram Hegde 1783a634bfcSVikram Hegde for (i = 0; i < post; i++) { 1793a634bfcSVikram Hegde if (!bitset_in_set(b, i)) 1803a634bfcSVikram Hegde return (i); 1813a634bfcSVikram Hegde } 1823a634bfcSVikram Hegde 1833a634bfcSVikram Hegde return (INTRMAP_IDX_FULL); /* no free index */ 1843a634bfcSVikram Hegde } 1853a634bfcSVikram Hegde 1863a634bfcSVikram Hegde /* 1873a634bfcSVikram Hegde * helper function to find 'count' contigous free 1883a634bfcSVikram Hegde * interrupt remapping table entries 1893a634bfcSVikram Hegde */ 1903a634bfcSVikram Hegde static uint_t 1913a634bfcSVikram Hegde bitset_find_multi_free(bitset_t *b, uint_t post, uint_t count) 1923a634bfcSVikram Hegde { 1933a634bfcSVikram Hegde uint_t i, j; 1943a634bfcSVikram Hegde uint_t cap = bitset_capacity(b); 1953a634bfcSVikram Hegde 1963a634bfcSVikram Hegde if (post == INTRMAP_IDX_FULL) { 1973a634bfcSVikram Hegde return (INTRMAP_IDX_FULL); 1983a634bfcSVikram Hegde } 1993a634bfcSVikram Hegde 2003a634bfcSVikram Hegde if (count > cap) 2013a634bfcSVikram Hegde return (INTRMAP_IDX_FULL); 2023a634bfcSVikram Hegde 2033a634bfcSVikram Hegde ASSERT(post < cap); 2043a634bfcSVikram Hegde 2053a634bfcSVikram Hegde for (i = post; (i + count) <= cap; i++) { 2063a634bfcSVikram Hegde for (j = 0; j < count; j++) { 2073a634bfcSVikram Hegde if (bitset_in_set(b, (i + j))) { 2083a634bfcSVikram Hegde i = i + j; 2093a634bfcSVikram Hegde break; 2103a634bfcSVikram Hegde } 2113a634bfcSVikram Hegde if (j == count - 1) 2123a634bfcSVikram Hegde return (i); 2133a634bfcSVikram Hegde } 2143a634bfcSVikram Hegde } 2153a634bfcSVikram Hegde 2163a634bfcSVikram Hegde for (i = 0; (i < post) && ((i + count) <= cap); i++) { 2173a634bfcSVikram Hegde for (j = 0; j < count; j++) { 2183a634bfcSVikram Hegde if (bitset_in_set(b, (i + j))) { 2193a634bfcSVikram Hegde i = i + j; 2203a634bfcSVikram Hegde break; 2213a634bfcSVikram Hegde } 2223a634bfcSVikram Hegde if (j == count - 1) 2233a634bfcSVikram Hegde return (i); 2243a634bfcSVikram Hegde } 2253a634bfcSVikram Hegde } 2263a634bfcSVikram Hegde 2273a634bfcSVikram Hegde return (INTRMAP_IDX_FULL); /* no free index */ 2283a634bfcSVikram Hegde } 2293a634bfcSVikram Hegde 2303a634bfcSVikram Hegde /* alloc one interrupt remapping table entry */ 2313a634bfcSVikram Hegde static int 2323a634bfcSVikram Hegde alloc_tbl_entry(intrmap_t *intrmap) 2333a634bfcSVikram Hegde { 2343a634bfcSVikram Hegde uint32_t idx; 2353a634bfcSVikram Hegde 2363a634bfcSVikram Hegde for (;;) { 2373a634bfcSVikram Hegde mutex_enter(&intrmap->intrmap_lock); 2383a634bfcSVikram Hegde idx = intrmap->intrmap_free; 2393a634bfcSVikram Hegde if (idx != INTRMAP_IDX_FULL) { 2403a634bfcSVikram Hegde bitset_add(&intrmap->intrmap_map, idx); 2413a634bfcSVikram Hegde intrmap->intrmap_free = 2423a634bfcSVikram Hegde bitset_find_free(&intrmap->intrmap_map, idx + 1); 2433a634bfcSVikram Hegde mutex_exit(&intrmap->intrmap_lock); 2443a634bfcSVikram Hegde break; 2453a634bfcSVikram Hegde } 2463a634bfcSVikram Hegde 2473a634bfcSVikram Hegde /* no free intr entry, use compatible format intr */ 2483a634bfcSVikram Hegde mutex_exit(&intrmap->intrmap_lock); 2493a634bfcSVikram Hegde 2503a634bfcSVikram Hegde if (intrmap_apic_mode != LOCAL_X2APIC) { 2513a634bfcSVikram Hegde break; 2523a634bfcSVikram Hegde } 2533a634bfcSVikram Hegde 2543a634bfcSVikram Hegde /* 2553a634bfcSVikram Hegde * x2apic mode not allowed compatible 2563a634bfcSVikram Hegde * interrupt 2573a634bfcSVikram Hegde */ 2583a634bfcSVikram Hegde delay(IMMU_ALLOC_RESOURCE_DELAY); 2593a634bfcSVikram Hegde } 2603a634bfcSVikram Hegde 2613a634bfcSVikram Hegde return (idx); 2623a634bfcSVikram Hegde } 2633a634bfcSVikram Hegde 2643a634bfcSVikram Hegde /* alloc 'cnt' contigous interrupt remapping table entries */ 2653a634bfcSVikram Hegde static int 2663a634bfcSVikram Hegde alloc_tbl_multi_entries(intrmap_t *intrmap, uint_t cnt) 2673a634bfcSVikram Hegde { 2683a634bfcSVikram Hegde uint_t idx, pos, i; 2693a634bfcSVikram Hegde 2703a634bfcSVikram Hegde for (; ; ) { 2713a634bfcSVikram Hegde mutex_enter(&intrmap->intrmap_lock); 2723a634bfcSVikram Hegde pos = intrmap->intrmap_free; 2733a634bfcSVikram Hegde idx = bitset_find_multi_free(&intrmap->intrmap_map, pos, cnt); 2743a634bfcSVikram Hegde 2753a634bfcSVikram Hegde if (idx != INTRMAP_IDX_FULL) { 2763a634bfcSVikram Hegde if (idx <= pos && pos < (idx + cnt)) { 2773a634bfcSVikram Hegde intrmap->intrmap_free = bitset_find_free( 2783a634bfcSVikram Hegde &intrmap->intrmap_map, idx + cnt); 2793a634bfcSVikram Hegde } 2803a634bfcSVikram Hegde for (i = 0; i < cnt; i++) { 2813a634bfcSVikram Hegde bitset_add(&intrmap->intrmap_map, idx + i); 2823a634bfcSVikram Hegde } 2833a634bfcSVikram Hegde mutex_exit(&intrmap->intrmap_lock); 284d2256d26SFrank Van Der Linden break; 2853a634bfcSVikram Hegde } 2863a634bfcSVikram Hegde 2873a634bfcSVikram Hegde mutex_exit(&intrmap->intrmap_lock); 2883a634bfcSVikram Hegde 2893a634bfcSVikram Hegde if (intrmap_apic_mode != LOCAL_X2APIC) { 2903a634bfcSVikram Hegde break; 2913a634bfcSVikram Hegde } 2923a634bfcSVikram Hegde 2933a634bfcSVikram Hegde /* x2apic mode not allowed comapitible interrupt */ 2943a634bfcSVikram Hegde delay(IMMU_ALLOC_RESOURCE_DELAY); 2953a634bfcSVikram Hegde } 2963a634bfcSVikram Hegde 2973a634bfcSVikram Hegde return (idx); 2983a634bfcSVikram Hegde } 2993a634bfcSVikram Hegde 3003a634bfcSVikram Hegde /* init interrupt remapping table */ 3013a634bfcSVikram Hegde static int 3023a634bfcSVikram Hegde init_unit(immu_t *immu) 3033a634bfcSVikram Hegde { 3043a634bfcSVikram Hegde intrmap_t *intrmap; 3053a634bfcSVikram Hegde size_t size; 3063a634bfcSVikram Hegde 3073a634bfcSVikram Hegde ddi_dma_attr_t intrmap_dma_attr = { 3083a634bfcSVikram Hegde DMA_ATTR_V0, 3093a634bfcSVikram Hegde 0U, 310d2256d26SFrank Van Der Linden 0xffffffffffffffffULL, 3113a634bfcSVikram Hegde 0xffffffffU, 3123a634bfcSVikram Hegde MMU_PAGESIZE, /* page aligned */ 3133a634bfcSVikram Hegde 0x1, 3143a634bfcSVikram Hegde 0x1, 3153a634bfcSVikram Hegde 0xffffffffU, 316d2256d26SFrank Van Der Linden 0xffffffffffffffffULL, 3173a634bfcSVikram Hegde 1, 3183a634bfcSVikram Hegde 4, 3193a634bfcSVikram Hegde 0 3203a634bfcSVikram Hegde }; 3213a634bfcSVikram Hegde 3223a634bfcSVikram Hegde ddi_device_acc_attr_t intrmap_acc_attr = { 3233a634bfcSVikram Hegde DDI_DEVICE_ATTR_V0, 3243a634bfcSVikram Hegde DDI_NEVERSWAP_ACC, 3253a634bfcSVikram Hegde DDI_STRICTORDER_ACC 3263a634bfcSVikram Hegde }; 3273a634bfcSVikram Hegde 328c94adbf9SFrank Van Der Linden /* 329c94adbf9SFrank Van Der Linden * Using interrupt remapping implies using the queue 330c94adbf9SFrank Van Der Linden * invalidation interface. According to Intel, 331c94adbf9SFrank Van Der Linden * hardware that supports interrupt remapping should 332c94adbf9SFrank Van Der Linden * also support QI. 333c94adbf9SFrank Van Der Linden */ 334c94adbf9SFrank Van Der Linden ASSERT(IMMU_ECAP_GET_QI(immu->immu_regs_excap)); 335c94adbf9SFrank Van Der Linden 3363a634bfcSVikram Hegde if (intrmap_apic_mode == LOCAL_X2APIC) { 3373a634bfcSVikram Hegde if (!IMMU_ECAP_GET_EIM(immu->immu_regs_excap)) { 3383a634bfcSVikram Hegde return (DDI_FAILURE); 3393a634bfcSVikram Hegde } 3403a634bfcSVikram Hegde } 3413a634bfcSVikram Hegde 3423a634bfcSVikram Hegde if (intrmap_irta_s > INTRMAP_MAX_IRTA_SIZE) { 3433a634bfcSVikram Hegde intrmap_irta_s = INTRMAP_MAX_IRTA_SIZE; 3443a634bfcSVikram Hegde } 3453a634bfcSVikram Hegde 3463a634bfcSVikram Hegde intrmap = kmem_zalloc(sizeof (intrmap_t), KM_SLEEP); 3473a634bfcSVikram Hegde 3483a634bfcSVikram Hegde if (ddi_dma_alloc_handle(immu->immu_dip, 3493a634bfcSVikram Hegde &intrmap_dma_attr, 3503a634bfcSVikram Hegde DDI_DMA_SLEEP, 3513a634bfcSVikram Hegde NULL, 3523a634bfcSVikram Hegde &(intrmap->intrmap_dma_hdl)) != DDI_SUCCESS) { 3533a634bfcSVikram Hegde kmem_free(intrmap, sizeof (intrmap_t)); 3543a634bfcSVikram Hegde return (DDI_FAILURE); 3553a634bfcSVikram Hegde } 3563a634bfcSVikram Hegde 3573a634bfcSVikram Hegde intrmap->intrmap_size = 1 << (intrmap_irta_s + 1); 3583a634bfcSVikram Hegde size = intrmap->intrmap_size * INTRMAP_RTE_SIZE; 3593a634bfcSVikram Hegde if (ddi_dma_mem_alloc(intrmap->intrmap_dma_hdl, 3603a634bfcSVikram Hegde size, 3613a634bfcSVikram Hegde &intrmap_acc_attr, 3623a634bfcSVikram Hegde DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED, 3633a634bfcSVikram Hegde DDI_DMA_SLEEP, 3643a634bfcSVikram Hegde NULL, 3653a634bfcSVikram Hegde &(intrmap->intrmap_vaddr), 3663a634bfcSVikram Hegde &size, 3673a634bfcSVikram Hegde &(intrmap->intrmap_acc_hdl)) != DDI_SUCCESS) { 3683a634bfcSVikram Hegde ddi_dma_free_handle(&(intrmap->intrmap_dma_hdl)); 3693a634bfcSVikram Hegde kmem_free(intrmap, sizeof (intrmap_t)); 3703a634bfcSVikram Hegde return (DDI_FAILURE); 3713a634bfcSVikram Hegde } 3723a634bfcSVikram Hegde 3733a634bfcSVikram Hegde ASSERT(!((uintptr_t)intrmap->intrmap_vaddr & MMU_PAGEOFFSET)); 3743a634bfcSVikram Hegde bzero(intrmap->intrmap_vaddr, size); 3753a634bfcSVikram Hegde intrmap->intrmap_paddr = pfn_to_pa( 3763a634bfcSVikram Hegde hat_getpfnum(kas.a_hat, intrmap->intrmap_vaddr)); 3773a634bfcSVikram Hegde 3783a634bfcSVikram Hegde mutex_init(&(intrmap->intrmap_lock), NULL, MUTEX_DRIVER, NULL); 3793a634bfcSVikram Hegde bitset_init(&intrmap->intrmap_map); 3803a634bfcSVikram Hegde bitset_resize(&intrmap->intrmap_map, intrmap->intrmap_size); 3813a634bfcSVikram Hegde intrmap->intrmap_free = 0; 3823a634bfcSVikram Hegde 3833a634bfcSVikram Hegde immu->immu_intrmap = intrmap; 3843a634bfcSVikram Hegde 3853a634bfcSVikram Hegde return (DDI_SUCCESS); 3863a634bfcSVikram Hegde } 3873a634bfcSVikram Hegde 3887ff178cdSJimmy Vetayases static immu_t * 3897ff178cdSJimmy Vetayases get_immu(dev_info_t *dip, uint16_t type, uchar_t ioapic_index) 3903a634bfcSVikram Hegde { 3913a634bfcSVikram Hegde immu_t *immu = NULL; 3923a634bfcSVikram Hegde 3937ff178cdSJimmy Vetayases if (!DDI_INTR_IS_MSI_OR_MSIX(type)) { 3947ff178cdSJimmy Vetayases immu = immu_dmar_ioapic_immu(ioapic_index); 3953a634bfcSVikram Hegde } else { 3967ff178cdSJimmy Vetayases if (dip != NULL) 3977ff178cdSJimmy Vetayases immu = immu_dmar_get_immu(dip); 3983a634bfcSVikram Hegde } 3993a634bfcSVikram Hegde 4007ff178cdSJimmy Vetayases return (immu); 4013a634bfcSVikram Hegde } 4023a634bfcSVikram Hegde 4033a634bfcSVikram Hegde static int 4043a634bfcSVikram Hegde get_top_pcibridge(dev_info_t *dip, void *arg) 4053a634bfcSVikram Hegde { 4063a634bfcSVikram Hegde dev_info_t **topdipp = arg; 4073a634bfcSVikram Hegde immu_devi_t *immu_devi; 4083a634bfcSVikram Hegde 4093a634bfcSVikram Hegde mutex_enter(&(DEVI(dip)->devi_lock)); 4103a634bfcSVikram Hegde immu_devi = DEVI(dip)->devi_iommu; 4113a634bfcSVikram Hegde mutex_exit(&(DEVI(dip)->devi_lock)); 4123a634bfcSVikram Hegde 4133a634bfcSVikram Hegde if (immu_devi == NULL || immu_devi->imd_pcib_type == IMMU_PCIB_BAD || 4143a634bfcSVikram Hegde immu_devi->imd_pcib_type == IMMU_PCIB_ENDPOINT) { 4153a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 4163a634bfcSVikram Hegde } 4173a634bfcSVikram Hegde 4183a634bfcSVikram Hegde *topdipp = dip; 4193a634bfcSVikram Hegde 4203a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 4213a634bfcSVikram Hegde } 4223a634bfcSVikram Hegde 4233a634bfcSVikram Hegde static dev_info_t * 4243a634bfcSVikram Hegde intrmap_top_pcibridge(dev_info_t *rdip) 4253a634bfcSVikram Hegde { 4263a634bfcSVikram Hegde dev_info_t *top_pcibridge = NULL; 4273a634bfcSVikram Hegde 4283a634bfcSVikram Hegde if (immu_walk_ancestor(rdip, NULL, get_top_pcibridge, 4293a634bfcSVikram Hegde &top_pcibridge, NULL, 0) != DDI_SUCCESS) { 4303a634bfcSVikram Hegde return (NULL); 4313a634bfcSVikram Hegde } 4323a634bfcSVikram Hegde 4333a634bfcSVikram Hegde return (top_pcibridge); 4343a634bfcSVikram Hegde } 4353a634bfcSVikram Hegde 4363a634bfcSVikram Hegde /* function to get interrupt request source id */ 4377ff178cdSJimmy Vetayases static uint32_t 4387ff178cdSJimmy Vetayases get_sid(dev_info_t *dip, uint16_t type, uchar_t ioapic_index) 4393a634bfcSVikram Hegde { 4407ff178cdSJimmy Vetayases dev_info_t *pdip; 4413a634bfcSVikram Hegde immu_devi_t *immu_devi; 4423a634bfcSVikram Hegde uint16_t sid; 4433a634bfcSVikram Hegde uchar_t svt, sq; 4443a634bfcSVikram Hegde 4453a634bfcSVikram Hegde if (!intrmap_enable_sid_verify) { 4467ff178cdSJimmy Vetayases return (0); 4473a634bfcSVikram Hegde } 4483a634bfcSVikram Hegde 4497ff178cdSJimmy Vetayases if (!DDI_INTR_IS_MSI_OR_MSIX(type)) { 4503a634bfcSVikram Hegde /* for interrupt through I/O APIC */ 4517ff178cdSJimmy Vetayases sid = immu_dmar_ioapic_sid(ioapic_index); 4523a634bfcSVikram Hegde svt = SVT_ALL_VERIFY; 4533a634bfcSVikram Hegde sq = SQ_VERIFY_ALL; 4543a634bfcSVikram Hegde } else { 4553a634bfcSVikram Hegde /* MSI/MSI-X interrupt */ 4563a634bfcSVikram Hegde ASSERT(dip); 4573a634bfcSVikram Hegde pdip = intrmap_top_pcibridge(dip); 4583a634bfcSVikram Hegde ASSERT(pdip); 4593a634bfcSVikram Hegde immu_devi = DEVI(pdip)->devi_iommu; 4603a634bfcSVikram Hegde ASSERT(immu_devi); 4613a634bfcSVikram Hegde if (immu_devi->imd_pcib_type == IMMU_PCIB_PCIE_PCI) { 4623a634bfcSVikram Hegde /* device behind pcie to pci bridge */ 4633a634bfcSVikram Hegde sid = (immu_devi->imd_bus << 8) | immu_devi->imd_sec; 4643a634bfcSVikram Hegde svt = SVT_BUS_VERIFY; 4653a634bfcSVikram Hegde sq = SQ_VERIFY_ALL; 4663a634bfcSVikram Hegde } else { 4673a634bfcSVikram Hegde /* pcie device or device behind pci to pci bridge */ 4683a634bfcSVikram Hegde sid = (immu_devi->imd_bus << 8) | 4693a634bfcSVikram Hegde immu_devi->imd_devfunc; 4703a634bfcSVikram Hegde svt = SVT_ALL_VERIFY; 4713a634bfcSVikram Hegde sq = SQ_VERIFY_ALL; 4723a634bfcSVikram Hegde } 4733a634bfcSVikram Hegde } 4743a634bfcSVikram Hegde 4757ff178cdSJimmy Vetayases return (sid | (svt << 18) | (sq << 16)); 4763a634bfcSVikram Hegde } 4773a634bfcSVikram Hegde 4783a634bfcSVikram Hegde static void 4793a634bfcSVikram Hegde intrmap_enable(immu_t *immu) 4803a634bfcSVikram Hegde { 4813a634bfcSVikram Hegde intrmap_t *intrmap; 4823a634bfcSVikram Hegde uint64_t irta_reg; 4833a634bfcSVikram Hegde 4843a634bfcSVikram Hegde intrmap = immu->immu_intrmap; 4853a634bfcSVikram Hegde 4863a634bfcSVikram Hegde irta_reg = intrmap->intrmap_paddr | intrmap_irta_s; 4873a634bfcSVikram Hegde if (intrmap_apic_mode == LOCAL_X2APIC) { 4883a634bfcSVikram Hegde irta_reg |= (0x1 << 11); 4893a634bfcSVikram Hegde } 4903a634bfcSVikram Hegde 4913a634bfcSVikram Hegde immu_regs_intrmap_enable(immu, irta_reg); 4923a634bfcSVikram Hegde } 4933a634bfcSVikram Hegde 4943a634bfcSVikram Hegde /* ####################################################################### */ 4953a634bfcSVikram Hegde 4963a634bfcSVikram Hegde /* 4973a634bfcSVikram Hegde * immu_intr_handler() 4983a634bfcSVikram Hegde * the fault event handler for a single immu unit 4993a634bfcSVikram Hegde */ 5003a634bfcSVikram Hegde int 5013a634bfcSVikram Hegde immu_intr_handler(immu_t *immu) 5023a634bfcSVikram Hegde { 5033a634bfcSVikram Hegde uint32_t status; 5043a634bfcSVikram Hegde int index, fault_reg_offset; 5053a634bfcSVikram Hegde int max_fault_index; 5063a634bfcSVikram Hegde boolean_t found_fault; 5073a634bfcSVikram Hegde dev_info_t *idip; 5083a634bfcSVikram Hegde 5093a634bfcSVikram Hegde mutex_enter(&(immu->immu_intr_lock)); 5103a634bfcSVikram Hegde mutex_enter(&(immu->immu_regs_lock)); 5113a634bfcSVikram Hegde 5123a634bfcSVikram Hegde /* read the fault status */ 5133a634bfcSVikram Hegde status = immu_regs_get32(immu, IMMU_REG_FAULT_STS); 5143a634bfcSVikram Hegde 5153a634bfcSVikram Hegde idip = immu->immu_dip; 5163a634bfcSVikram Hegde ASSERT(idip); 5173a634bfcSVikram Hegde 5183a634bfcSVikram Hegde /* check if we have a pending fault for this immu unit */ 5193a634bfcSVikram Hegde if ((status & IMMU_FAULT_STS_PPF) == 0) { 5203a634bfcSVikram Hegde mutex_exit(&(immu->immu_regs_lock)); 5213a634bfcSVikram Hegde mutex_exit(&(immu->immu_intr_lock)); 5223a634bfcSVikram Hegde return (DDI_INTR_UNCLAIMED); 5233a634bfcSVikram Hegde } 5243a634bfcSVikram Hegde 5253a634bfcSVikram Hegde /* 5263a634bfcSVikram Hegde * handle all primary pending faults 5273a634bfcSVikram Hegde */ 5283a634bfcSVikram Hegde index = IMMU_FAULT_GET_INDEX(status); 5293a634bfcSVikram Hegde max_fault_index = IMMU_CAP_GET_NFR(immu->immu_regs_cap) - 1; 5303a634bfcSVikram Hegde fault_reg_offset = IMMU_CAP_GET_FRO(immu->immu_regs_cap); 5313a634bfcSVikram Hegde 5323a634bfcSVikram Hegde found_fault = B_FALSE; 5333a634bfcSVikram Hegde _NOTE(CONSTCOND) 5343a634bfcSVikram Hegde while (1) { 5353a634bfcSVikram Hegde uint64_t val; 5363a634bfcSVikram Hegde uint8_t fault_reason; 5373a634bfcSVikram Hegde uint8_t fault_type; 5383a634bfcSVikram Hegde uint16_t sid; 5393a634bfcSVikram Hegde uint64_t pg_addr; 5403a634bfcSVikram Hegde uint64_t idx; 5413a634bfcSVikram Hegde 5423a634bfcSVikram Hegde /* read the higher 64bits */ 5433a634bfcSVikram Hegde val = immu_regs_get64(immu, fault_reg_offset + index * 16 + 8); 5443a634bfcSVikram Hegde 5453a634bfcSVikram Hegde /* check if this fault register has pending fault */ 5463a634bfcSVikram Hegde if (!IMMU_FRR_GET_F(val)) { 5473a634bfcSVikram Hegde break; 5483a634bfcSVikram Hegde } 5493a634bfcSVikram Hegde 5503a634bfcSVikram Hegde found_fault = B_TRUE; 5513a634bfcSVikram Hegde 5523a634bfcSVikram Hegde /* get the fault reason, fault type and sid */ 5533a634bfcSVikram Hegde fault_reason = IMMU_FRR_GET_FR(val); 5543a634bfcSVikram Hegde fault_type = IMMU_FRR_GET_FT(val); 5553a634bfcSVikram Hegde sid = IMMU_FRR_GET_SID(val); 5563a634bfcSVikram Hegde 5573a634bfcSVikram Hegde /* read the first 64bits */ 5583a634bfcSVikram Hegde val = immu_regs_get64(immu, fault_reg_offset + index * 16); 5593a634bfcSVikram Hegde pg_addr = val & IMMU_PAGEMASK; 5603a634bfcSVikram Hegde idx = val >> 48; 5613a634bfcSVikram Hegde 5623a634bfcSVikram Hegde /* clear the fault */ 5633a634bfcSVikram Hegde immu_regs_put32(immu, fault_reg_offset + index * 16 + 12, 5643a634bfcSVikram Hegde (((uint32_t)1) << 31)); 5653a634bfcSVikram Hegde 5663a634bfcSVikram Hegde /* report the fault info */ 5673a634bfcSVikram Hegde if (fault_reason < 0x20) { 5683a634bfcSVikram Hegde /* immu-remapping fault */ 5693a634bfcSVikram Hegde ddi_err(DER_WARN, idip, 5703a634bfcSVikram Hegde "generated a fault event when translating DMA %s\n" 5713a634bfcSVikram Hegde "\t on address 0x%" PRIx64 " for PCI(%d, %d, %d), " 5723a634bfcSVikram Hegde "the reason is:\n\t %s", 5733a634bfcSVikram Hegde fault_type ? "read" : "write", pg_addr, 5743a634bfcSVikram Hegde (sid >> 8) & 0xff, (sid >> 3) & 0x1f, sid & 0x7, 5753a634bfcSVikram Hegde immu_dvma_faults[MIN(fault_reason, 5763a634bfcSVikram Hegde DVMA_MAX_FAULTS)]); 577*50200e77SFrank Van Der Linden immu_print_fault_info(sid, pg_addr); 5783a634bfcSVikram Hegde } else if (fault_reason < 0x27) { 5793a634bfcSVikram Hegde /* intr-remapping fault */ 5803a634bfcSVikram Hegde ddi_err(DER_WARN, idip, 5813a634bfcSVikram Hegde "generated a fault event when translating " 5823a634bfcSVikram Hegde "interrupt request\n" 5833a634bfcSVikram Hegde "\t on index 0x%" PRIx64 " for PCI(%d, %d, %d), " 5843a634bfcSVikram Hegde "the reason is:\n\t %s", 5853a634bfcSVikram Hegde idx, 5863a634bfcSVikram Hegde (sid >> 8) & 0xff, (sid >> 3) & 0x1f, sid & 0x7, 5873a634bfcSVikram Hegde immu_intrmap_faults[MIN((fault_reason - 0x20), 5883a634bfcSVikram Hegde INTRMAP_MAX_FAULTS)]); 5893a634bfcSVikram Hegde } else { 5903a634bfcSVikram Hegde ddi_err(DER_WARN, idip, "Unknown fault reason: 0x%x", 5913a634bfcSVikram Hegde fault_reason); 5923a634bfcSVikram Hegde } 5933a634bfcSVikram Hegde 5943a634bfcSVikram Hegde index++; 5953a634bfcSVikram Hegde if (index > max_fault_index) 5963a634bfcSVikram Hegde index = 0; 5973a634bfcSVikram Hegde } 5983a634bfcSVikram Hegde 5993a634bfcSVikram Hegde /* Clear the fault */ 6003a634bfcSVikram Hegde if (!found_fault) { 6013a634bfcSVikram Hegde ddi_err(DER_MODE, idip, 6023a634bfcSVikram Hegde "Fault register set but no fault present"); 6033a634bfcSVikram Hegde } 6043a634bfcSVikram Hegde immu_regs_put32(immu, IMMU_REG_FAULT_STS, 1); 6053a634bfcSVikram Hegde mutex_exit(&(immu->immu_regs_lock)); 6063a634bfcSVikram Hegde mutex_exit(&(immu->immu_intr_lock)); 6073a634bfcSVikram Hegde return (DDI_INTR_CLAIMED); 6083a634bfcSVikram Hegde } 6093a634bfcSVikram Hegde /* ######################################################################### */ 6103a634bfcSVikram Hegde 6113a634bfcSVikram Hegde /* 6123a634bfcSVikram Hegde * Interrupt remap entry points 6133a634bfcSVikram Hegde */ 6143a634bfcSVikram Hegde 6153a634bfcSVikram Hegde /* initialize interrupt remapping */ 6163a634bfcSVikram Hegde static int 6173a634bfcSVikram Hegde immu_intrmap_init(int apic_mode) 6183a634bfcSVikram Hegde { 6193a634bfcSVikram Hegde immu_t *immu; 6203a634bfcSVikram Hegde int error = DDI_FAILURE; 6213a634bfcSVikram Hegde 6223a634bfcSVikram Hegde if (immu_intrmap_enable == B_FALSE) { 6233a634bfcSVikram Hegde return (DDI_SUCCESS); 6243a634bfcSVikram Hegde } 6253a634bfcSVikram Hegde 6263a634bfcSVikram Hegde intrmap_apic_mode = apic_mode; 6273a634bfcSVikram Hegde 6283a634bfcSVikram Hegde immu = list_head(&immu_list); 6293a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 6303a634bfcSVikram Hegde if ((immu->immu_intrmap_running == B_TRUE) && 6313a634bfcSVikram Hegde IMMU_ECAP_GET_IR(immu->immu_regs_excap)) { 6323a634bfcSVikram Hegde if (init_unit(immu) == DDI_SUCCESS) { 6333a634bfcSVikram Hegde error = DDI_SUCCESS; 6343a634bfcSVikram Hegde } 6353a634bfcSVikram Hegde } 6363a634bfcSVikram Hegde } 6373a634bfcSVikram Hegde 6383a634bfcSVikram Hegde /* 6393a634bfcSVikram Hegde * if all IOMMU units disable intr remapping, 6403a634bfcSVikram Hegde * return FAILURE 6413a634bfcSVikram Hegde */ 6423a634bfcSVikram Hegde return (error); 6433a634bfcSVikram Hegde } 6443a634bfcSVikram Hegde 6453a634bfcSVikram Hegde 6463a634bfcSVikram Hegde 6473a634bfcSVikram Hegde /* enable interrupt remapping */ 6483a634bfcSVikram Hegde static void 6493a634bfcSVikram Hegde immu_intrmap_switchon(int suppress_brdcst_eoi) 6503a634bfcSVikram Hegde { 6513a634bfcSVikram Hegde immu_t *immu; 6523a634bfcSVikram Hegde 6533a634bfcSVikram Hegde 6543a634bfcSVikram Hegde intrmap_suppress_brdcst_eoi = suppress_brdcst_eoi; 6553a634bfcSVikram Hegde 6563a634bfcSVikram Hegde immu = list_head(&immu_list); 6573a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 6583a634bfcSVikram Hegde if (immu->immu_intrmap_setup == B_TRUE) { 6593a634bfcSVikram Hegde intrmap_enable(immu); 6603a634bfcSVikram Hegde } 6613a634bfcSVikram Hegde } 6623a634bfcSVikram Hegde } 6633a634bfcSVikram Hegde 6643a634bfcSVikram Hegde /* alloc remapping entry for the interrupt */ 6653a634bfcSVikram Hegde static void 6667ff178cdSJimmy Vetayases immu_intrmap_alloc(void **intrmap_private_tbl, dev_info_t *dip, 6677ff178cdSJimmy Vetayases uint16_t type, int count, uchar_t ioapic_index) 6683a634bfcSVikram Hegde { 6693a634bfcSVikram Hegde immu_t *immu; 6703a634bfcSVikram Hegde intrmap_t *intrmap; 671*50200e77SFrank Van Der Linden immu_inv_wait_t *iwp; 6727ff178cdSJimmy Vetayases uint32_t idx, i; 6733a634bfcSVikram Hegde uint32_t sid_svt_sq; 6747ff178cdSJimmy Vetayases intrmap_private_t *intrmap_private; 6753a634bfcSVikram Hegde 6767ff178cdSJimmy Vetayases if (intrmap_private_tbl[0] == INTRMAP_DISABLE || 6777ff178cdSJimmy Vetayases intrmap_private_tbl[0] != NULL) { 6783a634bfcSVikram Hegde return; 6793a634bfcSVikram Hegde } 6803a634bfcSVikram Hegde 6817ff178cdSJimmy Vetayases intrmap_private_tbl[0] = 6823a634bfcSVikram Hegde kmem_zalloc(sizeof (intrmap_private_t), KM_SLEEP); 6837ff178cdSJimmy Vetayases intrmap_private = INTRMAP_PRIVATE(intrmap_private_tbl[0]); 6843a634bfcSVikram Hegde 6857ff178cdSJimmy Vetayases immu = get_immu(dip, type, ioapic_index); 6867ff178cdSJimmy Vetayases if ((immu != NULL) && (immu->immu_intrmap_running == B_TRUE)) { 6877ff178cdSJimmy Vetayases intrmap_private->ir_immu = immu; 6887ff178cdSJimmy Vetayases } else { 6893a634bfcSVikram Hegde goto intrmap_disable; 6903a634bfcSVikram Hegde } 6913a634bfcSVikram Hegde 6923a634bfcSVikram Hegde intrmap = immu->immu_intrmap; 6933a634bfcSVikram Hegde 6947ff178cdSJimmy Vetayases if (count == 1) { 6953a634bfcSVikram Hegde idx = alloc_tbl_entry(intrmap); 6963a634bfcSVikram Hegde } else { 6977ff178cdSJimmy Vetayases idx = alloc_tbl_multi_entries(intrmap, count); 6983a634bfcSVikram Hegde } 6993a634bfcSVikram Hegde 7003a634bfcSVikram Hegde if (idx == INTRMAP_IDX_FULL) { 7013a634bfcSVikram Hegde goto intrmap_disable; 7023a634bfcSVikram Hegde } 7033a634bfcSVikram Hegde 7047ff178cdSJimmy Vetayases intrmap_private->ir_idx = idx; 7053a634bfcSVikram Hegde 7067ff178cdSJimmy Vetayases sid_svt_sq = intrmap_private->ir_sid_svt_sq = 7077ff178cdSJimmy Vetayases get_sid(dip, type, ioapic_index); 708*50200e77SFrank Van Der Linden iwp = &intrmap_private->ir_inv_wait; 709*50200e77SFrank Van Der Linden immu_init_inv_wait(iwp, "intrmaplocal", B_TRUE); 7103a634bfcSVikram Hegde 7117ff178cdSJimmy Vetayases if (count == 1) { 7123a634bfcSVikram Hegde if (IMMU_CAP_GET_CM(immu->immu_regs_cap)) { 713*50200e77SFrank Van Der Linden immu_qinv_intr_one_cache(immu, idx, iwp); 7143a634bfcSVikram Hegde } else { 7153a634bfcSVikram Hegde immu_regs_wbf_flush(immu); 7163a634bfcSVikram Hegde } 7173a634bfcSVikram Hegde return; 7183a634bfcSVikram Hegde } 7193a634bfcSVikram Hegde 7207ff178cdSJimmy Vetayases for (i = 1; i < count; i++) { 7217ff178cdSJimmy Vetayases intrmap_private_tbl[i] = 7223a634bfcSVikram Hegde kmem_zalloc(sizeof (intrmap_private_t), KM_SLEEP); 7233a634bfcSVikram Hegde 7247ff178cdSJimmy Vetayases INTRMAP_PRIVATE(intrmap_private_tbl[i])->ir_immu = immu; 7257ff178cdSJimmy Vetayases INTRMAP_PRIVATE(intrmap_private_tbl[i])->ir_sid_svt_sq = 7267ff178cdSJimmy Vetayases sid_svt_sq; 7277ff178cdSJimmy Vetayases INTRMAP_PRIVATE(intrmap_private_tbl[i])->ir_idx = idx + i; 7283a634bfcSVikram Hegde } 7293a634bfcSVikram Hegde 7303a634bfcSVikram Hegde if (IMMU_CAP_GET_CM(immu->immu_regs_cap)) { 731*50200e77SFrank Van Der Linden immu_qinv_intr_caches(immu, idx, count, iwp); 7323a634bfcSVikram Hegde } else { 7333a634bfcSVikram Hegde immu_regs_wbf_flush(immu); 7343a634bfcSVikram Hegde } 7353a634bfcSVikram Hegde 7363a634bfcSVikram Hegde return; 7373a634bfcSVikram Hegde 7383a634bfcSVikram Hegde intrmap_disable: 7397ff178cdSJimmy Vetayases kmem_free(intrmap_private_tbl[0], sizeof (intrmap_private_t)); 7407ff178cdSJimmy Vetayases intrmap_private_tbl[0] = INTRMAP_DISABLE; 7413a634bfcSVikram Hegde } 7423a634bfcSVikram Hegde 7433a634bfcSVikram Hegde 7443a634bfcSVikram Hegde /* remapping the interrupt */ 7453a634bfcSVikram Hegde static void 7467ff178cdSJimmy Vetayases immu_intrmap_map(void *intrmap_private, void *intrmap_data, uint16_t type, 7477ff178cdSJimmy Vetayases int count) 7483a634bfcSVikram Hegde { 7493a634bfcSVikram Hegde immu_t *immu; 750*50200e77SFrank Van Der Linden immu_inv_wait_t *iwp; 7513a634bfcSVikram Hegde intrmap_t *intrmap; 7523a634bfcSVikram Hegde ioapic_rdt_t *irdt = (ioapic_rdt_t *)intrmap_data; 7533a634bfcSVikram Hegde msi_regs_t *mregs = (msi_regs_t *)intrmap_data; 7543a634bfcSVikram Hegde intrmap_rte_t irte; 7557ff178cdSJimmy Vetayases uint_t idx, i; 7563a634bfcSVikram Hegde uint32_t dst, sid_svt_sq; 7573a634bfcSVikram Hegde uchar_t vector, dlm, tm, rh, dm; 7583a634bfcSVikram Hegde 7597ff178cdSJimmy Vetayases if (intrmap_private == INTRMAP_DISABLE) 7603a634bfcSVikram Hegde return; 7613a634bfcSVikram Hegde 7627ff178cdSJimmy Vetayases idx = INTRMAP_PRIVATE(intrmap_private)->ir_idx; 7637ff178cdSJimmy Vetayases immu = INTRMAP_PRIVATE(intrmap_private)->ir_immu; 764*50200e77SFrank Van Der Linden iwp = &INTRMAP_PRIVATE(intrmap_private)->ir_inv_wait; 7653a634bfcSVikram Hegde intrmap = immu->immu_intrmap; 7667ff178cdSJimmy Vetayases sid_svt_sq = INTRMAP_PRIVATE(intrmap_private)->ir_sid_svt_sq; 7673a634bfcSVikram Hegde 7687ff178cdSJimmy Vetayases if (!DDI_INTR_IS_MSI_OR_MSIX(type)) { 7693a634bfcSVikram Hegde dm = RDT_DM(irdt->ir_lo); 7703a634bfcSVikram Hegde rh = 0; 7713a634bfcSVikram Hegde tm = RDT_TM(irdt->ir_lo); 7723a634bfcSVikram Hegde dlm = RDT_DLM(irdt->ir_lo); 7733a634bfcSVikram Hegde dst = irdt->ir_hi; 7743a634bfcSVikram Hegde 7753a634bfcSVikram Hegde /* 7763a634bfcSVikram Hegde * Mark the IRTE's TM as Edge to suppress broadcast EOI. 7773a634bfcSVikram Hegde */ 7783a634bfcSVikram Hegde if (intrmap_suppress_brdcst_eoi) { 7793a634bfcSVikram Hegde tm = TRIGGER_MODE_EDGE; 7803a634bfcSVikram Hegde } 7817ff178cdSJimmy Vetayases 7827ff178cdSJimmy Vetayases vector = RDT_VECTOR(irdt->ir_lo); 7833a634bfcSVikram Hegde } else { 7843a634bfcSVikram Hegde dm = MSI_ADDR_DM_PHYSICAL; 7853a634bfcSVikram Hegde rh = MSI_ADDR_RH_FIXED; 7863a634bfcSVikram Hegde tm = TRIGGER_MODE_EDGE; 7873a634bfcSVikram Hegde dlm = 0; 7883a634bfcSVikram Hegde dst = mregs->mr_addr; 7897ff178cdSJimmy Vetayases 7907ff178cdSJimmy Vetayases vector = mregs->mr_data & 0xff; 7913a634bfcSVikram Hegde } 7923a634bfcSVikram Hegde 7933a634bfcSVikram Hegde if (intrmap_apic_mode == LOCAL_APIC) 7943a634bfcSVikram Hegde dst = (dst & 0xFF) << 8; 7953a634bfcSVikram Hegde 7967ff178cdSJimmy Vetayases if (count == 1) { 7973a634bfcSVikram Hegde irte.lo = IRTE_LOW(dst, vector, dlm, tm, rh, dm, 0, 1); 7983a634bfcSVikram Hegde irte.hi = IRTE_HIGH(sid_svt_sq); 7993a634bfcSVikram Hegde 8003a634bfcSVikram Hegde /* set interrupt remapping table entry */ 8013a634bfcSVikram Hegde bcopy(&irte, intrmap->intrmap_vaddr + 8023a634bfcSVikram Hegde idx * INTRMAP_RTE_SIZE, 8033a634bfcSVikram Hegde INTRMAP_RTE_SIZE); 8043a634bfcSVikram Hegde 805*50200e77SFrank Van Der Linden immu_qinv_intr_one_cache(immu, idx, iwp); 8063a634bfcSVikram Hegde 8073a634bfcSVikram Hegde } else { 8087ff178cdSJimmy Vetayases for (i = 0; i < count; i++) { 8093a634bfcSVikram Hegde irte.lo = IRTE_LOW(dst, vector, dlm, tm, rh, dm, 0, 1); 8103a634bfcSVikram Hegde irte.hi = IRTE_HIGH(sid_svt_sq); 8113a634bfcSVikram Hegde 8123a634bfcSVikram Hegde /* set interrupt remapping table entry */ 8133a634bfcSVikram Hegde bcopy(&irte, intrmap->intrmap_vaddr + 8143a634bfcSVikram Hegde idx * INTRMAP_RTE_SIZE, 8153a634bfcSVikram Hegde INTRMAP_RTE_SIZE); 8163a634bfcSVikram Hegde vector++; 8173a634bfcSVikram Hegde idx++; 8183a634bfcSVikram Hegde } 8193a634bfcSVikram Hegde 820*50200e77SFrank Van Der Linden immu_qinv_intr_caches(immu, idx, count, iwp); 8213a634bfcSVikram Hegde } 8223a634bfcSVikram Hegde } 8233a634bfcSVikram Hegde 8243a634bfcSVikram Hegde /* free the remapping entry */ 8253a634bfcSVikram Hegde static void 8267ff178cdSJimmy Vetayases immu_intrmap_free(void **intrmap_privatep) 8273a634bfcSVikram Hegde { 8283a634bfcSVikram Hegde immu_t *immu; 829*50200e77SFrank Van Der Linden immu_inv_wait_t *iwp; 8303a634bfcSVikram Hegde intrmap_t *intrmap; 8313a634bfcSVikram Hegde uint32_t idx; 8323a634bfcSVikram Hegde 8337ff178cdSJimmy Vetayases if (*intrmap_privatep == INTRMAP_DISABLE || *intrmap_privatep == NULL) { 8347ff178cdSJimmy Vetayases *intrmap_privatep = NULL; 8353a634bfcSVikram Hegde return; 8363a634bfcSVikram Hegde } 8373a634bfcSVikram Hegde 8387ff178cdSJimmy Vetayases immu = INTRMAP_PRIVATE(*intrmap_privatep)->ir_immu; 839*50200e77SFrank Van Der Linden iwp = &INTRMAP_PRIVATE(*intrmap_privatep)->ir_inv_wait; 8403a634bfcSVikram Hegde intrmap = immu->immu_intrmap; 8417ff178cdSJimmy Vetayases idx = INTRMAP_PRIVATE(*intrmap_privatep)->ir_idx; 8423a634bfcSVikram Hegde 8433a634bfcSVikram Hegde bzero(intrmap->intrmap_vaddr + idx * INTRMAP_RTE_SIZE, 8443a634bfcSVikram Hegde INTRMAP_RTE_SIZE); 8453a634bfcSVikram Hegde 846*50200e77SFrank Van Der Linden immu_qinv_intr_one_cache(immu, idx, iwp); 8473a634bfcSVikram Hegde 8483a634bfcSVikram Hegde mutex_enter(&intrmap->intrmap_lock); 8493a634bfcSVikram Hegde bitset_del(&intrmap->intrmap_map, idx); 8503a634bfcSVikram Hegde if (intrmap->intrmap_free == INTRMAP_IDX_FULL) { 8513a634bfcSVikram Hegde intrmap->intrmap_free = idx; 8523a634bfcSVikram Hegde } 8533a634bfcSVikram Hegde mutex_exit(&intrmap->intrmap_lock); 8543a634bfcSVikram Hegde 8557ff178cdSJimmy Vetayases kmem_free(*intrmap_privatep, sizeof (intrmap_private_t)); 8567ff178cdSJimmy Vetayases *intrmap_privatep = NULL; 8573a634bfcSVikram Hegde } 8583a634bfcSVikram Hegde 8593a634bfcSVikram Hegde /* record the ioapic rdt entry */ 8603a634bfcSVikram Hegde static void 8617ff178cdSJimmy Vetayases immu_intrmap_rdt(void *intrmap_private, ioapic_rdt_t *irdt) 8623a634bfcSVikram Hegde { 8633a634bfcSVikram Hegde uint32_t rdt_entry, tm, pol, idx, vector; 8643a634bfcSVikram Hegde 8653a634bfcSVikram Hegde rdt_entry = irdt->ir_lo; 8663a634bfcSVikram Hegde 8677ff178cdSJimmy Vetayases if (intrmap_private != INTRMAP_DISABLE && intrmap_private != NULL) { 8687ff178cdSJimmy Vetayases idx = INTRMAP_PRIVATE(intrmap_private)->ir_idx; 8693a634bfcSVikram Hegde tm = RDT_TM(rdt_entry); 8703a634bfcSVikram Hegde pol = RDT_POL(rdt_entry); 8717ff178cdSJimmy Vetayases vector = RDT_VECTOR(rdt_entry); 8723a634bfcSVikram Hegde irdt->ir_lo = (tm << INTRMAP_IOAPIC_TM_SHIFT) | 8733a634bfcSVikram Hegde (pol << INTRMAP_IOAPIC_POL_SHIFT) | 8743a634bfcSVikram Hegde ((idx >> 15) << INTRMAP_IOAPIC_IDX15_SHIFT) | 8753a634bfcSVikram Hegde vector; 8763a634bfcSVikram Hegde irdt->ir_hi = (idx << INTRMAP_IOAPIC_IDX_SHIFT) | 8773a634bfcSVikram Hegde (1 << INTRMAP_IOAPIC_FORMAT_SHIFT); 8783a634bfcSVikram Hegde } else { 8793a634bfcSVikram Hegde irdt->ir_hi <<= APIC_ID_BIT_OFFSET; 8803a634bfcSVikram Hegde } 8813a634bfcSVikram Hegde } 8823a634bfcSVikram Hegde 8833a634bfcSVikram Hegde /* record the msi interrupt structure */ 8843a634bfcSVikram Hegde /*ARGSUSED*/ 8853a634bfcSVikram Hegde static void 8867ff178cdSJimmy Vetayases immu_intrmap_msi(void *intrmap_private, msi_regs_t *mregs) 8873a634bfcSVikram Hegde { 8883a634bfcSVikram Hegde uint_t idx; 8893a634bfcSVikram Hegde 8907ff178cdSJimmy Vetayases if (intrmap_private != INTRMAP_DISABLE && intrmap_private != NULL) { 8917ff178cdSJimmy Vetayases idx = INTRMAP_PRIVATE(intrmap_private)->ir_idx; 8923a634bfcSVikram Hegde 8933a634bfcSVikram Hegde mregs->mr_data = 0; 8943a634bfcSVikram Hegde mregs->mr_addr = MSI_ADDR_HDR | 8953a634bfcSVikram Hegde ((idx & 0x7fff) << INTRMAP_MSI_IDX_SHIFT) | 8963a634bfcSVikram Hegde (1 << INTRMAP_MSI_FORMAT_SHIFT) | 8973a634bfcSVikram Hegde (1 << INTRMAP_MSI_SHV_SHIFT) | 8983a634bfcSVikram Hegde ((idx >> 15) << INTRMAP_MSI_IDX15_SHIFT); 8993a634bfcSVikram Hegde } else { 9003a634bfcSVikram Hegde mregs->mr_addr = MSI_ADDR_HDR | 9013a634bfcSVikram Hegde (MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 9023a634bfcSVikram Hegde (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT) | 9033a634bfcSVikram Hegde (mregs->mr_addr << MSI_ADDR_DEST_SHIFT); 9043a634bfcSVikram Hegde mregs->mr_data = (MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | 9053a634bfcSVikram Hegde mregs->mr_data; 9063a634bfcSVikram Hegde } 9073a634bfcSVikram Hegde } 9083a634bfcSVikram Hegde 9093a634bfcSVikram Hegde /* ######################################################################### */ 9103a634bfcSVikram Hegde /* 9113a634bfcSVikram Hegde * Functions exported by immu_intr.c 9123a634bfcSVikram Hegde */ 9133a634bfcSVikram Hegde void 9143a634bfcSVikram Hegde immu_intrmap_setup(list_t *listp) 9153a634bfcSVikram Hegde { 9163a634bfcSVikram Hegde immu_t *immu; 9173a634bfcSVikram Hegde 9183a634bfcSVikram Hegde /* 9193a634bfcSVikram Hegde * Check if ACPI DMAR tables say that 9203a634bfcSVikram Hegde * interrupt remapping is supported 9213a634bfcSVikram Hegde */ 9223a634bfcSVikram Hegde if (immu_dmar_intrmap_supported() == B_FALSE) { 9233a634bfcSVikram Hegde return; 9243a634bfcSVikram Hegde } 9253a634bfcSVikram Hegde 9263a634bfcSVikram Hegde /* 9273a634bfcSVikram Hegde * Check if interrupt remapping is disabled. 9283a634bfcSVikram Hegde */ 9293a634bfcSVikram Hegde if (immu_intrmap_enable == B_FALSE) { 9303a634bfcSVikram Hegde return; 9313a634bfcSVikram Hegde } 9323a634bfcSVikram Hegde 9333a634bfcSVikram Hegde psm_vt_ops = &intrmap_ops; 9343a634bfcSVikram Hegde 9353a634bfcSVikram Hegde immu = list_head(listp); 9363a634bfcSVikram Hegde for (; immu; immu = list_next(listp, immu)) { 9373a634bfcSVikram Hegde mutex_init(&(immu->immu_intrmap_lock), NULL, 9383a634bfcSVikram Hegde MUTEX_DEFAULT, NULL); 9393a634bfcSVikram Hegde mutex_enter(&(immu->immu_intrmap_lock)); 940*50200e77SFrank Van Der Linden immu_init_inv_wait(&immu->immu_intrmap_inv_wait, 941*50200e77SFrank Van Der Linden "intrmapglobal", B_TRUE); 9423a634bfcSVikram Hegde immu->immu_intrmap_setup = B_TRUE; 9433a634bfcSVikram Hegde mutex_exit(&(immu->immu_intrmap_lock)); 9443a634bfcSVikram Hegde } 9453a634bfcSVikram Hegde } 9463a634bfcSVikram Hegde 9473a634bfcSVikram Hegde void 9483a634bfcSVikram Hegde immu_intrmap_startup(immu_t *immu) 9493a634bfcSVikram Hegde { 9503a634bfcSVikram Hegde /* do nothing */ 9513a634bfcSVikram Hegde mutex_enter(&(immu->immu_intrmap_lock)); 9523a634bfcSVikram Hegde if (immu->immu_intrmap_setup == B_TRUE) { 9533a634bfcSVikram Hegde immu->immu_intrmap_running = B_TRUE; 9543a634bfcSVikram Hegde } 9553a634bfcSVikram Hegde mutex_exit(&(immu->immu_intrmap_lock)); 9563a634bfcSVikram Hegde } 9573a634bfcSVikram Hegde 9583a634bfcSVikram Hegde /* 9593a634bfcSVikram Hegde * Register a Intel IOMMU unit (i.e. DMAR unit's) 9603a634bfcSVikram Hegde * interrupt handler 9613a634bfcSVikram Hegde */ 9623a634bfcSVikram Hegde void 9633a634bfcSVikram Hegde immu_intr_register(immu_t *immu) 9643a634bfcSVikram Hegde { 9653a634bfcSVikram Hegde int irq, vect; 9663a634bfcSVikram Hegde char intr_handler_name[IMMU_MAXNAMELEN]; 9673a634bfcSVikram Hegde uint32_t msi_data; 9683a634bfcSVikram Hegde uint32_t uaddr; 9693a634bfcSVikram Hegde uint32_t msi_addr; 9707ff178cdSJimmy Vetayases uint32_t localapic_id = 0; 9717ff178cdSJimmy Vetayases 9727ff178cdSJimmy Vetayases if (psm_get_localapicid) 9737ff178cdSJimmy Vetayases localapic_id = psm_get_localapicid(0); 9743a634bfcSVikram Hegde 9753a634bfcSVikram Hegde msi_addr = (MSI_ADDR_HDR | 9767ff178cdSJimmy Vetayases ((localapic_id & 0xFF) << MSI_ADDR_DEST_SHIFT) | 9773a634bfcSVikram Hegde (MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 9783a634bfcSVikram Hegde (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); 9793a634bfcSVikram Hegde 9803a634bfcSVikram Hegde if (intrmap_apic_mode == LOCAL_X2APIC) { 9817ff178cdSJimmy Vetayases uaddr = localapic_id & 0xFFFFFF00; 9823a634bfcSVikram Hegde } else { 9833a634bfcSVikram Hegde uaddr = 0; 9843a634bfcSVikram Hegde } 9853a634bfcSVikram Hegde 9863a634bfcSVikram Hegde /* Dont need to hold immu_intr_lock since we are in boot */ 9877ff178cdSJimmy Vetayases irq = vect = psm_get_ipivect(IMMU_INTR_IPL, -1); 9887ff178cdSJimmy Vetayases if (psm_xlate_vector_by_irq != NULL) 9897ff178cdSJimmy Vetayases vect = psm_xlate_vector_by_irq(irq); 9907ff178cdSJimmy Vetayases 9913a634bfcSVikram Hegde msi_data = ((MSI_DATA_DELIVERY_FIXED << 9923a634bfcSVikram Hegde MSI_DATA_DELIVERY_SHIFT) | vect); 9933a634bfcSVikram Hegde 9943a634bfcSVikram Hegde (void) snprintf(intr_handler_name, sizeof (intr_handler_name), 9953a634bfcSVikram Hegde "%s-intr-handler", immu->immu_name); 9963a634bfcSVikram Hegde 9973a634bfcSVikram Hegde (void) add_avintr((void *)NULL, IMMU_INTR_IPL, 9983a634bfcSVikram Hegde (avfunc)(immu_intr_handler), intr_handler_name, irq, 9993a634bfcSVikram Hegde (caddr_t)immu, NULL, NULL, NULL); 10003a634bfcSVikram Hegde 10013a634bfcSVikram Hegde immu_regs_intr_enable(immu, msi_addr, msi_data, uaddr); 10023a634bfcSVikram Hegde 10033a634bfcSVikram Hegde (void) immu_intr_handler(immu); 10043a634bfcSVikram Hegde } 1005