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