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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Copyright 2023 Oxide Computer Company 28 */ 29 30 /* 31 * The tphci driver can be used to exercise the mpxio framework together 32 * with tvhci/tclient. 33 */ 34 35 #include <sys/conf.h> 36 #include <sys/file.h> 37 #include <sys/open.h> 38 #include <sys/stat.h> 39 #include <sys/modctl.h> 40 #include <sys/ddi.h> 41 #include <sys/sunddi.h> 42 #include <sys/sunndi.h> 43 #include <sys/sunmdi.h> 44 #include <sys/disp.h> 45 46 /* cb_ops entry points */ 47 static int tphci_open(dev_t *, int, int, cred_t *); 48 static int tphci_close(dev_t, int, int, cred_t *); 49 static int tphci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 50 static int tphci_attach(dev_info_t *, ddi_attach_cmd_t); 51 static int tphci_detach(dev_info_t *, ddi_detach_cmd_t); 52 static int tphci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 53 54 /* bus_ops entry points */ 55 static int tphci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 56 void *); 57 static int tphci_initchild(dev_info_t *, dev_info_t *); 58 static int tphci_uninitchild(dev_info_t *, dev_info_t *); 59 static int tphci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *, 60 dev_info_t **); 61 static int tphci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t, 62 void *); 63 static int tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, 64 ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result); 65 66 67 static void *tphci_state; 68 struct tphci_state { 69 dev_info_t *dip; 70 }; 71 72 static struct cb_ops tphci_cb_ops = { 73 tphci_open, /* open */ 74 tphci_close, /* close */ 75 nodev, /* strategy */ 76 nodev, /* print */ 77 nodev, /* dump */ 78 nodev, /* read */ 79 nodev, /* write */ 80 tphci_ioctl, /* ioctl */ 81 nodev, /* devmap */ 82 nodev, /* mmap */ 83 nodev, /* segmap */ 84 nochpoll, /* chpoll */ 85 ddi_prop_op, /* cb_prop_op */ 86 0, /* streamtab */ 87 D_NEW | D_MP, /* cb_flag */ 88 CB_REV, /* rev */ 89 nodev, /* aread */ 90 nodev /* awrite */ 91 }; 92 93 static struct bus_ops tphci_bus_ops = { 94 BUSO_REV, /* busops_rev */ 95 nullbusmap, /* bus_map */ 96 NULL, /* bus_get_intrspec */ 97 NULL, /* bus_add_interspec */ 98 NULL, /* bus_remove_interspec */ 99 i_ddi_map_fault, /* bus_map_fault */ 100 ddi_no_dma_map, /* bus_dma_map */ 101 ddi_no_dma_allochdl, /* bus_dma_allochdl */ 102 NULL, /* bus_dma_freehdl */ 103 NULL, /* bus_dma_bindhdl */ 104 NULL, /* bus_dma_unbindhdl */ 105 NULL, /* bus_dma_flush */ 106 NULL, /* bus_dma_win */ 107 NULL, /* bus_dma_ctl */ 108 tphci_ctl, /* bus_ctl */ 109 ddi_bus_prop_op, /* bus_prop_op */ 110 NULL, /* bus_get_eventcookie */ 111 NULL, /* bus_add_eventcall */ 112 NULL, /* bus_remove_event */ 113 NULL, /* bus_post_event */ 114 NULL, /* bus_intr_ctl */ 115 tphci_bus_config, /* bus_config */ 116 tphci_bus_unconfig, /* bus_unconfig */ 117 NULL, /* bus_fm_init */ 118 NULL, /* bus_fm_fini */ 119 NULL, /* bus_fm_access_enter */ 120 NULL, /* bus_fm_access_exit */ 121 NULL, /* bus_power */ 122 tphci_intr_op /* bus_intr_op */ 123 }; 124 125 static struct dev_ops tphci_ops = { 126 DEVO_REV, 127 0, 128 tphci_getinfo, 129 nulldev, /* identify */ 130 nulldev, /* probe */ 131 tphci_attach, /* attach and detach are mandatory */ 132 tphci_detach, 133 nodev, /* reset */ 134 &tphci_cb_ops, /* cb_ops */ 135 &tphci_bus_ops, /* bus_ops */ 136 NULL, /* power */ 137 ddi_quiesce_not_needed, /* quiesce */ 138 }; 139 140 extern struct mod_ops mod_driverops; 141 142 static struct modldrv modldrv = { 143 &mod_driverops, 144 "test phci driver", 145 &tphci_ops 146 }; 147 148 static struct modlinkage modlinkage = { 149 MODREV_1, 150 &modldrv, 151 NULL 152 }; 153 154 int 155 _init(void) 156 { 157 int rval; 158 159 if ((rval = ddi_soft_state_init(&tphci_state, 160 sizeof (struct tphci_state), 2)) != 0) { 161 return (rval); 162 } 163 164 if ((rval = mod_install(&modlinkage)) != 0) { 165 ddi_soft_state_fini(&tphci_state); 166 } 167 return (rval); 168 } 169 170 171 int 172 _fini(void) 173 { 174 int rval; 175 176 /* 177 * don't start cleaning up until we know that the module remove 178 * has worked -- if this works, then we know that each instance 179 * has successfully been detached 180 */ 181 if ((rval = mod_remove(&modlinkage)) != 0) { 182 return (rval); 183 } 184 185 ddi_soft_state_fini(&tphci_state); 186 187 return (rval); 188 } 189 190 int 191 _info(struct modinfo *modinfop) 192 { 193 return (mod_info(&modlinkage, modinfop)); 194 } 195 196 /* ARGSUSED */ 197 static int 198 tphci_open(dev_t *devp, int flag, int otype, cred_t *credp) 199 { 200 struct tphci_state *phci; 201 202 if (otype != OTYP_CHR) { 203 return (EINVAL); 204 } 205 206 phci = ddi_get_soft_state(tphci_state, getminor(*devp)); 207 if (phci == NULL) { 208 return (ENXIO); 209 } 210 211 return (0); 212 } 213 214 215 /* ARGSUSED */ 216 static int 217 tphci_close(dev_t dev, int flag, int otype, cred_t *credp) 218 { 219 struct tphci_state *phci; 220 if (otype != OTYP_CHR) { 221 return (EINVAL); 222 } 223 224 phci = ddi_get_soft_state(tphci_state, getminor(dev)); 225 if (phci == NULL) { 226 return (ENXIO); 227 } 228 229 return (0); 230 } 231 232 /* ARGSUSED */ 233 static int 234 tphci_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 235 cred_t *credp, int *rval) 236 { 237 return (0); 238 } 239 240 /* 241 * attach the module 242 */ 243 static int 244 tphci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 245 { 246 char *vclass; 247 int instance, phci_regis = 0; 248 struct tphci_state *phci = NULL; 249 250 instance = ddi_get_instance(dip); 251 252 switch (cmd) { 253 case DDI_ATTACH: 254 break; 255 256 case DDI_RESUME: 257 case DDI_PM_RESUME: 258 return (0); /* nothing to do */ 259 260 default: 261 return (DDI_FAILURE); 262 } 263 264 /* 265 * Allocate phci data structure. 266 */ 267 if (ddi_soft_state_zalloc(tphci_state, instance) != DDI_SUCCESS) { 268 return (DDI_FAILURE); 269 } 270 271 phci = ddi_get_soft_state(tphci_state, instance); 272 ASSERT(phci != NULL); 273 phci->dip = dip; 274 275 /* bus_addr has the form #,<vhci_class> */ 276 vclass = strchr(ddi_get_name_addr(dip), ','); 277 if (vclass == NULL || vclass[1] == '\0') { 278 cmn_err(CE_NOTE, "tphci invalid bus_addr %s", 279 ddi_get_name_addr(dip)); 280 goto attach_fail; 281 } 282 283 /* 284 * Attach this instance with the mpxio framework 285 */ 286 if (mdi_phci_register(vclass + 1, dip, 0) != MDI_SUCCESS) { 287 cmn_err(CE_WARN, "%s mdi_phci_register failed", 288 ddi_node_name(dip)); 289 goto attach_fail; 290 } 291 phci_regis++; 292 293 if (ddi_create_minor_node(dip, "devctl", S_IFCHR, 294 instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) { 295 cmn_err(CE_NOTE, "%s ddi_create_minor_node failed", 296 ddi_node_name(dip)); 297 goto attach_fail; 298 } 299 300 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1); 301 ddi_report_dev(dip); 302 return (DDI_SUCCESS); 303 304 attach_fail: 305 if (phci_regis) 306 (void) mdi_phci_unregister(dip, 0); 307 308 ddi_soft_state_free(tphci_state, instance); 309 return (DDI_FAILURE); 310 } 311 312 313 /*ARGSUSED*/ 314 static int 315 tphci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 316 { 317 int instance = ddi_get_instance(dip); 318 319 switch (cmd) { 320 case DDI_DETACH: 321 break; 322 323 case DDI_SUSPEND: 324 case DDI_PM_SUSPEND: 325 return (0); /* nothing to do */ 326 327 default: 328 return (DDI_FAILURE); 329 } 330 331 if (mdi_phci_unregister(dip, 0) != MDI_SUCCESS) 332 return (DDI_FAILURE); 333 334 ddi_remove_minor_node(dip, NULL); 335 ddi_soft_state_free(tphci_state, instance); 336 337 return (DDI_SUCCESS); 338 } 339 340 /* 341 * tphci_getinfo() 342 * Given the device number, return the devinfo pointer or the 343 * instance number. 344 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach. 345 */ 346 347 /*ARGSUSED*/ 348 static int 349 tphci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 350 { 351 struct tphci_state *phci; 352 int instance = getminor((dev_t)arg); 353 354 switch (cmd) { 355 case DDI_INFO_DEVT2DEVINFO: 356 phci = ddi_get_soft_state(tphci_state, instance); 357 if (phci != NULL) 358 *result = phci->dip; 359 else { 360 *result = NULL; 361 return (DDI_FAILURE); 362 } 363 break; 364 365 case DDI_INFO_DEVT2INSTANCE: 366 *result = (void *)(uintptr_t)instance; 367 break; 368 369 default: 370 return (DDI_FAILURE); 371 } 372 373 return (DDI_SUCCESS); 374 } 375 376 /* 377 * Interrupt stuff. NO OP for pseudo drivers. 378 */ 379 /*ARGSUSED*/ 380 static int 381 tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 382 ddi_intr_handle_impl_t *hdlp, void *result) 383 { 384 return (DDI_FAILURE); 385 } 386 387 static int 388 tphci_ctl(dev_info_t *dip, dev_info_t *rdip, 389 ddi_ctl_enum_t ctlop, void *arg, void *result) 390 { 391 switch (ctlop) { 392 case DDI_CTLOPS_REPORTDEV: 393 if (rdip == (dev_info_t *)0) 394 return (DDI_FAILURE); 395 cmn_err(CE_CONT, "?tphci-device: %s%d\n", 396 ddi_get_name(rdip), ddi_get_instance(rdip)); 397 return (DDI_SUCCESS); 398 399 case DDI_CTLOPS_INITCHILD: 400 { 401 dev_info_t *child = (dev_info_t *)arg; 402 return (tphci_initchild(dip, child)); 403 } 404 405 case DDI_CTLOPS_UNINITCHILD: 406 { 407 dev_info_t *child = (dev_info_t *)arg; 408 return (tphci_uninitchild(dip, child)); 409 } 410 411 case DDI_CTLOPS_DMAPMAPC: 412 case DDI_CTLOPS_REPORTINT: 413 case DDI_CTLOPS_REGSIZE: 414 case DDI_CTLOPS_NREGS: 415 case DDI_CTLOPS_SIDDEV: 416 case DDI_CTLOPS_SLAVEONLY: 417 case DDI_CTLOPS_AFFINITY: 418 case DDI_CTLOPS_POKE: 419 case DDI_CTLOPS_PEEK: 420 /* 421 * These ops correspond to functions that "shouldn't" be called 422 * by a pseudo driver. So we whine when we're called. 423 */ 424 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 425 ddi_get_name(dip), ddi_get_instance(dip), 426 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip)); 427 return (DDI_FAILURE); 428 429 case DDI_CTLOPS_ATTACH: 430 case DDI_CTLOPS_BTOP: 431 case DDI_CTLOPS_BTOPR: 432 case DDI_CTLOPS_DETACH: 433 case DDI_CTLOPS_DVMAPAGESIZE: 434 case DDI_CTLOPS_IOMIN: 435 case DDI_CTLOPS_POWER: 436 case DDI_CTLOPS_PTOB: 437 default: 438 /* 439 * The ops that we pass up (default). We pass up memory 440 * allocation oriented ops that we receive - these may be 441 * associated with pseudo HBA drivers below us with target 442 * drivers below them that use ddi memory allocation 443 * interfaces like scsi_alloc_consistent_buf. 444 */ 445 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 446 } 447 } 448 449 static int 450 tphci_initchild(dev_info_t *dip, dev_info_t *child) 451 { 452 _NOTE(ARGUNUSED(dip)) 453 ddi_set_name_addr(child, "0"); 454 return (DDI_SUCCESS); 455 } 456 457 /*ARGSUSED*/ 458 static int 459 tphci_uninitchild(dev_info_t *dip, dev_info_t *child) 460 { 461 ddi_set_name_addr(child, NULL); 462 return (DDI_SUCCESS); 463 } 464 465 static int 466 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid) 467 { 468 char *tmp; 469 470 i_ddi_parse_name(devnm, cname, paddr, NULL); 471 if ((strcmp(*cname, "tclient") != 0) && 472 (strcmp(*cname, "tphci") != 0) || *paddr == NULL) 473 return (-1); 474 475 tmp = strchr(*paddr, ','); 476 if (tmp == NULL || tmp[1] == '\0') 477 return (-1); 478 479 *guid = tmp + 1; 480 return (0); 481 } 482 483 static int 484 tphci_bus_config(dev_info_t *parent, uint_t flags, 485 ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 486 { 487 _NOTE(ARGUNUSED(flags)) 488 char *cname, *paddr, *guid, *devnm; 489 mdi_pathinfo_t *pip; 490 int len, rval; 491 boolean_t enteredv; 492 493 switch (op) { 494 case BUS_CONFIG_ONE: 495 break; 496 case BUS_CONFIG_DRIVER: /* no direct children to configure */ 497 case BUS_CONFIG_ALL: 498 return (NDI_SUCCESS); 499 default: 500 return (NDI_FAILURE); 501 } 502 503 /* only implement BUS_CONFIG_ONE */ 504 devnm = i_ddi_strdup((char *)arg, KM_SLEEP); 505 len = strlen(devnm) + 1; 506 507 /* caddr is hardcoded in the form *,<guid> */ 508 if (tp_decode_name(devnm, &cname, &paddr, &guid) != 0) { 509 cmn_err(CE_NOTE, "tphci_bus_config -- invalid device %s", 510 (char *)arg); 511 kmem_free(devnm, len); 512 return (NDI_FAILURE); 513 } 514 515 mdi_devi_enter(parent, &enteredv); 516 rval = mdi_pi_alloc(parent, cname, guid, paddr, 0, &pip); 517 kmem_free(devnm, len); 518 if (rval != MDI_SUCCESS) { 519 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_alloc failed"); 520 mdi_devi_exit(parent, enteredv); 521 return (NDI_FAILURE); 522 } 523 524 /* 525 * Hold the path and exit the pHCI while calling mdi_pi_online 526 * to avoid deadlock with power management of pHCI. 527 */ 528 mdi_hold_path(pip); 529 mdi_devi_exit_phci(parent); 530 rval = mdi_pi_online(pip, 0); 531 mdi_devi_enter_phci(parent); 532 mdi_rele_path(pip); 533 534 if (rval != MDI_SUCCESS) { 535 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_online failed"); 536 (void) mdi_pi_free(pip, 0); 537 mdi_devi_exit(parent, enteredv); 538 return (NDI_FAILURE); 539 } 540 541 if (childp) { 542 *childp = mdi_pi_get_client(pip); 543 ndi_hold_devi(*childp); 544 } 545 mdi_devi_exit(parent, enteredv); 546 547 return (NDI_SUCCESS); 548 } 549 550 static int 551 tphci_bus_unconfig(dev_info_t *parent, uint_t flags, 552 ddi_bus_config_op_t op, void *arg) 553 { 554 int rval = MDI_SUCCESS; 555 boolean_t enteredv; 556 mdi_pathinfo_t *pip, *next; 557 char *devnm, *cname, *caddr; 558 559 switch (op) { 560 case BUS_UNCONFIG_ONE: 561 devnm = (char *)arg; 562 i_ddi_parse_name(devnm, &cname, &caddr, NULL); 563 if (strcmp(cname, "tclient") != 0) 564 return (NDI_SUCCESS); /* no such device */ 565 566 mdi_devi_enter(parent, &enteredv); 567 pip = mdi_pi_find(parent, NULL, caddr); 568 if (pip) { 569 mdi_hold_path(pip); 570 mdi_devi_exit_phci(parent); 571 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE); 572 mdi_devi_enter_phci(parent); 573 mdi_rele_path(pip); 574 575 if (rval == MDI_SUCCESS) 576 (void) mdi_pi_free(pip, 0); 577 } 578 mdi_devi_exit(parent, enteredv); 579 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE); 580 581 case BUS_UNCONFIG_ALL: 582 if (flags & NDI_AUTODETACH) 583 return (NDI_FAILURE); 584 585 mdi_devi_enter(parent, &enteredv); 586 next = mdi_get_next_client_path(parent, NULL); 587 while ((pip = next) != NULL) { 588 next = mdi_get_next_client_path(parent, pip); 589 590 mdi_hold_path(pip); 591 mdi_devi_exit_phci(parent); 592 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE); 593 mdi_devi_enter_phci(parent); 594 mdi_rele_path(pip); 595 596 if (rval != MDI_SUCCESS) 597 break; 598 (void) mdi_pi_free(pip, 0); 599 } 600 mdi_devi_exit(parent, enteredv); 601 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE); 602 603 case BUS_UNCONFIG_DRIVER: /* nothing to do */ 604 return (NDI_SUCCESS); 605 606 default: 607 return (NDI_FAILURE); 608 } 609 /*NOTREACHED*/ 610 } 611