xref: /titanic_44/usr/src/uts/common/xen/io/xpvd.c (revision cd21e7c548ae2a3b5e522244bf798f2a6b4ba02d)
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