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 /* 222973Sgovinda * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * Interrupt Vector Table Configuration 300Sstevel@tonic-gate */ 310Sstevel@tonic-gate 322973Sgovinda #include <sys/types.h> 330Sstevel@tonic-gate #include <sys/cpuvar.h> 340Sstevel@tonic-gate #include <sys/ivintr.h> 350Sstevel@tonic-gate #include <sys/intreg.h> 360Sstevel@tonic-gate #include <sys/cmn_err.h> 370Sstevel@tonic-gate #include <sys/privregs.h> 380Sstevel@tonic-gate #include <sys/sunddi.h> 390Sstevel@tonic-gate 402973Sgovinda /* 412973Sgovinda * Allocate an Interrupt Vector Table and some interrupt vector data structures 422973Sgovinda * for the reserved pool as part of the startup code. First try to allocate an 432973Sgovinda * interrupt vector data structure from the reserved pool, otherwise allocate it 442973Sgovinda * using kmem cache method. 452973Sgovinda */ 462973Sgovinda static kmutex_t intr_vec_mutex; /* Protect interrupt vector table */ 470Sstevel@tonic-gate 480Sstevel@tonic-gate /* 492973Sgovinda * Global softint linked list - used by softint mdb dcmd. 500Sstevel@tonic-gate */ 512973Sgovinda static kmutex_t softint_mutex; /* Protect global softint linked list */ 522973Sgovinda intr_vec_t *softint_list = NULL; 532973Sgovinda 542973Sgovinda /* Reserved pool for interrupt allocation */ 552973Sgovinda intr_vec_t *intr_vec_pool = NULL; /* For HW and single target SW intrs */ 562973Sgovinda intr_vecx_t *intr_vecx_pool = NULL; /* For multi target SW intrs */ 57*3273Sgovinda static kmutex_t intr_vec_pool_mutex; /* Protect interrupt vector pool */ 582973Sgovinda 592973Sgovinda /* Kmem cache handle for interrupt allocation */ 602973Sgovinda kmem_cache_t *intr_vec_cache = NULL; /* For HW and single target SW intrs */ 610Sstevel@tonic-gate 620Sstevel@tonic-gate /* 632973Sgovinda * init_ivintr() - Initialize an Interrupt Vector Table. 640Sstevel@tonic-gate */ 652973Sgovinda void 662973Sgovinda init_ivintr() 672973Sgovinda { 682973Sgovinda mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL); 692973Sgovinda mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL); 70*3273Sgovinda mutex_init(&intr_vec_pool_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 { 922973Sgovinda if (intr_vec_cache) 932973Sgovinda kmem_cache_destroy(intr_vec_cache); 942973Sgovinda 95*3273Sgovinda mutex_destroy(&intr_vec_pool_mutex); 96*3273Sgovinda mutex_destroy(&softint_mutex); 972973Sgovinda mutex_destroy(&intr_vec_mutex); 982973Sgovinda } 990Sstevel@tonic-gate 1000Sstevel@tonic-gate /* 1012973Sgovinda * iv_alloc() - Allocate an interrupt vector data structure. 1022973Sgovinda * 1032973Sgovinda * This function allocates an interrupt vector data structure for hardware 1042973Sgovinda * and single or multi target software interrupts either from the reserved 1052973Sgovinda * pool or using kmem cache method. 1060Sstevel@tonic-gate */ 1072973Sgovinda static intr_vec_t * 1082973Sgovinda iv_alloc(softint_type_t type) 1092973Sgovinda { 1102973Sgovinda intr_vec_t *iv_p; 1112973Sgovinda int i, count; 1122973Sgovinda 1132973Sgovinda count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV; 1142973Sgovinda 1152973Sgovinda /* 1162973Sgovinda * First try to allocate an interrupt vector data structure from the 1172973Sgovinda * reserved pool, otherwise allocate it using kmem_cache_alloc(). 1182973Sgovinda */ 119*3273Sgovinda mutex_enter(&intr_vec_pool_mutex); 1202973Sgovinda for (i = 0; i < count; i++) { 1212973Sgovinda iv_p = (type == SOFTINT_MT) ? 1222973Sgovinda (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i]; 1230Sstevel@tonic-gate 124*3273Sgovinda if (iv_p->iv_pil == 0) { 125*3273Sgovinda iv_p->iv_pil = 1; /* Default PIL */ 1262973Sgovinda break; 127*3273Sgovinda } 1282973Sgovinda } 129*3273Sgovinda mutex_exit(&intr_vec_pool_mutex); 1302973Sgovinda 1312973Sgovinda if (i < count) 1322973Sgovinda return (iv_p); 1332973Sgovinda 1342973Sgovinda if (type == SOFTINT_MT) 1352973Sgovinda cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi " 1362973Sgovinda "target software interrupts, %d", MAX_RSVD_IVX); 1372973Sgovinda 1382973Sgovinda /* 1392973Sgovinda * If the interrupt vector data structure reserved pool is already 1402973Sgovinda * exhausted, then allocate an interrupt vector data structure using 1412973Sgovinda * kmem_cache_alloc(), but only for the hardware and single software 1422973Sgovinda * interrupts. Create a kmem cache for the interrupt allocation, 1432973Sgovinda * if it is not already available. 1442973Sgovinda */ 1452973Sgovinda if (intr_vec_cache == NULL) 1462973Sgovinda intr_vec_cache = kmem_cache_create("intr_vec_cache", 1472973Sgovinda sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0); 1482973Sgovinda 1492973Sgovinda iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP); 1502973Sgovinda bzero(iv_p, sizeof (intr_vec_t)); 151*3273Sgovinda iv_p->iv_flags = IV_CACHE_ALLOC; 1522973Sgovinda 1532973Sgovinda return (iv_p); 1542973Sgovinda } 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate /* 1572973Sgovinda * iv_free() - Free an interrupt vector data structure. 1580Sstevel@tonic-gate */ 1592973Sgovinda static void 1602973Sgovinda iv_free(intr_vec_t *iv_p) 1610Sstevel@tonic-gate { 1622973Sgovinda if (iv_p->iv_flags & IV_CACHE_ALLOC) { 1632973Sgovinda ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT)); 1642973Sgovinda kmem_cache_free(intr_vec_cache, iv_p); 1650Sstevel@tonic-gate } else { 166*3273Sgovinda mutex_enter(&intr_vec_pool_mutex); 167*3273Sgovinda bzero(iv_p, (iv_p->iv_flags & IV_SOFTINT_MT) ? 168*3273Sgovinda sizeof (intr_vecx_t) : sizeof (intr_vec_t)); 169*3273Sgovinda mutex_exit(&intr_vec_pool_mutex); 1700Sstevel@tonic-gate } 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1742973Sgovinda * add_ivintr() - Add an interrupt handler to the system 1750Sstevel@tonic-gate */ 1760Sstevel@tonic-gate int 1770Sstevel@tonic-gate add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler, 1782973Sgovinda caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload) 1790Sstevel@tonic-gate { 1802973Sgovinda intr_vec_t *iv_p, *new_iv_p; 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate if (inum >= MAXIVNUM || pil > PIL_MAX) 1830Sstevel@tonic-gate return (EINVAL); 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate ASSERT((uintptr_t)intr_handler > KERNELBASE); 1862973Sgovinda 1870Sstevel@tonic-gate /* Make sure the payload buffer address is 64 bit aligned */ 1880Sstevel@tonic-gate VERIFY(((uint64_t)intr_payload & 0x7) == 0); 1890Sstevel@tonic-gate 1902973Sgovinda new_iv_p = iv_alloc(SOFTINT_ST); 1912973Sgovinda mutex_enter(&intr_vec_mutex); 1922973Sgovinda 1932973Sgovinda for (iv_p = (intr_vec_t *)intr_vec_table[inum]; 1942973Sgovinda iv_p; iv_p = iv_p->iv_vec_next) { 1952973Sgovinda if (iv_p->iv_pil == pil) { 1962973Sgovinda mutex_exit(&intr_vec_mutex); 1972973Sgovinda iv_free(new_iv_p); 1982973Sgovinda return (EINVAL); 1992973Sgovinda } 2002973Sgovinda } 2010Sstevel@tonic-gate 2022973Sgovinda ASSERT(iv_p == NULL); 2030Sstevel@tonic-gate 2042973Sgovinda new_iv_p->iv_handler = intr_handler; 2052973Sgovinda new_iv_p->iv_arg1 = intr_arg1; 2062973Sgovinda new_iv_p->iv_arg2 = intr_arg2; 2072973Sgovinda new_iv_p->iv_payload_buf = intr_payload; 2082973Sgovinda new_iv_p->iv_pil = (ushort_t)pil; 2092973Sgovinda new_iv_p->iv_inum = inum; 2102973Sgovinda 2112973Sgovinda new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum]; 2122973Sgovinda intr_vec_table[inum] = (uint64_t)new_iv_p; 2132973Sgovinda 2142973Sgovinda mutex_exit(&intr_vec_mutex); 2150Sstevel@tonic-gate return (0); 2160Sstevel@tonic-gate } 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate /* 2192973Sgovinda * rem_ivintr() - Remove an interrupt handler from the system 2200Sstevel@tonic-gate */ 2212973Sgovinda int 2222973Sgovinda rem_ivintr(uint_t inum, uint_t pil) 2230Sstevel@tonic-gate { 2242973Sgovinda intr_vec_t *iv_p, *prev_iv_p; 2250Sstevel@tonic-gate 2262973Sgovinda if (inum >= MAXIVNUM || pil > PIL_MAX) 2272973Sgovinda return (EINVAL); 2280Sstevel@tonic-gate 2292973Sgovinda mutex_enter(&intr_vec_mutex); 2300Sstevel@tonic-gate 2312973Sgovinda for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum]; 2322973Sgovinda iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next) 2332973Sgovinda if (iv_p->iv_pil == pil) 2342973Sgovinda break; 2352973Sgovinda 2362973Sgovinda if (iv_p == NULL) { 2372973Sgovinda mutex_exit(&intr_vec_mutex); 2382973Sgovinda return (EIO); 2390Sstevel@tonic-gate } 2400Sstevel@tonic-gate 2412973Sgovinda ASSERT(iv_p->iv_pil_next == NULL); 2422973Sgovinda 2432973Sgovinda if (prev_iv_p == iv_p) 2442973Sgovinda intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next; 2452973Sgovinda else 2462973Sgovinda prev_iv_p->iv_vec_next = iv_p->iv_vec_next; 2472973Sgovinda 2482973Sgovinda mutex_exit(&intr_vec_mutex); 2492973Sgovinda 2502973Sgovinda iv_free(iv_p); 2512973Sgovinda return (0); 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate /* 2550Sstevel@tonic-gate * add_softintr() - add a software interrupt handler to the system 2560Sstevel@tonic-gate */ 2572973Sgovinda uint64_t 2582973Sgovinda add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1, 2592973Sgovinda softint_type_t type) 2600Sstevel@tonic-gate { 2612973Sgovinda intr_vec_t *iv_p; 2620Sstevel@tonic-gate 2632973Sgovinda if (pil > PIL_MAX) 2642973Sgovinda return (NULL); 2652973Sgovinda 2662973Sgovinda iv_p = iv_alloc(type); 2670Sstevel@tonic-gate 2682973Sgovinda iv_p->iv_handler = (intrfunc)intr_handler; 2692973Sgovinda iv_p->iv_arg1 = intr_arg1; 2702973Sgovinda iv_p->iv_pil = (ushort_t)pil; 2712973Sgovinda if (type == SOFTINT_MT) 2722973Sgovinda iv_p->iv_flags |= IV_SOFTINT_MT; 2730Sstevel@tonic-gate 2742973Sgovinda mutex_enter(&softint_mutex); 2752973Sgovinda if (softint_list) 2762973Sgovinda iv_p->iv_vec_next = softint_list; 2772973Sgovinda softint_list = iv_p; 2782973Sgovinda mutex_exit(&softint_mutex); 2790Sstevel@tonic-gate 2802973Sgovinda return ((uint64_t)iv_p); 2810Sstevel@tonic-gate } 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate /* 2840Sstevel@tonic-gate * rem_softintr() - remove a software interrupt handler from the system 2850Sstevel@tonic-gate */ 2862973Sgovinda int 2872973Sgovinda rem_softintr(uint64_t softint_id) 2880Sstevel@tonic-gate { 2892973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id; 2902973Sgovinda 2912973Sgovinda ASSERT(iv_p != NULL); 2922973Sgovinda 2932973Sgovinda if (iv_p->iv_flags & IV_SOFTINT_PEND) 2942973Sgovinda return (EIO); 2952973Sgovinda 2962973Sgovinda ASSERT(iv_p->iv_pil_next == NULL); 2970Sstevel@tonic-gate 2982973Sgovinda mutex_enter(&softint_mutex); 2992973Sgovinda if (softint_list == iv_p) { 3002973Sgovinda softint_list = iv_p->iv_vec_next; 3012973Sgovinda } else { 3022973Sgovinda intr_vec_t *list = softint_list; 3032973Sgovinda 3042973Sgovinda while (list && (list->iv_vec_next != iv_p)) 3052973Sgovinda list = list->iv_vec_next; 3062973Sgovinda 3072973Sgovinda list->iv_vec_next = iv_p->iv_vec_next; 3082973Sgovinda } 3092973Sgovinda mutex_exit(&softint_mutex); 3102973Sgovinda 3112973Sgovinda iv_free(iv_p); 3122973Sgovinda return (0); 3130Sstevel@tonic-gate } 3140Sstevel@tonic-gate 3152973Sgovinda /* 3162973Sgovinda * update_softint_arg2() - Update softint arg2. 3172973Sgovinda * 3182973Sgovinda * NOTE: Do not grab any mutex in this function since it may get called 3192973Sgovinda * from the high-level interrupt context. 3202973Sgovinda */ 3210Sstevel@tonic-gate int 3222973Sgovinda update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2) 3230Sstevel@tonic-gate { 3242973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id; 3250Sstevel@tonic-gate 3262973Sgovinda ASSERT(iv_p != NULL); 3270Sstevel@tonic-gate 3282973Sgovinda if (iv_p->iv_flags & IV_SOFTINT_PEND) 3292973Sgovinda return (EIO); 3300Sstevel@tonic-gate 3312973Sgovinda iv_p->iv_arg2 = intr_arg2; 3322973Sgovinda return (0); 3330Sstevel@tonic-gate } 3340Sstevel@tonic-gate 3352973Sgovinda /* 3362973Sgovinda * update_softint_pri() - Update softint priority. 3372973Sgovinda */ 3380Sstevel@tonic-gate int 3392973Sgovinda update_softint_pri(uint64_t softint_id, uint_t pil) 3400Sstevel@tonic-gate { 3412973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id; 3420Sstevel@tonic-gate 3432973Sgovinda ASSERT(iv_p != NULL); 3440Sstevel@tonic-gate 3452973Sgovinda if (pil > PIL_MAX) 3462973Sgovinda return (EINVAL); 3472973Sgovinda 3482973Sgovinda iv_p->iv_pil = pil; 3492973Sgovinda return (0); 3500Sstevel@tonic-gate } 351