summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4/os/dmv.c
blob: d2c94102c0a507d966abe96222bfa42eb90edf26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/kobj.h>
#include <sys/membar.h>
#include <sys/dmv.h>
#include <sys/prom_debug.h>
#include <sys/machsystm.h>
#include <vm/vm_dep.h>

/*
 * Implementation of databearing mondo vector handler registration routines.
 * See PSARC 1998/222 for more details.
 */

/*
 * The dmv_interface_*_version variables are provided to protect a
 * driver against changes in the databearing mondo interfaces.
 *
 * The major version is incremented when an incompatible change
 * is made to an interface; for instance, a routine which used to take
 * 3 parameters now takes 4, or a routine which used have the semantics
 * "do X" now has the semantics "do Y".  Before calling any of the
 * databearing mondo routines, a driver must check the major version
 * it was compiled with (i.e., the constant DMV_INTERFACE_MAJOR_VERSION)
 * against the contents of dmv_interface_major_version.  If the two
 * are different, the driver must refuse to operate.
 *
 * The minor version is incremented when an upward-compatible change
 * is made to an interface; for instance, a routine now supports a new
 * flag bit (in an existing flags argument).  A client can use the
 * minor version to see whether a feature it depends on is available
 * in its environment; in order to enable this, the documentation
 * for new features should note which major and minor version the
 * feature first appears in.
 */

int dmv_interface_major_version = DMV_INTERFACE_MAJOR_VERSION;
int dmv_interface_minor_version = DMV_INTERFACE_MINOR_VERSION;

/*
 * These are where the number of hardware and software DMV inums are kept.
 * If they're zero, we use the platform's default values.  (These are not
 * patchable in /etc/system, since the dispatch table is allocated before
 * /etc/system is loaded; however, you could patch them by adb'ing unix.)
 */

uint_t dmv_hwint = 0;
uint_t dmv_swint = 0;
uint_t dmv_totalints = 0;

struct dmv_disp *dmv_dispatch_table = (struct dmv_disp *)0;

/*
 * dmv_disp_lock protects the dispatch table from being modified by two
 * threads concurrently.  It is not used to protect the table from being
 * modified while being used by the actual interrupt dispatch code; see
 * comments at the end of dmv.h for the rationale.
 */

kmutex_t dmv_disp_lock;

/*
 * dmv_add_intr is called to add a databearing mondo interrupt handler
 * for a real device to the system.  Only one handler may be registered
 * for a dmv_inum at any one time.
 *
 * Note that if a processor receives a databearing mondo interrupt
 * for which a handler has not been registered, the behavior is
 * undefined.  (Current practice for normal mondos which are unhandled
 * depends on whether DEBUG is on; a DEBUG kernel prints an error
 * and breaks to OBP, while a non-DEBUG kernel simply panics.  This
 * model will likely be followed for databearing mondos.)
 *
 * Parameters:
 *	dmv_inum	interrupt number for the device.
 *
 *	routine		pointer to the device's vectored interrupt
 *			handler.  This routine is subject to the
 *			constraints outlined below in "Handler
 *			Characteristics and Environment".
 *
 *	arg		argument which will be passed to the device's
 *			handler.
 *
 * Return value:	0 if the handler was added successfully, -1 if
 *			handler was already registered for the given
 *			dmv_inum.
 *
 * Handler Characteristics and Environment
 *
 *   Handler Entry:
 *
 *	On entry to the handler, the %g registers are set as follows:
 *
 *	%g1	The argument (arg) passed to dmv_add_intr().
 *	%g2	Word 0 of the incoming mondo vector.
 *
 *
 *   Handler Constraints:
 *
 *	While executing, the handler must obey the following rules:
 *
 *	1. The handler is limited to the use of registers %g1 through
 *	   %g7.
 *
 *	2. The handler may not modify %cwp (i.e., may not execute a
 *	   SAVE or RESTORE instruction).
 *
 *	3. The handler may not read or write the stack.
 *
 *	4. The handler may not call any other DDI or kernel routines.
 *
 *	5. The handler may not call any other routines inside the
 *	   handler's own driver, since this would modify %o7; however,
 *	   it is permissible to jump to a routine within the handler's
 *	   driver.
 *
 *	6. The handler may read the Incoming Interrupt Vector Data
 *	   registers, and the Interrupt Vector Receive register, but
 *	   must not modify these registers.  (Note: symbols for the
 *	   ASIs and addresses of these registers are in <sys/spitasi.h>
 *	   and <sys/intreg.h>.)
 *
 *	7. The handler may read or write driver-private data
 *	   structures; in order to protect against simultaneous
 *	   modification by other driver routines, nonblocking data
 *	   sharing algorithms must be used.  (For instance,
 *	   compare-and-swap could be used to update counters or add
 *	   entries to linked lists; producer-consumer queues are
 *	   another possibility.)
 *
 *	8. The handler should neither read nor write any other
 *	   processor register nor kernel data item which is not
 *	   explicitly mentioned in this list.  [Yes, this is rather
 *	   strict; the intent here is that as handler implementations
 *	   are done, and more experience is gained, additional items
 *	   may be permitted.]
 *
 *
 *   Handler Exit:
 *
 *	When the handler's processing is complete, the handler must
 *	exit by jumping to the label dmv_finish_intr.  At this time,
 *	the handler may optionally request the execution of a soft
 *	interrupt routine in order to do further processing at normal
 *	interrupt level.  It is strongly advised that drivers do
 *	minimal processing in their databearing mondo handlers;
 *	whenever possible, tasks should be postponed to a later
 *	soft interrupt routine.  (This is analogous to the DDI
 *	"high-level interrupt" concept, although a databearing mondo
 *	handler's environment is even more restrictive than that of
 *	a high-level interrupt routine.)
 *
 *	Soft interrupt routines should be registered by calling
 *	add_softintr(), which will return an interrupt number.  This
 *	interrupt number should be saved in a driver-private data
 *	structure for later use.
 *
 *	The contents of %g1 on entry to dmv_finish_intr determine
 *	whether a soft interrupt routine will be called, as follows:
 *
 *		If %g1 is less than zero, no interrupt will be queued.
 *
 *		Otherwise, %g1 is assumed to be an interrupt number
 *		obtained from add_softintr.  This interrupt routine
 *		will be executed in the normal way at the requested
 *		priority.  (Note that this routine may or may not
 *		execute on the same CPU as the current handler.)
 */

int
dmv_add_intr(int dmv_inum, void (*routine)(), void *arg)
{
	if (dmv_inum < 0 || dmv_inum >= dmv_hwint)
		return (-1);

	mutex_enter(&dmv_disp_lock);

	if (dmv_dispatch_table[dmv_inum].dmv_func != 0) {
		mutex_exit(&dmv_disp_lock);
		return (-1);
	}

	dmv_dispatch_table[dmv_inum].dmv_arg = arg;

	membar_sync();

	dmv_dispatch_table[dmv_inum].dmv_func = routine;

	mutex_exit(&dmv_disp_lock);
	return (0);
}

/*
 * dmv_add_softintr is called to add a databearing mondo interrupt
 * handler for a pseudo-device to the system.
 *
 * Parameters:
 *	routine		pointer to the device's vectored interrupt
 *			handler.  This routine is subject to the
 *			constraints outlined above in "Handler
 *			Characteristics and Environment".
 *
 *	arg		argument which will be passed to the device's
 *			handler.
 *
 * Return value:	dmv_inum allocated if one was available, -1 if
 *			all soft dmv_inums are already allocated
 */

int
dmv_add_softintr(void (*routine)(void), void *arg)
{
	int i;

	mutex_enter(&dmv_disp_lock);

	for (i = dmv_hwint; i < dmv_totalints; i++) {
		if (dmv_dispatch_table[i].dmv_func == 0) {

			dmv_dispatch_table[i].dmv_arg = arg;

			membar_sync();

			dmv_dispatch_table[i].dmv_func = routine;

			mutex_exit(&dmv_disp_lock);
			return (i);
		}
	}

	mutex_exit(&dmv_disp_lock);
	return (-1);
}

/*
 * dmv_rem_intr is called to remove a databearing interrupt handler
 * from the system.
 *
 * Parameters:
 *	dmv_inum	interrupt number for the device.
 *
 * Return value:	0 if the handler was removed successfully, -1
 *			if no handler was registered for the given
 *			dmv_inum.
 */

int
dmv_rem_intr(int dmv_inum)
{
	if (dmv_inum < 0 || dmv_inum >= (dmv_totalints))
		return (-1);

	mutex_enter(&dmv_disp_lock);

	if (dmv_dispatch_table[dmv_inum].dmv_func == 0) {
		mutex_exit(&dmv_disp_lock);
		return (-1);
	}

	dmv_dispatch_table[dmv_inum].dmv_func = 0;

	mutex_exit(&dmv_disp_lock);
	return (0);
}


/*
 * Allocate the dmv dispatch table from nucleus data memory.
 */
int
ndata_alloc_dmv(struct memlist *ndata)
{
	size_t alloc_sz;

	uint_t plat_hwint = 0;
	uint_t plat_swint = 0;

	void (*plat_dmv_params)(uint_t *, uint_t *);


	/*
	 * Get platform default values, if they exist
	 */

	plat_dmv_params = (void (*)(uint_t *, uint_t *))
	    kobj_getsymvalue("plat_dmv_params", 0);

	if (plat_dmv_params)
		(*plat_dmv_params)(&plat_hwint, &plat_swint);

	/*
	 * Set sizes to platform defaults if user hasn't set them
	 */

	if (dmv_hwint == 0)
		dmv_hwint = plat_hwint;

	if (dmv_swint == 0)
		dmv_swint = plat_swint;

	/*
	 * Allocate table if we need it
	 */
	dmv_totalints = dmv_hwint + dmv_swint;

	if (dmv_totalints != 0) {

		alloc_sz = sizeof (struct dmv_disp) * (dmv_totalints + 1);

		dmv_dispatch_table = ndata_alloc(ndata, alloc_sz,
		    sizeof (struct dmv_disp));

		if (dmv_dispatch_table == NULL)
			return (-1);

		bzero(dmv_dispatch_table, alloc_sz);
		/* use uintptr_t to suppress the gcc warning */
		PRM_DEBUG((uintptr_t)dmv_dispatch_table);
	}

	return (0);
}