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 2019 Peter Tribble. 28 * Copyright 2023 Oxide Computer Company 29 */ 30 31 /* 32 * Safari Configurator (gptwocfg) 33 */ 34 35 #include <sys/types.h> 36 #include <sys/cred.h> 37 #include <sys/mman.h> 38 #include <sys/kmem.h> 39 #include <sys/conf.h> 40 #include <sys/cmn_err.h> 41 #include <sys/ddi.h> 42 #include <sys/sunddi.h> 43 #include <sys/sunndi.h> 44 #include <sys/modctl.h> 45 #include <sys/stat.h> 46 #include <sys/param.h> 47 #include <sys/autoconf.h> 48 #include <sys/ksynch.h> 49 #include <sys/promif.h> 50 #include <sys/ndi_impldefs.h> 51 #include <sys/ddi_impldefs.h> 52 #include <sys/gp2cfg.h> 53 #include <sys/machsystm.h> 54 #include <sys/platform_module.h> 55 56 #ifdef DEBUG 57 int gptwocfg_debug = 0; 58 59 static void debug(char *, uintptr_t, uintptr_t, 60 uintptr_t, uintptr_t, uintptr_t); 61 62 #define GPTWO_DEBUG0(level, flag, s) if (gptwocfg_debug >= level) \ 63 cmn_err(flag, s) 64 #define GPTWO_DEBUG1(level, flag, fmt, a1) if (gptwocfg_debug >= level) \ 65 debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); 66 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2) if (gptwocfg_debug >= level) \ 67 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); 68 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) \ 69 if (gptwocfg_debug >= level) \ 70 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0); 71 #else 72 #define GPTWO_DEBUG0(level, flag, s) 73 #define GPTWO_DEBUG1(level, flag, fmt, a1) 74 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2) 75 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) 76 #endif 77 78 kmutex_t gptwo_handle_list_lock; 79 gptwocfg_handle_list_t *gptwocfg_handle_list; 80 81 static kmutex_t gptwo_config_list_lock; 82 static gptwocfg_config_t *gptwo_config_list; 83 84 static gptwo_new_nodes_t * 85 gptwocfg_get_obp_created_nodes(dev_info_t *, uint_t); 86 87 void (*gptwocfg_unclaim_address)(uint_t); 88 89 extern caddr_t efcode_vaddr; 90 extern int efcode_size; 91 92 #define GPTWO_NUMBER_OF_DEVICE_TYPES 6 93 94 static kmutex_t gptwocfg_ops_table_lock; 95 gptwocfg_ops_t *gptwocfg_ops_table[GPTWO_NUMBER_OF_DEVICE_TYPES]; 96 97 /* 98 * Module linkage information for the kernel. 99 */ 100 101 extern struct mod_ops mod_miscops; 102 103 static struct modlmisc modlmisc = { 104 &mod_miscops, /* Type of module */ 105 "gptwo configurator", 106 }; 107 108 static struct modlinkage modlinkage = { 109 MODREV_1, (void *)&modlmisc, NULL 110 }; 111 112 int 113 _init(void) 114 { 115 unsigned int i; 116 117 GPTWO_DEBUG0(1, CE_WARN, "gptwocfg (Safari Configurator) " 118 "has been loaded\n"); 119 120 mutex_init(&gptwo_config_list_lock, NULL, MUTEX_DRIVER, NULL); 121 mutex_init(&gptwocfg_ops_table_lock, NULL, MUTEX_DRIVER, NULL); 122 gptwo_config_list = NULL; 123 124 mutex_init(&gptwo_handle_list_lock, NULL, MUTEX_DRIVER, NULL); 125 gptwocfg_handle_list = NULL; 126 127 for (i = 0; i < GPTWO_NUMBER_OF_DEVICE_TYPES; i++) 128 gptwocfg_ops_table[i] = NULL; 129 130 return (mod_install(&modlinkage)); 131 } 132 133 int 134 _fini(void) 135 { 136 int error; 137 138 error = mod_remove(&modlinkage); 139 if (error != 0) { 140 return (error); 141 } 142 mutex_destroy(&gptwo_config_list_lock); 143 mutex_destroy(&gptwocfg_ops_table_lock); 144 mutex_destroy(&gptwo_handle_list_lock); 145 146 return (0); 147 } 148 149 int 150 _info(struct modinfo *modinfop) 151 { 152 return (mod_info(&modlinkage, modinfop)); 153 } 154 155 gptwo_new_nodes_t * 156 gptwocfg_allocate_node_list(int number_of_nodes) 157 { 158 gptwo_new_nodes_t *gptwo_new_nodes; 159 int size; 160 161 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_allocate_node_list- %d nodes", 162 number_of_nodes); 163 164 size = sizeof (gptwo_new_nodes_t) + 165 ((number_of_nodes -1) * sizeof (dev_info_t *)); 166 167 gptwo_new_nodes = kmem_zalloc(size, KM_SLEEP); 168 169 gptwo_new_nodes->gptwo_number_of_nodes = number_of_nodes; 170 gptwo_new_nodes->gptwo_version = GP2_VERSION; 171 172 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_allocate_node_list- returned %p\n", 173 gptwo_new_nodes); 174 175 return (gptwo_new_nodes); 176 } 177 178 void 179 gptwocfg_free_node_list(gptwo_new_nodes_t *gptwo_new_nodes) 180 { 181 int size; 182 183 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_free_node_list- %p %d nodes", 184 gptwo_new_nodes, gptwo_new_nodes->gptwo_number_of_nodes); 185 186 size = sizeof (gptwo_new_nodes_t) + 187 ((gptwo_new_nodes->gptwo_number_of_nodes - 1) * 188 sizeof (dev_info_t *)); 189 190 kmem_free(gptwo_new_nodes, size); 191 } 192 193 void 194 gptwocfg_register_ops(uint_t type, gptwo_cfgfunc_t *cfg_func, 195 gptwo_uncfgfunc_t *uncfg_func) 196 { 197 /* KM_SLEEP guarantees success */ 198 gptwocfg_ops_t *ops = kmem_zalloc(sizeof (gptwocfg_ops_t), KM_SLEEP); 199 200 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_register_ops: type=%x ops=%lx\n", 201 type, ops); 202 ASSERT(type < GPTWO_NUMBER_OF_DEVICE_TYPES); 203 ops->gptwocfg_type = type; 204 ops->gptwocfg_version = GPTWOCFG_OPS_VERSION; 205 ops->gptwocfg_configure = cfg_func; 206 ops->gptwocfg_unconfigure = uncfg_func; 207 208 mutex_enter(&gptwocfg_ops_table_lock); 209 gptwocfg_ops_table[type] = ops; 210 mutex_exit(&gptwocfg_ops_table_lock); 211 } 212 213 214 215 void 216 gptwocfg_unregister_ops(uint_t type) 217 { 218 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unregister_ops: type=%x\n", type); 219 220 ASSERT(type < GPTWO_NUMBER_OF_DEVICE_TYPES); 221 222 mutex_enter(&gptwocfg_ops_table_lock); 223 kmem_free(gptwocfg_ops_table[type], sizeof (gptwocfg_ops_t)); 224 gptwocfg_ops_table[type] = NULL; 225 mutex_exit(&gptwocfg_ops_table_lock); 226 } 227 228 gptwocfg_cookie_t 229 gptwocfg_configure(dev_info_t *ap, spcd_t *pcd, gptwo_aid_t id) 230 { 231 gptwo_new_nodes_t *new_nodes = NULL; 232 gptwocfg_config_t *config; 233 gptwocfg_ops_t *ops; 234 235 GPTWO_DEBUG3(1, CE_CONT, "gptwocfg_configure: ap=0x%p pcd=%p id=%x\n", 236 ap, pcd, id); 237 238 /* 239 * Look to see if the port is already configured. 240 */ 241 mutex_enter(&gptwo_config_list_lock); 242 config = gptwo_config_list; 243 while (config != NULL) { 244 if (config->gptwo_portid == id) { 245 cmn_err(CE_WARN, "gptwocfg: gptwocfg_configure: " 246 "0x%x Port already configured\n", id); 247 mutex_exit(&gptwo_config_list_lock); 248 return (NULL); 249 } 250 config = config->gptwo_next; 251 } 252 mutex_exit(&gptwo_config_list_lock); 253 254 if (pcd == NULL) { 255 GPTWO_DEBUG0(1, CE_CONT, "gptwocfg_configure: pcd=NULL\n"); 256 return (NULL); 257 } 258 259 if ((pcd->spcd_magic != PCD_MAGIC) || 260 (pcd->spcd_version != PCD_VERSION)) { 261 cmn_err(CE_WARN, "gptwocfg: Invalid Port " 262 "Configuration Descriptor\n"); 263 return (NULL); 264 } 265 266 if (pcd->spcd_ptype >= GPTWO_NUMBER_OF_DEVICE_TYPES) { 267 cmn_err(CE_WARN, 268 "gptwocfg: Invalid device type %x", pcd->spcd_ptype); 269 return (NULL); 270 } 271 272 if (pcd->spcd_prsv != SPCD_RSV_PASS) { 273 cmn_err(CE_WARN, 274 "gptwocfg: Agent at ID %x has not passed test(s)\n", id); 275 return (NULL); 276 } 277 278 mutex_enter(&gptwocfg_ops_table_lock); 279 280 ops = gptwocfg_ops_table[pcd->spcd_ptype]; 281 282 if (ops == NULL) { 283 cmn_err(CE_WARN, "gptwocfg: Ops for type %x have not been " 284 "registered\n", pcd->spcd_ptype); 285 mutex_exit(&gptwocfg_ops_table_lock); 286 return (NULL); 287 } 288 289 if (ops->gptwocfg_configure == NULL) { 290 cmn_err(CE_WARN, "gptwocfg: no configure routine registered " 291 "for sfaari type %x\n", pcd->spcd_ptype); 292 mutex_exit(&gptwocfg_ops_table_lock); 293 return (NULL); 294 } 295 296 new_nodes = ops->gptwocfg_configure(ap, pcd, id); 297 298 mutex_exit(&gptwocfg_ops_table_lock); 299 300 if (new_nodes != NULL) { 301 config = kmem_zalloc(sizeof (gptwocfg_config_t), KM_SLEEP); 302 config->gptwo_version = GP2_VERSION; 303 config->gptwo_ap = ap; 304 config->gptwo_portid = id; 305 config->gptwo_nodes = new_nodes; 306 config->gptwo_ops = ops; 307 308 /* 309 * put config on config list 310 */ 311 mutex_enter(&gptwo_config_list_lock); 312 config->gptwo_next = gptwo_config_list; 313 gptwo_config_list = config; 314 mutex_exit(&gptwo_config_list_lock); 315 } else { 316 config = NULL; 317 } 318 319 return ((gptwocfg_cookie_t)config); 320 } 321 322 gptwocfg_cookie_t 323 gptwocfg_unconfigure(dev_info_t *ap, gptwo_aid_t id) 324 { 325 int i; 326 int failure = 0; 327 dev_info_t *saf_dip; 328 gptwocfg_config_t *config, *temp; 329 gptwo_new_nodes_t *obp_nodes; 330 gptwocfg_ops_t *ops; 331 332 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_unconfigure: ap=0x%p id=0x%lx\n", 333 ap, id); 334 335 mutex_enter(&gptwo_config_list_lock); 336 config = gptwo_config_list; 337 while (config != NULL) { 338 if (config->gptwo_portid == id) { 339 break; 340 } 341 config = config->gptwo_next; 342 } 343 mutex_exit(&gptwo_config_list_lock); 344 345 if (config == NULL) { 346 /* 347 * There is no config structure associated with this agent id 348 * so it was probably built by firmware at start of day. We 349 * need to create a config structure before we can continue. 350 */ 351 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure: id=0x%lx " 352 "No config structure - Need to build one\n", id); 353 354 obp_nodes = gptwocfg_get_obp_created_nodes(ap, id); 355 356 if (obp_nodes != NULL) { 357 config = kmem_zalloc(sizeof (gptwocfg_config_t), 358 KM_SLEEP); 359 config->gptwo_version = GP2_VERSION; 360 config->gptwo_ap = ap; 361 config->gptwo_portid = id; 362 config->gptwo_nodes = obp_nodes; 363 364 /* 365 * put config on config list 366 */ 367 mutex_enter(&gptwo_config_list_lock); 368 config->gptwo_next = gptwo_config_list; 369 gptwo_config_list = config; 370 mutex_exit(&gptwo_config_list_lock); 371 } else { 372 cmn_err(CE_WARN, "gptwocfg: gptwocfg_unconfigure: " 373 "No OBP created nodes for ap=0x%lx agent id=0x%x", 374 (long)ap, id); 375 return (NULL); 376 } 377 } 378 379 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure config=0x%lx\n", 380 config); 381 382 ops = config->gptwo_ops; 383 384 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure: ops=%lx\n", ops); 385 386 ndi_devi_enter(ap); 387 388 for (i = 0; i < config->gptwo_nodes->gptwo_number_of_nodes; i++) { 389 dev_info_t *fdip = NULL; 390 391 saf_dip = config->gptwo_nodes->gptwo_nodes[i]; 392 393 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure saf_dip=0x%lx\n", 394 saf_dip); 395 396 if (saf_dip == NULL) { 397 GPTWO_DEBUG0(1, CE_CONT, "gptwocfg_unconfigure: " 398 "skipping NULLL saf device\n"); 399 400 continue; 401 } 402 403 config->gptwo_nodes->gptwo_nodes[i] = NULL; 404 405 if (ops) { 406 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_configure " 407 "ops->gptwocfg_configure=%lx\n", 408 ops->gptwocfg_configure); 409 410 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure " 411 "ops->gptwocfg_unconfigure=%lx\n", 412 ops->gptwocfg_unconfigure); 413 414 if (ops->gptwocfg_unconfigure != NULL) { 415 config->gptwo_nodes->gptwo_nodes[i] = 416 ops->gptwocfg_unconfigure(saf_dip); 417 418 } 419 } 420 421 GPTWO_DEBUG1(1, CE_CONT, "e_ddi_branch_destroy <%s>\n", 422 ddi_get_name(saf_dip)); 423 424 ASSERT(e_ddi_branch_held(saf_dip)); 425 426 /* 427 * Don't hold parent busy when calling 428 * e_ddi_branch_unconfigure/destroy/referenced() 429 */ 430 ndi_devi_exit(ap); 431 if (e_ddi_branch_destroy(saf_dip, &fdip, 0)) { 432 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 433 434 /* 435 * If non-NULL, fdip is held and must be released. 436 */ 437 if (fdip != NULL) { 438 (void) ddi_pathname(fdip, path); 439 ddi_release_devi(fdip); 440 } else { 441 (void) ddi_pathname(saf_dip, path); 442 } 443 444 cmn_err(CE_WARN, "saf node removal failed: %s (%p)", 445 path, fdip ? (void *)fdip : (void *)saf_dip); 446 447 kmem_free(path, MAXPATHLEN); 448 449 config->gptwo_nodes->gptwo_nodes[i] = saf_dip; 450 failure = 1; 451 } 452 ndi_devi_enter(ap); 453 } 454 455 ndi_devi_exit(ap); 456 457 if (!failure) { 458 gptwocfg_free_node_list(config->gptwo_nodes); 459 460 mutex_enter(&gptwo_config_list_lock); 461 if (gptwo_config_list == config) { 462 gptwo_config_list = config->gptwo_next; 463 } else { 464 temp = gptwo_config_list; 465 while (temp->gptwo_next != config) { 466 temp = temp->gptwo_next; 467 } 468 temp->gptwo_next = config->gptwo_next; 469 } 470 mutex_exit(&gptwo_config_list_lock); 471 472 kmem_free(config, sizeof (gptwocfg_config_t)); 473 config = NULL; 474 } 475 476 return (config); 477 } 478 479 int 480 gptwocfg_next_node(gptwocfg_cookie_t c, dev_info_t *previous, dev_info_t **next) 481 { 482 gptwocfg_config_t *cookie; 483 int i, j; 484 485 GPTWO_DEBUG3(1, CE_WARN, "gptwocfg_next_node" 486 "(c=0x%lx, previous=0x%lx, next=0x%lx)\n", c, previous, next); 487 488 cookie = (gptwocfg_config_t *)c; 489 490 for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; i++) { 491 GPTWO_DEBUG1(1, CE_WARN, "0x%lx\n", 492 cookie->gptwo_nodes->gptwo_nodes[i]); 493 } 494 495 if (previous == NULL) { 496 for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; 497 i++) { 498 if (cookie->gptwo_nodes->gptwo_nodes[i]) { 499 *next = cookie->gptwo_nodes->gptwo_nodes[i]; 500 GPTWO_DEBUG1(1, CE_WARN, "returned 0x%lx\n", 501 *next); 502 return (1); 503 } 504 } 505 return (0); 506 } 507 508 for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; i++) { 509 if (cookie->gptwo_nodes->gptwo_nodes[i] == previous) { 510 for (j = i + 1; 511 j < cookie->gptwo_nodes->gptwo_number_of_nodes; 512 j++) { 513 if (cookie->gptwo_nodes->gptwo_nodes[j]) { 514 *next = 515 cookie->gptwo_nodes->gptwo_nodes[j]; 516 GPTWO_DEBUG1(1, CE_WARN, 517 "returned 0x%lx\n", *next); 518 return (1); 519 } 520 } 521 *next = NULL; 522 GPTWO_DEBUG1(1, CE_WARN, "returned 0x%lx\n", 523 *next); 524 return (1); 525 } 526 } 527 528 /* 529 * previous is probably an invalid dev_info. 530 */ 531 return (0); 532 } 533 534 static gptwo_new_nodes_t * 535 gptwocfg_get_obp_created_nodes(dev_info_t *ap, uint_t id) 536 { 537 gptwo_new_nodes_t *obp_nodes; 538 dev_info_t *saf_dev; 539 int i = 0, nodes = 0; 540 541 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_get_obp_created_nodes - ap=0x%lx " 542 "id=0x%x\n", ap, id); 543 544 ndi_devi_enter(ap); 545 546 /* 547 * First go through all the children of the attachment point 548 * to count matching safari agent ids 549 */ 550 saf_dev = ddi_get_child(ap); 551 while (saf_dev != NULL) { 552 if (ddi_getprop(DDI_DEV_T_ANY, saf_dev, DDI_PROP_DONTPASS, 553 "portid", -1) == id) { 554 nodes++; 555 } 556 saf_dev = ddi_get_next_sibling(saf_dev); 557 } 558 559 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_obp_created_nodes - %d nodes " 560 "found\n", nodes); 561 562 obp_nodes = gptwocfg_allocate_node_list(nodes); 563 564 /* 565 * Then fill in the nodes structure. 566 */ 567 saf_dev = ddi_get_child(ap); 568 while ((saf_dev != NULL) && (i < nodes)) { 569 if (ddi_getprop(DDI_DEV_T_ANY, saf_dev, DDI_PROP_DONTPASS, 570 "portid", -1) == id) { 571 /* 572 * Branch rooted at this dip must have been 573 * held by the DR driver. 574 */ 575 ASSERT(e_ddi_branch_held(saf_dev)); 576 obp_nodes->gptwo_nodes[i++] = saf_dev; 577 } 578 saf_dev = ddi_get_next_sibling(saf_dev); 579 } 580 581 ndi_devi_exit(ap); 582 583 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_obp_created_nodes - " 584 "Returning 0x%lx\n", obp_nodes); 585 586 return (obp_nodes); 587 } 588 589 void 590 gptwocfg_save_handle(dev_info_t *dip, fco_handle_t fco_handle) 591 { 592 gptwocfg_handle_list_t *h; 593 594 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_save_handle - " 595 "dip=%lx fco_handle=%lx\n", dip, fco_handle); 596 597 h = kmem_zalloc(sizeof (gptwocfg_handle_list_t), KM_SLEEP); 598 599 mutex_enter(&gptwo_handle_list_lock); 600 601 h->next = gptwocfg_handle_list; 602 h->dip = dip; 603 h->fco_handle = fco_handle; 604 gptwocfg_handle_list = h; 605 606 mutex_exit(&gptwo_handle_list_lock); 607 } 608 609 fco_handle_t 610 gptwocfg_get_handle(dev_info_t *dip) 611 { 612 gptwocfg_handle_list_t *h, *last; 613 fco_handle_t fco_handle; 614 615 mutex_enter(&gptwo_handle_list_lock); 616 617 h = last = gptwocfg_handle_list; 618 619 while (h != NULL) { 620 if (h->dip == dip) { 621 if (h == gptwocfg_handle_list) 622 gptwocfg_handle_list = h->next; 623 else 624 last->next = h->next; 625 626 mutex_exit(&gptwo_handle_list_lock); 627 628 fco_handle = h->fco_handle; 629 630 kmem_free(h, sizeof (gptwocfg_handle_list_t)); 631 632 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_get_handle - " 633 "dip=%lx fco_handle=%lx\n", dip, fco_handle); 634 635 return (fco_handle); 636 } 637 last = h; 638 h = h->next; 639 } 640 641 mutex_exit(&gptwo_handle_list_lock); 642 643 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_handle - dip=%lx NO HANDLE\n", 644 dip); 645 646 return (0); 647 } 648 649 void 650 gptwocfg_devi_attach_to_parent(dev_info_t *dip) 651 { 652 (void) i_ndi_config_node(dip, DS_LINKED, 0); 653 } 654 655 #ifdef DEBUG 656 static void 657 debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, 658 uintptr_t a4, uintptr_t a5) 659 { 660 cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); 661 } 662 #endif 663