10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52973Sgovinda * Common Development and Distribution License (the "License").
62973Sgovinda * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*9038SEvan.Yan@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate * Interrupt Vector Table Configuration
280Sstevel@tonic-gate */
290Sstevel@tonic-gate
302973Sgovinda #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/cpuvar.h>
320Sstevel@tonic-gate #include <sys/ivintr.h>
330Sstevel@tonic-gate #include <sys/intreg.h>
340Sstevel@tonic-gate #include <sys/cmn_err.h>
350Sstevel@tonic-gate #include <sys/privregs.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate
382973Sgovinda /*
392973Sgovinda * Allocate an Interrupt Vector Table and some interrupt vector data structures
402973Sgovinda * for the reserved pool as part of the startup code. First try to allocate an
412973Sgovinda * interrupt vector data structure from the reserved pool, otherwise allocate it
422973Sgovinda * using kmem cache method.
432973Sgovinda */
442973Sgovinda static kmutex_t intr_vec_mutex; /* Protect interrupt vector table */
450Sstevel@tonic-gate
460Sstevel@tonic-gate /*
472973Sgovinda * Global softint linked list - used by softint mdb dcmd.
480Sstevel@tonic-gate */
492973Sgovinda static kmutex_t softint_mutex; /* Protect global softint linked list */
502973Sgovinda intr_vec_t *softint_list = NULL;
512973Sgovinda
522973Sgovinda /* Reserved pool for interrupt allocation */
532973Sgovinda intr_vec_t *intr_vec_pool = NULL; /* For HW and single target SW intrs */
542973Sgovinda intr_vecx_t *intr_vecx_pool = NULL; /* For multi target SW intrs */
553273Sgovinda static kmutex_t intr_vec_pool_mutex; /* Protect interrupt vector pool */
562973Sgovinda
572973Sgovinda /* Kmem cache handle for interrupt allocation */
582973Sgovinda kmem_cache_t *intr_vec_cache = NULL; /* For HW and single target SW intrs */
59*9038SEvan.Yan@Sun.COM static kmutex_t intr_vec_cache_mutex; /* Protect intr_vec_cache usage */
600Sstevel@tonic-gate
610Sstevel@tonic-gate /*
622973Sgovinda * init_ivintr() - Initialize an Interrupt Vector Table.
630Sstevel@tonic-gate */
642973Sgovinda void
init_ivintr()652973Sgovinda init_ivintr()
662973Sgovinda {
672973Sgovinda mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL);
682973Sgovinda mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL);
693273Sgovinda mutex_init(&intr_vec_pool_mutex, NULL, MUTEX_DRIVER, NULL);
70*9038SEvan.Yan@Sun.COM mutex_init(&intr_vec_cache_mutex, NULL, MUTEX_DRIVER, NULL);
712973Sgovinda
722973Sgovinda /*
732973Sgovinda * Initialize the reserved interrupt vector data structure pools
742973Sgovinda * used for hardware and software interrupts.
752973Sgovinda */
762973Sgovinda intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table +
772973Sgovinda (MAXIVNUM * sizeof (intr_vec_t *)));
782973Sgovinda intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool +
792973Sgovinda (MAX_RSVD_IV * sizeof (intr_vec_t)));
802973Sgovinda
812973Sgovinda bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *));
822973Sgovinda bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t));
832973Sgovinda bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t));
842973Sgovinda }
850Sstevel@tonic-gate
860Sstevel@tonic-gate /*
872973Sgovinda * fini_ivintr() - Uninitialize an Interrupt Vector Table.
880Sstevel@tonic-gate */
892973Sgovinda void
fini_ivintr()902973Sgovinda fini_ivintr()
912973Sgovinda {
92*9038SEvan.Yan@Sun.COM mutex_enter(&intr_vec_cache_mutex);
93*9038SEvan.Yan@Sun.COM if (intr_vec_cache) {
942973Sgovinda kmem_cache_destroy(intr_vec_cache);
95*9038SEvan.Yan@Sun.COM intr_vec_cache = NULL;
96*9038SEvan.Yan@Sun.COM }
97*9038SEvan.Yan@Sun.COM mutex_exit(&intr_vec_cache_mutex);
982973Sgovinda
993273Sgovinda mutex_destroy(&intr_vec_pool_mutex);
1003273Sgovinda mutex_destroy(&softint_mutex);
1012973Sgovinda mutex_destroy(&intr_vec_mutex);
102*9038SEvan.Yan@Sun.COM mutex_destroy(&intr_vec_cache_mutex);
1032973Sgovinda }
1040Sstevel@tonic-gate
1050Sstevel@tonic-gate /*
1062973Sgovinda * iv_alloc() - Allocate an interrupt vector data structure.
1072973Sgovinda *
1082973Sgovinda * This function allocates an interrupt vector data structure for hardware
1092973Sgovinda * and single or multi target software interrupts either from the reserved
1102973Sgovinda * pool or using kmem cache method.
1110Sstevel@tonic-gate */
1122973Sgovinda static intr_vec_t *
iv_alloc(softint_type_t type)1132973Sgovinda iv_alloc(softint_type_t type)
1142973Sgovinda {
1152973Sgovinda intr_vec_t *iv_p;
1162973Sgovinda int i, count;
1172973Sgovinda
1182973Sgovinda count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV;
1192973Sgovinda
1202973Sgovinda /*
1212973Sgovinda * First try to allocate an interrupt vector data structure from the
1222973Sgovinda * reserved pool, otherwise allocate it using kmem_cache_alloc().
1232973Sgovinda */
1243273Sgovinda mutex_enter(&intr_vec_pool_mutex);
1252973Sgovinda for (i = 0; i < count; i++) {
1262973Sgovinda iv_p = (type == SOFTINT_MT) ?
1272973Sgovinda (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i];
1280Sstevel@tonic-gate
1293273Sgovinda if (iv_p->iv_pil == 0) {
1303273Sgovinda iv_p->iv_pil = 1; /* Default PIL */
1312973Sgovinda break;
1323273Sgovinda }
1332973Sgovinda }
1343273Sgovinda mutex_exit(&intr_vec_pool_mutex);
1352973Sgovinda
1362973Sgovinda if (i < count)
1372973Sgovinda return (iv_p);
1382973Sgovinda
1392973Sgovinda if (type == SOFTINT_MT)
1402973Sgovinda cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi "
1412973Sgovinda "target software interrupts, %d", MAX_RSVD_IVX);
1422973Sgovinda
1432973Sgovinda /*
1442973Sgovinda * If the interrupt vector data structure reserved pool is already
1452973Sgovinda * exhausted, then allocate an interrupt vector data structure using
1462973Sgovinda * kmem_cache_alloc(), but only for the hardware and single software
1472973Sgovinda * interrupts. Create a kmem cache for the interrupt allocation,
1482973Sgovinda * if it is not already available.
1492973Sgovinda */
150*9038SEvan.Yan@Sun.COM mutex_enter(&intr_vec_cache_mutex);
1512973Sgovinda if (intr_vec_cache == NULL)
1522973Sgovinda intr_vec_cache = kmem_cache_create("intr_vec_cache",
1532973Sgovinda sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
154*9038SEvan.Yan@Sun.COM mutex_exit(&intr_vec_cache_mutex);
1552973Sgovinda
1562973Sgovinda iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP);
1572973Sgovinda bzero(iv_p, sizeof (intr_vec_t));
1583273Sgovinda iv_p->iv_flags = IV_CACHE_ALLOC;
1592973Sgovinda
1602973Sgovinda return (iv_p);
1612973Sgovinda }
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate /*
1642973Sgovinda * iv_free() - Free an interrupt vector data structure.
1650Sstevel@tonic-gate */
1662973Sgovinda static void
iv_free(intr_vec_t * iv_p)1672973Sgovinda iv_free(intr_vec_t *iv_p)
1680Sstevel@tonic-gate {
1692973Sgovinda if (iv_p->iv_flags & IV_CACHE_ALLOC) {
1702973Sgovinda ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT));
1712973Sgovinda kmem_cache_free(intr_vec_cache, iv_p);
1720Sstevel@tonic-gate } else {
1733273Sgovinda mutex_enter(&intr_vec_pool_mutex);
1743273Sgovinda bzero(iv_p, (iv_p->iv_flags & IV_SOFTINT_MT) ?
1753273Sgovinda sizeof (intr_vecx_t) : sizeof (intr_vec_t));
1763273Sgovinda mutex_exit(&intr_vec_pool_mutex);
1770Sstevel@tonic-gate }
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate /*
1812973Sgovinda * add_ivintr() - Add an interrupt handler to the system
1820Sstevel@tonic-gate */
1830Sstevel@tonic-gate int
add_ivintr(uint_t inum,uint_t pil,intrfunc intr_handler,caddr_t intr_arg1,caddr_t intr_arg2,caddr_t intr_payload)1840Sstevel@tonic-gate add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler,
1852973Sgovinda caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload)
1860Sstevel@tonic-gate {
1872973Sgovinda intr_vec_t *iv_p, *new_iv_p;
1880Sstevel@tonic-gate
1890Sstevel@tonic-gate if (inum >= MAXIVNUM || pil > PIL_MAX)
1900Sstevel@tonic-gate return (EINVAL);
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate ASSERT((uintptr_t)intr_handler > KERNELBASE);
1932973Sgovinda
1940Sstevel@tonic-gate /* Make sure the payload buffer address is 64 bit aligned */
1950Sstevel@tonic-gate VERIFY(((uint64_t)intr_payload & 0x7) == 0);
1960Sstevel@tonic-gate
1972973Sgovinda new_iv_p = iv_alloc(SOFTINT_ST);
1982973Sgovinda mutex_enter(&intr_vec_mutex);
1992973Sgovinda
2002973Sgovinda for (iv_p = (intr_vec_t *)intr_vec_table[inum];
2012973Sgovinda iv_p; iv_p = iv_p->iv_vec_next) {
2022973Sgovinda if (iv_p->iv_pil == pil) {
2032973Sgovinda mutex_exit(&intr_vec_mutex);
2042973Sgovinda iv_free(new_iv_p);
2052973Sgovinda return (EINVAL);
2062973Sgovinda }
2072973Sgovinda }
2080Sstevel@tonic-gate
2092973Sgovinda ASSERT(iv_p == NULL);
2100Sstevel@tonic-gate
2112973Sgovinda new_iv_p->iv_handler = intr_handler;
2122973Sgovinda new_iv_p->iv_arg1 = intr_arg1;
2132973Sgovinda new_iv_p->iv_arg2 = intr_arg2;
2142973Sgovinda new_iv_p->iv_payload_buf = intr_payload;
2152973Sgovinda new_iv_p->iv_pil = (ushort_t)pil;
2162973Sgovinda new_iv_p->iv_inum = inum;
2172973Sgovinda
2182973Sgovinda new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum];
2192973Sgovinda intr_vec_table[inum] = (uint64_t)new_iv_p;
2202973Sgovinda
2212973Sgovinda mutex_exit(&intr_vec_mutex);
2220Sstevel@tonic-gate return (0);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate /*
2262973Sgovinda * rem_ivintr() - Remove an interrupt handler from the system
2270Sstevel@tonic-gate */
2282973Sgovinda int
rem_ivintr(uint_t inum,uint_t pil)2292973Sgovinda rem_ivintr(uint_t inum, uint_t pil)
2300Sstevel@tonic-gate {
2312973Sgovinda intr_vec_t *iv_p, *prev_iv_p;
2320Sstevel@tonic-gate
2332973Sgovinda if (inum >= MAXIVNUM || pil > PIL_MAX)
2342973Sgovinda return (EINVAL);
2350Sstevel@tonic-gate
2362973Sgovinda mutex_enter(&intr_vec_mutex);
2370Sstevel@tonic-gate
2382973Sgovinda for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum];
2392973Sgovinda iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next)
2402973Sgovinda if (iv_p->iv_pil == pil)
2412973Sgovinda break;
2422973Sgovinda
2432973Sgovinda if (iv_p == NULL) {
2442973Sgovinda mutex_exit(&intr_vec_mutex);
2452973Sgovinda return (EIO);
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate
2482973Sgovinda ASSERT(iv_p->iv_pil_next == NULL);
2492973Sgovinda
2502973Sgovinda if (prev_iv_p == iv_p)
2512973Sgovinda intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next;
2522973Sgovinda else
2532973Sgovinda prev_iv_p->iv_vec_next = iv_p->iv_vec_next;
2542973Sgovinda
2552973Sgovinda mutex_exit(&intr_vec_mutex);
2562973Sgovinda
2572973Sgovinda iv_free(iv_p);
2582973Sgovinda return (0);
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate
2610Sstevel@tonic-gate /*
2620Sstevel@tonic-gate * add_softintr() - add a software interrupt handler to the system
2630Sstevel@tonic-gate */
2642973Sgovinda uint64_t
add_softintr(uint_t pil,softintrfunc intr_handler,caddr_t intr_arg1,softint_type_t type)2652973Sgovinda add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1,
2662973Sgovinda softint_type_t type)
2670Sstevel@tonic-gate {
2682973Sgovinda intr_vec_t *iv_p;
2690Sstevel@tonic-gate
2702973Sgovinda if (pil > PIL_MAX)
2712973Sgovinda return (NULL);
2722973Sgovinda
2732973Sgovinda iv_p = iv_alloc(type);
2740Sstevel@tonic-gate
2752973Sgovinda iv_p->iv_handler = (intrfunc)intr_handler;
2762973Sgovinda iv_p->iv_arg1 = intr_arg1;
2772973Sgovinda iv_p->iv_pil = (ushort_t)pil;
2782973Sgovinda if (type == SOFTINT_MT)
2792973Sgovinda iv_p->iv_flags |= IV_SOFTINT_MT;
2800Sstevel@tonic-gate
2812973Sgovinda mutex_enter(&softint_mutex);
2822973Sgovinda if (softint_list)
2832973Sgovinda iv_p->iv_vec_next = softint_list;
2842973Sgovinda softint_list = iv_p;
2852973Sgovinda mutex_exit(&softint_mutex);
2860Sstevel@tonic-gate
2872973Sgovinda return ((uint64_t)iv_p);
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate /*
2910Sstevel@tonic-gate * rem_softintr() - remove a software interrupt handler from the system
2920Sstevel@tonic-gate */
2932973Sgovinda int
rem_softintr(uint64_t softint_id)2942973Sgovinda rem_softintr(uint64_t softint_id)
2950Sstevel@tonic-gate {
2962973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id;
2972973Sgovinda
2982973Sgovinda ASSERT(iv_p != NULL);
2992973Sgovinda
3002973Sgovinda if (iv_p->iv_flags & IV_SOFTINT_PEND)
3012973Sgovinda return (EIO);
3022973Sgovinda
3032973Sgovinda ASSERT(iv_p->iv_pil_next == NULL);
3040Sstevel@tonic-gate
3052973Sgovinda mutex_enter(&softint_mutex);
3062973Sgovinda if (softint_list == iv_p) {
3072973Sgovinda softint_list = iv_p->iv_vec_next;
3082973Sgovinda } else {
3092973Sgovinda intr_vec_t *list = softint_list;
3102973Sgovinda
3112973Sgovinda while (list && (list->iv_vec_next != iv_p))
3122973Sgovinda list = list->iv_vec_next;
3132973Sgovinda
3142973Sgovinda list->iv_vec_next = iv_p->iv_vec_next;
3152973Sgovinda }
3162973Sgovinda mutex_exit(&softint_mutex);
3172973Sgovinda
3182973Sgovinda iv_free(iv_p);
3192973Sgovinda return (0);
3200Sstevel@tonic-gate }
3210Sstevel@tonic-gate
3222973Sgovinda /*
3232973Sgovinda * update_softint_arg2() - Update softint arg2.
3242973Sgovinda *
3252973Sgovinda * NOTE: Do not grab any mutex in this function since it may get called
3262973Sgovinda * from the high-level interrupt context.
3272973Sgovinda */
3280Sstevel@tonic-gate int
update_softint_arg2(uint64_t softint_id,caddr_t intr_arg2)3292973Sgovinda update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2)
3300Sstevel@tonic-gate {
3312973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id;
3320Sstevel@tonic-gate
3332973Sgovinda ASSERT(iv_p != NULL);
3340Sstevel@tonic-gate
3352973Sgovinda if (iv_p->iv_flags & IV_SOFTINT_PEND)
3362973Sgovinda return (EIO);
3370Sstevel@tonic-gate
3382973Sgovinda iv_p->iv_arg2 = intr_arg2;
3392973Sgovinda return (0);
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate
3422973Sgovinda /*
3432973Sgovinda * update_softint_pri() - Update softint priority.
3442973Sgovinda */
3450Sstevel@tonic-gate int
update_softint_pri(uint64_t softint_id,uint_t pil)3462973Sgovinda update_softint_pri(uint64_t softint_id, uint_t pil)
3470Sstevel@tonic-gate {
3482973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id;
3490Sstevel@tonic-gate
3502973Sgovinda ASSERT(iv_p != NULL);
3510Sstevel@tonic-gate
3522973Sgovinda if (pil > PIL_MAX)
3532973Sgovinda return (EINVAL);
3542973Sgovinda
3552973Sgovinda iv_p->iv_pil = pil;
3562973Sgovinda return (0);
3570Sstevel@tonic-gate }
358