1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Interrupt Vector Table Configuration
28 */
29
30 #include <sys/types.h>
31 #include <sys/cpuvar.h>
32 #include <sys/ivintr.h>
33 #include <sys/intreg.h>
34 #include <sys/cmn_err.h>
35 #include <sys/privregs.h>
36 #include <sys/sunddi.h>
37
38 /*
39 * Allocate an Interrupt Vector Table and some interrupt vector data structures
40 * for the reserved pool as part of the startup code. First try to allocate an
41 * interrupt vector data structure from the reserved pool, otherwise allocate it
42 * using kmem cache method.
43 */
44 static kmutex_t intr_vec_mutex; /* Protect interrupt vector table */
45
46 /*
47 * Global softint linked list - used by softint mdb dcmd.
48 */
49 static kmutex_t softint_mutex; /* Protect global softint linked list */
50 intr_vec_t *softint_list = NULL;
51
52 /* Reserved pool for interrupt allocation */
53 intr_vec_t *intr_vec_pool = NULL; /* For HW and single target SW intrs */
54 intr_vecx_t *intr_vecx_pool = NULL; /* For multi target SW intrs */
55 static kmutex_t intr_vec_pool_mutex; /* Protect interrupt vector pool */
56
57 /* Kmem cache handle for interrupt allocation */
58 kmem_cache_t *intr_vec_cache = NULL; /* For HW and single target SW intrs */
59 static kmutex_t intr_vec_cache_mutex; /* Protect intr_vec_cache usage */
60
61 /*
62 * init_ivintr() - Initialize an Interrupt Vector Table.
63 */
64 void
init_ivintr()65 init_ivintr()
66 {
67 mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL);
68 mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL);
69 mutex_init(&intr_vec_pool_mutex, NULL, MUTEX_DRIVER, NULL);
70 mutex_init(&intr_vec_cache_mutex, NULL, MUTEX_DRIVER, NULL);
71
72 /*
73 * Initialize the reserved interrupt vector data structure pools
74 * used for hardware and software interrupts.
75 */
76 intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table +
77 (MAXIVNUM * sizeof (intr_vec_t *)));
78 intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool +
79 (MAX_RSVD_IV * sizeof (intr_vec_t)));
80
81 bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *));
82 bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t));
83 bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t));
84 }
85
86 /*
87 * fini_ivintr() - Uninitialize an Interrupt Vector Table.
88 */
89 void
fini_ivintr()90 fini_ivintr()
91 {
92 mutex_enter(&intr_vec_cache_mutex);
93 if (intr_vec_cache) {
94 kmem_cache_destroy(intr_vec_cache);
95 intr_vec_cache = NULL;
96 }
97 mutex_exit(&intr_vec_cache_mutex);
98
99 mutex_destroy(&intr_vec_pool_mutex);
100 mutex_destroy(&softint_mutex);
101 mutex_destroy(&intr_vec_mutex);
102 mutex_destroy(&intr_vec_cache_mutex);
103 }
104
105 /*
106 * iv_alloc() - Allocate an interrupt vector data structure.
107 *
108 * This function allocates an interrupt vector data structure for hardware
109 * and single or multi target software interrupts either from the reserved
110 * pool or using kmem cache method.
111 */
112 static intr_vec_t *
iv_alloc(softint_type_t type)113 iv_alloc(softint_type_t type)
114 {
115 intr_vec_t *iv_p;
116 int i, count;
117
118 count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV;
119
120 /*
121 * First try to allocate an interrupt vector data structure from the
122 * reserved pool, otherwise allocate it using kmem_cache_alloc().
123 */
124 mutex_enter(&intr_vec_pool_mutex);
125 for (i = 0; i < count; i++) {
126 iv_p = (type == SOFTINT_MT) ?
127 (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i];
128
129 if (iv_p->iv_pil == 0) {
130 iv_p->iv_pil = 1; /* Default PIL */
131 break;
132 }
133 }
134 mutex_exit(&intr_vec_pool_mutex);
135
136 if (i < count)
137 return (iv_p);
138
139 if (type == SOFTINT_MT)
140 cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi "
141 "target software interrupts, %d", MAX_RSVD_IVX);
142
143 /*
144 * If the interrupt vector data structure reserved pool is already
145 * exhausted, then allocate an interrupt vector data structure using
146 * kmem_cache_alloc(), but only for the hardware and single software
147 * interrupts. Create a kmem cache for the interrupt allocation,
148 * if it is not already available.
149 */
150 mutex_enter(&intr_vec_cache_mutex);
151 if (intr_vec_cache == NULL)
152 intr_vec_cache = kmem_cache_create("intr_vec_cache",
153 sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
154 mutex_exit(&intr_vec_cache_mutex);
155
156 iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP);
157 bzero(iv_p, sizeof (intr_vec_t));
158 iv_p->iv_flags = IV_CACHE_ALLOC;
159
160 return (iv_p);
161 }
162
163 /*
164 * iv_free() - Free an interrupt vector data structure.
165 */
166 static void
iv_free(intr_vec_t * iv_p)167 iv_free(intr_vec_t *iv_p)
168 {
169 if (iv_p->iv_flags & IV_CACHE_ALLOC) {
170 ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT));
171 kmem_cache_free(intr_vec_cache, iv_p);
172 } else {
173 mutex_enter(&intr_vec_pool_mutex);
174 bzero(iv_p, (iv_p->iv_flags & IV_SOFTINT_MT) ?
175 sizeof (intr_vecx_t) : sizeof (intr_vec_t));
176 mutex_exit(&intr_vec_pool_mutex);
177 }
178 }
179
180 /*
181 * add_ivintr() - Add an interrupt handler to the system
182 */
183 int
add_ivintr(uint_t inum,uint_t pil,intrfunc intr_handler,caddr_t intr_arg1,caddr_t intr_arg2,caddr_t intr_payload)184 add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler,
185 caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload)
186 {
187 intr_vec_t *iv_p, *new_iv_p;
188
189 if (inum >= MAXIVNUM || pil > PIL_MAX)
190 return (EINVAL);
191
192 ASSERT((uintptr_t)intr_handler > KERNELBASE);
193
194 /* Make sure the payload buffer address is 64 bit aligned */
195 VERIFY(((uint64_t)intr_payload & 0x7) == 0);
196
197 new_iv_p = iv_alloc(SOFTINT_ST);
198 mutex_enter(&intr_vec_mutex);
199
200 for (iv_p = (intr_vec_t *)intr_vec_table[inum];
201 iv_p; iv_p = iv_p->iv_vec_next) {
202 if (iv_p->iv_pil == pil) {
203 mutex_exit(&intr_vec_mutex);
204 iv_free(new_iv_p);
205 return (EINVAL);
206 }
207 }
208
209 ASSERT(iv_p == NULL);
210
211 new_iv_p->iv_handler = intr_handler;
212 new_iv_p->iv_arg1 = intr_arg1;
213 new_iv_p->iv_arg2 = intr_arg2;
214 new_iv_p->iv_payload_buf = intr_payload;
215 new_iv_p->iv_pil = (ushort_t)pil;
216 new_iv_p->iv_inum = inum;
217
218 new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum];
219 intr_vec_table[inum] = (uint64_t)new_iv_p;
220
221 mutex_exit(&intr_vec_mutex);
222 return (0);
223 }
224
225 /*
226 * rem_ivintr() - Remove an interrupt handler from the system
227 */
228 int
rem_ivintr(uint_t inum,uint_t pil)229 rem_ivintr(uint_t inum, uint_t pil)
230 {
231 intr_vec_t *iv_p, *prev_iv_p;
232
233 if (inum >= MAXIVNUM || pil > PIL_MAX)
234 return (EINVAL);
235
236 mutex_enter(&intr_vec_mutex);
237
238 for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum];
239 iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next)
240 if (iv_p->iv_pil == pil)
241 break;
242
243 if (iv_p == NULL) {
244 mutex_exit(&intr_vec_mutex);
245 return (EIO);
246 }
247
248 ASSERT(iv_p->iv_pil_next == NULL);
249
250 if (prev_iv_p == iv_p)
251 intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next;
252 else
253 prev_iv_p->iv_vec_next = iv_p->iv_vec_next;
254
255 mutex_exit(&intr_vec_mutex);
256
257 iv_free(iv_p);
258 return (0);
259 }
260
261 /*
262 * add_softintr() - add a software interrupt handler to the system
263 */
264 uint64_t
add_softintr(uint_t pil,softintrfunc intr_handler,caddr_t intr_arg1,softint_type_t type)265 add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1,
266 softint_type_t type)
267 {
268 intr_vec_t *iv_p;
269
270 if (pil > PIL_MAX)
271 return ((uint64_t)NULL);
272
273 iv_p = iv_alloc(type);
274
275 iv_p->iv_handler = (intrfunc)intr_handler;
276 iv_p->iv_arg1 = intr_arg1;
277 iv_p->iv_pil = (ushort_t)pil;
278 if (type == SOFTINT_MT)
279 iv_p->iv_flags |= IV_SOFTINT_MT;
280
281 mutex_enter(&softint_mutex);
282 if (softint_list)
283 iv_p->iv_vec_next = softint_list;
284 softint_list = iv_p;
285 mutex_exit(&softint_mutex);
286
287 return ((uint64_t)iv_p);
288 }
289
290 /*
291 * rem_softintr() - remove a software interrupt handler from the system
292 */
293 int
rem_softintr(uint64_t softint_id)294 rem_softintr(uint64_t softint_id)
295 {
296 intr_vec_t *iv_p = (intr_vec_t *)softint_id;
297
298 ASSERT(iv_p != NULL);
299
300 if (iv_p->iv_flags & IV_SOFTINT_PEND)
301 return (EIO);
302
303 ASSERT(iv_p->iv_pil_next == NULL);
304
305 mutex_enter(&softint_mutex);
306 if (softint_list == iv_p) {
307 softint_list = iv_p->iv_vec_next;
308 } else {
309 intr_vec_t *list = softint_list;
310
311 while (list && (list->iv_vec_next != iv_p))
312 list = list->iv_vec_next;
313
314 list->iv_vec_next = iv_p->iv_vec_next;
315 }
316 mutex_exit(&softint_mutex);
317
318 iv_free(iv_p);
319 return (0);
320 }
321
322 /*
323 * update_softint_arg2() - Update softint arg2.
324 *
325 * NOTE: Do not grab any mutex in this function since it may get called
326 * from the high-level interrupt context.
327 */
328 int
update_softint_arg2(uint64_t softint_id,caddr_t intr_arg2)329 update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2)
330 {
331 intr_vec_t *iv_p = (intr_vec_t *)softint_id;
332
333 ASSERT(iv_p != NULL);
334
335 if (iv_p->iv_flags & IV_SOFTINT_PEND)
336 return (EIO);
337
338 iv_p->iv_arg2 = intr_arg2;
339 return (0);
340 }
341
342 /*
343 * update_softint_pri() - Update softint priority.
344 */
345 int
update_softint_pri(uint64_t softint_id,uint_t pil)346 update_softint_pri(uint64_t softint_id, uint_t pil)
347 {
348 intr_vec_t *iv_p = (intr_vec_t *)softint_id;
349
350 ASSERT(iv_p != NULL);
351
352 if (pil > PIL_MAX)
353 return (EINVAL);
354
355 iv_p->iv_pil = pil;
356 return (0);
357 }
358