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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Host to hypervisor virtual devices nexus driver
29 *
30 * TODO:
31 * - Add watchpoints on vbd/vif and enumerate/offline on watch callback
32 * - Add DR IOCTLs
33 * - Filter/restrict property lookups into xenstore
34 */
35
36 #include <sys/conf.h>
37 #include <sys/kmem.h>
38 #include <sys/debug.h>
39 #include <sys/modctl.h>
40 #include <sys/autoconf.h>
41 #include <sys/ddi_impldefs.h>
42 #include <sys/ddi_subrdefs.h>
43 #include <sys/ddi.h>
44 #include <sys/sunddi.h>
45 #include <sys/sunndi.h>
46 #include <sys/avintr.h>
47 #include <sys/psm.h>
48 #include <sys/spl.h>
49 #include <sys/promif.h>
50 #include <sys/list.h>
51 #include <sys/bootconf.h>
52 #include <sys/bootsvcs.h>
53 #include <util/sscanf.h>
54 #include <sys/mach_intr.h>
55 #include <sys/bootinfo.h>
56 #ifdef XPV_HVM_DRIVER
57 #include <sys/xpv_support.h>
58 #include <sys/hypervisor.h>
59 #include <sys/archsystm.h>
60 #include <sys/cpu.h>
61 #include <public/xen.h>
62 #include <public/event_channel.h>
63 #include <public/io/xenbus.h>
64 #else
65 #include <sys/hypervisor.h>
66 #include <sys/evtchn_impl.h>
67 #include <sys/xen_mmu.h>
68 #endif
69 #include <xen/sys/xenbus_impl.h>
70 #include <xen/sys/xendev.h>
71
72 /*
73 * DDI dev_ops entrypoints
74 */
75 static int xpvd_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
76 static int xpvd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
77 static int xpvd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
78
79
80 /*
81 * NDI bus_ops entrypoints
82 */
83 static int xpvd_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
84 void *);
85 static int xpvd_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
86 ddi_intr_handle_impl_t *, void *);
87 static int xpvd_prop_op(dev_t, dev_info_t *, dev_info_t *, ddi_prop_op_t,
88 int, char *, caddr_t, int *);
89 static int xpvd_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
90 void *, dev_info_t **);
91 static int xpvd_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
92 void *);
93 static int xpvd_get_eventcookie(dev_info_t *, dev_info_t *,
94 char *, ddi_eventcookie_t *);
95 static int xpvd_add_eventcall(dev_info_t *, dev_info_t *,
96 ddi_eventcookie_t, void (*)(dev_info_t *,
97 ddi_eventcookie_t, void *, void *),
98 void *, ddi_callback_id_t *);
99 static int xpvd_remove_eventcall(dev_info_t *, ddi_callback_id_t);
100 static int xpvd_post_event(dev_info_t *, dev_info_t *,
101 ddi_eventcookie_t, void *);
102
103 /*
104 * misc functions
105 */
106 static int xpvd_enable_intr(dev_info_t *, ddi_intr_handle_impl_t *, int);
107 static void xpvd_disable_intr(dev_info_t *, ddi_intr_handle_impl_t *, int);
108 static int xpvd_removechild(dev_info_t *);
109 static int xpvd_initchild(dev_info_t *);
110 static int xpvd_name_child(dev_info_t *, char *, int);
111 static boolean_t i_xpvd_parse_devname(char *, xendev_devclass_t *,
112 domid_t *, int *);
113
114
115 /* Extern declarations */
116 extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
117 psm_intr_op_t, int *);
118
119 struct bus_ops xpvd_bus_ops = {
120 BUSO_REV,
121 i_ddi_bus_map,
122 NULL,
123 NULL,
124 NULL,
125 i_ddi_map_fault,
126 ddi_dma_map,
127 ddi_dma_allochdl,
128 ddi_dma_freehdl,
129 ddi_dma_bindhdl,
130 ddi_dma_unbindhdl,
131 ddi_dma_flush,
132 ddi_dma_win,
133 ddi_dma_mctl,
134 xpvd_ctlops,
135 xpvd_prop_op,
136 xpvd_get_eventcookie,
137 xpvd_add_eventcall,
138 xpvd_remove_eventcall,
139 xpvd_post_event,
140 0, /* (*bus_intr_ctl)(); */
141 xpvd_bus_config,
142 xpvd_bus_unconfig,
143 NULL, /* (*bus_fm_init)(); */
144 NULL, /* (*bus_fm_fini)(); */
145 NULL, /* (*bus_fm_access_enter)(); */
146 NULL, /* (*bus_fm_access_exit)(); */
147 NULL, /* (*bus_power)(); */
148 xpvd_intr_ops /* (*bus_intr_op)(); */
149 };
150
151 struct dev_ops xpvd_ops = {
152 DEVO_REV, /* devo_rev */
153 0, /* refcnt */
154 xpvd_info, /* info */
155 nulldev, /* identify */
156 nulldev, /* probe */
157 xpvd_attach, /* attach */
158 xpvd_detach, /* detach */
159 nulldev, /* reset */
160 (struct cb_ops *)0, /* driver operations */
161 &xpvd_bus_ops, /* bus operations */
162 NULL, /* power */
163 ddi_quiesce_not_needed, /* quiesce */
164 };
165
166
167 dev_info_t *xpvd_dip;
168
169 #define CF_DBG 0x1
170 #define ALL_DBG 0xff
171
172 static ndi_event_definition_t xpvd_ndi_event_defs[] = {
173 { 0, XS_OE_STATE, EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
174 { 1, XS_HP_STATE, EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
175 };
176
177 #define XENDEV_N_NDI_EVENTS \
178 (sizeof (xpvd_ndi_event_defs) / sizeof (xpvd_ndi_event_defs[0]))
179
180 static ndi_event_set_t xpvd_ndi_events = {
181 NDI_EVENTS_REV1, XENDEV_N_NDI_EVENTS, xpvd_ndi_event_defs
182 };
183
184 static ndi_event_hdl_t xpvd_ndi_event_handle;
185
186 /*
187 * Hypervisor interrupt capabilities
188 */
189 #define XENDEV_INTR_CAPABILITIES \
190 (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_PENDING)
191
192 /*
193 * Module linkage information for the kernel.
194 */
195
196 static struct modldrv modldrv = {
197 &mod_driverops, /* Type of module */
198 "virtual device nexus driver",
199 &xpvd_ops, /* driver ops */
200 };
201
202 static struct modlinkage modlinkage = {
203 MODREV_1,
204 (void *)&modldrv,
205 NULL
206 };
207
208 int
_init(void)209 _init(void)
210 {
211 return (mod_install(&modlinkage));
212 }
213
214 int
_fini(void)215 _fini(void)
216 {
217 return (mod_remove(&modlinkage));
218 }
219
220 int
_info(struct modinfo * modinfop)221 _info(struct modinfo *modinfop)
222 {
223 return (mod_info(&modlinkage, modinfop));
224 }
225
226 /* ARGSUSED */
227 static int
xpvd_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)228 xpvd_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
229 {
230 switch (cmd) {
231 default:
232 return (DDI_FAILURE);
233
234 case DDI_INFO_DEVT2INSTANCE:
235 *result = (void *)0;
236 return (DDI_SUCCESS);
237
238 case DDI_INFO_DEVT2DEVINFO:
239 *result = (void *)xpvd_dip;
240 return (DDI_SUCCESS);
241 }
242 }
243
244 /*ARGSUSED*/
245 static int
xpvd_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)246 xpvd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
247 {
248 extern void xvdi_watch_devices(int);
249 #ifdef XPV_HVM_DRIVER
250 extern dev_info_t *xpv_dip;
251
252 if (xpv_dip == NULL) {
253 if (ddi_hold_installed_driver(ddi_name_to_major("xpv")) ==
254 NULL) {
255 cmn_err(CE_WARN, "Couldn't initialize xpv framework");
256 return (DDI_FAILURE);
257 }
258 }
259 #endif /* XPV_HVM_DRIVER */
260
261 if (ndi_event_alloc_hdl(devi, 0, &xpvd_ndi_event_handle,
262 NDI_SLEEP) != NDI_SUCCESS) {
263 xpvd_dip = NULL;
264 return (DDI_FAILURE);
265 }
266 if (ndi_event_bind_set(xpvd_ndi_event_handle, &xpvd_ndi_events,
267 NDI_SLEEP) != NDI_SUCCESS) {
268 (void) ndi_event_free_hdl(xpvd_ndi_event_handle);
269 xpvd_dip = NULL;
270 return (DDI_FAILURE);
271 }
272
273 #ifdef XPV_HVM_DRIVER
274 (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi, DDI_NO_AUTODETACH, 1);
275
276 /*
277 * Report our version to dom0.
278 */
279 if (xenbus_printf(XBT_NULL, "guest/xpvd", "version", "%d",
280 HVMPV_XPVD_VERS))
281 cmn_err(CE_WARN, "xpvd: couldn't write version\n");
282 #endif /* XPV_HVM_DRIVER */
283
284 /* watch both frontend and backend for new devices */
285 if (DOMAIN_IS_INITDOMAIN(xen_info))
286 (void) xs_register_xenbus_callback(xvdi_watch_devices);
287 else
288 xvdi_watch_devices(XENSTORE_UP);
289
290 xpvd_dip = devi;
291 ddi_report_dev(devi);
292
293 return (DDI_SUCCESS);
294 }
295
296 /*ARGSUSED*/
297 static int
xpvd_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)298 xpvd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
299 {
300 return (DDI_FAILURE);
301 }
302
303 /*
304 * xpvd_prop_op()
305 *
306 * Query xenstore for the value of properties if DDI_PROP_NOTPROM
307 * is not set. Xenstore property values are represented as ascii strings.
308 */
309 static int
xpvd_prop_op(dev_t dev,dev_info_t * dip,dev_info_t * ch_dip,ddi_prop_op_t prop_op,int mod_flags,char * name,caddr_t valuep,int * lengthp)310 xpvd_prop_op(dev_t dev, dev_info_t *dip, dev_info_t *ch_dip,
311 ddi_prop_op_t prop_op, int mod_flags, char *name, caddr_t valuep,
312 int *lengthp)
313 {
314 caddr_t buff;
315 struct xendev_ppd *pdp;
316 void *prop_str;
317 size_t prop_len;
318 unsigned int len;
319 int rv;
320
321 pdp = (struct xendev_ppd *)ddi_get_parent_data(ch_dip);
322
323 if ((pdp == NULL) || !(mod_flags & (DDI_PROP_CANSLEEP)) ||
324 (mod_flags & DDI_PROP_NOTPROM) || (pdp->xd_xsdev.nodename == NULL))
325 goto toss_off;
326 /*
327 * First try reading the property off the the frontend. if that
328 * fails, try and read it from the backend node. If that
329 * also fails, pass the request on the DDI framework
330 */
331 prop_str = NULL;
332 if ((xenbus_read(XBT_NULL, pdp->xd_xsdev.nodename, name, &prop_str,
333 &len) == 0) && (prop_str != NULL) && (strlen(prop_str) != 0))
334 goto got_xs_prop;
335
336 prop_str = NULL;
337 if ((pdp->xd_xsdev.otherend != NULL) &&
338 (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, name, &prop_str,
339 &len) == 0) && (prop_str != NULL) && (strlen(prop_str) != 0))
340 goto got_xs_prop;
341
342 toss_off:
343 return (ddi_bus_prop_op(dev, dip, ch_dip, prop_op,
344 mod_flags | DDI_PROP_NOTPROM, name, valuep, lengthp));
345
346 got_xs_prop:
347 prop_len = strlen(prop_str) + 1;
348 rv = DDI_PROP_SUCCESS;
349
350 switch (prop_op) {
351 case PROP_LEN:
352 *lengthp = prop_len;
353 break;
354
355 case PROP_LEN_AND_VAL_ALLOC:
356 buff = kmem_alloc((size_t)prop_len, KM_SLEEP);
357 *(caddr_t *)valuep = (caddr_t)buff;
358 break;
359 case PROP_LEN_AND_VAL_BUF:
360 buff = (caddr_t)valuep;
361 if (*lengthp < prop_len)
362 rv = DDI_PROP_BUF_TOO_SMALL;
363 break;
364 default:
365 rv = DDI_PROP_INVAL_ARG;
366 break;
367 }
368
369 if ((rv == DDI_PROP_SUCCESS) && (prop_len > 0)) {
370 bcopy(prop_str, buff, prop_len);
371 *lengthp = prop_len;
372 }
373 kmem_free(prop_str, len);
374 return (rv);
375 }
376
377
378 /*
379 * return address of the device's interrupt spec structure.
380 */
381 /*ARGSUSED*/
382 struct intrspec *
xpvd_get_ispec(dev_info_t * rdip,uint_t inumber)383 xpvd_get_ispec(dev_info_t *rdip, uint_t inumber)
384 {
385 struct xendev_ppd *pdp;
386
387 ASSERT(inumber == 0);
388
389 if ((pdp = ddi_get_parent_data(rdip)) == NULL)
390 return (NULL);
391
392 return (&pdp->xd_ispec);
393 }
394
395 /*
396 * return (and determine) the interrupt priority of the device.
397 */
398 /*ARGSUSED*/
399 static int
xpvd_get_priority(dev_info_t * dip,int inum,int * pri)400 xpvd_get_priority(dev_info_t *dip, int inum, int *pri)
401 {
402 struct xendev_ppd *pdp;
403 struct intrspec *ispec;
404 int *intpriorities;
405 uint_t num_intpriorities;
406
407 DDI_INTR_NEXDBG((CE_CONT, "xpvd_get_priority: dip = 0x%p\n",
408 (void *)dip));
409
410 ASSERT(inum == 0);
411
412 if ((pdp = ddi_get_parent_data(dip)) == NULL)
413 return (DDI_FAILURE);
414
415 ispec = &pdp->xd_ispec;
416
417 /*
418 * Set the default priority based on the device class. The
419 * "interrupt-priorities" property can be used to override
420 * the default.
421 */
422 if (ispec->intrspec_pri == 0) {
423 ispec->intrspec_pri = xendev_devclass_ipl(pdp->xd_devclass);
424 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
425 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
426 "interrupt-priorities", &intpriorities,
427 &num_intpriorities) == DDI_PROP_SUCCESS) {
428 ispec->intrspec_pri = intpriorities[0];
429 ddi_prop_free(intpriorities);
430 }
431 }
432 *pri = ispec->intrspec_pri;
433 return (DDI_SUCCESS);
434 }
435
436
437 /*
438 * xpvd_intr_ops: bus_intr_op() function for interrupt support
439 */
440 /* ARGSUSED */
441 static int
xpvd_intr_ops(dev_info_t * pdip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)442 xpvd_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
443 ddi_intr_handle_impl_t *hdlp, void *result)
444 {
445 int priority = 0;
446 struct intrspec *ispec;
447 struct xendev_ppd *pdp;
448
449 DDI_INTR_NEXDBG((CE_CONT,
450 "xpvd_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
451 (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
452
453 /* Process the request */
454 switch (intr_op) {
455 case DDI_INTROP_SUPPORTED_TYPES:
456 /* Fixed supported by default */
457 *(int *)result = DDI_INTR_TYPE_FIXED;
458 break;
459
460 case DDI_INTROP_NINTRS:
461 *(int *)result = 1;
462 break;
463
464 case DDI_INTROP_ALLOC:
465 /*
466 * FIXED interrupts: just return available interrupts
467 */
468 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
469 /*
470 * event channels are edge-triggered, maskable,
471 * and support int pending.
472 */
473 hdlp->ih_cap |= XENDEV_INTR_CAPABILITIES;
474 *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */
475 } else {
476 return (DDI_FAILURE);
477 }
478 break;
479
480 case DDI_INTROP_FREE:
481 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
482 if (ispec == NULL)
483 return (DDI_FAILURE);
484 ispec->intrspec_pri = 0; /* mark as un-initialized */
485 break;
486
487 case DDI_INTROP_GETPRI:
488 if (xpvd_get_priority(rdip, hdlp->ih_inum, &priority) !=
489 DDI_SUCCESS)
490 return (DDI_FAILURE);
491 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: priority = 0x%x\n",
492 priority));
493 *(int *)result = priority;
494 break;
495
496 case DDI_INTROP_SETPRI:
497 /* Validate the interrupt priority passed */
498 if (*(int *)result > LOCK_LEVEL)
499 return (DDI_FAILURE);
500
501 /* Ensure that PSM is all initialized */
502 if (psm_intr_ops == NULL)
503 return (DDI_FAILURE);
504
505 /* Change the priority */
506 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
507 PSM_FAILURE)
508 return (DDI_FAILURE);
509
510 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
511 if (ispec == NULL)
512 return (DDI_FAILURE);
513 ispec->intrspec_pri = *(int *)result;
514 break;
515
516 case DDI_INTROP_ADDISR:
517 /* update ispec */
518 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
519 if (ispec == NULL)
520 return (DDI_FAILURE);
521 ispec->intrspec_func = hdlp->ih_cb_func;
522
523 break;
524
525 case DDI_INTROP_REMISR:
526 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
527 pdp = (struct xendev_ppd *)ddi_get_parent_data(rdip);
528
529 ASSERT(pdp != NULL);
530 ASSERT(pdp->xd_evtchn != INVALID_EVTCHN);
531
532 if (ispec) {
533 ispec->intrspec_vec = 0;
534 ispec->intrspec_func = (uint_t (*)()) 0;
535 }
536 pdp->xd_evtchn = INVALID_EVTCHN;
537 break;
538
539 case DDI_INTROP_GETCAP:
540 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
541 /*
542 * event channels are edge-triggered, maskable,
543 * and support int pending.
544 */
545 *(int *)result = XENDEV_INTR_CAPABILITIES;
546 } else {
547 *(int *)result = 0;
548 return (DDI_FAILURE);
549 }
550 DDI_INTR_NEXDBG((CE_CONT, "xpvd: GETCAP returned = %x\n",
551 *(int *)result));
552 break;
553 case DDI_INTROP_SETCAP:
554 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: SETCAP cap=0x%x\n",
555 *(int *)result));
556 if (psm_intr_ops == NULL)
557 return (DDI_FAILURE);
558
559 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
560 DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
561 " returned failure\n"));
562 return (DDI_FAILURE);
563 }
564 break;
565
566 case DDI_INTROP_ENABLE:
567 if (psm_intr_ops == NULL)
568 return (DDI_FAILURE);
569
570 if (xpvd_enable_intr(rdip, hdlp, (int)hdlp->ih_inum) !=
571 DDI_SUCCESS)
572 return (DDI_FAILURE);
573
574 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: ENABLE vec=0x%x\n",
575 hdlp->ih_vector));
576 break;
577
578 case DDI_INTROP_DISABLE:
579 if (psm_intr_ops == NULL)
580 return (DDI_FAILURE);
581 xpvd_disable_intr(rdip, hdlp, hdlp->ih_inum);
582 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: DISABLE vec = %x\n",
583 hdlp->ih_vector));
584 break;
585
586 case DDI_INTROP_BLOCKENABLE:
587 case DDI_INTROP_BLOCKDISABLE:
588 return (DDI_FAILURE);
589
590 case DDI_INTROP_SETMASK:
591 case DDI_INTROP_CLRMASK:
592 #ifdef XPV_HVM_DRIVER
593 return (DDI_ENOTSUP);
594 #else
595 /*
596 * Handle this here
597 */
598 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
599 return (DDI_FAILURE);
600 if (intr_op == DDI_INTROP_SETMASK) {
601 ec_disable_irq(hdlp->ih_vector);
602 } else {
603 ec_enable_irq(hdlp->ih_vector);
604 }
605 break;
606 #endif
607 case DDI_INTROP_GETPENDING:
608 #ifdef XPV_HVM_DRIVER
609 return (DDI_ENOTSUP);
610 #else
611 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
612 return (DDI_FAILURE);
613 *(int *)result = ec_pending_irq(hdlp->ih_vector);
614 DDI_INTR_NEXDBG((CE_CONT, "xpvd: GETPENDING returned = %x\n",
615 *(int *)result));
616 break;
617 #endif
618
619 case DDI_INTROP_NAVAIL:
620 *(int *)result = 1;
621 DDI_INTR_NEXDBG((CE_CONT, "xpvd: NAVAIL returned = %x\n",
622 *(int *)result));
623 break;
624
625 default:
626 return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
627 }
628
629 return (DDI_SUCCESS);
630 }
631
632
633 static int
xpvd_enable_intr(dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp,int inum)634 xpvd_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp, int inum)
635 {
636 int vector;
637 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
638
639 DDI_INTR_NEXDBG((CE_CONT, "xpvd_enable_intr: hdlp %p inum %x\n",
640 (void *)hdlp, inum));
641
642 ihdl_plat_datap->ip_ispecp = xpvd_get_ispec(rdip, inum);
643 if (ihdl_plat_datap->ip_ispecp == NULL)
644 return (DDI_FAILURE);
645
646 /* translate the interrupt if needed */
647 (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
648 DDI_INTR_NEXDBG((CE_CONT, "xpvd_enable_intr: priority=%x vector=%x\n",
649 hdlp->ih_pri, vector));
650
651 /* Add the interrupt handler */
652 if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
653 DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1,
654 hdlp->ih_cb_arg2, NULL, rdip))
655 return (DDI_FAILURE);
656
657 /* Note this really is an irq. */
658 hdlp->ih_vector = (ushort_t)vector;
659
660 return (DDI_SUCCESS);
661 }
662
663
664 static void
xpvd_disable_intr(dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp,int inum)665 xpvd_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp, int inum)
666 {
667 int vector;
668 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
669
670 DDI_INTR_NEXDBG((CE_CONT, "xpvd_disable_intr: \n"));
671 ihdl_plat_datap->ip_ispecp = xpvd_get_ispec(rdip, inum);
672 if (ihdl_plat_datap->ip_ispecp == NULL)
673 return;
674
675 /* translate the interrupt if needed */
676 (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
677
678 /* Disable the interrupt handler */
679 rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector);
680 ihdl_plat_datap->ip_ispecp = NULL;
681 }
682
683 /*ARGSUSED*/
684 static int
xpvd_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)685 xpvd_ctlops(dev_info_t *dip, dev_info_t *rdip,
686 ddi_ctl_enum_t ctlop, void *arg, void *result)
687 {
688 switch (ctlop) {
689 case DDI_CTLOPS_REPORTDEV:
690 if (rdip == (dev_info_t *)0)
691 return (DDI_FAILURE);
692 cmn_err(CE_CONT, "?%s@%s, %s%d\n", ddi_node_name(rdip),
693 ddi_get_name_addr(rdip), ddi_driver_name(rdip),
694 ddi_get_instance(rdip));
695 return (DDI_SUCCESS);
696
697 case DDI_CTLOPS_INITCHILD:
698 return (xpvd_initchild((dev_info_t *)arg));
699
700 case DDI_CTLOPS_UNINITCHILD:
701 return (xpvd_removechild((dev_info_t *)arg));
702
703 case DDI_CTLOPS_SIDDEV:
704 return (DDI_SUCCESS);
705
706 case DDI_CTLOPS_REGSIZE:
707 case DDI_CTLOPS_NREGS:
708 return (DDI_FAILURE);
709
710 case DDI_CTLOPS_POWER: {
711 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
712 }
713
714 default:
715 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
716 }
717
718 /* NOTREACHED */
719
720 }
721
722 /*
723 * Assign the address portion of the node name
724 */
725 static int
xpvd_name_child(dev_info_t * child,char * addr,int addrlen)726 xpvd_name_child(dev_info_t *child, char *addr, int addrlen)
727 {
728 int *domain, *vdev;
729 uint_t ndomain, nvdev;
730 char *prop_str;
731
732 /*
733 * i_xpvd_parse_devname() knows the formats used by this
734 * routine. If this code changes, so must that.
735 */
736
737 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
738 "domain", &domain, &ndomain) != DDI_PROP_SUCCESS)
739 return (DDI_FAILURE);
740 ASSERT(ndomain == 1);
741
742 /*
743 * Use "domain" and "vdev" properties (backend drivers).
744 */
745 if (*domain != DOMID_SELF) {
746 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
747 DDI_PROP_DONTPASS, "vdev", &vdev, &nvdev)
748 != DDI_PROP_SUCCESS) {
749 ddi_prop_free(domain);
750 return (DDI_FAILURE);
751 }
752 ASSERT(nvdev == 1);
753
754 (void) snprintf(addr, addrlen, "%d,%d", domain[0], vdev[0]);
755 ddi_prop_free(vdev);
756 ddi_prop_free(domain);
757 return (DDI_SUCCESS);
758 }
759 ddi_prop_free(domain);
760
761 /*
762 * Use "unit-address" property (frontend/softdev drivers).
763 */
764 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
765 "unit-address", &prop_str) != DDI_PROP_SUCCESS)
766 return (DDI_FAILURE);
767 (void) strlcpy(addr, prop_str, addrlen);
768 ddi_prop_free(prop_str);
769 return (DDI_SUCCESS);
770 }
771
772 static int
xpvd_initchild(dev_info_t * child)773 xpvd_initchild(dev_info_t *child)
774 {
775 char addr[80];
776
777 /*
778 * Pseudo nodes indicate a prototype node with per-instance
779 * properties to be merged into the real h/w device node.
780 */
781 if (ndi_dev_is_persistent_node(child) == 0) {
782 ddi_set_parent_data(child, NULL);
783
784 /*
785 * Try to merge the properties from this prototype
786 * node into real h/w nodes.
787 */
788 if (ndi_merge_node(child, xpvd_name_child) == DDI_SUCCESS) {
789 /*
790 * Merged ok - return failure to remove the node.
791 */
792 ddi_set_name_addr(child, NULL);
793 return (DDI_FAILURE);
794 }
795
796 /*
797 * The child was not merged into a h/w node,
798 * but there's not much we can do with it other
799 * than return failure to cause the node to be removed.
800 */
801 cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
802 ddi_get_name(child), ddi_get_name_addr(child),
803 ddi_get_name(child));
804 ddi_set_name_addr(child, NULL);
805 return (DDI_NOT_WELL_FORMED);
806 }
807
808 if (xvdi_init_dev(child) != DDI_SUCCESS)
809 return (DDI_FAILURE);
810
811 if (xpvd_name_child(child, addr, sizeof (addr)) != DDI_SUCCESS) {
812 xvdi_uninit_dev(child);
813 return (DDI_FAILURE);
814 }
815 ddi_set_name_addr(child, addr);
816
817 return (DDI_SUCCESS);
818 }
819
820 static int
xpvd_removechild(dev_info_t * dip)821 xpvd_removechild(dev_info_t *dip)
822 {
823 xvdi_uninit_dev(dip);
824
825 ddi_set_name_addr(dip, NULL);
826
827 /*
828 * Strip the node to properly convert it back to prototype
829 * form.
830 */
831 ddi_remove_minor_node(dip, NULL);
832
833 return (DDI_SUCCESS);
834 }
835
836 static int
xpvd_bus_unconfig(dev_info_t * parent,uint_t flag,ddi_bus_config_op_t op,void * device_name)837 xpvd_bus_unconfig(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op,
838 void *device_name)
839 {
840 return (ndi_busop_bus_unconfig(parent, flag, op, device_name));
841 }
842
843 /*
844 * Given the name of a child of xpvd, determine the device class,
845 * domain and vdevnum to which it refers.
846 */
847 static boolean_t
i_xpvd_parse_devname(char * name,xendev_devclass_t * devclassp,domid_t * domp,int * vdevp)848 i_xpvd_parse_devname(char *name, xendev_devclass_t *devclassp,
849 domid_t *domp, int *vdevp)
850 {
851 int len = strlen(name) + 1;
852 char *device_name = i_ddi_strdup(name, KM_SLEEP);
853 char *cname = NULL, *caddr = NULL;
854 boolean_t ret;
855
856 i_ddi_parse_name(device_name, &cname, &caddr, NULL);
857
858 if ((cname == NULL) || (strlen(cname) == 0) ||
859 (caddr == NULL) || (strlen(caddr) == 0)) {
860 ret = B_FALSE;
861 goto done;
862 }
863
864 *devclassp = xendev_nodename_to_devclass(cname);
865 if (*devclassp < 0) {
866 ret = B_FALSE;
867 goto done;
868 }
869
870 /*
871 * Parsing the address component requires knowledge of how
872 * xpvd_name_child() works. If that code changes, so must
873 * this.
874 */
875
876 /* Backend format is "<domain>,<vdev>". */
877 if (sscanf(caddr, "%hu,%d", domp, vdevp) == 2) {
878 ret = B_TRUE;
879 goto done;
880 }
881
882 /* Frontend format is "<vdev>". */
883 *domp = DOMID_SELF;
884 if (sscanf(caddr, "%d", vdevp) == 1)
885 ret = B_TRUE;
886 done:
887 kmem_free(device_name, len);
888 return (ret);
889 }
890
891 /*
892 * xpvd_bus_config()
893 *
894 * BUS_CONFIG_ONE:
895 * Enumerate the exact instance of a driver.
896 *
897 * BUS_CONFIG_ALL:
898 * Enumerate all the instances of all the possible children (seen before
899 * and never seen before).
900 *
901 * BUS_CONFIG_DRIVER:
902 * Enumerate all the instances of a particular driver.
903 */
904 static int
xpvd_bus_config(dev_info_t * parent,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** childp)905 xpvd_bus_config(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op,
906 void *arg, dev_info_t **childp)
907 {
908 int circ;
909 char *cname = NULL;
910
911 ndi_devi_enter(parent, &circ);
912
913 switch (op) {
914 case BUS_CONFIG_ONE: {
915 xendev_devclass_t devclass;
916 domid_t dom;
917 int vdev;
918
919 if (!i_xpvd_parse_devname(arg, &devclass, &dom, &vdev)) {
920 ndi_devi_exit(parent, circ);
921 return (NDI_FAILURE);
922 }
923
924 *childp = xvdi_find_dev(parent, devclass, dom, vdev);
925 if (*childp == NULL)
926 *childp = xvdi_create_dev(parent, devclass, dom, vdev);
927
928 ndi_devi_exit(parent, circ);
929
930 if (*childp == NULL)
931 return (NDI_FAILURE);
932 else
933 return (ndi_busop_bus_config(parent, flag,
934 op, arg, childp, 0));
935 }
936
937 case BUS_CONFIG_DRIVER: {
938 xendev_devclass_t devclass = XEN_INVAL;
939
940 cname = ddi_major_to_name((major_t)(uintptr_t)arg);
941 if (cname != NULL)
942 devclass = xendev_nodename_to_devclass(cname);
943
944 if (devclass == XEN_INVAL) {
945 ndi_devi_exit(parent, circ);
946 return (NDI_FAILURE);
947 } else {
948 xendev_enum_class(parent, devclass);
949 ndi_devi_exit(parent, circ);
950 return (ndi_busop_bus_config(parent, flag, op,
951 arg, childp, 0));
952 }
953 /* NOTREACHED */
954 }
955
956 case BUS_CONFIG_ALL:
957 xendev_enum_all(parent, B_FALSE);
958 ndi_devi_exit(parent, circ);
959
960 return (ndi_busop_bus_config(parent, flag, op,
961 arg, childp, 0));
962
963 default:
964 ndi_devi_exit(parent, circ);
965 return (NDI_FAILURE);
966 }
967 }
968
969 /*ARGSUSED*/
970 static int
xpvd_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)971 xpvd_get_eventcookie(dev_info_t *dip, dev_info_t *rdip,
972 char *eventname, ddi_eventcookie_t *cookie)
973 {
974 return (ndi_event_retrieve_cookie(xpvd_ndi_event_handle,
975 rdip, eventname, cookie, NDI_EVENT_NOPASS));
976 }
977
978 /*ARGSUSED*/
979 static int
xpvd_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void (* callback)(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata),void * arg,ddi_callback_id_t * cb_id)980 xpvd_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
981 ddi_eventcookie_t cookie, void (*callback)(dev_info_t *dip,
982 ddi_eventcookie_t cookie, void *arg, void *bus_impldata),
983 void *arg, ddi_callback_id_t *cb_id)
984 {
985 return (ndi_event_add_callback(xpvd_ndi_event_handle,
986 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
987 }
988
989 /*ARGSUSED*/
990 static int
xpvd_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)991 xpvd_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
992 {
993 return (ndi_event_remove_callback(xpvd_ndi_event_handle,
994 cb_id));
995 }
996
997 /*ARGSUSED*/
998 static int
xpvd_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * bus_impldata)999 xpvd_post_event(dev_info_t *dip, dev_info_t *rdip,
1000 ddi_eventcookie_t cookie, void *bus_impldata)
1001 {
1002 return (ndi_event_run_callbacks(xpvd_ndi_event_handle, rdip,
1003 cookie, bus_impldata));
1004 }
1005