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