xref: /illumos-gate/usr/src/uts/sun4/io/ivintr.c (revision e86372a01d2d16a5dd4a64e144ed978ba17fe7dd)
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
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
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 *
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
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
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
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
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
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
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
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