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 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 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 * 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 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 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 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 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 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 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 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