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 #include <sys/debug.h> 28 #include <sys/types.h> 29 #include <sys/varargs.h> 30 #include <sys/errno.h> 31 #include <sys/cred.h> 32 #include <sys/dditypes.h> 33 #include <sys/devops.h> 34 #include <sys/modctl.h> 35 #include <sys/poll.h> 36 #include <sys/conf.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/sunndi.h> 40 #include <sys/ndi_impldefs.h> 41 #include <sys/stat.h> 42 #include <sys/kmem.h> 43 #include <sys/vmem.h> 44 #include <sys/processor.h> 45 #include <sys/spitregs.h> 46 #include <sys/cpuvar.h> 47 #include <sys/cpupart.h> 48 #include <sys/mem_config.h> 49 #include <sys/ddi_impldefs.h> 50 #include <sys/systm.h> 51 #include <sys/machsystm.h> 52 #include <sys/autoconf.h> 53 #include <sys/cmn_err.h> 54 #include <sys/sysmacros.h> 55 #include <sys/x_call.h> 56 #include <sys/promif.h> 57 #include <sys/prom_plat.h> 58 #include <sys/membar.h> 59 #include <vm/seg_kmem.h> 60 #include <sys/mem_cage.h> 61 #include <sys/stack.h> 62 #include <sys/archsystm.h> 63 #include <vm/hat_sfmmu.h> 64 #include <sys/pte.h> 65 #include <sys/mmu.h> 66 #include <sys/cpu_module.h> 67 #include <sys/obpdefs.h> 68 #include <sys/note.h> 69 70 #include <sys/starfire.h> /* plat_max_... decls */ 71 #include <sys/cvc.h> 72 #include <sys/cpu_sgnblk_defs.h> 73 #include <sys/drmach.h> 74 #include <sys/dr_util.h> 75 #include <sys/pda.h> 76 77 #include <sys/sysevent.h> 78 #include <sys/sysevent/dr.h> 79 #include <sys/sysevent/eventdefs.h> 80 81 82 extern void bcopy32_il(uint64_t, uint64_t); 83 extern void flush_ecache_il( 84 uint64_t physaddr, int size, int linesz); 85 extern uint_t ldphysio_il(uint64_t physaddr); 86 extern void stphysio_il(uint64_t physaddr, uint_t value); 87 88 extern uint64_t mc_get_mem_alignment(void); 89 extern uint64_t mc_get_asr_addr(pnode_t); 90 extern uint64_t mc_get_idle_addr(pnode_t); 91 extern uint64_t mc_get_alignment_mask(pnode_t); 92 extern int mc_read_asr(pnode_t, uint_t *); 93 extern int mc_write_asr(pnode_t, uint_t); 94 extern uint64_t mc_asr_to_pa(uint_t); 95 extern uint_t mc_pa_to_asr(uint_t, uint64_t); 96 97 extern int pc_madr_add(int, int, int, int); 98 99 typedef struct { 100 struct drmach_node *node; 101 void *data; 102 } drmach_node_walk_args_t; 103 104 typedef struct drmach_node { 105 void *here; 106 107 pnode_t (*get_dnode)(struct drmach_node *node); 108 int (*walk)(struct drmach_node *node, void *data, 109 int (*cb)(drmach_node_walk_args_t *args)); 110 } drmach_node_t; 111 112 typedef struct { 113 int min_index; 114 int max_index; 115 int arr_sz; 116 drmachid_t *arr; 117 } drmach_array_t; 118 119 typedef struct { 120 void *isa; 121 122 sbd_error_t *(*release)(drmachid_t); 123 sbd_error_t *(*status)(drmachid_t, drmach_status_t *); 124 125 char name[MAXNAMELEN]; 126 } drmach_common_t; 127 128 typedef struct { 129 drmach_common_t cm; 130 int bnum; 131 int assigned; 132 int powered; 133 int connect_cpuid; 134 int cond; 135 drmach_node_t *tree; 136 drmach_array_t *devices; 137 } drmach_board_t; 138 139 typedef struct { 140 drmach_common_t cm; 141 drmach_board_t *bp; 142 int unum; 143 int busy; 144 int powered; 145 const char *type; 146 drmach_node_t *node; 147 } drmach_device_t; 148 149 typedef struct { 150 int flags; 151 drmach_device_t *dp; 152 sbd_error_t *err; 153 dev_info_t *dip; 154 } drmach_config_args_t; 155 156 typedef struct { 157 uint64_t idle_addr; 158 drmach_device_t *mem; 159 } drmach_mc_idle_script_t; 160 161 typedef struct { 162 uint64_t masr_addr; 163 uint_t masr; 164 uint_t _filler; 165 } drmach_rename_script_t; 166 167 typedef struct { 168 void (*run)(void *arg); 169 caddr_t data; 170 pda_handle_t *ph; 171 struct memlist *c_ml; 172 uint64_t s_copybasepa; 173 uint64_t t_copybasepa; 174 drmach_device_t *restless_mc; /* diagnostic output */ 175 } drmach_copy_rename_program_t; 176 177 typedef enum { 178 DO_IDLE, 179 DO_UNIDLE, 180 DO_PAUSE, 181 DO_UNPAUSE 182 } drmach_iopc_op_t; 183 184 typedef struct { 185 drmach_board_t *obj; 186 int ndevs; 187 void *a; 188 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t); 189 sbd_error_t *err; 190 } drmach_board_cb_data_t; 191 192 static caddr_t drmach_shutdown_va; 193 194 static int drmach_initialized; 195 static drmach_array_t *drmach_boards; 196 197 static int drmach_cpu_delay = 100; 198 static int drmach_cpu_ntries = 50000; 199 200 volatile uchar_t *drmach_xt_mb; 201 202 /* 203 * Do not change the drmach_shutdown_mbox structure without 204 * considering the drmach_shutdown_asm assembly language code. 205 */ 206 struct drmach_shutdown_mbox { 207 uint64_t estack; 208 uint64_t flushaddr; 209 int size; 210 int linesize; 211 uint64_t physaddr; 212 }; 213 struct drmach_shutdown_mbox *drmach_shutdown_asm_mbox; 214 static sbd_error_t *drmach_device_new(drmach_node_t *, 215 drmach_board_t *, drmach_device_t **); 216 static sbd_error_t *drmach_cpu_new(drmach_device_t *); 217 static sbd_error_t *drmach_mem_new(drmach_device_t *); 218 static sbd_error_t *drmach_io_new(drmach_device_t *); 219 220 extern struct cpu *SIGBCPU; 221 222 #ifdef DEBUG 223 224 #define DRMACH_PR if (drmach_debug) printf 225 int drmach_debug = 0; /* set to non-zero to enable debug messages */ 226 #else 227 228 #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf 229 #endif /* DEBUG */ 230 231 #define DRMACH_OBJ(id) ((drmach_common_t *)id) 232 233 #define DRMACH_IS_BOARD_ID(id) \ 234 ((id != 0) && \ 235 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new)) 236 237 #define DRMACH_IS_CPU_ID(id) \ 238 ((id != 0) && \ 239 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new)) 240 241 #define DRMACH_IS_MEM_ID(id) \ 242 ((id != 0) && \ 243 (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new)) 244 245 #define DRMACH_IS_IO_ID(id) \ 246 ((id != 0) && \ 247 (DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 248 249 #define DRMACH_IS_DEVICE_ID(id) \ 250 ((id != 0) && \ 251 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 252 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 253 DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 254 255 #define DRMACH_IS_ID(id) \ 256 ((id != 0) && \ 257 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \ 258 DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 259 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 260 DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 261 262 #define DRMACH_CPUID2BNUM(cpuid) \ 263 ((cpuid) / MAX_CPU_UNITS_PER_BOARD) 264 265 #define DRMACH_INTERNAL_ERROR() \ 266 drerr_new(1, ESTF_INTERNAL, drmach_ie_fmt, __LINE__) 267 static char *drmach_ie_fmt = "drmach.c %d"; 268 269 static struct { 270 const char *name; 271 const char *type; 272 sbd_error_t *(*new)(drmach_device_t *); 273 } name2type[] = { 274 { "SUNW,UltraSPARC", DRMACH_DEVTYPE_CPU, drmach_cpu_new }, 275 { "mem-unit", DRMACH_DEVTYPE_MEM, drmach_mem_new }, 276 { "pci", DRMACH_DEVTYPE_PCI, drmach_io_new }, 277 { "sbus", DRMACH_DEVTYPE_SBUS, drmach_io_new }, 278 }; 279 280 /* node types to cleanup when a board is unconfigured */ 281 #define MISC_COUNTER_TIMER_DEVNAME "counter-timer" 282 #define MISC_PERF_COUNTER_DEVNAME "perf-counter" 283 284 /* utility */ 285 #define MBYTE (1048576ull) 286 287 /* 288 * This is necessary because the CPU support needs 289 * to call cvc_assign_iocpu. 290 */ 291 #ifndef lint 292 char _depends_on[] = "drv/cvc"; 293 #endif /* lint */ 294 295 /* 296 * drmach autoconfiguration data structures and interfaces 297 */ 298 299 extern struct mod_ops mod_miscops; 300 301 static struct modlmisc modlmisc = { 302 &mod_miscops, 303 "Sun Enterprise 10000 DR" 304 }; 305 306 static struct modlinkage modlinkage = { 307 MODREV_1, 308 (void *)&modlmisc, 309 NULL 310 }; 311 312 static kmutex_t drmach_i_lock; 313 314 int 315 _init(void) 316 { 317 int err; 318 319 /* check that we have the correct version of obp */ 320 if (prom_test("SUNW,UE10000,add-brd") != 0) { 321 322 cmn_err(CE_WARN, "!OBP/SSP upgrade is required to enable " 323 "DR Functionality"); 324 325 return (-1); 326 } 327 328 mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL); 329 330 drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena, 331 NCPU * sizeof (uchar_t), VM_SLEEP); 332 drmach_shutdown_asm_mbox = (struct drmach_shutdown_mbox *) 333 vmem_alloc(static_alloc_arena, sizeof (struct drmach_shutdown_mbox), 334 VM_SLEEP); 335 336 if ((err = mod_install(&modlinkage)) != 0) { 337 mutex_destroy(&drmach_i_lock); 338 vmem_free(static_alloc_arena, (void *)drmach_xt_mb, 339 NCPU * sizeof (uchar_t)); 340 vmem_free(static_alloc_arena, (void *)drmach_shutdown_asm_mbox, 341 sizeof (struct drmach_shutdown_mbox)); 342 } 343 344 return (err); 345 } 346 347 int 348 _fini(void) 349 { 350 static int drmach_fini(void); 351 352 if (drmach_fini()) 353 return (DDI_FAILURE); 354 else 355 return (mod_remove(&modlinkage)); 356 } 357 358 int 359 _info(struct modinfo *modinfop) 360 { 361 return (mod_info(&modlinkage, modinfop)); 362 } 363 364 static pnode_t 365 drmach_node_obp_get_dnode(drmach_node_t *np) 366 { 367 return ((pnode_t)(uintptr_t)np->here); 368 } 369 370 static int 371 drmach_node_obp_walk(drmach_node_t *np, void *data, 372 int (*cb)(drmach_node_walk_args_t *args)) 373 { 374 pnode_t nodeid; 375 int rv; 376 drmach_node_walk_args_t args; 377 378 /* initialized args structure for callback */ 379 args.node = np; 380 args.data = data; 381 382 nodeid = prom_childnode(prom_rootnode()); 383 384 /* save our new position with in the tree */ 385 np->here = (void *)(uintptr_t)nodeid; 386 387 rv = 0; 388 while (nodeid != OBP_NONODE) { 389 rv = (*cb)(&args); 390 if (rv) 391 break; 392 393 nodeid = prom_nextnode(nodeid); 394 395 /* save our new position with in the tree */ 396 np->here = (void *)(uintptr_t)nodeid; 397 } 398 399 return (rv); 400 } 401 402 static drmach_node_t * 403 drmach_node_new(void) 404 { 405 drmach_node_t *np; 406 407 np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP); 408 409 np->get_dnode = drmach_node_obp_get_dnode; 410 np->walk = drmach_node_obp_walk; 411 412 return (np); 413 } 414 415 static void 416 drmach_node_dispose(drmach_node_t *np) 417 { 418 kmem_free(np, sizeof (*np)); 419 } 420 421 static dev_info_t * 422 drmach_node_get_dip(drmach_node_t *np) 423 { 424 pnode_t nodeid; 425 426 nodeid = np->get_dnode(np); 427 if (nodeid == OBP_NONODE) 428 return (NULL); 429 else { 430 dev_info_t *dip; 431 432 /* The root node doesn't have to be held */ 433 dip = e_ddi_nodeid_to_dip(nodeid); 434 if (dip) { 435 /* 436 * Branch rooted at dip is already held, so release 437 * hold acquired in e_ddi_nodeid_to_dip() 438 */ 439 ddi_release_devi(dip); 440 ASSERT(e_ddi_branch_held(dip)); 441 } 442 443 return (dip); 444 } 445 /*NOTREACHED*/ 446 } 447 448 static pnode_t 449 drmach_node_get_dnode(drmach_node_t *np) 450 { 451 return (np->get_dnode(np)); 452 } 453 454 static int 455 drmach_node_walk(drmach_node_t *np, void *param, 456 int (*cb)(drmach_node_walk_args_t *args)) 457 { 458 return (np->walk(np, param, cb)); 459 } 460 461 static int 462 drmach_node_get_prop(drmach_node_t *np, char *name, void *buf) 463 { 464 pnode_t nodeid; 465 int rv; 466 467 nodeid = np->get_dnode(np); 468 if (nodeid == OBP_NONODE) 469 rv = -1; 470 else if (prom_getproplen(nodeid, (caddr_t)name) < 0) 471 rv = -1; 472 else { 473 (void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf); 474 rv = 0; 475 } 476 477 return (rv); 478 } 479 480 static int 481 drmach_node_get_proplen(drmach_node_t *np, char *name, int *len) 482 { 483 pnode_t nodeid; 484 int rv; 485 486 nodeid = np->get_dnode(np); 487 if (nodeid == OBP_NONODE) 488 rv = -1; 489 else { 490 *len = prom_getproplen(nodeid, (caddr_t)name); 491 rv = (*len < 0 ? -1 : 0); 492 } 493 494 return (rv); 495 } 496 497 static drmachid_t 498 drmach_node_dup(drmach_node_t *np) 499 { 500 drmach_node_t *dup; 501 502 dup = drmach_node_new(); 503 dup->here = np->here; 504 505 return (dup); 506 } 507 508 /* 509 * drmach_array provides convenient array construction, access, 510 * bounds checking and array destruction logic. 511 */ 512 513 static drmach_array_t * 514 drmach_array_new(int min_index, int max_index) 515 { 516 drmach_array_t *arr; 517 518 arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP); 519 520 arr->arr_sz = (max_index - min_index + 1) * sizeof (void *); 521 if (arr->arr_sz > 0) { 522 arr->min_index = min_index; 523 arr->max_index = max_index; 524 525 arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP); 526 return (arr); 527 } else { 528 kmem_free(arr, sizeof (*arr)); 529 return (0); 530 } 531 } 532 533 static int 534 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val) 535 { 536 if (idx < arr->min_index || idx > arr->max_index) 537 return (-1); 538 else { 539 arr->arr[idx - arr->min_index] = val; 540 return (0); 541 } 542 /*NOTREACHED*/ 543 } 544 545 static int 546 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val) 547 { 548 if (idx < arr->min_index || idx > arr->max_index) 549 return (-1); 550 else { 551 *val = arr->arr[idx - arr->min_index]; 552 return (0); 553 } 554 /*NOTREACHED*/ 555 } 556 557 static int 558 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val) 559 { 560 int rv; 561 562 *idx = arr->min_index; 563 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 564 *idx += 1; 565 566 return (rv); 567 } 568 569 static int 570 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val) 571 { 572 int rv; 573 574 *idx += 1; 575 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 576 *idx += 1; 577 578 return (rv); 579 } 580 581 static void 582 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t)) 583 { 584 drmachid_t val; 585 int idx; 586 int rv; 587 588 rv = drmach_array_first(arr, &idx, &val); 589 while (rv == 0) { 590 (*disposer)(val); 591 rv = drmach_array_next(arr, &idx, &val); 592 } 593 594 kmem_free(arr->arr, arr->arr_sz); 595 kmem_free(arr, sizeof (*arr)); 596 } 597 598 /*ARGSUSED*/ 599 static int 600 drmach_prom_select(pnode_t nodeid, void *arg, uint_t flags) 601 { 602 int rprop[64]; 603 pnode_t saved; 604 drmach_config_args_t *ap = (drmach_config_args_t *)arg; 605 drmach_device_t *dp = ap->dp; 606 sbd_error_t *err; 607 608 saved = drmach_node_get_dnode(dp->node); 609 610 if (nodeid != saved) 611 return (DDI_FAILURE); 612 613 if (saved == OBP_NONODE) { 614 err = DRMACH_INTERNAL_ERROR(); 615 DRERR_SET_C(&ap->err, &err); 616 return (DDI_FAILURE); 617 } 618 619 if (prom_getprop(nodeid, OBP_REG, (caddr_t)rprop) <= 0) { 620 return (DDI_FAILURE); 621 } 622 623 return (DDI_SUCCESS); 624 } 625 626 /*ARGSUSED*/ 627 static void 628 drmach_branch_callback(dev_info_t *rdip, void *arg, uint_t flags) 629 { 630 drmach_config_args_t *ap = (drmach_config_args_t *)arg; 631 632 ASSERT(ap->dip == NULL); 633 634 ap->dip = rdip; 635 } 636 637 sbd_error_t * 638 drmach_configure(drmachid_t id, int flags) 639 { 640 drmach_device_t *dp; 641 sbd_error_t *err; 642 drmach_config_args_t ca; 643 devi_branch_t b = {0}; 644 dev_info_t *fdip = NULL; 645 646 if (!DRMACH_IS_DEVICE_ID(id)) 647 return (drerr_new(0, ESTF_INAPPROP, NULL)); 648 dp = id; 649 650 ca.dp = dp; 651 ca.flags = flags; 652 ca.err = NULL; /* will be set if error detected */ 653 ca.dip = NULL; 654 655 b.arg = &ca; 656 b.type = DEVI_BRANCH_PROM; 657 b.create.prom_branch_select = drmach_prom_select; 658 b.devi_branch_callback = drmach_branch_callback; 659 660 if (e_ddi_branch_create(ddi_root_node(), &b, &fdip, 661 DEVI_BRANCH_CHILD | DEVI_BRANCH_CONFIGURE) != 0) { 662 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 663 664 /* 665 * If non-NULL, fdip is returned held and must be released. 666 */ 667 if (fdip != NULL) { 668 (void) ddi_pathname(fdip, path); 669 ddi_release_devi(fdip); 670 } else if (ca.dip != NULL) { 671 /* safe to call ddi_pathname as dip already held */ 672 (void) ddi_pathname(ca.dip, path); 673 } else { 674 (void) strcpy(path, "<none>"); 675 } 676 677 err = drerr_new(1, ESTF_DRVFAIL, path); 678 DRERR_SET_C(&ca.err, &err); 679 kmem_free(path, MAXPATHLEN); 680 } 681 682 return (ca.err); 683 } 684 685 static sbd_error_t * 686 drmach_device_new(drmach_node_t *node, 687 drmach_board_t *bp, drmach_device_t **dpp) 688 { 689 int i; 690 int rv; 691 drmach_device_t *dp; 692 sbd_error_t *err; 693 char name[OBP_MAXDRVNAME]; 694 695 rv = drmach_node_get_prop(node, OBP_NAME, name); 696 if (rv) { 697 /* every node is expected to have a name */ 698 err = drerr_new(1, ESTF_GETPROP, 699 "PROM Node 0x%x: property %s", 700 (uint_t)node->get_dnode(node), OBP_NAME); 701 702 return (err); 703 } 704 705 /* 706 * The node currently being examined is not listed in the name2type[] 707 * array. In this case, the node is no interest to drmach. Both 708 * dp and err are initialized here to yield nothing (no device or 709 * error structure) for this case. 710 */ 711 for (i = 0; i < sizeof (name2type) / sizeof (name2type[0]); i++) 712 if (strcmp(name2type[i].name, name) == 0) 713 break; 714 715 if (i < sizeof (name2type) / sizeof (name2type[0])) { 716 dp = kmem_zalloc(sizeof (drmach_device_t), KM_SLEEP); 717 718 dp->bp = bp; 719 dp->unum = -1; 720 dp->node = drmach_node_dup(node); 721 dp->type = name2type[i].type; 722 723 err = (name2type[i].new)(dp); 724 if (err) { 725 drmach_node_dispose(node); 726 kmem_free(dp, sizeof (*dp)); 727 dp = NULL; 728 } 729 730 *dpp = dp; 731 return (err); 732 } 733 734 /* 735 * The node currently being examined is not listed in the name2type[] 736 * array. In this case, the node is no interest to drmach. Both 737 * dp and err are initialized here to yield nothing (no device or 738 * error structure) for this case. 739 */ 740 *dpp = NULL; 741 return (NULL); 742 } 743 744 static void 745 drmach_device_dispose(drmachid_t id) 746 { 747 drmach_device_t *self = id; 748 749 if (self->node) 750 drmach_node_dispose(self->node); 751 752 kmem_free(self, sizeof (*self)); 753 } 754 755 static sbd_error_t * 756 drmach_device_get_prop(drmach_device_t *dp, char *name, void *buf) 757 { 758 sbd_error_t *err = NULL; 759 int rv; 760 761 rv = drmach_node_get_prop(dp->node, name, buf); 762 if (rv) { 763 err = drerr_new(1, ESTF_GETPROP, 764 "%s::%s: property %s", 765 dp->bp->cm.name, dp->cm.name, name); 766 } 767 768 return (err); 769 } 770 771 static sbd_error_t * 772 drmach_device_get_proplen(drmach_device_t *dp, char *name, int *len) 773 { 774 sbd_error_t *err = NULL; 775 int rv; 776 777 rv = drmach_node_get_proplen(dp->node, name, len); 778 if (rv) { 779 err = drerr_new(1, ESTF_GETPROPLEN, 780 "%s::%s: property %s", 781 dp->bp->cm.name, dp->cm.name, name); 782 } 783 784 return (err); 785 } 786 787 static drmach_board_t * 788 drmach_board_new(int bnum) 789 { 790 static sbd_error_t *drmach_board_release(drmachid_t); 791 static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *); 792 793 drmach_board_t *bp; 794 795 bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP); 796 797 bp->cm.isa = (void *)drmach_board_new; 798 bp->cm.release = drmach_board_release; 799 bp->cm.status = drmach_board_status; 800 801 (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name)); 802 803 bp->bnum = bnum; 804 bp->devices = NULL; 805 bp->connect_cpuid = -1; 806 bp->tree = drmach_node_new(); 807 bp->assigned = !drmach_initialized; 808 bp->powered = !drmach_initialized; 809 810 (void) drmach_array_set(drmach_boards, bnum, bp); 811 return (bp); 812 } 813 814 static void 815 drmach_board_dispose(drmachid_t id) 816 { 817 drmach_board_t *bp; 818 819 ASSERT(DRMACH_IS_BOARD_ID(id)); 820 bp = id; 821 822 if (bp->tree) 823 drmach_node_dispose(bp->tree); 824 825 if (bp->devices) 826 drmach_array_dispose(bp->devices, drmach_device_dispose); 827 828 kmem_free(bp, sizeof (*bp)); 829 } 830 831 static sbd_error_t * 832 drmach_board_status(drmachid_t id, drmach_status_t *stat) 833 { 834 sbd_error_t *err = NULL; 835 drmach_board_t *bp; 836 837 if (!DRMACH_IS_BOARD_ID(id)) 838 return (drerr_new(0, ESTF_INAPPROP, NULL)); 839 bp = id; 840 841 stat->assigned = bp->assigned; 842 stat->powered = bp->powered; 843 stat->busy = 0; /* assume not busy */ 844 stat->configured = 0; /* assume not configured */ 845 stat->empty = 0; 846 stat->cond = bp->cond = SBD_COND_OK; 847 (void) strncpy(stat->type, "System Brd", sizeof (stat->type)); 848 stat->info[0] = '\0'; 849 850 if (bp->devices) { 851 int rv; 852 int d_idx; 853 drmachid_t d_id; 854 855 rv = drmach_array_first(bp->devices, &d_idx, &d_id); 856 while (rv == 0) { 857 drmach_status_t d_stat; 858 859 err = drmach_status(d_id, &d_stat); 860 if (err) 861 break; 862 863 stat->busy |= d_stat.busy; 864 stat->configured |= d_stat.configured; 865 866 rv = drmach_array_next(bp->devices, &d_idx, &d_id); 867 } 868 } 869 870 return (err); 871 } 872 873 /* a simple routine to reduce redundancy of this common logic */ 874 static pda_handle_t 875 drmach_pda_open(void) 876 { 877 pda_handle_t ph; 878 879 ph = pda_open(); 880 if (ph == NULL) { 881 /* catch in debug kernels */ 882 ASSERT(0); 883 cmn_err(CE_WARN, "pda_open failed"); 884 } 885 886 return (ph); 887 } 888 889 #ifdef DEBUG 890 int drmach_init_break = 0; 891 #endif 892 893 static int 894 hold_rele_branch(dev_info_t *rdip, void *arg) 895 { 896 int i; 897 int *holdp = (int *)arg; 898 char *name = ddi_node_name(rdip); 899 900 /* 901 * For Starfire, we must be children of the root devinfo node 902 */ 903 ASSERT(ddi_get_parent(rdip) == ddi_root_node()); 904 905 for (i = 0; i < sizeof (name2type) / sizeof (name2type[0]); i++) 906 if (strcmp(name2type[i].name, name) == 0) 907 break; 908 909 if (i == sizeof (name2type) / sizeof (name2type[0])) { 910 /* Not of interest to us */ 911 return (DDI_WALK_PRUNECHILD); 912 } 913 914 if (*holdp) { 915 ASSERT(!e_ddi_branch_held(rdip)); 916 e_ddi_branch_hold(rdip); 917 } else { 918 ASSERT(e_ddi_branch_held(rdip)); 919 e_ddi_branch_rele(rdip); 920 } 921 922 return (DDI_WALK_PRUNECHILD); 923 } 924 925 static int 926 drmach_init(void) 927 { 928 pnode_t nodeid; 929 dev_info_t *rdip; 930 int hold, circ; 931 932 #ifdef DEBUG 933 if (drmach_init_break) 934 debug_enter("drmach_init: drmach_init_break set\n"); 935 #endif 936 mutex_enter(&drmach_i_lock); 937 if (drmach_initialized) { 938 mutex_exit(&drmach_i_lock); 939 return (0); 940 } 941 942 drmach_boards = drmach_array_new(0, MAX_BOARDS - 1); 943 944 nodeid = prom_childnode(prom_rootnode()); 945 do { 946 int bnum; 947 drmachid_t id; 948 949 bnum = -1; 950 (void) prom_getprop(nodeid, OBP_BOARDNUM, (caddr_t)&bnum); 951 if (bnum == -1) 952 continue; 953 954 if (drmach_array_get(drmach_boards, bnum, &id) == -1) { 955 cmn_err(CE_WARN, "OBP node 0x%x has" 956 " invalid property value, %s=%d", 957 nodeid, OBP_BOARDNUM, bnum); 958 959 /* clean up */ 960 drmach_array_dispose( 961 drmach_boards, drmach_board_dispose); 962 963 mutex_exit(&drmach_i_lock); 964 return (-1); 965 } else if (id == NULL) 966 (void) drmach_board_new(bnum); 967 } while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE); 968 969 drmach_shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP); 970 971 /* 972 * Walk immediate children of devinfo root node and hold 973 * all devinfo branches of interest. 974 */ 975 hold = 1; 976 rdip = ddi_root_node(); 977 978 ndi_devi_enter(rdip, &circ); 979 ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold); 980 ndi_devi_exit(rdip, circ); 981 982 drmach_initialized = 1; 983 984 mutex_exit(&drmach_i_lock); 985 986 return (0); 987 } 988 989 static int 990 drmach_fini(void) 991 { 992 dev_info_t *rdip; 993 int hold, circ; 994 995 if (drmach_initialized) { 996 int busy = 0; 997 int rv; 998 int idx; 999 drmachid_t id; 1000 1001 ASSERT(drmach_boards != NULL); 1002 1003 rv = drmach_array_first(drmach_boards, &idx, &id); 1004 while (rv == 0) { 1005 sbd_error_t *err; 1006 drmach_status_t stat; 1007 1008 err = drmach_board_status(id, &stat); 1009 if (err) { 1010 /* catch in debug kernels */ 1011 ASSERT(0); 1012 sbd_err_clear(&err); 1013 busy = 1; 1014 } else 1015 busy |= stat.busy; 1016 1017 rv = drmach_array_next(drmach_boards, &idx, &id); 1018 } 1019 1020 if (busy) 1021 return (-1); 1022 1023 drmach_array_dispose(drmach_boards, drmach_board_dispose); 1024 drmach_boards = NULL; 1025 1026 vmem_free(heap_arena, drmach_shutdown_va, PAGESIZE); 1027 1028 /* 1029 * Walk immediate children of the root devinfo node 1030 * releasing holds acquired on branches in drmach_init() 1031 */ 1032 hold = 0; 1033 rdip = ddi_root_node(); 1034 1035 ndi_devi_enter(rdip, &circ); 1036 ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold); 1037 ndi_devi_exit(rdip, circ); 1038 1039 mutex_destroy(&drmach_i_lock); 1040 1041 drmach_initialized = 0; 1042 } 1043 if (drmach_xt_mb != NULL) { 1044 vmem_free(static_alloc_arena, (void *)drmach_xt_mb, 1045 NCPU * sizeof (uchar_t)); 1046 } 1047 if (drmach_shutdown_asm_mbox != NULL) { 1048 vmem_free(static_alloc_arena, (void *)drmach_shutdown_asm_mbox, 1049 sizeof (struct drmach_shutdown_mbox)); 1050 } 1051 return (0); 1052 } 1053 1054 static sbd_error_t * 1055 drmach_get_mc_asr_addr(drmachid_t id, uint64_t *pa) 1056 { 1057 drmach_device_t *dp; 1058 pnode_t nodeid; 1059 uint64_t addr; 1060 1061 if (!DRMACH_IS_MEM_ID(id)) 1062 return (drerr_new(0, ESTF_INAPPROP, NULL)); 1063 dp = id; 1064 1065 nodeid = drmach_node_get_dnode(dp->node); 1066 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) 1067 return (DRMACH_INTERNAL_ERROR()); 1068 1069 addr = mc_get_asr_addr(nodeid); 1070 if (addr == (uint64_t)-1) 1071 return (DRMACH_INTERNAL_ERROR()); 1072 1073 *pa = addr; 1074 return (NULL); 1075 } 1076 1077 static sbd_error_t * 1078 drmach_get_mc_idle_addr(drmachid_t id, uint64_t *pa) 1079 { 1080 drmach_device_t *dp; 1081 pnode_t nodeid; 1082 uint64_t addr; 1083 1084 if (!DRMACH_IS_MEM_ID(id)) 1085 return (drerr_new(0, ESTF_INAPPROP, NULL)); 1086 dp = id; 1087 1088 nodeid = drmach_node_get_dnode(dp->node); 1089 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) 1090 return (DRMACH_INTERNAL_ERROR()); 1091 1092 addr = mc_get_idle_addr(nodeid); 1093 if (addr == (uint64_t)-1) 1094 return (DRMACH_INTERNAL_ERROR()); 1095 1096 *pa = addr; 1097 return (NULL); 1098 } 1099 1100 static sbd_error_t * 1101 drmach_read_mc_asr(drmachid_t id, uint_t *mcregp) 1102 { 1103 drmach_device_t *dp; 1104 pnode_t nodeid; 1105 sbd_error_t *err; 1106 1107 if (!DRMACH_IS_MEM_ID(id)) 1108 return (drerr_new(0, ESTF_INAPPROP, NULL)); 1109 dp = id; 1110 1111 nodeid = drmach_node_get_dnode(dp->node); 1112 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) 1113 err = DRMACH_INTERNAL_ERROR(); 1114 else if (mc_read_asr(nodeid, mcregp) == -1) 1115 err = DRMACH_INTERNAL_ERROR(); 1116 else 1117 err = NULL; 1118 1119 return (err); 1120 } 1121 1122 static sbd_error_t * 1123 drmach_write_mc_asr(drmachid_t id, uint_t mcreg) 1124 { 1125 drmach_device_t *dp; 1126 pnode_t nodeid; 1127 sbd_error_t *err; 1128 1129 if (!DRMACH_IS_MEM_ID(id)) 1130 return (drerr_new(0, ESTF_INAPPROP, NULL)); 1131 dp = id; 1132 1133 nodeid = drmach_node_get_dnode(dp->node); 1134 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) 1135 err = DRMACH_INTERNAL_ERROR(); 1136 else if (mc_write_asr(nodeid, mcreg) == -1) 1137 err = DRMACH_INTERNAL_ERROR(); 1138 else 1139 err = NULL; 1140 1141 return (err); 1142 } 1143 1144 static sbd_error_t * 1145 drmach_prep_rename_script(drmach_device_t *s_mem, drmach_device_t *t_mem, 1146 uint64_t t_slice_offset, caddr_t buf, int buflen) 1147 { 1148 int i, b, m; 1149 drmach_mc_idle_script_t *isp; 1150 drmach_rename_script_t *rsp; 1151 int s_bd, t_bd; 1152 uint_t s_masr, t_masr; 1153 uint64_t s_new_basepa, t_new_basepa; 1154 int b_idx, rv; 1155 sbd_error_t *err; 1156 drmachid_t b_id; 1157 drmach_board_t *brd; 1158 1159 #ifdef DEBUG 1160 /* 1161 * Starfire CPU/MEM/IO boards have only one MC per board. 1162 * This function has been coded with that fact in mind. 1163 */ 1164 ASSERT(MAX_MEM_UNITS_PER_BOARD == 1); 1165 1166 /* 1167 * calculate the maximum space that could be consumed, 1168 * then verify the available buffer space is adequate. 1169 */ 1170 m = sizeof (drmach_mc_idle_script_t *) * 2; /* two MCs */ 1171 b = sizeof (drmach_rename_script_t *) * 3 * MAX_CPU_UNITS_PER_BOARD; 1172 b += sizeof (drmach_rename_script_t *) * 3 * MAX_IO_UNITS_PER_BOARD; 1173 b *= MAX_BOARDS; 1174 b += sizeof (drmach_rename_script_t *) * 3; 1175 b += sizeof (drmach_rename_script_t *) * 1; 1176 ASSERT(m + b < buflen); 1177 #endif 1178 1179 /* 1180 * construct an array of MC idle register addresses of 1181 * both MCs. The array is zero terminated -- as expected 1182 * by drmach_copy_rename_prog__relocatable(). 1183 */ 1184 isp = (drmach_mc_idle_script_t *)buf; 1185 1186 /* source mc */ 1187 err = drmach_get_mc_idle_addr(s_mem, &isp->idle_addr); 1188 if (err) 1189 return (err); 1190 isp->mem = s_mem; 1191 isp += 1; 1192 1193 /* target mc */ 1194 err = drmach_get_mc_idle_addr(t_mem, &isp->idle_addr); 1195 if (err) 1196 return (err); 1197 isp->mem = t_mem; 1198 isp += 1; 1199 1200 /* terminator */ 1201 isp->idle_addr = 0; 1202 isp->mem = NULL; 1203 isp += 1; 1204 1205 /* fetch source mc asr register value */ 1206 err = drmach_read_mc_asr(s_mem, &s_masr); 1207 if (err) 1208 return (err); 1209 else if (s_masr & STARFIRE_MC_INTERLEAVE_MASK) { 1210 return (drerr_new(1, ESTF_INTERBOARD, "%s::%s", 1211 s_mem->bp->cm.name, s_mem->cm.name)); 1212 } 1213 1214 /* fetch target mc asr register value */ 1215 err = drmach_read_mc_asr(t_mem, &t_masr); 1216 if (err) 1217 return (err); 1218 else if (t_masr & STARFIRE_MC_INTERLEAVE_MASK) { 1219 return (drerr_new(1, ESTF_INTERBOARD, "%s::%s", 1220 t_mem->bp->cm.name, t_mem->cm.name)); 1221 } 1222 1223 /* get new source base pa from target's masr */ 1224 s_new_basepa = mc_asr_to_pa(t_masr); 1225 1226 /* 1227 * remove any existing slice offset to realign 1228 * memory with board's slice boundary 1229 */ 1230 s_new_basepa &= ~ (mc_get_mem_alignment() - 1); 1231 1232 /* get new target base pa from source's masr */ 1233 t_new_basepa = mc_asr_to_pa(s_masr); 1234 1235 /* remove any existing slice offset, then apply new offset */ 1236 t_new_basepa &= ~ (mc_get_mem_alignment() - 1); 1237 t_new_basepa += t_slice_offset; 1238 1239 /* encode new base pa into s_masr. turn off mem present bit */ 1240 s_masr = mc_pa_to_asr(s_masr, s_new_basepa); 1241 s_masr &= ~STARFIRE_MC_MEM_PRESENT_MASK; 1242 1243 /* encode new base pa into t_masr. turn on mem present bit */ 1244 t_masr = mc_pa_to_asr(t_masr, t_new_basepa); 1245 t_masr |= STARFIRE_MC_MEM_PRESENT_MASK; 1246 1247 /* 1248 * Step 0: Mark source memory as not present. 1249 */ 1250 m = 0; 1251 rsp = (drmach_rename_script_t *)isp; 1252 err = drmach_get_mc_asr_addr(s_mem, &rsp[m].masr_addr); 1253 if (err) 1254 return (err); 1255 rsp[m].masr = s_masr; 1256 m++; 1257 1258 /* 1259 * Step 1: Write source base address to target MC 1260 * with present bit off. 1261 */ 1262 err = drmach_get_mc_asr_addr(t_mem, &rsp[m].masr_addr); 1263 if (err) 1264 return (err); 1265 rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK; 1266 m++; 1267 1268 /* 1269 * Step 2: Now rewrite target reg with present bit on. 1270 */ 1271 rsp[m].masr_addr = rsp[m-1].masr_addr; 1272 rsp[m].masr = t_masr; 1273 m++; 1274 1275 s_bd = s_mem->bp->bnum; 1276 t_bd = t_mem->bp->bnum; 1277 1278 DRMACH_PR("preparing script for CPU and IO units:\n"); 1279 1280 rv = drmach_array_first(drmach_boards, &b_idx, &b_id); 1281 if (rv) { 1282 /* catch this in debug kernels */ 1283 ASSERT(0); 1284 return (DRMACH_INTERNAL_ERROR()); 1285 } 1286 1287 do { 1288 int d_idx; 1289 drmachid_t d_id; 1290 drmach_device_t *device; 1291 1292 ASSERT(DRMACH_IS_BOARD_ID(b_id)); 1293 brd = b_id; 1294 b = brd->bnum; 1295 1296 /* 1297 * Step 3: Update PC MADR tables for CPUs. 1298 */ 1299 if (brd->devices == NULL) { 1300 /* devices not initialized */ 1301 continue; 1302 } 1303 1304 rv = drmach_array_first(brd->devices, &d_idx, &d_id); 1305 if (rv) { 1306 /* must mean no devices on this board */ 1307 break; 1308 } 1309 1310 DRMACH_PR("\t%s\n", brd->cm.name); 1311 1312 do { 1313 ASSERT(DRMACH_IS_DEVICE_ID(d_id)); 1314 1315 if (!DRMACH_IS_CPU_ID(d_id)) 1316 continue; 1317 1318 device = d_id; 1319 i = device->unum; 1320 1321 DRMACH_PR("\t\t%s\n", device->cm.name); 1322 1323 /* 1324 * Disabled detaching mem node. 1325 */ 1326 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, s_bd, i); 1327 rsp[m].masr = s_masr; 1328 m++; 1329 /* 1330 * Always write masr with present bit 1331 * off and then again with it on. 1332 */ 1333 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, t_bd, i); 1334 rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK; 1335 m++; 1336 rsp[m].masr_addr = rsp[m-1].masr_addr; 1337 rsp[m].masr = t_masr; 1338 m++; 1339 1340 } while (drmach_array_next(brd->devices, &d_idx, &d_id) == 0); 1341 1342 /* 1343 * Step 4: Update PC MADR tables for IOs. 1344 */ 1345 rv = drmach_array_first(brd->devices, &d_idx, &d_id); 1346 /* this worked for previous loop, must work here too */ 1347 ASSERT(rv == 0); 1348 1349 do { 1350 ASSERT(DRMACH_IS_DEVICE_ID(d_id)); 1351 1352 if (!DRMACH_IS_IO_ID(d_id)) 1353 continue; 1354 1355 device = d_id; 1356 i = device->unum; 1357 1358 DRMACH_PR("\t\t%s\n", device->cm.name); 1359 1360 /* 1361 * Disabled detaching mem node. 1362 */ 1363 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, s_bd, i+4); 1364 rsp[m].masr = s_masr; 1365 m++; 1366 /* 1367 * Always write masr with present bit 1368 * off and then again with it on. 1369 */ 1370 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, t_bd, i+4); 1371 rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK; 1372 m++; 1373 rsp[m].masr_addr = rsp[m-1].masr_addr; 1374 rsp[m].masr = t_masr; 1375 m++; 1376 1377 } while (drmach_array_next(brd->devices, &d_idx, &d_id) == 0); 1378 } while (drmach_array_next(drmach_boards, &b_idx, &b_id) == 0); 1379 1380 /* 1381 * Zero masr_addr value indicates the END. 1382 */ 1383 rsp[m].masr_addr = 0ull; 1384 rsp[m].masr = 0; 1385 DRMACH_PR("number of steps in rename script = %d\n", m); 1386 m++; 1387 1388 /* paranoia */ 1389 ASSERT((caddr_t)&rsp[m] <= buf + buflen); 1390 1391 #ifdef DEBUG 1392 { 1393 int j; 1394 1395 DRMACH_PR("mc idle register address list:"); 1396 isp = (drmach_mc_idle_script_t *)buf; 1397 DRMACH_PR("source mc idle addr 0x%lx, mem id %p", 1398 isp[0].idle_addr, (void *)isp[0].mem); 1399 DRMACH_PR("target mc idle addr 0x%lx, mem id %p", 1400 isp[1].idle_addr, (void *)isp[1].mem); 1401 ASSERT(isp[2].idle_addr == 0); 1402 1403 DRMACH_PR("copy-rename script:"); 1404 for (j = 0; j < m; j++) { 1405 DRMACH_PR("0x%lx = 0x%08x", 1406 rsp[j].masr_addr, rsp[j].masr); 1407 } 1408 1409 DELAY(1000000); 1410 } 1411 #endif 1412 1413 /* return number of bytes consumed */ 1414 b = (caddr_t)&rsp[m] - buf; 1415 DRMACH_PR("total number of bytes consumed is %d\n", b); 1416 ASSERT(b <= buflen); 1417 1418 #ifdef lint 1419 buflen = buflen; 1420 #endif 1421 1422 return (NULL); 1423 } 1424 1425 /* 1426 * The routine performs the necessary memory COPY and MC adr SWITCH. 1427 * Both operations MUST be at the same "level" so that the stack is 1428 * maintained correctly between the copy and switch. The switch 1429 * portion implements a caching mechanism to guarantee the code text 1430 * is cached prior to execution. This is to guard against possible 1431 * memory access while the MC adr's are being modified. 1432 * 1433 * IMPORTANT: The _drmach_copy_rename_end() function must immediately 1434 * follow drmach_copy_rename_prog__relocatable() so that the correct 1435 * "length" of the drmach_copy_rename_prog__relocatable can be 1436 * calculated. This routine MUST be a LEAF function, i.e. it can 1437 * make NO function calls, primarily for two reasons: 1438 * 1439 * 1. We must keep the stack consistent across the "switch". 1440 * 2. Function calls are compiled to relative offsets, and 1441 * we execute this function we'll be executing it from 1442 * a copied version in a different area of memory, thus 1443 * the relative offsets will be bogus. 1444 * 1445 * Moreover, it must have the "__relocatable" suffix to inform DTrace 1446 * providers (and anything else, for that matter) that this 1447 * function's text is manually relocated elsewhere before it is 1448 * executed. That is, it cannot be safely instrumented with any 1449 * methodology that is PC-relative. 1450 */ 1451 static void 1452 drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog) 1453 { 1454 extern void drmach_exec_script_il(drmach_rename_script_t *rsp); 1455 1456 drmach_mc_idle_script_t *isp; 1457 struct memlist *ml; 1458 int csize; 1459 int lnsize; 1460 uint64_t caddr; 1461 1462 isp = (drmach_mc_idle_script_t *)prog->data; 1463 1464 caddr = ecache_flushaddr; 1465 csize = (cpunodes[CPU->cpu_id].ecache_size << 1); 1466 lnsize = cpunodes[CPU->cpu_id].ecache_linesize; 1467 1468 /* 1469 * DO COPY. 1470 */ 1471 for (ml = prog->c_ml; ml; ml = ml->next) { 1472 uint64_t s_pa, t_pa; 1473 uint64_t nbytes; 1474 1475 s_pa = prog->s_copybasepa + ml->address; 1476 t_pa = prog->t_copybasepa + ml->address; 1477 nbytes = ml->size; 1478 1479 while (nbytes != 0ull) { 1480 /* 1481 * This copy does NOT use an ASI 1482 * that avoids the Ecache, therefore 1483 * the dst_pa addresses may remain 1484 * in our Ecache after the dst_pa 1485 * has been removed from the system. 1486 * A subsequent write-back to memory 1487 * will cause an ARB-stop because the 1488 * physical address no longer exists 1489 * in the system. Therefore we must 1490 * flush out local Ecache after we 1491 * finish the copy. 1492 */ 1493 1494 /* copy 32 bytes at src_pa to dst_pa */ 1495 bcopy32_il(s_pa, t_pa); 1496 1497 /* increment by 32 bytes */ 1498 s_pa += (4 * sizeof (uint64_t)); 1499 t_pa += (4 * sizeof (uint64_t)); 1500 1501 /* decrement by 32 bytes */ 1502 nbytes -= (4 * sizeof (uint64_t)); 1503 } 1504 } 1505 1506 /* 1507 * Since bcopy32_il() does NOT use an ASI to bypass 1508 * the Ecache, we need to flush our Ecache after 1509 * the copy is complete. 1510 */ 1511 flush_ecache_il(caddr, csize, lnsize); /* inline version */ 1512 1513 /* 1514 * Wait for MCs to go idle. 1515 */ 1516 do { 1517 register int t = 10; 1518 register uint_t v; 1519 1520 /* loop t cycles waiting for each mc to indicate it's idle */ 1521 do { 1522 v = ldphysio_il(isp->idle_addr) 1523 & STARFIRE_MC_IDLE_MASK; 1524 1525 } while (v != STARFIRE_MC_IDLE_MASK && t-- > 0); 1526 1527 /* bailout if timedout */ 1528 if (t <= 0) { 1529 prog->restless_mc = isp->mem; 1530 return; 1531 } 1532 1533 isp += 1; 1534 1535 /* stop if terminating zero has been reached */ 1536 } while (isp->idle_addr != 0); 1537 1538 /* advance passed terminating zero */ 1539 isp += 1; 1540 1541 /* 1542 * The following inline assembly routine caches 1543 * the rename script and then caches the code that 1544 * will do the rename. This is necessary 1545 * so that we don't have any memory references during 1546 * the reprogramming. We accomplish this by first 1547 * jumping through the code to guarantee it's cached 1548 * before we actually execute it. 1549 */ 1550 drmach_exec_script_il((drmach_rename_script_t *)isp); 1551 } 1552 1553 static void 1554 drmach_copy_rename_end(void) 1555 { 1556 /* 1557 * IMPORTANT: This function's location MUST be located immediately 1558 * following drmach_copy_rename_prog__relocatable to 1559 * accurately estimate its size. Note that this assumes 1560 * the compiler keeps these functions in the order in 1561 * which they appear :-o 1562 */ 1563 } 1564 1565 sbd_error_t * 1566 drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset, 1567 drmachid_t s_id, struct memlist *c_ml, drmachid_t *pgm_id) 1568 { 1569 drmach_device_t *s_mem; 1570 drmach_device_t *t_mem; 1571 struct memlist *x_ml; 1572 uint64_t off_mask, s_copybasepa, t_copybasepa, t_basepa; 1573 int len; 1574 caddr_t bp, wp; 1575 pda_handle_t ph; 1576 sbd_error_t *err; 1577 drmach_copy_rename_program_t *prog; 1578 1579 if (!DRMACH_IS_MEM_ID(s_id)) 1580 return (drerr_new(0, ESTF_INAPPROP, NULL)); 1581 if (!DRMACH_IS_MEM_ID(t_id)) 1582 return (drerr_new(0, ESTF_INAPPROP, NULL)); 1583 s_mem = s_id; 1584 t_mem = t_id; 1585 1586 /* get starting physical address of target memory */ 1587 err = drmach_mem_get_base_physaddr(t_id, &t_basepa); 1588 if (err) 1589 return (err); 1590 1591 /* calculate slice offset mask from slice size */ 1592 off_mask = mc_get_mem_alignment() - 1; 1593 1594 /* calculate source and target base pa */ 1595 s_copybasepa = c_ml->address; 1596 t_copybasepa = t_basepa + ((c_ml->address & off_mask) - t_slice_offset); 1597 1598 /* paranoia */ 1599 ASSERT((c_ml->address & off_mask) >= t_slice_offset); 1600 1601 /* adjust copy memlist addresses to be relative to copy base pa */ 1602 x_ml = c_ml; 1603 while (x_ml != NULL) { 1604 x_ml->address -= s_copybasepa; 1605 x_ml = x_ml->next; 1606 } 1607 1608 #ifdef DEBUG 1609 { 1610 uint64_t s_basepa, s_size, t_size; 1611 1612 x_ml = c_ml; 1613 while (x_ml->next != NULL) 1614 x_ml = x_ml->next; 1615 1616 DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n", 1617 s_copybasepa, 1618 s_copybasepa + x_ml->address + x_ml->size); 1619 1620 DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n", 1621 t_copybasepa, 1622 t_copybasepa + x_ml->address + x_ml->size); 1623 1624 DRMACH_PR("copy memlist (relative to copy base pa):\n"); 1625 MEMLIST_DUMP(c_ml); 1626 1627 err = drmach_mem_get_base_physaddr(s_id, &s_basepa); 1628 ASSERT(err == NULL); 1629 1630 err = drmach_mem_get_size(s_id, &s_size); 1631 ASSERT(err == NULL); 1632 1633 err = drmach_mem_get_size(t_id, &t_size); 1634 ASSERT(err == NULL); 1635 1636 DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n", 1637 s_basepa, s_size); 1638 DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n", 1639 t_basepa, t_size); 1640 1641 ASSERT(s_copybasepa + x_ml->address + x_ml->size <= s_basepa + s_size); 1642 ASSERT(t_copybasepa + x_ml->address + x_ml->size <= t_basepa + t_size); 1643 } 1644 #endif 1645 1646 ph = drmach_pda_open(); 1647 if (ph == NULL) 1648 return (DRMACH_INTERNAL_ERROR()); 1649 1650 /* 1651 * bp will be page aligned, since we're calling 1652 * kmem_zalloc() with an exact multiple of PAGESIZE. 1653 */ 1654 wp = bp = kmem_zalloc(PAGESIZE, KM_SLEEP); 1655 1656 /* allocate space for copy rename struct */ 1657 len = sizeof (drmach_copy_rename_program_t); 1658 DRMACH_PR("prog = 0x%p, header len %d\n", (void *)wp, len); 1659 prog = (drmach_copy_rename_program_t *)wp; 1660 wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1); 1661 1662 /* 1663 * Copy the code for the copy-rename routine into 1664 * a page aligned piece of memory. We do this to guarantee 1665 * that we're executing within the same page and thus reduce 1666 * the possibility of cache collisions between different 1667 * pages. 1668 */ 1669 len = (int)((ulong_t)drmach_copy_rename_end - 1670 (ulong_t)drmach_copy_rename_prog__relocatable); 1671 ASSERT(wp + len < bp + PAGESIZE); 1672 bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len); 1673 1674 DRMACH_PR("copy-rename function 0x%p, len %d\n", (void *)wp, len); 1675 prog->run = (void (*)())wp; 1676 wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1); 1677 1678 /* 1679 * Prepare data page that will contain script of 1680 * operations to perform during copy-rename. 1681 * Allocate temporary buffer to hold script. 1682 */ 1683 err = drmach_prep_rename_script(s_mem, t_mem, t_slice_offset, 1684 wp, PAGESIZE - (wp - bp)); 1685 if (err) { 1686 (void) drmach_copy_rename_fini(prog); 1687 return (err); 1688 } 1689 1690 DRMACH_PR("copy-rename script 0x%p, len %d\n", (void *)wp, len); 1691 prog->data = wp; 1692 wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1); 1693 1694 prog->ph = ph; 1695 prog->s_copybasepa = s_copybasepa; 1696 prog->t_copybasepa = t_copybasepa; 1697 prog->c_ml = c_ml; 1698 *pgm_id = prog; 1699 1700 return (NULL); 1701 } 1702 1703 sbd_error_t * 1704 drmach_copy_rename_fini(drmachid_t id) 1705 { 1706 drmach_copy_rename_program_t *prog = id; 1707 sbd_error_t *err = NULL; 1708 1709 if (prog->c_ml != NULL) 1710 memlist_delete(prog->c_ml); 1711 1712 if (prog->ph != NULL) 1713 pda_close(prog->ph); 1714 1715 if (prog->restless_mc != 0) { 1716 cmn_err(CE_WARN, "MC did not idle; OBP Node 0x%x", 1717 (uint_t)drmach_node_get_dnode(prog->restless_mc->node)); 1718 1719 err = DRMACH_INTERNAL_ERROR(); 1720 } 1721 1722 kmem_free(prog, PAGESIZE); 1723 1724 return (err); 1725 } 1726 1727 static sbd_error_t * 1728 drmach_io_new(drmach_device_t *dp) 1729 { 1730 static sbd_error_t *drmach_io_release(drmachid_t); 1731 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *); 1732 1733 sbd_error_t *err; 1734 int portid; 1735 1736 err = drmach_device_get_prop(dp, "upa-portid", &portid); 1737 if (err == NULL) { 1738 ASSERT(portid & 0x40); 1739 dp->unum = portid & 1; 1740 } 1741 1742 dp->cm.isa = (void *)drmach_io_new; 1743 dp->cm.release = drmach_io_release; 1744 dp->cm.status = drmach_io_status; 1745 1746 (void) snprintf(dp->cm.name, sizeof (dp->cm.name), "%s%d", dp->type, 1747 dp->unum); 1748 1749 return (err); 1750 } 1751 1752 static void 1753 drmach_iopc_op(pda_handle_t ph, drmach_iopc_op_t op) 1754 { 1755 register int b; 1756 1757 for (b = 0; b < MAX_BOARDS; b++) { 1758 int p; 1759 ushort_t bda_ioc; 1760 board_desc_t *bdesc; 1761 1762 if (pda_board_present(ph, b) == 0) 1763 continue; 1764 1765 bdesc = (board_desc_t *)pda_get_board_info(ph, b); 1766 /* 1767 * Update PCs for IOCs. 1768 */ 1769 bda_ioc = bdesc->bda_ioc; 1770 for (p = 0; p < MAX_IOCS; p++) { 1771 u_longlong_t idle_addr; 1772 uchar_t value; 1773 1774 if (BDA_NBL(bda_ioc, p) != BDAN_GOOD) 1775 continue; 1776 1777 idle_addr = STARFIRE_BB_PC_ADDR(b, p, 1); 1778 1779 switch (op) { 1780 case DO_PAUSE: 1781 value = STARFIRE_BB_PC_PAUSE(p); 1782 break; 1783 1784 case DO_IDLE: 1785 value = STARFIRE_BB_PC_IDLE(p); 1786 break; 1787 1788 case DO_UNPAUSE: 1789 value = ldbphysio(idle_addr); 1790 value &= ~STARFIRE_BB_PC_PAUSE(p); 1791 break; 1792 1793 case DO_UNIDLE: 1794 value = ldbphysio(idle_addr); 1795 value &= ~STARFIRE_BB_PC_IDLE(p); 1796 break; 1797 1798 default: 1799 cmn_err(CE_PANIC, 1800 "drmach_iopc_op: unknown op (%d)", 1801 (int)op); 1802 /*NOTREACHED*/ 1803 } 1804 stbphysio(idle_addr, value); 1805 } 1806 } 1807 } 1808 1809 void 1810 drmach_copy_rename(drmachid_t id) 1811 { 1812 drmach_copy_rename_program_t *prog = id; 1813 uint64_t neer; 1814 1815 /* 1816 * UPA IDLE 1817 * Protocol = PAUSE -> IDLE -> UNPAUSE 1818 * In reality since we only "idle" the IOPCs it's sufficient 1819 * to just issue the IDLE operation since (in theory) all IOPCs 1820 * in the field are PC6. However, we'll be robust and do the 1821 * proper workaround protocol so that we never have to worry! 1822 */ 1823 drmach_iopc_op(prog->ph, DO_PAUSE); 1824 drmach_iopc_op(prog->ph, DO_IDLE); 1825 DELAY(100); 1826 drmach_iopc_op(prog->ph, DO_UNPAUSE); 1827 DELAY(100); 1828 1829 /* disable CE reporting */ 1830 neer = get_error_enable(); 1831 set_error_enable(neer & ~EER_CEEN); 1832 1833 /* run the copy/rename program */ 1834 prog->run(prog); 1835 1836 /* enable CE reporting */ 1837 set_error_enable(neer); 1838 1839 /* 1840 * UPA UNIDLE 1841 * Protocol = UNIDLE 1842 */ 1843 drmach_iopc_op(prog->ph, DO_UNIDLE); 1844 DELAY(100); 1845 } 1846 1847 /* 1848 * The counter-timer and perf-counter nodes are not being cleaned 1849 * up after a board that was present at start of day is detached. 1850 * If the board has become unconfigured with this operation, walk 1851 * the prom tree and find all counter-timer and perf-counter nodes 1852 * that have the same board number as the board that was just 1853 * unconfigured and remove them. 1854 */ 1855 static sbd_error_t * 1856 drmach_remove_counter_nodes(drmachid_t id) 1857 { 1858 int num; 1859 char name[OBP_MAXDRVNAME]; 1860 pnode_t child; 1861 dev_info_t *dip; 1862 sbd_error_t *err; 1863 drmach_status_t stat; 1864 drmach_board_t *bp; 1865 1866 if (!DRMACH_IS_BOARD_ID(id)) { 1867 return (drerr_new(0, ESTF_INAPPROP, NULL)); 1868 } 1869 1870 if ((err = drmach_board_status(id, &stat)) != NULL) { 1871 return (err); 1872 } 1873 1874 /* 1875 * Only clean up the counter-timer and perf-counter 1876 * nodes when the entire board is unconfigured. 1877 */ 1878 if (stat.configured) { 1879 return (NULL); 1880 } 1881 1882 bp = (drmach_board_t *)id; 1883 1884 err = NULL; 1885 1886 for (child = prom_childnode(prom_rootnode()); child != OBP_NONODE; 1887 child = prom_nextnode(child)) { 1888 1889 if (prom_getprop(child, OBP_BOARDNUM, (caddr_t)&num) == -1) { 1890 continue; 1891 } 1892 1893 if (bp->bnum != num) { 1894 continue; 1895 } 1896 1897 if (prom_getprop(child, OBP_NAME, (caddr_t)name) == -1) { 1898 continue; 1899 } 1900 1901 if (strncmp(name, MISC_COUNTER_TIMER_DEVNAME, OBP_MAXDRVNAME) && 1902 strncmp(name, MISC_PERF_COUNTER_DEVNAME, OBP_MAXDRVNAME)) { 1903 continue; 1904 } 1905 1906 /* Root node doesn't have to be held */ 1907 dip = e_ddi_nodeid_to_dip(child); 1908 1909 /* 1910 * If the node is only in the OBP tree, then 1911 * we don't have to remove it. 1912 */ 1913 if (dip) { 1914 dev_info_t *fdip = NULL; 1915 1916 DRMACH_PR("removing %s devinfo node\n", name); 1917 1918 e_ddi_branch_hold(dip); 1919 ddi_release_devi(dip); /* held in e_ddi_nodeid_to_dip */ 1920 1921 if (e_ddi_branch_destroy(dip, &fdip, 0)) { 1922 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1923 1924 /* 1925 * If non-NULL, fdip is held and must be 1926 * released. 1927 */ 1928 if (fdip != NULL) { 1929 (void) ddi_pathname(fdip, path); 1930 ddi_release_devi(fdip); 1931 } else { 1932 (void) ddi_pathname(dip, path); 1933 } 1934 1935 err = drerr_new(1, ESTF_DRVFAIL, path); 1936 kmem_free(path, MAXPATHLEN); 1937 e_ddi_branch_rele(dip); 1938 break; 1939 } 1940 } 1941 } 1942 1943 return (err); 1944 } 1945 1946 /*ARGSUSED*/ 1947 sbd_error_t * 1948 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts) 1949 { 1950 /* allow status and ncm operations to always succeed */ 1951 if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) { 1952 return (NULL); 1953 } 1954 1955 /* check all other commands for the required option string */ 1956 if ((opts->size > 0) && (opts->copts != NULL)) { 1957 1958 DRMACH_PR("platform options: %s\n", opts->copts); 1959 1960 if (strstr(opts->copts, "xfdr") != NULL) { 1961 return (NULL); 1962 } 1963 } 1964 1965 return (drerr_new(0, ESTF_SUPPORT, NULL)); 1966 } 1967 1968 /*ARGSUSED*/ 1969 sbd_error_t * 1970 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts) 1971 { 1972 sbd_error_t *err = NULL; 1973 1974 switch (cmd) { 1975 case SBD_CMD_UNCONFIGURE: 1976 1977 err = drmach_remove_counter_nodes(id); 1978 break; 1979 1980 case SBD_CMD_CONFIGURE: 1981 case SBD_CMD_DISCONNECT: 1982 case SBD_CMD_CONNECT: 1983 case SBD_CMD_GETNCM: 1984 case SBD_CMD_STATUS: 1985 break; 1986 1987 default: 1988 break; 1989 } 1990 1991 return (err); 1992 } 1993 1994 sbd_error_t * 1995 drmach_board_assign(int bnum, drmachid_t *id) 1996 { 1997 sbd_error_t *err; 1998 1999 if (!drmach_initialized && drmach_init() == -1) { 2000 err = DRMACH_INTERNAL_ERROR(); 2001 } else if (drmach_array_get(drmach_boards, bnum, id) == -1) { 2002 err = drerr_new(1, ESTF_BNUM, "%d", bnum); 2003 } else if (*id != NULL) { 2004 err = NULL; 2005 } else { 2006 drmach_board_t *bp; 2007 2008 *id = (drmachid_t)drmach_board_new(bnum); 2009 bp = *id; 2010 bp->assigned = 1; 2011 err = NULL; 2012 } 2013 2014 return (err); 2015 } 2016 2017 static int 2018 drmach_attach_board(void *arg) 2019 { 2020 drmach_board_t *obj = (drmach_board_t *)arg; 2021 cpuset_t cset; 2022 int retval; 2023 2024 /* 2025 * OBP disables traps during the board probe. 2026 * So, in order to prevent cross-call/cross-trap timeouts, 2027 * and thus panics, we effectively block anybody from 2028 * issuing xc's/xt's by doing a promsafe_xc_attention. 2029 * In the previous version of Starfire DR (2.6), a timeout 2030 * suspension mechanism was implemented in the send-mondo 2031 * assembly. That mechanism is unnecessary with the 2032 * existence of xc_attention/xc_dismissed. 2033 */ 2034 cset = cpu_ready_set; 2035 promsafe_xc_attention(cset); 2036 2037 retval = prom_starfire_add_brd(obj->connect_cpuid); 2038 2039 xc_dismissed(cset); 2040 2041 return (retval); 2042 } 2043 2044 sbd_error_t * 2045 drmach_board_connect(drmachid_t id, drmach_opts_t *opts) 2046 { 2047 drmach_board_t *obj = (drmach_board_t *)id; 2048 int retval; 2049 sbd_error_t *err; 2050 char *cptr, *copts; 2051 2052 if (!DRMACH_IS_BOARD_ID(id)) 2053 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2054 2055 if (opts->size > 0) 2056 copts = opts->copts; 2057 2058 if ((cptr = strstr(copts, "cpuid=")) != NULL) { 2059 int cpuid; 2060 2061 cptr += strlen("cpuid="); 2062 cpuid = stoi(&cptr); 2063 2064 if (DRMACH_CPUID2BNUM(cpuid) == obj->bnum) { 2065 obj->connect_cpuid = cpuid; 2066 obj->assigned = 1; 2067 } else 2068 return (drerr_new(1, ESTF_SETCPUVAL, "%d", cpuid)); 2069 } else { 2070 /* cpuid was not specified */ 2071 obj->connect_cpuid = -1; 2072 } 2073 2074 if (obj->connect_cpuid == -1) { 2075 err = drerr_new(1, ESTF_NOCPUID, obj->cm.name); 2076 return (err); 2077 } 2078 2079 cmn_err(CE_CONT, "DRMACH: PROM attach %s CPU %d\n", 2080 obj->cm.name, obj->connect_cpuid); 2081 2082 retval = prom_tree_update(drmach_attach_board, obj); 2083 2084 if (retval == 0) 2085 err = NULL; 2086 else { 2087 cmn_err(CE_WARN, "prom error: prom_starfire_add_brd(%d) " 2088 "returned %d", obj->connect_cpuid, retval); 2089 2090 err = drerr_new(1, ESTF_PROBE, obj->cm.name); 2091 } 2092 2093 obj->connect_cpuid = -1; 2094 2095 return (err); 2096 } 2097 2098 /*ARGSUSED*/ 2099 sbd_error_t * 2100 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts) 2101 { 2102 drmach_board_t *bp; 2103 int rv; 2104 int d_idx; /* device index */ 2105 drmachid_t d_id; /* device ID */ 2106 sbd_error_t *err; 2107 2108 if (!DRMACH_IS_BOARD_ID(id)) 2109 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2110 2111 bp = id; 2112 2113 /* 2114 * We need to make sure all of the board's device nodes 2115 * have been removed from the Solaris device tree before 2116 * continuing with the disconnect. Otherwise, we could 2117 * disconnect the board and remove the OBP device tree 2118 * nodes with Solaris device tree nodes remaining. 2119 * 2120 * On Starfire, Solaris device tree nodes are deleted 2121 * during unconfigure by drmach_unconfigure(). It's 2122 * necessary to do this here because drmach_unconfigure() 2123 * failures are not handled during unconfigure. 2124 */ 2125 if (bp->devices) { 2126 rv = drmach_array_first(bp->devices, &d_idx, &d_id); 2127 while (rv == 0) { 2128 err = drmach_unconfigure(d_id, DRMACH_DEVI_REMOVE); 2129 if (err) 2130 return (err); 2131 2132 rv = drmach_array_next(bp->devices, &d_idx, &d_id); 2133 } 2134 } 2135 2136 /* 2137 * Starfire board Solaris device tree counter nodes, 2138 * which are only present on start-of-day boards, are 2139 * removed in the dr_post_op() code flow after the 2140 * board is unconfigured. We call the counter node 2141 * removal function here because unconfigure errors 2142 * can cause the dr_post_op() function to be skipped 2143 * after an unconfigure operation even though all of 2144 * the board's devices have been transitioned to the 2145 * unconfigured state. 2146 */ 2147 err = drmach_remove_counter_nodes(id); 2148 if (err) 2149 return (err); 2150 2151 return (NULL); 2152 } 2153 2154 static int 2155 drmach_board_find_devices_cb(drmach_node_walk_args_t *args) 2156 { 2157 drmach_node_t *node = args->node; 2158 drmach_board_cb_data_t *data = args->data; 2159 drmach_board_t *obj = data->obj; 2160 2161 int rv; 2162 int bnum; 2163 drmach_device_t *device; 2164 2165 rv = drmach_node_get_prop(node, OBP_BOARDNUM, &bnum); 2166 if (rv) { 2167 /* 2168 * if the node does not have a board# property, then 2169 * by that information alone it is known that drmach 2170 * is not interested in it. 2171 */ 2172 return (0); 2173 } else if (bnum != obj->bnum) 2174 return (0); 2175 2176 /* 2177 * Create a device data structure from this node data. 2178 * The call may yield nothing if the node is not of interest 2179 * to drmach. 2180 */ 2181 data->err = drmach_device_new(node, obj, &device); 2182 if (data->err) 2183 return (-1); 2184 else if (device == NULL) { 2185 /* 2186 * drmach_device_new examined the node we passed in 2187 * and determined that it was one not of interest to 2188 * drmach. So, it is skipped. 2189 */ 2190 return (0); 2191 } 2192 2193 rv = drmach_array_set(obj->devices, data->ndevs++, device); 2194 if (rv) { 2195 drmach_device_dispose(device); 2196 data->err = DRMACH_INTERNAL_ERROR(); 2197 return (-1); 2198 } 2199 2200 data->err = (*data->found)(data->a, device->type, device->unum, device); 2201 return (data->err == NULL ? 0 : -1); 2202 } 2203 2204 sbd_error_t * 2205 drmach_board_find_devices(drmachid_t id, void *a, 2206 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t)) 2207 { 2208 extern int plat_max_cpu_units_per_board(); 2209 extern int plat_max_mem_units_per_board(); 2210 extern int plat_max_io_units_per_board(); 2211 2212 drmach_board_t *obj = (drmach_board_t *)id; 2213 sbd_error_t *err; 2214 int max_devices; 2215 int rv; 2216 drmach_board_cb_data_t data; 2217 2218 max_devices = plat_max_cpu_units_per_board(); 2219 max_devices += plat_max_mem_units_per_board(); 2220 max_devices += plat_max_io_units_per_board(); 2221 2222 obj->devices = drmach_array_new(0, max_devices); 2223 2224 data.obj = obj; 2225 data.ndevs = 0; 2226 data.found = found; 2227 data.a = a; 2228 data.err = NULL; 2229 2230 rv = drmach_node_walk(obj->tree, &data, drmach_board_find_devices_cb); 2231 if (rv == 0) 2232 err = NULL; 2233 else { 2234 drmach_array_dispose(obj->devices, drmach_device_dispose); 2235 obj->devices = NULL; 2236 2237 if (data.err) 2238 err = data.err; 2239 else 2240 err = DRMACH_INTERNAL_ERROR(); 2241 } 2242 2243 return (err); 2244 } 2245 2246 int 2247 drmach_board_lookup(int bnum, drmachid_t *id) 2248 { 2249 int rv = 0; 2250 2251 if (!drmach_initialized && drmach_init() == -1) { 2252 *id = 0; 2253 rv = -1; 2254 } else if (drmach_array_get(drmach_boards, bnum, id)) { 2255 *id = 0; 2256 rv = -1; 2257 } 2258 return (rv); 2259 } 2260 2261 sbd_error_t * 2262 drmach_board_name(int bnum, char *buf, int buflen) 2263 { 2264 (void) snprintf(buf, buflen, "SB%d", bnum); 2265 return (NULL); 2266 } 2267 2268 sbd_error_t * 2269 drmach_board_poweroff(drmachid_t id) 2270 { 2271 drmach_board_t *bp; 2272 sbd_error_t *err; 2273 drmach_status_t stat; 2274 2275 if (!DRMACH_IS_BOARD_ID(id)) 2276 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2277 bp = id; 2278 2279 err = drmach_board_status(id, &stat); 2280 if (err) 2281 return (err); 2282 else if (stat.configured || stat.busy) 2283 return (drerr_new(0, ESTF_CONFIGBUSY, bp->cm.name)); 2284 else { 2285 /* board power off is essentially a noop for Starfire */ 2286 bp->powered = 0; 2287 return (NULL); 2288 } 2289 /*NOTREACHED*/ 2290 } 2291 2292 sbd_error_t * 2293 drmach_board_poweron(drmachid_t id) 2294 { 2295 drmach_board_t *bp; 2296 2297 if (!DRMACH_IS_BOARD_ID(id)) 2298 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2299 bp = id; 2300 2301 /* board power on is essentially a noop for Starfire */ 2302 bp->powered = 1; 2303 2304 return (NULL); 2305 } 2306 2307 static sbd_error_t * 2308 drmach_board_release(drmachid_t id) 2309 { 2310 if (!DRMACH_IS_BOARD_ID(id)) 2311 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2312 return (NULL); 2313 } 2314 2315 /*ARGSUSED*/ 2316 sbd_error_t * 2317 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force) 2318 { 2319 return (NULL); 2320 } 2321 2322 sbd_error_t * 2323 drmach_board_unassign(drmachid_t id) 2324 { 2325 drmach_board_t *bp; 2326 sbd_error_t *err; 2327 drmach_status_t stat; 2328 2329 if (!DRMACH_IS_BOARD_ID(id)) 2330 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2331 bp = id; 2332 2333 err = drmach_board_status(id, &stat); 2334 if (err) 2335 return (err); 2336 else if (stat.configured || stat.busy) 2337 return (drerr_new(0, ESTF_CONFIGBUSY, bp->cm.name)); 2338 else if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0) 2339 return (DRMACH_INTERNAL_ERROR()); 2340 else { 2341 drmach_board_dispose(bp); 2342 return (NULL); 2343 } 2344 /*NOTREACHED*/ 2345 } 2346 2347 static sbd_error_t * 2348 drmach_cpu_new(drmach_device_t *dp) 2349 { 2350 static sbd_error_t *drmach_cpu_release(drmachid_t); 2351 static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *); 2352 2353 sbd_error_t *err; 2354 int portid; 2355 2356 err = drmach_device_get_prop(dp, "upa-portid", &portid); 2357 if (err == NULL) 2358 dp->unum = portid & 3; 2359 2360 dp->cm.isa = (void *)drmach_cpu_new; 2361 dp->cm.release = drmach_cpu_release; 2362 dp->cm.status = drmach_cpu_status; 2363 2364 (void) snprintf(dp->cm.name, sizeof (dp->cm.name), "%s%d", dp->type, 2365 dp->unum); 2366 2367 return (err); 2368 } 2369 2370 /* 2371 * drmach_cpu_obp_detach() 2372 * This requires two steps, first, we must put the cpuid into the OBP 2373 * idle loop (Idle in Program) state. Then we call OBP to place the CPU 2374 * into the "Detached" state, which does any special processing to 2375 * actually detach the cpu, such as flushing ecache, and also ensures 2376 * that a subsequent breakpoint won't restart the cpu (if it was just in 2377 * Idle in Program state). 2378 */ 2379 static void 2380 drmach_cpu_obp_detach(int cpuid) 2381 { 2382 /* 2383 * Cpu may not be under OBP's control. Eg, if cpu exited to download 2384 * helper on a prior attach. 2385 */ 2386 if (CPU_SGN_EXISTS(cpuid) && 2387 !SGN_CPU_IS_OS(cpuid) && 2388 !SGN_CPU_IS_OBP(cpuid)) { 2389 cmn_err(CE_WARN, 2390 "unexpected signature (0x%x) for cpu %d", 2391 get_cpu_sgn(cpuid), cpuid); 2392 } 2393 2394 /* 2395 * Now we place the CPU into the "Detached" idle loop in OBP. 2396 * This is so that the CPU won't be restarted if we break into 2397 * OBP with a breakpoint or BREAK key from the console, and also 2398 * if we need to do any special processing, such as flushing the 2399 * cpu's ecache, disabling interrupts (by turning of the ET bit in 2400 * the PSR) and/or spinning in BBSRAM rather than global memory. 2401 */ 2402 DRMACH_PR("prom_starfire_rm_cpu(%d)\n", cpuid); 2403 prom_starfire_rm_cpu(cpuid); 2404 } 2405 2406 /* 2407 * drmach_cpu_obp_is_detached() returns TRUE if the cpu sigblock signature state 2408 * is SIGBST_DETACHED; otherwise it returns FALSE. This routine should only 2409 * be called after we have asked OBP to detach the CPU. It should NOT be 2410 * called as a check during any other flow. 2411 */ 2412 static int 2413 drmach_cpu_obp_is_detached(int cpuid) 2414 { 2415 if (!CPU_SGN_EXISTS(cpuid) || 2416 (SGN_CPU_IS_OS(cpuid) && SGN_CPU_STATE_IS_DETACHED(cpuid))) 2417 return (1); 2418 else 2419 return (0); 2420 } 2421 2422 static int 2423 drmach_cpu_start(struct cpu *cp) 2424 { 2425 int cpuid = cp->cpu_id; 2426 int ntries = drmach_cpu_ntries; 2427 extern void restart_other_cpu(int); 2428 2429 ASSERT(MUTEX_HELD(&cpu_lock)); 2430 ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0); 2431 2432 cp->cpu_flags &= ~CPU_POWEROFF; 2433 2434 /* 2435 * NOTE: restart_other_cpu pauses cpus during the 2436 * slave cpu start. This helps to quiesce the 2437 * bus traffic a bit which makes the tick sync 2438 * routine in the prom more robust. 2439 */ 2440 DRMACH_PR("COLD START for cpu (%d)\n", cpuid); 2441 2442 prom_starfire_add_cpu(cpuid); 2443 2444 restart_other_cpu(cpuid); 2445 2446 /* 2447 * Wait for the cpu to reach its idle thread before 2448 * we zap him with a request to blow away the mappings 2449 * he (might) have for the drmach_shutdown_asm code 2450 * he may have executed on unconfigure. 2451 */ 2452 while ((cp->cpu_thread != cp->cpu_idle_thread) && (ntries > 0)) { 2453 DELAY(drmach_cpu_delay); 2454 ntries--; 2455 } 2456 2457 DRMACH_PR("waited %d out of %d loops for cpu %d\n", 2458 drmach_cpu_ntries - ntries, drmach_cpu_ntries, cpuid); 2459 2460 xt_one(cpuid, vtag_flushpage_tl1, 2461 (uint64_t)drmach_shutdown_va, (uint64_t)ksfmmup); 2462 2463 return (0); 2464 } 2465 2466 /* 2467 * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after 2468 * it has been offlined. The function of this routine is to get the cpu 2469 * spinning in a safe place. The requirement is that the system will not 2470 * reference anything on the detaching board (memory and i/o is detached 2471 * elsewhere) and that the CPU not reference anything on any other board 2472 * in the system. This isolation is required during and after the writes 2473 * to the domain masks to remove the board from the domain. 2474 * 2475 * To accomplish this isolation the following is done: 2476 * 1) Create a locked mapping to a location in BBSRAM where 2477 * the cpu will execute. 2478 * 2) Copy the target function (drmach_shutdown_asm) in which 2479 * the cpu will execute into BBSRAM. 2480 * 3) Jump into function with BBSRAM. 2481 * Function will: 2482 * 3.1) Flush its Ecache (displacement). 2483 * 3.2) Flush its Dcache with HW mechanism. 2484 * 3.3) Flush its Icache with HW mechanism. 2485 * 3.4) Flush all valid and _unlocked_ D-TLB entries. 2486 * 3.5) Flush all valid and _unlocked_ I-TLB entries. 2487 * 3.6) Clear xt_mb to signal completion. Note: cache line is 2488 * recovered by drmach_cpu_poweroff(). 2489 * 4) Jump into a tight loop. 2490 */ 2491 #define DRMACH_BBSRAM_OFFSET 0x1000 2492 2493 static void 2494 drmach_cpu_stop_self(void) 2495 { 2496 int cpuid = (int)CPU->cpu_id; 2497 tte_t tte; 2498 volatile uint_t *src, *dst; 2499 uint_t funclen; 2500 uint64_t bbsram_pa, bbsram_offset; 2501 uint_t bbsram_pfn; 2502 uint64_t bbsram_addr; 2503 void (*bbsram_func)(uint64_t); 2504 extern void drmach_shutdown_asm(uint64_t); 2505 extern void drmach_shutdown_asm_end(void); 2506 2507 funclen = (uint_t)drmach_shutdown_asm_end - (uint_t)drmach_shutdown_asm; 2508 ASSERT(funclen <= MMU_PAGESIZE); 2509 /* 2510 * We'll start from the 0th's base. 2511 */ 2512 bbsram_pa = STARFIRE_UPAID2UPS(cpuid) | STARFIRE_PSI_BASE; 2513 bbsram_offset = bbsram_pa | 0xfe0ULL; 2514 bbsram_pa += ldphysio(bbsram_offset) + DRMACH_BBSRAM_OFFSET; 2515 2516 bbsram_pfn = (uint_t)(bbsram_pa >> MMU_PAGESHIFT); 2517 2518 bbsram_addr = (uint64_t)drmach_shutdown_va; 2519 drmach_shutdown_asm_mbox->estack = bbsram_addr + (uint64_t)funclen; 2520 2521 tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) | 2522 TTE_PFN_INTHI(bbsram_pfn); 2523 tte.tte_intlo = TTE_PFN_INTLO(bbsram_pfn) | 2524 TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT; 2525 sfmmu_dtlb_ld_kva(drmach_shutdown_va, &tte); /* load dtlb */ 2526 sfmmu_itlb_ld_kva(drmach_shutdown_va, &tte); /* load itlb */ 2527 2528 for (src = (uint_t *)drmach_shutdown_asm, dst = (uint_t *)bbsram_addr; 2529 src < (uint_t *)drmach_shutdown_asm_end; src++, dst++) 2530 *dst = *src; 2531 2532 bbsram_func = (void (*)())bbsram_addr; 2533 drmach_shutdown_asm_mbox->flushaddr = ecache_flushaddr; 2534 drmach_shutdown_asm_mbox->size = (cpunodes[cpuid].ecache_size << 1); 2535 drmach_shutdown_asm_mbox->linesize = cpunodes[cpuid].ecache_linesize; 2536 drmach_shutdown_asm_mbox->physaddr 2537 = va_to_pa((void *)&drmach_xt_mb[cpuid]); 2538 2539 /* 2540 * Signal to drmach_cpu_poweroff() is via drmach_xt_mb cleared 2541 * by asm code 2542 */ 2543 2544 (*bbsram_func)(va_to_pa((void *)drmach_shutdown_asm_mbox)); 2545 } 2546 2547 static void 2548 drmach_cpu_shutdown_self(void) 2549 { 2550 cpu_t *cp = CPU; 2551 int cpuid = cp->cpu_id; 2552 extern void flush_windows(void); 2553 2554 flush_windows(); 2555 2556 (void) spl8(); 2557 2558 ASSERT(cp->cpu_intr_actv == 0); 2559 ASSERT(cp->cpu_thread == cp->cpu_idle_thread || 2560 cp->cpu_thread == cp->cpu_startup_thread); 2561 2562 cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF; 2563 2564 drmach_cpu_stop_self(); 2565 2566 cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid); 2567 } 2568 2569 /* a helper routine to keep the math in one place */ 2570 static processorid_t 2571 drmach_cpu_calc_id(drmach_device_t *dp) 2572 { 2573 return (dp->bp->bnum * MAX_CPU_UNITS_PER_BOARD + dp->unum); 2574 } 2575 2576 /* 2577 * Move bootproc (SIGBCPU) to another cpu. If dst_cpu is NULL, a 2578 * destination cpu is chosen from the set of cpus not located on the 2579 * same board as the current bootproc cpu. 2580 */ 2581 static sbd_error_t * 2582 drmach_cpu_juggle_bootproc(drmach_device_t *dst_cpu) 2583 { 2584 processorid_t cpuid; 2585 struct cpu *cp; 2586 sbd_error_t *err; 2587 int rv; 2588 2589 ASSERT(MUTEX_HELD(&cpu_lock)); 2590 2591 /* dst_cpu is NULL when target cpu is unspecified. So, pick one. */ 2592 if (dst_cpu == NULL) { 2593 int avoid_board = DRMACH_CPUID2BNUM(SIGBCPU->cpu_id); 2594 int max_cpuid = MAX_BOARDS * MAX_CPU_UNITS_PER_BOARD; 2595 2596 for (cpuid = 0; cpuid < max_cpuid; cpuid++) 2597 if (DRMACH_CPUID2BNUM(cpuid) != avoid_board) { 2598 cp = cpu_get(cpuid); 2599 if (cp != NULL && cpu_is_online(cp)) 2600 break; 2601 } 2602 2603 if (cpuid == max_cpuid) { 2604 err = drerr_new(1, ESTF_JUGGLE, NULL); 2605 return (err); 2606 } 2607 2608 /* else, cp points to the selected target cpu */ 2609 } else { 2610 cpuid = drmach_cpu_calc_id(dst_cpu); 2611 2612 if ((cp = cpu_get(cpuid)) == NULL) { 2613 err = drerr_new(1, ESTF_NODEV, "%s::%s", 2614 dst_cpu->bp->cm.name, dst_cpu->cm.name); 2615 return (err); 2616 } 2617 2618 if (cpuid == SIGBCPU->cpu_id) { 2619 cmn_err(CE_WARN, 2620 "SIGBCPU(%d) same as new selection(%d)", 2621 SIGBCPU->cpu_id, cpuid); 2622 2623 /* technically not an error, but a no-op */ 2624 return (NULL); 2625 } 2626 } 2627 2628 cmn_err(CE_NOTE, "?relocating SIGBCPU from %d to %d", 2629 SIGBCPU->cpu_id, cpuid); 2630 2631 DRMACH_PR("moving SIGBCPU to CPU %d\n", cpuid); 2632 2633 /* 2634 * Tell OBP to initialize cvc-offset field of new CPU0 2635 * so that it's in sync with OBP and cvc_server 2636 */ 2637 prom_starfire_init_console(cpuid); 2638 2639 /* 2640 * Assign cvc to new cpu0's bbsram for I/O. This has to be 2641 * done BEFORE cpu0 is moved via obp, since this logic 2642 * will cause obp_helper to switch to a different bbsram for 2643 * cvc I/O. We don't want cvc writing to a buffer from which 2644 * nobody will pick up the data! 2645 */ 2646 cvc_assign_iocpu(cpuid); 2647 2648 rv = prom_starfire_move_cpu0(cpuid); 2649 2650 if (rv == 0) { 2651 SIGBCPU = cp; 2652 2653 DRMACH_PR("successfully juggled to CPU %d\n", cpuid); 2654 return (NULL); 2655 } else { 2656 DRMACH_PR("prom error: prom_starfire_move_cpu0(%d) " 2657 "returned %d\n", cpuid, rv); 2658 2659 /* 2660 * The move failed, hopefully obp_helper is still back 2661 * at the old bootproc. Move cvc back there. 2662 */ 2663 cvc_assign_iocpu(SIGBCPU->cpu_id); 2664 2665 2666 err = drerr_new(1, ESTF_MOVESIGB, "CPU %d", cpuid); 2667 return (err); 2668 } 2669 /*NOTREACHED*/ 2670 } 2671 2672 static sbd_error_t * 2673 drmach_cpu_release(drmachid_t id) 2674 { 2675 drmach_device_t *dp; 2676 processorid_t cpuid; 2677 struct cpu *cp; 2678 sbd_error_t *err; 2679 2680 if (!DRMACH_IS_CPU_ID(id)) 2681 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2682 dp = id; 2683 cpuid = drmach_cpu_calc_id(dp); 2684 2685 ASSERT(MUTEX_HELD(&cpu_lock)); 2686 2687 cp = cpu_get(cpuid); 2688 if (cp == NULL) 2689 err = DRMACH_INTERNAL_ERROR(); 2690 else if (SIGBCPU->cpu_id == cp->cpu_id) 2691 err = drmach_cpu_juggle_bootproc(NULL); 2692 else 2693 err = NULL; 2694 2695 return (err); 2696 } 2697 2698 static sbd_error_t * 2699 drmach_cpu_status(drmachid_t id, drmach_status_t *stat) 2700 { 2701 drmach_device_t *dp; 2702 2703 ASSERT(DRMACH_IS_CPU_ID(id)); 2704 dp = id; 2705 2706 stat->assigned = dp->bp->assigned; 2707 stat->powered = dp->bp->powered; 2708 mutex_enter(&cpu_lock); 2709 stat->configured = (cpu_get(drmach_cpu_calc_id(dp)) != NULL); 2710 mutex_exit(&cpu_lock); 2711 stat->busy = dp->busy; 2712 (void) strncpy(stat->type, dp->type, sizeof (stat->type)); 2713 stat->info[0] = '\0'; 2714 2715 return (NULL); 2716 } 2717 2718 sbd_error_t * 2719 drmach_cpu_disconnect(drmachid_t id) 2720 { 2721 drmach_device_t *cpu; 2722 int cpuid; 2723 int ntries; 2724 int p; 2725 u_longlong_t pc_addr; 2726 uchar_t rvalue; 2727 2728 if (!DRMACH_IS_CPU_ID(id)) 2729 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2730 cpu = id; 2731 2732 cpuid = drmach_cpu_calc_id(cpu); 2733 if (SIGBCPU->cpu_id == cpuid) { 2734 /* this cpu is SIGBCPU, can't disconnect */ 2735 return (drerr_new(1, ESTF_HASSIGB, "%s::%s", 2736 cpu->bp->cm.name, cpu->cm.name)); 2737 } 2738 2739 /* 2740 * Make sure SIGBST_DETACHED is set before 2741 * mapping out the sig block. 2742 */ 2743 ntries = drmach_cpu_ntries; 2744 while (!drmach_cpu_obp_is_detached(cpuid) && ntries) { 2745 DELAY(drmach_cpu_delay); 2746 ntries--; 2747 } 2748 if (!drmach_cpu_obp_is_detached(cpuid)) { 2749 cmn_err(CE_WARN, "failed to mark cpu %d detached in sigblock", 2750 cpuid); 2751 } 2752 2753 /* map out signature block */ 2754 if (CPU_SGN_EXISTS(cpuid)) { 2755 CPU_SGN_MAPOUT(cpuid); 2756 } 2757 2758 /* 2759 * We now PC IDLE the processor to guarantee we 2760 * stop any transactions from coming from it. 2761 */ 2762 p = cpu->unum & 1; 2763 pc_addr = STARFIRE_BB_PC_ADDR(cpu->bp->bnum, cpu->unum, 0); 2764 2765 DRMACH_PR("PC idle cpu %d (addr = 0x%llx, port = %d, p = %d)", 2766 drmach_cpu_calc_id(cpu), pc_addr, cpu->unum, p); 2767 2768 rvalue = ldbphysio(pc_addr); 2769 rvalue |= STARFIRE_BB_PC_IDLE(p); 2770 stbphysio(pc_addr, rvalue); 2771 DELAY(50000); 2772 2773 return (NULL); 2774 } 2775 2776 sbd_error_t * 2777 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid) 2778 { 2779 drmach_device_t *cpu; 2780 2781 if (!DRMACH_IS_CPU_ID(id)) 2782 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2783 cpu = id; 2784 2785 *cpuid = drmach_cpu_calc_id(cpu); 2786 return (NULL); 2787 } 2788 2789 sbd_error_t * 2790 drmach_cpu_get_impl(drmachid_t id, int *ip) 2791 { 2792 drmach_device_t *cpu; 2793 int impl; 2794 2795 if (!DRMACH_IS_CPU_ID(id)) 2796 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2797 2798 cpu = id; 2799 2800 if (drmach_node_get_prop(cpu->node, "implementation#", &impl) == -1) { 2801 return (DRMACH_INTERNAL_ERROR()); 2802 } 2803 2804 *ip = impl; 2805 2806 return (NULL); 2807 } 2808 2809 void 2810 drmach_cpu_flush_ecache_sync(void) 2811 { 2812 ASSERT(curthread->t_bound_cpu == CPU); 2813 2814 /* 2815 * Now let's flush our ecache thereby removing all references 2816 * to the target (detaching) memory from all ecache's in 2817 * system. 2818 */ 2819 cpu_flush_ecache(); 2820 2821 /* 2822 * Delay 100 usec out of paranoia to insure everything 2823 * (hardware queues) has drained before we start reprogramming 2824 * the hardware. 2825 */ 2826 DELAY(100); 2827 } 2828 2829 sbd_error_t * 2830 drmach_get_dip(drmachid_t id, dev_info_t **dip) 2831 { 2832 drmach_device_t *dp; 2833 2834 if (!DRMACH_IS_DEVICE_ID(id)) 2835 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2836 dp = id; 2837 2838 *dip = drmach_node_get_dip(dp->node); 2839 return (NULL); 2840 } 2841 2842 sbd_error_t * 2843 drmach_io_is_attached(drmachid_t id, int *yes) 2844 { 2845 drmach_device_t *dp; 2846 dev_info_t *dip; 2847 int state; 2848 2849 if (!DRMACH_IS_IO_ID(id)) 2850 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2851 dp = id; 2852 2853 dip = drmach_node_get_dip(dp->node); 2854 if (dip == NULL) { 2855 *yes = 0; 2856 return (NULL); 2857 } 2858 2859 state = ddi_get_devstate(dip); 2860 *yes = (i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP)); 2861 2862 return (NULL); 2863 } 2864 2865 sbd_error_t * 2866 drmach_io_pre_release(drmachid_t id) 2867 { 2868 if (!DRMACH_IS_IO_ID(id)) 2869 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2870 return (NULL); 2871 } 2872 2873 static sbd_error_t * 2874 drmach_io_release(drmachid_t id) 2875 { 2876 if (!DRMACH_IS_IO_ID(id)) 2877 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2878 return (NULL); 2879 } 2880 2881 sbd_error_t * 2882 drmach_io_unrelease(drmachid_t id) 2883 { 2884 if (!DRMACH_IS_IO_ID(id)) 2885 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2886 return (NULL); 2887 } 2888 2889 /*ARGSUSED*/ 2890 sbd_error_t * 2891 drmach_io_post_release(drmachid_t id) 2892 { 2893 return (NULL); 2894 } 2895 2896 /*ARGSUSED*/ 2897 sbd_error_t * 2898 drmach_io_post_attach(drmachid_t id) 2899 { 2900 return (NULL); 2901 } 2902 2903 static sbd_error_t * 2904 drmach_io_status(drmachid_t id, drmach_status_t *stat) 2905 { 2906 drmach_device_t *dp; 2907 sbd_error_t *err; 2908 int configured; 2909 2910 ASSERT(DRMACH_IS_IO_ID(id)); 2911 dp = id; 2912 2913 err = drmach_io_is_attached(id, &configured); 2914 if (err) 2915 return (err); 2916 2917 stat->assigned = dp->bp->assigned; 2918 stat->powered = dp->bp->powered; 2919 stat->configured = (configured != 0); 2920 stat->busy = dp->busy; 2921 (void) strncpy(stat->type, dp->type, sizeof (stat->type)); 2922 stat->info[0] = '\0'; 2923 2924 return (NULL); 2925 } 2926 2927 static sbd_error_t * 2928 drmach_mem_new(drmach_device_t *dp) 2929 { 2930 static sbd_error_t *drmach_mem_release(drmachid_t); 2931 static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *); 2932 2933 dp->unum = 0; 2934 dp->cm.isa = (void *)drmach_mem_new; 2935 dp->cm.release = drmach_mem_release; 2936 dp->cm.status = drmach_mem_status; 2937 2938 (void) snprintf(dp->cm.name, sizeof (dp->cm.name), "%s", dp->type); 2939 2940 return (NULL); 2941 } 2942 2943 sbd_error_t * 2944 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size) 2945 { 2946 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 2947 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 2948 pda_handle_t ph; 2949 int rv; 2950 2951 ASSERT(size != 0); 2952 2953 if (!DRMACH_IS_MEM_ID(id)) 2954 return (drerr_new(0, ESTF_INAPPROP, NULL)); 2955 2956 rv = kcage_range_add(basepfn, npages, KCAGE_DOWN); 2957 if (rv == ENOMEM) { 2958 cmn_err(CE_WARN, "%lu megabytes not available to kernel cage", 2959 (ulong_t)(size == 0 ? 0 : size / MBYTE)); 2960 } else if (rv != 0) { 2961 /* catch this in debug kernels */ 2962 ASSERT(0); 2963 2964 cmn_err(CE_WARN, "unexpected kcage_range_add" 2965 " return value %d", rv); 2966 } 2967 2968 /* 2969 * Update the PDA (post2obp) structure with the 2970 * range of the newly added memory. 2971 */ 2972 ph = drmach_pda_open(); 2973 if (ph != NULL) { 2974 pda_mem_add_span(ph, basepa, size); 2975 pda_close(ph); 2976 } 2977 2978 return (NULL); 2979 } 2980 2981 sbd_error_t * 2982 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size) 2983 { 2984 drmach_device_t *mem = id; 2985 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 2986 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 2987 uint_t mcreg; 2988 sbd_error_t *err; 2989 pda_handle_t ph; 2990 int rv; 2991 2992 err = drmach_read_mc_asr(id, &mcreg); 2993 if (err) 2994 return (err); 2995 else if (mcreg & STARFIRE_MC_INTERLEAVE_MASK) { 2996 return (drerr_new(1, ESTF_INTERBOARD, "%s::%s", 2997 mem->bp->cm.name, mem->cm.name)); 2998 } 2999 3000 if (size > 0) { 3001 rv = kcage_range_delete_post_mem_del(basepfn, npages); 3002 if (rv != 0) { 3003 cmn_err(CE_WARN, 3004 "unexpected kcage_range_delete_post_mem_del" 3005 " return value %d", rv); 3006 return (DRMACH_INTERNAL_ERROR()); 3007 } 3008 } 3009 3010 /* 3011 * Update the PDA (post2obp) structure with the 3012 * range of removed memory. 3013 */ 3014 ph = drmach_pda_open(); 3015 if (ph != NULL) { 3016 if (size > 0) 3017 pda_mem_del_span(ph, basepa, size); 3018 3019 /* update PDA to board's new mc register settings */ 3020 pda_mem_sync(ph, mem->bp->bnum, 0); 3021 3022 pda_close(ph); 3023 } 3024 3025 return (NULL); 3026 } 3027 3028 /* support routine for enable and disable */ 3029 static sbd_error_t * 3030 drmach_mem_update_interconnect(drmachid_t id, uint_t mcreg) 3031 { 3032 drmach_device_t *dp; 3033 pda_handle_t ph; 3034 int b; 3035 3036 if (!DRMACH_IS_MEM_ID(id)) 3037 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3038 dp = id; 3039 3040 ph = drmach_pda_open(); 3041 if (ph == NULL) 3042 return (DRMACH_INTERNAL_ERROR()); 3043 3044 for (b = 0; b < MAX_BOARDS; b++) { 3045 int p; 3046 int rv; 3047 ushort_t bda_proc, bda_ioc; 3048 board_desc_t *bdesc; 3049 3050 if (pda_board_present(ph, b) == 0) 3051 continue; 3052 3053 bdesc = (board_desc_t *)pda_get_board_info(ph, b); 3054 3055 /* 3056 * Update PCs for CPUs. 3057 */ 3058 3059 /* make sure definition in platmod is in sync with pda */ 3060 ASSERT(MAX_PROCMODS == MAX_CPU_UNITS_PER_BOARD); 3061 3062 bda_proc = bdesc->bda_proc; 3063 for (p = 0; p < MAX_PROCMODS; p++) { 3064 if (BDA_NBL(bda_proc, p) != BDAN_GOOD) 3065 continue; 3066 3067 rv = pc_madr_add(b, dp->bp->bnum, p, mcreg); 3068 if (rv) { 3069 pda_close(ph); 3070 return (DRMACH_INTERNAL_ERROR()); 3071 } 3072 } 3073 3074 /* 3075 * Update PCs for IOCs. 3076 */ 3077 3078 /* make sure definition in platmod is in sync with pda */ 3079 ASSERT(MAX_IOCS == MAX_IO_UNITS_PER_BOARD); 3080 3081 bda_ioc = bdesc->bda_ioc; 3082 for (p = 0; p < MAX_IOCS; p++) { 3083 if (BDA_NBL(bda_ioc, p) != BDAN_GOOD) 3084 continue; 3085 3086 rv = pc_madr_add(b, dp->bp->bnum, p + 4, mcreg); 3087 if (rv) { 3088 pda_close(ph); 3089 return (DRMACH_INTERNAL_ERROR()); 3090 } 3091 } 3092 } 3093 3094 pda_close(ph); 3095 return (NULL); 3096 } 3097 3098 sbd_error_t * 3099 drmach_mem_disable(drmachid_t id) 3100 { 3101 sbd_error_t *err; 3102 uint_t mcreg; 3103 3104 err = drmach_read_mc_asr(id, &mcreg); 3105 if (err == NULL) { 3106 ASSERT(mcreg & STARFIRE_MC_MEM_PRESENT_MASK); 3107 3108 /* Turn off presence bit. */ 3109 mcreg &= ~STARFIRE_MC_MEM_PRESENT_MASK; 3110 3111 err = drmach_mem_update_interconnect(id, mcreg); 3112 if (err == NULL) 3113 err = drmach_write_mc_asr(id, mcreg); 3114 } 3115 3116 return (err); 3117 } 3118 3119 sbd_error_t * 3120 drmach_mem_enable(drmachid_t id) 3121 { 3122 sbd_error_t *err; 3123 uint_t mcreg; 3124 3125 err = drmach_read_mc_asr(id, &mcreg); 3126 if (err == NULL) { 3127 mcreg |= STARFIRE_MC_MEM_PRESENT_MASK; 3128 3129 err = drmach_write_mc_asr(id, mcreg); 3130 if (err == NULL) 3131 err = drmach_mem_update_interconnect(id, mcreg); 3132 } 3133 3134 return (err); 3135 } 3136 3137 sbd_error_t * 3138 drmach_mem_get_alignment(drmachid_t id, uint64_t *mask) 3139 { 3140 drmach_device_t *mem; 3141 sbd_error_t *err; 3142 pnode_t nodeid; 3143 3144 if (!DRMACH_IS_MEM_ID(id)) 3145 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3146 mem = id; 3147 3148 nodeid = drmach_node_get_dnode(mem->node); 3149 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) 3150 err = DRMACH_INTERNAL_ERROR(); 3151 else { 3152 uint64_t size; 3153 3154 size = mc_get_alignment_mask(nodeid); 3155 if (size == (uint64_t)-1) 3156 err = DRMACH_INTERNAL_ERROR(); 3157 else { 3158 *mask = size - 1; 3159 err = NULL; 3160 } 3161 } 3162 3163 return (err); 3164 } 3165 3166 sbd_error_t * 3167 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa) 3168 { 3169 sbd_error_t *err; 3170 uint_t mcreg; 3171 3172 err = drmach_read_mc_asr(id, &mcreg); 3173 if (err == NULL) 3174 *pa = mc_asr_to_pa(mcreg); 3175 3176 return (err); 3177 } 3178 3179 /* 3180 * Use of this routine after copy/rename will yield incorrect results, 3181 * because the OBP MEMAVAIL property will not correctly reflect the 3182 * programming of the MCs. 3183 */ 3184 sbd_error_t * 3185 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml) 3186 { 3187 drmach_device_t *mem; 3188 int rv, i, rlen, rblks; 3189 sbd_error_t *err; 3190 struct memlist *mlist; 3191 struct sf_memunit_regspec *rlist; 3192 3193 if (!DRMACH_IS_MEM_ID(id)) 3194 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3195 mem = id; 3196 3197 err = drmach_device_get_proplen(mem, "dr-available", &rlen); 3198 if (err) 3199 return (err); 3200 3201 rlist = kmem_zalloc(rlen, KM_SLEEP); 3202 3203 err = drmach_device_get_prop(mem, "dr-available", rlist); 3204 if (err) { 3205 kmem_free(rlist, rlen); 3206 return (err); 3207 } 3208 3209 mlist = NULL; 3210 rblks = rlen / sizeof (struct sf_memunit_regspec); 3211 for (i = 0; i < rblks; i++) { 3212 uint64_t addr, size; 3213 3214 addr = (uint64_t)rlist[i].regspec_addr_hi << 32; 3215 addr |= (uint64_t)rlist[i].regspec_addr_lo; 3216 size = (uint64_t)rlist[i].regspec_size_hi << 32; 3217 size |= (uint64_t)rlist[i].regspec_size_lo; 3218 3219 mlist = memlist_add_span(mlist, addr, size); 3220 } 3221 3222 kmem_free(rlist, rlen); 3223 3224 /* 3225 * Make sure the incoming memlist doesn't already 3226 * intersect with what's present in the system (phys_install). 3227 */ 3228 memlist_read_lock(); 3229 rv = memlist_intersect(phys_install, mlist); 3230 memlist_read_unlock(); 3231 if (rv) { 3232 #ifdef DEBUG 3233 DRMACH_PR("OBP derived memlist intersects" 3234 " with phys_install\n"); 3235 memlist_dump(mlist); 3236 3237 DRMACH_PR("phys_install memlist:\n"); 3238 memlist_dump(phys_install); 3239 #endif 3240 3241 memlist_delete(mlist); 3242 return (DRMACH_INTERNAL_ERROR()); 3243 } 3244 3245 #ifdef DEBUG 3246 DRMACH_PR("OBP derived memlist:"); 3247 memlist_dump(mlist); 3248 #endif 3249 3250 *ml = mlist; 3251 return (NULL); 3252 } 3253 3254 sbd_error_t * 3255 drmach_mem_get_size(drmachid_t id, uint64_t *bytes) 3256 { 3257 drmach_device_t *mem; 3258 pda_handle_t ph; 3259 pgcnt_t npages; 3260 3261 if (!DRMACH_IS_MEM_ID(id)) 3262 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3263 mem = id; 3264 3265 ph = drmach_pda_open(); 3266 if (ph == NULL) 3267 return (DRMACH_INTERNAL_ERROR()); 3268 3269 npages = pda_get_mem_size(ph, mem->bp->bnum); 3270 *bytes = (uint64_t)npages << PAGESHIFT; 3271 3272 pda_close(ph); 3273 return (NULL); 3274 } 3275 3276 sbd_error_t * 3277 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes) 3278 { 3279 if (!DRMACH_IS_MEM_ID(id)) 3280 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3281 3282 *bytes = mc_get_mem_alignment(); 3283 return (NULL); 3284 } 3285 3286 /* field debugging tool */ 3287 processorid_t drmach_mem_cpu_affinity_nail = 0; 3288 3289 processorid_t 3290 drmach_mem_cpu_affinity(drmachid_t id) 3291 { 3292 drmach_device_t *mp; 3293 drmach_board_t *bp; 3294 processorid_t cpuid; 3295 3296 if (!DRMACH_IS_MEM_ID(id)) 3297 return (CPU_CURRENT); 3298 3299 if (drmach_mem_cpu_affinity_nail) { 3300 cpuid = drmach_mem_cpu_affinity_nail; 3301 3302 if (cpuid < 0 || cpuid > NCPU) 3303 return (CPU_CURRENT); 3304 3305 mutex_enter(&cpu_lock); 3306 if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid])) 3307 cpuid = CPU_CURRENT; 3308 mutex_exit(&cpu_lock); 3309 3310 return (cpuid); 3311 } 3312 3313 /* try to choose a proc on the target board */ 3314 mp = id; 3315 bp = mp->bp; 3316 if (bp->devices) { 3317 int rv; 3318 int d_idx; 3319 drmachid_t d_id; 3320 3321 rv = drmach_array_first(bp->devices, &d_idx, &d_id); 3322 while (rv == 0) { 3323 if (DRMACH_IS_CPU_ID(d_id)) { 3324 cpuid = drmach_cpu_calc_id(d_id); 3325 3326 mutex_enter(&cpu_lock); 3327 if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) { 3328 mutex_exit(&cpu_lock); 3329 DRMACH_PR("drmach_mem_cpu_affinity: " 3330 "selected cpuid=%d\n", cpuid); 3331 return (cpuid); 3332 } else { 3333 mutex_exit(&cpu_lock); 3334 } 3335 } 3336 3337 rv = drmach_array_next(bp->devices, &d_idx, &d_id); 3338 } 3339 } 3340 3341 /* otherwise, this proc, wherever it is */ 3342 DRMACH_PR("drmach_mem_cpu_affinity: using default CPU_CURRENT\n"); 3343 3344 return (CPU_CURRENT); 3345 } 3346 3347 static sbd_error_t * 3348 drmach_mem_release(drmachid_t id) 3349 { 3350 if (!DRMACH_IS_MEM_ID(id)) 3351 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3352 return (NULL); 3353 } 3354 3355 static sbd_error_t * 3356 drmach_mem_status(drmachid_t id, drmach_status_t *stat) 3357 { 3358 drmach_device_t *dp; 3359 sbd_error_t *err; 3360 uint64_t pa, slice_size; 3361 struct memlist *ml; 3362 3363 ASSERT(DRMACH_IS_MEM_ID(id)); 3364 dp = id; 3365 3366 /* get starting physical address of target memory */ 3367 err = drmach_mem_get_base_physaddr(id, &pa); 3368 if (err) 3369 return (err); 3370 3371 /* round down to slice boundary */ 3372 slice_size = mc_get_mem_alignment(); 3373 pa &= ~ (slice_size - 1); 3374 3375 /* stop at first span that is in slice */ 3376 memlist_read_lock(); 3377 for (ml = phys_install; ml; ml = ml->next) 3378 if (ml->address >= pa && ml->address < pa + slice_size) 3379 break; 3380 memlist_read_unlock(); 3381 3382 stat->assigned = dp->bp->assigned; 3383 stat->powered = dp->bp->powered; 3384 stat->configured = (ml != NULL); 3385 stat->busy = dp->busy; 3386 (void) strncpy(stat->type, dp->type, sizeof (stat->type)); 3387 stat->info[0] = '\0'; 3388 3389 return (NULL); 3390 } 3391 3392 static int 3393 drmach_detach_board(void *arg) 3394 { 3395 cpuset_t cset; 3396 int retval; 3397 drmach_board_t *bp = (drmach_board_t *)arg; 3398 3399 cset = cpu_ready_set; 3400 promsafe_xc_attention(cset); 3401 3402 retval = prom_starfire_rm_brd(bp->bnum); 3403 3404 xc_dismissed(cset); 3405 3406 return (retval); 3407 } 3408 3409 sbd_error_t * 3410 drmach_board_deprobe(drmachid_t id) 3411 { 3412 drmach_board_t *bp; 3413 int retval; 3414 3415 if (!DRMACH_IS_BOARD_ID(id)) 3416 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3417 bp = id; 3418 3419 cmn_err(CE_CONT, "DR: PROM detach board %d\n", bp->bnum); 3420 3421 retval = prom_tree_update(drmach_detach_board, bp); 3422 3423 if (retval == 0) 3424 return (NULL); 3425 else { 3426 cmn_err(CE_WARN, "prom error: prom_starfire_rm_brd(%d) " 3427 "returned %d", bp->bnum, retval); 3428 return (drerr_new(1, ESTF_DEPROBE, "%s", bp->cm.name)); 3429 } 3430 } 3431 3432 /*ARGSUSED*/ 3433 static sbd_error_t * 3434 drmach_pt_juggle_bootproc(drmachid_t id, drmach_opts_t *opts) 3435 { 3436 drmach_device_t *cpu; 3437 sbd_error_t *err; 3438 3439 if (!DRMACH_IS_CPU_ID(id)) 3440 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3441 cpu = id; 3442 3443 mutex_enter(&cpu_lock); 3444 3445 err = drmach_cpu_juggle_bootproc(cpu); 3446 3447 mutex_exit(&cpu_lock); 3448 3449 return (err); 3450 } 3451 3452 /*ARGSUSED*/ 3453 static sbd_error_t * 3454 drmach_pt_dump_pdainfo(drmachid_t id, drmach_opts_t *opts) 3455 { 3456 drmach_board_t *bp; 3457 int board; 3458 int i; 3459 pda_handle_t ph; 3460 board_desc_t *bdesc; 3461 3462 if (!DRMACH_IS_BOARD_ID(id)) 3463 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3464 bp = id; 3465 board = bp->bnum; 3466 3467 ph = drmach_pda_open(); 3468 if (ph == NULL) 3469 return (DRMACH_INTERNAL_ERROR()); 3470 3471 if (pda_board_present(ph, board) == 0) { 3472 cmn_err(CE_CONT, "board %d is MISSING\n", board); 3473 pda_close(ph); 3474 return (DRMACH_INTERNAL_ERROR()); 3475 } 3476 3477 cmn_err(CE_CONT, "board %d is PRESENT\n", board); 3478 3479 bdesc = (board_desc_t *)pda_get_board_info(ph, board); 3480 if (bdesc == NULL) { 3481 cmn_err(CE_CONT, 3482 "no board descriptor found for board %d\n", 3483 board); 3484 pda_close(ph); 3485 return (DRMACH_INTERNAL_ERROR()); 3486 } 3487 3488 /* make sure definition in platmod is in sync with pda */ 3489 ASSERT(MAX_PROCMODS == MAX_CPU_UNITS_PER_BOARD); 3490 3491 for (i = 0; i < MAX_PROCMODS; i++) { 3492 if (BDA_NBL(bdesc->bda_proc, i) == BDAN_GOOD) 3493 cmn_err(CE_CONT, 3494 "proc %d.%d PRESENT\n", board, i); 3495 else 3496 cmn_err(CE_CONT, 3497 "proc %d.%d MISSING\n", board, i); 3498 } 3499 3500 for (i = 0; i < MAX_MGROUPS; i++) { 3501 if (BDA_NBL(bdesc->bda_mgroup, i) == BDAN_GOOD) 3502 cmn_err(CE_CONT, 3503 "mgroup %d.%d PRESENT\n", board, i); 3504 else 3505 cmn_err(CE_CONT, 3506 "mgroup %d.%d MISSING\n", board, i); 3507 } 3508 3509 /* make sure definition in platmod is in sync with pda */ 3510 ASSERT(MAX_IOCS == MAX_IO_UNITS_PER_BOARD); 3511 3512 for (i = 0; i < MAX_IOCS; i++) { 3513 int s; 3514 3515 if (BDA_NBL(bdesc->bda_ioc, i) == BDAN_GOOD) { 3516 cmn_err(CE_CONT, 3517 "ioc %d.%d PRESENT\n", board, i); 3518 for (s = 0; s < MAX_SLOTS_PER_IOC; s++) { 3519 if (BDA_NBL(bdesc->bda_ios[i], s) != BDAN_GOOD) 3520 continue; 3521 cmn_err(CE_CONT, 3522 "..scard %d.%d.%d PRESENT\n", 3523 board, i, s); 3524 } 3525 } else { 3526 cmn_err(CE_CONT, 3527 "ioc %d.%d MISSING\n", 3528 board, i); 3529 } 3530 } 3531 3532 cmn_err(CE_CONT, 3533 "board %d memsize = %d pages\n", 3534 board, pda_get_mem_size(ph, board)); 3535 3536 pda_close(ph); 3537 3538 return (NULL); 3539 } 3540 3541 /*ARGSUSED*/ 3542 sbd_error_t * 3543 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts) 3544 { 3545 struct memlist *ml; 3546 uint64_t src_pa; 3547 uint64_t dst_pa; 3548 uint64_t dst; 3549 3550 dst_pa = va_to_pa(&dst); 3551 3552 memlist_read_lock(); 3553 for (ml = phys_install; ml; ml = ml->next) { 3554 uint64_t nbytes; 3555 3556 src_pa = ml->address; 3557 nbytes = ml->size; 3558 3559 while (nbytes != 0ull) { 3560 3561 /* copy 32 bytes at arc_pa to dst_pa */ 3562 bcopy32_il(src_pa, dst_pa); 3563 3564 /* increment by 32 bytes */ 3565 src_pa += (4 * sizeof (uint64_t)); 3566 3567 /* decrement by 32 bytes */ 3568 nbytes -= (4 * sizeof (uint64_t)); 3569 } 3570 } 3571 memlist_read_unlock(); 3572 3573 return (NULL); 3574 } 3575 3576 static struct { 3577 const char *name; 3578 sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts); 3579 } drmach_pt_arr[] = { 3580 { "juggle", drmach_pt_juggle_bootproc }, 3581 { "pda", drmach_pt_dump_pdainfo }, 3582 { "readmem", drmach_pt_readmem }, 3583 3584 /* the following line must always be last */ 3585 { NULL, NULL } 3586 }; 3587 3588 /*ARGSUSED*/ 3589 sbd_error_t * 3590 drmach_passthru(drmachid_t id, drmach_opts_t *opts) 3591 { 3592 int i; 3593 sbd_error_t *err; 3594 3595 i = 0; 3596 while (drmach_pt_arr[i].name != NULL) { 3597 int len = strlen(drmach_pt_arr[i].name); 3598 3599 if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0) 3600 break; 3601 3602 i += 1; 3603 } 3604 3605 if (drmach_pt_arr[i].name == NULL) 3606 err = drerr_new(0, ESTF_UNKPTCMD, opts->copts); 3607 else 3608 err = (*drmach_pt_arr[i].handler)(id, opts); 3609 3610 return (err); 3611 } 3612 3613 sbd_error_t * 3614 drmach_release(drmachid_t id) 3615 { 3616 drmach_common_t *cp; 3617 if (!DRMACH_IS_DEVICE_ID(id)) 3618 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3619 cp = id; 3620 3621 return (cp->release(id)); 3622 } 3623 3624 sbd_error_t * 3625 drmach_status(drmachid_t id, drmach_status_t *stat) 3626 { 3627 drmach_common_t *cp; 3628 3629 if (!DRMACH_IS_ID(id)) 3630 return (drerr_new(0, ESTF_NOTID, NULL)); 3631 cp = id; 3632 3633 return (cp->status(id, stat)); 3634 } 3635 3636 sbd_error_t * 3637 drmach_unconfigure(drmachid_t id, int flags) 3638 { 3639 drmach_device_t *dp; 3640 pnode_t nodeid; 3641 dev_info_t *dip, *fdip = NULL; 3642 3643 if (!DRMACH_IS_DEVICE_ID(id)) 3644 return (drerr_new(0, ESTF_INAPPROP, NULL)); 3645 3646 dp = id; 3647 3648 nodeid = drmach_node_get_dnode(dp->node); 3649 if (nodeid == OBP_NONODE) 3650 return (DRMACH_INTERNAL_ERROR()); 3651 3652 dip = e_ddi_nodeid_to_dip(nodeid); 3653 if (dip == NULL) 3654 return (NULL); 3655 3656 /* 3657 * Branch already held, so hold acquired in 3658 * e_ddi_nodeid_to_dip() can be released 3659 */ 3660 ddi_release_devi(dip); 3661 3662 if (flags & DEVI_BRANCH_DESTROY) 3663 flags |= DEVI_BRANCH_EVENT; 3664 3665 /* 3666 * Force flag is no longer necessary. See starcat/io/drmach.c 3667 * for details. 3668 */ 3669 ASSERT(e_ddi_branch_held(dip)); 3670 if (e_ddi_branch_unconfigure(dip, &fdip, flags)) { 3671 sbd_error_t *err; 3672 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 3673 3674 /* 3675 * If non-NULL, fdip is returned held and must be released. 3676 */ 3677 if (fdip != NULL) { 3678 (void) ddi_pathname(fdip, path); 3679 ndi_rele_devi(fdip); 3680 } else { 3681 (void) ddi_pathname(dip, path); 3682 } 3683 3684 err = drerr_new(1, ESTF_DRVFAIL, path); 3685 3686 kmem_free(path, MAXPATHLEN); 3687 3688 return (err); 3689 } 3690 3691 return (NULL); 3692 } 3693 3694 /* 3695 * drmach interfaces to legacy Starfire platmod logic 3696 * linkage via runtime symbol look up, called from plat_cpu_power* 3697 */ 3698 3699 /* 3700 * Start up a cpu. It is possible that we're attempting to restart 3701 * the cpu after an UNCONFIGURE in which case the cpu will be 3702 * spinning in its cache. So, all we have to do is wakeup him up. 3703 * Under normal circumstances the cpu will be coming from a previous 3704 * CONNECT and thus will be spinning in OBP. In both cases, the 3705 * startup sequence is the same. 3706 */ 3707 int 3708 drmach_cpu_poweron(struct cpu *cp) 3709 { 3710 DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id); 3711 3712 ASSERT(MUTEX_HELD(&cpu_lock)); 3713 3714 if (drmach_cpu_start(cp) != 0) 3715 return (EBUSY); 3716 else 3717 return (0); 3718 } 3719 3720 int 3721 drmach_cpu_poweroff(struct cpu *cp) 3722 { 3723 int ntries, cnt; 3724 processorid_t cpuid = cp->cpu_id; 3725 void drmach_cpu_shutdown_self(void); 3726 3727 DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id); 3728 3729 ASSERT(MUTEX_HELD(&cpu_lock)); 3730 3731 /* 3732 * Capture all CPUs (except for detaching proc) to prevent 3733 * crosscalls to the detaching proc until it has cleared its 3734 * bit in cpu_ready_set. 3735 * 3736 * The CPU's remain paused and the prom_mutex is known to be free. 3737 * This prevents the x-trap victim from blocking when doing prom 3738 * IEEE-1275 calls at a high PIL level. 3739 */ 3740 promsafe_pause_cpus(); 3741 3742 /* 3743 * Quiesce interrupts on the target CPU. We do this by setting 3744 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to 3745 * prevent it from receiving cross calls and cross traps. 3746 * This prevents the processor from receiving any new soft interrupts. 3747 */ 3748 mp_cpu_quiesce(cp); 3749 3750 /* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */ 3751 drmach_xt_mb[cpuid] = 0x80; 3752 3753 xt_one_unchecked(cpuid, (xcfunc_t *)idle_stop_xcall, 3754 (uint64_t)drmach_cpu_shutdown_self, NULL); 3755 3756 ntries = drmach_cpu_ntries; 3757 cnt = 0; 3758 while (drmach_xt_mb[cpuid] && ntries) { 3759 DELAY(drmach_cpu_delay); 3760 ntries--; 3761 cnt++; 3762 } 3763 3764 drmach_xt_mb[cpuid] = 0; /* steal the cache line back */ 3765 3766 start_cpus(); 3767 3768 DRMACH_PR("waited %d out of %d tries for " 3769 "drmach_cpu_shutdown_self on cpu%d", 3770 drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id); 3771 3772 drmach_cpu_obp_detach(cpuid); 3773 3774 CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid); 3775 3776 return (0); 3777 } 3778 3779 /*ARGSUSED*/ 3780 int 3781 drmach_verify_sr(dev_info_t *dip, int sflag) 3782 { 3783 return (0); 3784 } 3785 3786 void 3787 drmach_suspend_last(void) 3788 { 3789 } 3790 3791 void 3792 drmach_resume_first(void) 3793 { 3794 } 3795 3796 /* 3797 * Log a DR sysevent. 3798 * Return value: 0 success, non-zero failure. 3799 */ 3800 int 3801 drmach_log_sysevent(int board, char *hint, int flag, int verbose) 3802 { 3803 sysevent_t *ev; 3804 sysevent_id_t eid; 3805 int rv, km_flag; 3806 sysevent_value_t evnt_val; 3807 sysevent_attr_list_t *evnt_attr_list = NULL; 3808 char attach_pnt[MAXNAMELEN]; 3809 3810 km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP; 3811 attach_pnt[0] = '\0'; 3812 if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) { 3813 rv = -1; 3814 goto logexit; 3815 } 3816 if (verbose) 3817 DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n", 3818 attach_pnt, hint, flag, verbose); 3819 3820 if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, 3821 SUNW_KERN_PUB"dr", km_flag)) == NULL) { 3822 rv = -2; 3823 goto logexit; 3824 } 3825 evnt_val.value_type = SE_DATA_TYPE_STRING; 3826 evnt_val.value.sv_string = attach_pnt; 3827 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, 3828 &evnt_val, km_flag)) != 0) 3829 goto logexit; 3830 3831 evnt_val.value_type = SE_DATA_TYPE_STRING; 3832 evnt_val.value.sv_string = hint; 3833 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, 3834 &evnt_val, km_flag)) != 0) { 3835 sysevent_free_attr(evnt_attr_list); 3836 goto logexit; 3837 } 3838 3839 (void) sysevent_attach_attributes(ev, evnt_attr_list); 3840 3841 /* 3842 * Log the event but do not sleep waiting for its 3843 * delivery. This provides insulation from syseventd. 3844 */ 3845 rv = log_sysevent(ev, SE_NOSLEEP, &eid); 3846 3847 logexit: 3848 if (ev) 3849 sysevent_free(ev); 3850 if ((rv != 0) && verbose) 3851 cmn_err(CE_WARN, 3852 "drmach_log_sysevent failed (rv %d) for %s %s\n", 3853 rv, attach_pnt, hint); 3854 3855 return (rv); 3856 } 3857 3858 /*ARGSUSED*/ 3859 int 3860 drmach_allow_memrange_modify(drmachid_t id) 3861 { 3862 return (1); /* TRUE */ 3863 } 3864