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 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <sys/types.h>
29 #include <sys/systm.h>
30 #include <sys/sysmacros.h>
31 #include <sys/kobj.h>
32 #include <sys/membar.h>
33 #include <sys/dmv.h>
34 #include <sys/prom_debug.h>
35 #include <sys/machsystm.h>
36 #include <vm/vm_dep.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
dmv_add_intr(int dmv_inum,void (* routine)(),void * arg)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
dmv_add_softintr(void (* routine)(void),void * arg)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
dmv_rem_intr(int dmv_inum)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
ndata_alloc_dmv(struct memlist * ndata)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