xref: /illumos-gate/usr/src/uts/sun4/os/dmv.c (revision 264d6c47)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/sysmacros.h>
32 #include <sys/kobj.h>
33 #include <sys/membar.h>
34 #include <sys/dmv.h>
35 #include <sys/prom_debug.h>
36 #include <sys/machsystm.h>
37 
38 /*
39  * Implementation of databearing mondo vector handler registration routines.
40  * See PSARC 1998/222 for more details.
41  */
42 
43 /*
44  * The dmv_interface_*_version variables are provided to protect a
45  * driver against changes in the databearing mondo interfaces.
46  *
47  * The major version is incremented when an incompatible change
48  * is made to an interface; for instance, a routine which used to take
49  * 3 parameters now takes 4, or a routine which used have the semantics
50  * "do X" now has the semantics "do Y".  Before calling any of the
51  * databearing mondo routines, a driver must check the major version
52  * it was compiled with (i.e., the constant DMV_INTERFACE_MAJOR_VERSION)
53  * against the contents of dmv_interface_major_version.  If the two
54  * are different, the driver must refuse to operate.
55  *
56  * The minor version is incremented when an upward-compatible change
57  * is made to an interface; for instance, a routine now supports a new
58  * flag bit (in an existing flags argument).  A client can use the
59  * minor version to see whether a feature it depends on is available
60  * in its environment; in order to enable this, the documentation
61  * for new features should note which major and minor version the
62  * feature first appears in.
63  */
64 
65 int dmv_interface_major_version = DMV_INTERFACE_MAJOR_VERSION;
66 int dmv_interface_minor_version = DMV_INTERFACE_MINOR_VERSION;
67 
68 /*
69  * These are where the number of hardware and software DMV inums are kept.
70  * If they're zero, we use the platform's default values.  (These are not
71  * patchable in /etc/system, since the dispatch table is allocated before
72  * /etc/system is loaded; however, you could patch them by adb'ing unix.)
73  */
74 
75 uint_t dmv_hwint = 0;
76 uint_t dmv_swint = 0;
77 uint_t dmv_totalints = 0;
78 
79 struct dmv_disp *dmv_dispatch_table = (struct dmv_disp *)0;
80 
81 /*
82  * dmv_disp_lock protects the dispatch table from being modified by two
83  * threads concurrently.  It is not used to protect the table from being
84  * modified while being used by the actual interrupt dispatch code; see
85  * comments at the end of dmv.h for the rationale.
86  */
87 
88 kmutex_t dmv_disp_lock;
89 
90 /*
91  * dmv_add_intr is called to add a databearing mondo interrupt handler
92  * for a real device to the system.  Only one handler may be registered
93  * for a dmv_inum at any one time.
94  *
95  * Note that if a processor receives a databearing mondo interrupt
96  * for which a handler has not been registered, the behavior is
97  * undefined.  (Current practice for normal mondos which are unhandled
98  * depends on whether DEBUG is on; a DEBUG kernel prints an error
99  * and breaks to OBP, while a non-DEBUG kernel simply panics.  This
100  * model will likely be followed for databearing mondos.)
101  *
102  * Parameters:
103  *	dmv_inum	interrupt number for the device.
104  *
105  *	routine		pointer to the device's vectored interrupt
106  *			handler.  This routine is subject to the
107  *			constraints outlined below in "Handler
108  *			Characteristics and Environment".
109  *
110  *	arg		argument which will be passed to the device's
111  *			handler.
112  *
113  * Return value:	0 if the handler was added successfully, -1 if
114  *			handler was already registered for the given
115  *			dmv_inum.
116  *
117  * Handler Characteristics and Environment
118  *
119  *   Handler Entry:
120  *
121  *	On entry to the handler, the %g registers are set as follows:
122  *
123  *	%g1	The argument (arg) passed to dmv_add_intr().
124  *	%g2	Word 0 of the incoming mondo vector.
125  *
126  *
127  *   Handler Constraints:
128  *
129  *	While executing, the handler must obey the following rules:
130  *
131  *	1. The handler is limited to the use of registers %g1 through
132  *	   %g7.
133  *
134  *	2. The handler may not modify %cwp (i.e., may not execute a
135  *	   SAVE or RESTORE instruction).
136  *
137  *	3. The handler may not read or write the stack.
138  *
139  *	4. The handler may not call any other DDI or kernel routines.
140  *
141  *	5. The handler may not call any other routines inside the
142  *	   handler's own driver, since this would modify %o7; however,
143  *	   it is permissible to jump to a routine within the handler's
144  *	   driver.
145  *
146  *	6. The handler may read the Incoming Interrupt Vector Data
147  *	   registers, and the Interrupt Vector Receive register, but
148  *	   must not modify these registers.  (Note: symbols for the
149  *	   ASIs and addresses of these registers are in <sys/spitasi.h>
150  *	   and <sys/intreg.h>.)
151  *
152  *	7. The handler may read or write driver-private data
153  *	   structures; in order to protect against simultaneous
154  *	   modification by other driver routines, nonblocking data
155  *	   sharing algorithms must be used.  (For instance,
156  *	   compare-and-swap could be used to update counters or add
157  *	   entries to linked lists; producer-consumer queues are
158  *	   another possibility.)
159  *
160  *	8. The handler should neither read nor write any other
161  *	   processor register nor kernel data item which is not
162  *	   explicitly mentioned in this list.  [Yes, this is rather
163  *	   strict; the intent here is that as handler implementations
164  *	   are done, and more experience is gained, additional items
165  *	   may be permitted.]
166  *
167  *
168  *   Handler Exit:
169  *
170  *	When the handler's processing is complete, the handler must
171  *	exit by jumping to the label dmv_finish_intr.  At this time,
172  *	the handler may optionally request the execution of a soft
173  *	interrupt routine in order to do further processing at normal
174  *	interrupt level.  It is strongly advised that drivers do
175  *	minimal processing in their databearing mondo handlers;
176  *	whenever possible, tasks should be postponed to a later
177  *	soft interrupt routine.  (This is analogous to the DDI
178  *	"high-level interrupt" concept, although a databearing mondo
179  *	handler's environment is even more restrictive than that of
180  *	a high-level interrupt routine.)
181  *
182  *	Soft interrupt routines should be registered by calling
183  *	add_softintr(), which will return an interrupt number.  This
184  *	interrupt number should be saved in a driver-private data
185  *	structure for later use.
186  *
187  *	The contents of %g1 on entry to dmv_finish_intr determine
188  *	whether a soft interrupt routine will be called, as follows:
189  *
190  *		If %g1 is less than zero, no interrupt will be queued.
191  *
192  *		Otherwise, %g1 is assumed to be an interrupt number
193  *		obtained from add_softintr.  This interrupt routine
194  *		will be executed in the normal way at the requested
195  *		priority.  (Note that this routine may or may not
196  *		execute on the same CPU as the current handler.)
197  */
198 
199 int
200 dmv_add_intr(int dmv_inum, void (*routine)(), void *arg)
201 {
202 	if (dmv_inum < 0 || dmv_inum >= dmv_hwint)
203 		return (-1);
204 
205 	mutex_enter(&dmv_disp_lock);
206 
207 	if (dmv_dispatch_table[dmv_inum].dmv_func != 0) {
208 		mutex_exit(&dmv_disp_lock);
209 		return (-1);
210 	}
211 
212 	dmv_dispatch_table[dmv_inum].dmv_arg = arg;
213 
214 	membar_sync();
215 
216 	dmv_dispatch_table[dmv_inum].dmv_func = routine;
217 
218 	mutex_exit(&dmv_disp_lock);
219 	return (0);
220 }
221 
222 /*
223  * dmv_add_softintr is called to add a databearing mondo interrupt
224  * handler for a pseudo-device to the system.
225  *
226  * Parameters:
227  *	routine		pointer to the device's vectored interrupt
228  *			handler.  This routine is subject to the
229  *			constraints outlined above in "Handler
230  *			Characteristics and Environment".
231  *
232  *	arg		argument which will be passed to the device's
233  *			handler.
234  *
235  * Return value:	dmv_inum allocated if one was available, -1 if
236  *			all soft dmv_inums are already allocated
237  */
238 
239 int
240 dmv_add_softintr(void (*routine)(void), void *arg)
241 {
242 	int i;
243 
244 	mutex_enter(&dmv_disp_lock);
245 
246 	for (i = dmv_hwint; i < dmv_totalints; i++) {
247 		if (dmv_dispatch_table[i].dmv_func == 0) {
248 
249 			dmv_dispatch_table[i].dmv_arg = arg;
250 
251 			membar_sync();
252 
253 			dmv_dispatch_table[i].dmv_func = routine;
254 
255 			mutex_exit(&dmv_disp_lock);
256 			return (i);
257 		}
258 	}
259 
260 	mutex_exit(&dmv_disp_lock);
261 	return (-1);
262 }
263 
264 /*
265  * dmv_rem_intr is called to remove a databearing interrupt handler
266  * from the system.
267  *
268  * Parameters:
269  *	dmv_inum	interrupt number for the device.
270  *
271  * Return value:	0 if the handler was removed successfully, -1
272  *			if no handler was registered for the given
273  *			dmv_inum.
274  */
275 
276 int
277 dmv_rem_intr(int dmv_inum)
278 {
279 	if (dmv_inum < 0 || dmv_inum >= (dmv_totalints))
280 		return (-1);
281 
282 	mutex_enter(&dmv_disp_lock);
283 
284 	if (dmv_dispatch_table[dmv_inum].dmv_func == 0) {
285 		mutex_exit(&dmv_disp_lock);
286 		return (-1);
287 	}
288 
289 	dmv_dispatch_table[dmv_inum].dmv_func = 0;
290 
291 	mutex_exit(&dmv_disp_lock);
292 	return (0);
293 }
294 
295 
296 /*
297  * Allocate the dmv dispatch table from nucleus data memory.
298  */
299 int
300 ndata_alloc_dmv(struct memlist *ndata)
301 {
302 	size_t alloc_sz;
303 
304 	uint_t plat_hwint = 0;
305 	uint_t plat_swint = 0;
306 
307 	void (*plat_dmv_params)(uint_t *, uint_t *);
308 
309 
310 	/*
311 	 * Get platform default values, if they exist
312 	 */
313 
314 	plat_dmv_params = (void (*)(uint_t *, uint_t *))
315 	    kobj_getsymvalue("plat_dmv_params", 0);
316 
317 	if (plat_dmv_params)
318 		(*plat_dmv_params)(&plat_hwint, &plat_swint);
319 
320 	/*
321 	 * Set sizes to platform defaults if user hasn't set them
322 	 */
323 
324 	if (dmv_hwint == 0)
325 		dmv_hwint = plat_hwint;
326 
327 	if (dmv_swint == 0)
328 		dmv_swint = plat_swint;
329 
330 	/*
331 	 * Allocate table if we need it
332 	 */
333 	dmv_totalints = dmv_hwint + dmv_swint;
334 
335 	if (dmv_totalints != 0) {
336 
337 		alloc_sz = sizeof (struct dmv_disp) * (dmv_totalints + 1);
338 
339 		dmv_dispatch_table = ndata_alloc(ndata, alloc_sz,
340 		    sizeof (struct dmv_disp));
341 
342 		if (dmv_dispatch_table == NULL)
343 			return (-1);
344 
345 		bzero(dmv_dispatch_table, alloc_sz);
346 		/* use uintptr_t to suppress the gcc warning */
347 		PRM_DEBUG((uintptr_t)dmv_dispatch_table);
348 	}
349 
350 	return (0);
351 }
352