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 2004 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 PRM_DEBUG(dmv_dispatch_table); 347 } 348 349 return (0); 350 } 351