1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 29 #include <sys/debug.h> 30 #include <sys/types.h> 31 #include <sys/varargs.h> 32 #include <sys/errno.h> 33 #include <sys/cred.h> 34 #include <sys/dditypes.h> 35 #include <sys/devops.h> 36 #include <sys/modctl.h> 37 #include <sys/poll.h> 38 #include <sys/conf.h> 39 #include <sys/ddi.h> 40 #include <sys/sunddi.h> 41 #include <sys/sunndi.h> 42 #include <sys/ndi_impldefs.h> 43 #include <sys/stat.h> 44 #include <sys/kmem.h> 45 #include <sys/vmem.h> 46 #include <sys/opl_olympus_regs.h> 47 #include <sys/cpuvar.h> 48 #include <sys/cpupart.h> 49 #include <sys/mem_config.h> 50 #include <sys/ddi_impldefs.h> 51 #include <sys/systm.h> 52 #include <sys/machsystm.h> 53 #include <sys/autoconf.h> 54 #include <sys/cmn_err.h> 55 #include <sys/sysmacros.h> 56 #include <sys/x_call.h> 57 #include <sys/promif.h> 58 #include <sys/prom_plat.h> 59 #include <sys/membar.h> 60 #include <vm/seg_kmem.h> 61 #include <sys/mem_cage.h> 62 #include <sys/stack.h> 63 #include <sys/archsystm.h> 64 #include <vm/hat_sfmmu.h> 65 #include <sys/pte.h> 66 #include <sys/mmu.h> 67 #include <sys/cpu_module.h> 68 #include <sys/obpdefs.h> 69 #include <sys/note.h> 70 #include <sys/ontrap.h> 71 #include <sys/cpu_sgnblk_defs.h> 72 #include <sys/opl.h> 73 74 75 #include <sys/promimpl.h> 76 #include <sys/prom_plat.h> 77 #include <sys/kobj.h> 78 79 #include <sys/sysevent.h> 80 #include <sys/sysevent/dr.h> 81 #include <sys/sysevent/eventdefs.h> 82 83 #include <sys/drmach.h> 84 #include <sys/dr_util.h> 85 86 #include <sys/fcode.h> 87 #include <sys/opl_cfg.h> 88 89 extern void bcopy32_il(uint64_t, uint64_t); 90 extern void flush_cache_il(void); 91 extern void drmach_sleep_il(void); 92 93 typedef struct { 94 struct drmach_node *node; 95 void *data; 96 } drmach_node_walk_args_t; 97 98 typedef struct drmach_node { 99 void *here; 100 101 pnode_t (*get_dnode)(struct drmach_node *node); 102 int (*walk)(struct drmach_node *node, void *data, 103 int (*cb)(drmach_node_walk_args_t *args)); 104 dev_info_t *(*n_getdip)(struct drmach_node *node); 105 int (*n_getproplen)(struct drmach_node *node, char *name, 106 int *len); 107 int (*n_getprop)(struct drmach_node *node, char *name, 108 void *buf, int len); 109 int (*get_parent)(struct drmach_node *node, 110 struct drmach_node *pnode); 111 } drmach_node_t; 112 113 typedef struct { 114 int min_index; 115 int max_index; 116 int arr_sz; 117 drmachid_t *arr; 118 } drmach_array_t; 119 120 typedef struct { 121 void *isa; 122 123 void (*dispose)(drmachid_t); 124 sbd_error_t *(*release)(drmachid_t); 125 sbd_error_t *(*status)(drmachid_t, drmach_status_t *); 126 127 char name[MAXNAMELEN]; 128 } drmach_common_t; 129 130 typedef struct { 131 uint32_t core_present; 132 uint32_t core_hotadded; 133 uint32_t core_started; 134 } drmach_cmp_t; 135 136 typedef struct { 137 drmach_common_t cm; 138 int bnum; 139 int assigned; 140 int powered; 141 int connected; 142 int cond; 143 drmach_node_t *tree; 144 drmach_array_t *devices; 145 int boot_board; /* if board exists on bootup */ 146 drmach_cmp_t cores[OPL_MAX_COREID_PER_BOARD]; 147 } drmach_board_t; 148 149 typedef struct { 150 drmach_common_t cm; 151 drmach_board_t *bp; 152 int unum; 153 int portid; 154 int busy; 155 int powered; 156 const char *type; 157 drmach_node_t *node; 158 } drmach_device_t; 159 160 typedef struct drmach_cpu { 161 drmach_device_t dev; 162 processorid_t cpuid; 163 int sb; 164 int chipid; 165 int coreid; 166 int strandid; 167 int status; 168 #define OPL_CPU_HOTADDED 1 169 } drmach_cpu_t; 170 171 typedef struct drmach_mem { 172 drmach_device_t dev; 173 uint64_t slice_base; 174 uint64_t slice_size; 175 uint64_t base_pa; /* lowest installed memory base */ 176 uint64_t nbytes; /* size of installed memory */ 177 struct memlist *memlist; 178 } drmach_mem_t; 179 180 typedef struct drmach_io { 181 drmach_device_t dev; 182 int channel; 183 int leaf; 184 } drmach_io_t; 185 186 typedef struct drmach_domain_info { 187 uint32_t floating; 188 int allow_dr; 189 } drmach_domain_info_t; 190 191 drmach_domain_info_t drmach_domain; 192 193 typedef struct { 194 int flags; 195 drmach_device_t *dp; 196 sbd_error_t *err; 197 dev_info_t *dip; 198 } drmach_config_args_t; 199 200 typedef struct { 201 drmach_board_t *obj; 202 int ndevs; 203 void *a; 204 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t); 205 sbd_error_t *err; 206 } drmach_board_cb_data_t; 207 208 static drmach_array_t *drmach_boards; 209 210 static sbd_error_t *drmach_device_new(drmach_node_t *, 211 drmach_board_t *, int, drmachid_t *); 212 static sbd_error_t *drmach_cpu_new(drmach_device_t *, drmachid_t *); 213 static sbd_error_t *drmach_mem_new(drmach_device_t *, drmachid_t *); 214 static sbd_error_t *drmach_io_new(drmach_device_t *, drmachid_t *); 215 216 static dev_info_t *drmach_node_ddi_get_dip(drmach_node_t *np); 217 static int drmach_node_ddi_get_prop(drmach_node_t *np, 218 char *name, void *buf, int len); 219 static int drmach_node_ddi_get_proplen(drmach_node_t *np, 220 char *name, int *len); 221 222 static int drmach_get_portid(drmach_node_t *); 223 static sbd_error_t *drmach_i_status(drmachid_t, drmach_status_t *); 224 static int opl_check_dr_status(); 225 static void drmach_io_dispose(drmachid_t); 226 static sbd_error_t *drmach_io_release(drmachid_t); 227 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *); 228 static int drmach_init(void); 229 static void drmach_fini(void); 230 static void drmach_swap_pa(drmach_mem_t *, drmach_mem_t *); 231 static drmach_board_t *drmach_get_board_by_bnum(int); 232 233 /* options for the second argument in drmach_add_remove_cpu() */ 234 #define HOTADD_CPU 1 235 #define HOTREMOVE_CPU 2 236 237 #define ON_BOARD_CORE_NUM(x) (((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \ 238 (OPL_MAX_COREID_PER_BOARD - 1)) 239 240 extern struct cpu *SIGBCPU; 241 242 static int drmach_name2type_idx(char *); 243 static drmach_board_t *drmach_board_new(int, int); 244 245 #ifdef DEBUG 246 247 #define DRMACH_PR if (drmach_debug) printf 248 int drmach_debug = 1; /* set to non-zero to enable debug messages */ 249 #else 250 251 #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf 252 #endif /* DEBUG */ 253 254 255 #define DRMACH_OBJ(id) ((drmach_common_t *)id) 256 257 #define DRMACH_NULL_ID(id) ((id) == 0) 258 259 #define DRMACH_IS_BOARD_ID(id) \ 260 ((id != 0) && \ 261 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new)) 262 263 #define DRMACH_IS_CPU_ID(id) \ 264 ((id != 0) && \ 265 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new)) 266 267 #define DRMACH_IS_MEM_ID(id) \ 268 ((id != 0) && \ 269 (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new)) 270 271 #define DRMACH_IS_IO_ID(id) \ 272 ((id != 0) && \ 273 (DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 274 275 #define DRMACH_IS_DEVICE_ID(id) \ 276 ((id != 0) && \ 277 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 278 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 279 DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 280 281 #define DRMACH_IS_ID(id) \ 282 ((id != 0) && \ 283 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \ 284 DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 285 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 286 DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 287 288 #define DRMACH_INTERNAL_ERROR() \ 289 drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__) 290 291 static char *drmach_ie_fmt = "drmach.c %d"; 292 293 static struct { 294 const char *name; 295 const char *type; 296 sbd_error_t *(*new)(drmach_device_t *, drmachid_t *); 297 } drmach_name2type[] = { 298 { "cpu", DRMACH_DEVTYPE_CPU, drmach_cpu_new }, 299 { "pseudo-mc", DRMACH_DEVTYPE_MEM, drmach_mem_new }, 300 { "pci", DRMACH_DEVTYPE_PCI, drmach_io_new }, 301 }; 302 303 /* utility */ 304 #define MBYTE (1048576ull) 305 306 /* 307 * drmach autoconfiguration data structures and interfaces 308 */ 309 310 extern struct mod_ops mod_miscops; 311 312 static struct modlmisc modlmisc = { 313 &mod_miscops, 314 "OPL DR 1.1" 315 }; 316 317 static struct modlinkage modlinkage = { 318 MODREV_1, 319 (void *)&modlmisc, 320 NULL 321 }; 322 323 static krwlock_t drmach_boards_rwlock; 324 325 typedef const char *fn_t; 326 327 int 328 _init(void) 329 { 330 int err; 331 332 if ((err = drmach_init()) != 0) { 333 return (err); 334 } 335 336 if ((err = mod_install(&modlinkage)) != 0) { 337 drmach_fini(); 338 } 339 340 return (err); 341 } 342 343 int 344 _fini(void) 345 { 346 int err; 347 348 if ((err = mod_remove(&modlinkage)) == 0) 349 drmach_fini(); 350 351 return (err); 352 } 353 354 int 355 _info(struct modinfo *modinfop) 356 { 357 return (mod_info(&modlinkage, modinfop)); 358 } 359 360 struct drmach_mc_lookup { 361 int bnum; 362 drmach_board_t *bp; 363 dev_info_t *dip; /* rv - set if found */ 364 }; 365 366 #define _ptob64(p) ((uint64_t)(p) << PAGESHIFT) 367 #define _b64top(b) ((pgcnt_t)((b) >> PAGESHIFT)) 368 369 static int 370 drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp) 371 { 372 uint64_t memory_ranges[128]; 373 int len; 374 struct memlist *ml; 375 int rv; 376 hwd_sb_t *hwd; 377 hwd_memory_t *pm; 378 379 len = sizeof (memory_ranges); 380 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 381 DDI_PROP_DONTPASS, "sb-mem-ranges", 382 (caddr_t)&memory_ranges[0], &len) != DDI_PROP_SUCCESS) { 383 mp->slice_base = 0; 384 mp->slice_size = 0; 385 return (-1); 386 } 387 mp->slice_base = memory_ranges[0]; 388 mp->slice_size = memory_ranges[1]; 389 390 if (!mp->dev.bp->boot_board) { 391 int i; 392 393 rv = opl_read_hwd(mp->dev.bp->bnum, NULL, NULL, NULL, &hwd); 394 395 if (rv != 0) { 396 return (-1); 397 } 398 399 ml = NULL; 400 pm = &hwd->sb_cmu.cmu_memory; 401 for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) { 402 if (pm->mem_chunks[i].chnk_size > 0) { 403 ml = memlist_add_span(ml, 404 pm->mem_chunks[i].chnk_start_address, 405 pm->mem_chunks[i].chnk_size); 406 } 407 } 408 } else { 409 /* 410 * we intersect phys_install to get base_pa. 411 * This only works at bootup time. 412 */ 413 414 memlist_read_lock(); 415 ml = memlist_dup(phys_install); 416 memlist_read_unlock(); 417 418 ml = memlist_del_span(ml, 0ull, mp->slice_base); 419 if (ml) { 420 uint64_t basepa, endpa; 421 endpa = _ptob64(physmax + 1); 422 423 basepa = mp->slice_base + mp->slice_size; 424 425 ml = memlist_del_span(ml, basepa, endpa - basepa); 426 } 427 } 428 429 if (ml) { 430 uint64_t nbytes = 0; 431 struct memlist *p; 432 for (p = ml; p; p = p->next) { 433 nbytes += p->size; 434 } 435 if ((mp->nbytes = nbytes) > 0) 436 mp->base_pa = ml->address; 437 else 438 mp->base_pa = 0; 439 mp->memlist = ml; 440 } else { 441 mp->base_pa = 0; 442 mp->nbytes = 0; 443 } 444 return (0); 445 } 446 447 448 struct drmach_hotcpu { 449 drmach_board_t *bp; 450 int bnum; 451 int core_id; 452 int rv; 453 int option; 454 }; 455 456 static int 457 drmach_cpu_cb(dev_info_t *dip, void *arg) 458 { 459 struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg; 460 char name[OBP_MAXDRVNAME]; 461 int len = OBP_MAXDRVNAME; 462 int bnum, core_id, strand_id; 463 drmach_board_t *bp; 464 465 if (dip == ddi_root_node()) { 466 return (DDI_WALK_CONTINUE); 467 } 468 469 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 470 DDI_PROP_DONTPASS, "name", 471 (caddr_t)name, &len) != DDI_PROP_SUCCESS) { 472 return (DDI_WALK_PRUNECHILD); 473 } 474 475 /* only cmp has board number */ 476 bnum = -1; 477 len = sizeof (bnum); 478 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 479 DDI_PROP_DONTPASS, OBP_BOARDNUM, 480 (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) { 481 bnum = -1; 482 } 483 484 if (strcmp(name, "cmp") == 0) { 485 if (bnum != p->bnum) 486 return (DDI_WALK_PRUNECHILD); 487 return (DDI_WALK_CONTINUE); 488 } 489 /* we have already pruned all unwanted cores and cpu's above */ 490 if (strcmp(name, "core") == 0) { 491 return (DDI_WALK_CONTINUE); 492 } 493 if (strcmp(name, "cpu") == 0) { 494 processorid_t cpuid; 495 len = sizeof (cpuid); 496 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 497 DDI_PROP_DONTPASS, "cpuid", 498 (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) { 499 p->rv = -1; 500 return (DDI_WALK_TERMINATE); 501 } 502 503 core_id = p->core_id; 504 505 bnum = LSB_ID(cpuid); 506 507 if (ON_BOARD_CORE_NUM(cpuid) != core_id) 508 return (DDI_WALK_CONTINUE); 509 510 bp = p->bp; 511 ASSERT(bnum == bp->bnum); 512 513 if (p->option == HOTADD_CPU) { 514 if (prom_hotaddcpu(cpuid) != 0) { 515 p->rv = -1; 516 return (DDI_WALK_TERMINATE); 517 } 518 strand_id = STRAND_ID(cpuid); 519 bp->cores[core_id].core_hotadded |= (1 << strand_id); 520 } else if (p->option == HOTREMOVE_CPU) { 521 if (prom_hotremovecpu(cpuid) != 0) { 522 p->rv = -1; 523 return (DDI_WALK_TERMINATE); 524 } 525 strand_id = STRAND_ID(cpuid); 526 bp->cores[core_id].core_hotadded &= ~(1 << strand_id); 527 } 528 return (DDI_WALK_CONTINUE); 529 } 530 531 return (DDI_WALK_PRUNECHILD); 532 } 533 534 535 static int 536 drmach_add_remove_cpu(int bnum, int core_id, int option) 537 { 538 struct drmach_hotcpu arg; 539 drmach_board_t *bp; 540 541 bp = drmach_get_board_by_bnum(bnum); 542 ASSERT(bp); 543 544 arg.bp = bp; 545 arg.bnum = bnum; 546 arg.core_id = core_id; 547 arg.rv = 0; 548 arg.option = option; 549 ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg); 550 return (arg.rv); 551 } 552 553 struct drmach_setup_core_arg { 554 drmach_board_t *bp; 555 }; 556 557 static int 558 drmach_setup_core_cb(dev_info_t *dip, void *arg) 559 { 560 struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg; 561 char name[OBP_MAXDRVNAME]; 562 int len = OBP_MAXDRVNAME; 563 int bnum; 564 int core_id, strand_id; 565 566 if (dip == ddi_root_node()) { 567 return (DDI_WALK_CONTINUE); 568 } 569 570 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 571 DDI_PROP_DONTPASS, "name", 572 (caddr_t)name, &len) != DDI_PROP_SUCCESS) { 573 return (DDI_WALK_PRUNECHILD); 574 } 575 576 /* only cmp has board number */ 577 bnum = -1; 578 len = sizeof (bnum); 579 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 580 DDI_PROP_DONTPASS, OBP_BOARDNUM, 581 (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) { 582 bnum = -1; 583 } 584 585 if (strcmp(name, "cmp") == 0) { 586 if (bnum != p->bp->bnum) 587 return (DDI_WALK_PRUNECHILD); 588 return (DDI_WALK_CONTINUE); 589 } 590 /* we have already pruned all unwanted cores and cpu's above */ 591 if (strcmp(name, "core") == 0) { 592 return (DDI_WALK_CONTINUE); 593 } 594 if (strcmp(name, "cpu") == 0) { 595 processorid_t cpuid; 596 len = sizeof (cpuid); 597 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 598 DDI_PROP_DONTPASS, "cpuid", 599 (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) { 600 return (DDI_WALK_TERMINATE); 601 } 602 bnum = LSB_ID(cpuid); 603 ASSERT(bnum == p->bp->bnum); 604 core_id = ON_BOARD_CORE_NUM(cpuid); 605 strand_id = STRAND_ID(cpuid); 606 p->bp->cores[core_id].core_present |= (1 << strand_id); 607 return (DDI_WALK_CONTINUE); 608 } 609 610 return (DDI_WALK_PRUNECHILD); 611 } 612 613 614 static void 615 drmach_setup_core_info(drmach_board_t *obj) 616 { 617 struct drmach_setup_core_arg arg; 618 int i; 619 620 for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) { 621 obj->cores[i].core_present = 0; 622 obj->cores[i].core_hotadded = 0; 623 obj->cores[i].core_started = 0; 624 } 625 arg.bp = obj; 626 ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg); 627 628 for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) { 629 if (obj->boot_board) { 630 obj->cores[i].core_hotadded = 631 obj->cores[i].core_started = 632 obj->cores[i].core_present; 633 } 634 } 635 } 636 637 /* 638 * drmach_node_* routines serve the purpose of separating the 639 * rest of the code from the device tree and OBP. This is necessary 640 * because of In-Kernel-Probing. Devices probed after stod, are probed 641 * by the in-kernel-prober, not OBP. These devices, therefore, do not 642 * have dnode ids. 643 */ 644 645 typedef struct { 646 drmach_node_walk_args_t *nwargs; 647 int (*cb)(drmach_node_walk_args_t *args); 648 int err; 649 } drmach_node_ddi_walk_args_t; 650 651 static int 652 drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg) 653 { 654 drmach_node_ddi_walk_args_t *nargs; 655 656 nargs = (drmach_node_ddi_walk_args_t *)arg; 657 658 /* 659 * dip doesn't have to be held here as we are called 660 * from ddi_walk_devs() which holds the dip. 661 */ 662 nargs->nwargs->node->here = (void *)dip; 663 664 nargs->err = nargs->cb(nargs->nwargs); 665 666 667 /* 668 * Set "here" to NULL so that unheld dip is not accessible 669 * outside ddi_walk_devs() 670 */ 671 nargs->nwargs->node->here = NULL; 672 673 if (nargs->err) 674 return (DDI_WALK_TERMINATE); 675 else 676 return (DDI_WALK_CONTINUE); 677 } 678 679 static int 680 drmach_node_ddi_walk(drmach_node_t *np, void *data, 681 int (*cb)(drmach_node_walk_args_t *args)) 682 { 683 drmach_node_walk_args_t args; 684 drmach_node_ddi_walk_args_t nargs; 685 686 687 /* initialized args structure for callback */ 688 args.node = np; 689 args.data = data; 690 691 nargs.nwargs = &args; 692 nargs.cb = cb; 693 nargs.err = 0; 694 695 /* 696 * Root node doesn't have to be held in any way. 697 */ 698 ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, 699 (void *)&nargs); 700 701 return (nargs.err); 702 } 703 704 static int 705 drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp) 706 { 707 dev_info_t *ndip; 708 static char *fn = "drmach_node_ddi_get_parent"; 709 710 ndip = np->n_getdip(np); 711 if (ndip == NULL) { 712 cmn_err(CE_WARN, "%s: NULL dip", fn); 713 return (-1); 714 } 715 716 bcopy(np, pp, sizeof (drmach_node_t)); 717 718 pp->here = (void *)ddi_get_parent(ndip); 719 if (pp->here == NULL) { 720 cmn_err(CE_WARN, "%s: NULL parent dip", fn); 721 return (-1); 722 } 723 724 return (0); 725 } 726 727 /*ARGSUSED*/ 728 static pnode_t 729 drmach_node_ddi_get_dnode(drmach_node_t *np) 730 { 731 return ((pnode_t)NULL); 732 } 733 734 static drmach_node_t * 735 drmach_node_new(void) 736 { 737 drmach_node_t *np; 738 739 np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP); 740 741 np->get_dnode = drmach_node_ddi_get_dnode; 742 np->walk = drmach_node_ddi_walk; 743 np->n_getdip = drmach_node_ddi_get_dip; 744 np->n_getproplen = drmach_node_ddi_get_proplen; 745 np->n_getprop = drmach_node_ddi_get_prop; 746 np->get_parent = drmach_node_ddi_get_parent; 747 748 return (np); 749 } 750 751 static void 752 drmach_node_dispose(drmach_node_t *np) 753 { 754 kmem_free(np, sizeof (*np)); 755 } 756 757 static dev_info_t * 758 drmach_node_ddi_get_dip(drmach_node_t *np) 759 { 760 return ((dev_info_t *)np->here); 761 } 762 763 static int 764 drmach_node_walk(drmach_node_t *np, void *param, 765 int (*cb)(drmach_node_walk_args_t *args)) 766 { 767 return (np->walk(np, param, cb)); 768 } 769 770 static int 771 drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len) 772 { 773 int rv = 0; 774 dev_info_t *ndip; 775 static char *fn = "drmach_node_ddi_get_prop"; 776 777 778 ndip = np->n_getdip(np); 779 if (ndip == NULL) { 780 cmn_err(CE_WARN, "%s: NULL dip", fn); 781 rv = -1; 782 } else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip, 783 DDI_PROP_DONTPASS, name, 784 (caddr_t)buf, &len) != DDI_PROP_SUCCESS) { 785 rv = -1; 786 } 787 788 return (rv); 789 } 790 791 static int 792 drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len) 793 { 794 int rv = 0; 795 dev_info_t *ndip; 796 797 ndip = np->n_getdip(np); 798 if (ndip == NULL) { 799 rv = -1; 800 } else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, 801 name, len) != DDI_PROP_SUCCESS) { 802 rv = -1; 803 } 804 805 return (rv); 806 } 807 808 static drmachid_t 809 drmach_node_dup(drmach_node_t *np) 810 { 811 drmach_node_t *dup; 812 813 dup = drmach_node_new(); 814 dup->here = np->here; 815 dup->get_dnode = np->get_dnode; 816 dup->walk = np->walk; 817 dup->n_getdip = np->n_getdip; 818 dup->n_getproplen = np->n_getproplen; 819 dup->n_getprop = np->n_getprop; 820 dup->get_parent = np->get_parent; 821 822 return (dup); 823 } 824 825 /* 826 * drmach_array provides convenient array construction, access, 827 * bounds checking and array destruction logic. 828 */ 829 830 static drmach_array_t * 831 drmach_array_new(int min_index, int max_index) 832 { 833 drmach_array_t *arr; 834 835 arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP); 836 837 arr->arr_sz = (max_index - min_index + 1) * sizeof (void *); 838 if (arr->arr_sz > 0) { 839 arr->min_index = min_index; 840 arr->max_index = max_index; 841 842 arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP); 843 return (arr); 844 } else { 845 kmem_free(arr, sizeof (*arr)); 846 return (0); 847 } 848 } 849 850 static int 851 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val) 852 { 853 if (idx < arr->min_index || idx > arr->max_index) 854 return (-1); 855 else { 856 arr->arr[idx - arr->min_index] = val; 857 return (0); 858 } 859 /*NOTREACHED*/ 860 } 861 862 static int 863 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val) 864 { 865 if (idx < arr->min_index || idx > arr->max_index) 866 return (-1); 867 else { 868 *val = arr->arr[idx - arr->min_index]; 869 return (0); 870 } 871 /*NOTREACHED*/ 872 } 873 874 static int 875 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val) 876 { 877 int rv; 878 879 *idx = arr->min_index; 880 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 881 *idx += 1; 882 883 return (rv); 884 } 885 886 static int 887 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val) 888 { 889 int rv; 890 891 *idx += 1; 892 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 893 *idx += 1; 894 895 return (rv); 896 } 897 898 static void 899 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t)) 900 { 901 drmachid_t val; 902 int idx; 903 int rv; 904 905 rv = drmach_array_first(arr, &idx, &val); 906 while (rv == 0) { 907 (*disposer)(val); 908 rv = drmach_array_next(arr, &idx, &val); 909 } 910 911 kmem_free(arr->arr, arr->arr_sz); 912 kmem_free(arr, sizeof (*arr)); 913 } 914 915 static drmach_board_t * 916 drmach_get_board_by_bnum(int bnum) 917 { 918 drmachid_t id; 919 920 if (drmach_array_get(drmach_boards, bnum, &id) == 0) 921 return ((drmach_board_t *)id); 922 else 923 return (NULL); 924 } 925 926 static pnode_t 927 drmach_node_get_dnode(drmach_node_t *np) 928 { 929 return (np->get_dnode(np)); 930 } 931 932 /*ARGSUSED*/ 933 sbd_error_t * 934 drmach_configure(drmachid_t id, int flags) 935 { 936 drmach_device_t *dp; 937 sbd_error_t *err = NULL; 938 dev_info_t *rdip; 939 dev_info_t *fdip = NULL; 940 941 if (DRMACH_IS_CPU_ID(id)) { 942 return (NULL); 943 } 944 if (!DRMACH_IS_DEVICE_ID(id)) 945 return (drerr_new(0, EOPL_INAPPROP, NULL)); 946 dp = id; 947 rdip = dp->node->n_getdip(dp->node); 948 949 ASSERT(rdip); 950 951 ASSERT(e_ddi_branch_held(rdip)); 952 953 if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) { 954 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 955 dev_info_t *dip = (fdip != NULL) ? fdip : rdip; 956 957 (void) ddi_pathname(dip, path); 958 err = drerr_new(1, EOPL_DRVFAIL, path); 959 960 kmem_free(path, MAXPATHLEN); 961 962 /* If non-NULL, fdip is returned held and must be released */ 963 if (fdip != NULL) 964 ddi_release_devi(fdip); 965 } 966 967 return (err); 968 } 969 970 971 static sbd_error_t * 972 drmach_device_new(drmach_node_t *node, 973 drmach_board_t *bp, int portid, drmachid_t *idp) 974 { 975 int i; 976 int rv; 977 drmach_device_t proto; 978 sbd_error_t *err; 979 char name[OBP_MAXDRVNAME]; 980 981 rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME); 982 if (rv) { 983 /* every node is expected to have a name */ 984 err = drerr_new(1, EOPL_GETPROP, 985 "device node %s: property %s", 986 ddi_node_name(node->n_getdip(node)), "name"); 987 return (err); 988 } 989 990 /* 991 * The node currently being examined is not listed in the name2type[] 992 * array. In this case, the node is no interest to drmach. Both 993 * dp and err are initialized here to yield nothing (no device or 994 * error structure) for this case. 995 */ 996 i = drmach_name2type_idx(name); 997 998 999 if (i < 0) { 1000 *idp = (drmachid_t)0; 1001 return (NULL); 1002 } 1003 1004 /* device specific new function will set unum */ 1005 1006 bzero(&proto, sizeof (proto)); 1007 proto.type = drmach_name2type[i].type; 1008 proto.bp = bp; 1009 proto.node = node; 1010 proto.portid = portid; 1011 1012 return (drmach_name2type[i].new(&proto, idp)); 1013 } 1014 1015 static void 1016 drmach_device_dispose(drmachid_t id) 1017 { 1018 drmach_device_t *self = id; 1019 1020 self->cm.dispose(id); 1021 } 1022 1023 1024 static drmach_board_t * 1025 drmach_board_new(int bnum, int boot_board) 1026 { 1027 static sbd_error_t *drmach_board_release(drmachid_t); 1028 static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *); 1029 1030 drmach_board_t *bp; 1031 1032 bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP); 1033 1034 bp->cm.isa = (void *)drmach_board_new; 1035 bp->cm.release = drmach_board_release; 1036 bp->cm.status = drmach_board_status; 1037 1038 (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name)); 1039 1040 bp->bnum = bnum; 1041 bp->devices = NULL; 1042 bp->connected = boot_board; 1043 bp->tree = drmach_node_new(); 1044 bp->assigned = boot_board; 1045 bp->powered = boot_board; 1046 bp->boot_board = boot_board; 1047 1048 /* 1049 * If this is not bootup initialization, we have to wait till 1050 * IKP sets up the device nodes in drmach_board_connect(). 1051 */ 1052 if (boot_board) 1053 drmach_setup_core_info(bp); 1054 1055 drmach_array_set(drmach_boards, bnum, bp); 1056 return (bp); 1057 } 1058 1059 static void 1060 drmach_board_dispose(drmachid_t id) 1061 { 1062 drmach_board_t *bp; 1063 1064 ASSERT(DRMACH_IS_BOARD_ID(id)); 1065 bp = id; 1066 1067 if (bp->tree) 1068 drmach_node_dispose(bp->tree); 1069 1070 if (bp->devices) 1071 drmach_array_dispose(bp->devices, drmach_device_dispose); 1072 1073 kmem_free(bp, sizeof (*bp)); 1074 } 1075 1076 static sbd_error_t * 1077 drmach_board_status(drmachid_t id, drmach_status_t *stat) 1078 { 1079 sbd_error_t *err = NULL; 1080 drmach_board_t *bp; 1081 1082 if (!DRMACH_IS_BOARD_ID(id)) 1083 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1084 bp = id; 1085 1086 stat->assigned = bp->assigned; 1087 stat->powered = bp->powered; 1088 stat->busy = 0; /* assume not busy */ 1089 stat->configured = 0; /* assume not configured */ 1090 stat->empty = 0; 1091 stat->cond = bp->cond = SBD_COND_OK; 1092 strncpy(stat->type, "System Brd", sizeof (stat->type)); 1093 stat->info[0] = '\0'; 1094 1095 if (bp->devices) { 1096 int rv; 1097 int d_idx; 1098 drmachid_t d_id; 1099 1100 rv = drmach_array_first(bp->devices, &d_idx, &d_id); 1101 while (rv == 0) { 1102 drmach_status_t d_stat; 1103 1104 err = drmach_i_status(d_id, &d_stat); 1105 if (err) 1106 break; 1107 1108 stat->busy |= d_stat.busy; 1109 stat->configured |= d_stat.configured; 1110 1111 rv = drmach_array_next(bp->devices, &d_idx, &d_id); 1112 } 1113 } 1114 1115 return (err); 1116 } 1117 1118 int 1119 drmach_board_is_floating(drmachid_t id) 1120 { 1121 drmach_board_t *bp; 1122 1123 if (!DRMACH_IS_BOARD_ID(id)) 1124 return (0); 1125 1126 bp = (drmach_board_t *)id; 1127 1128 return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0); 1129 } 1130 1131 static int 1132 drmach_init(void) 1133 { 1134 dev_info_t *rdip; 1135 int i, rv, len; 1136 int *floating; 1137 1138 rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL); 1139 1140 drmach_boards = drmach_array_new(0, MAX_BOARDS - 1); 1141 1142 rdip = ddi_root_node(); 1143 1144 if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 1145 "floating-boards", &len) != DDI_PROP_SUCCESS) { 1146 cmn_err(CE_WARN, "Cannot get floating-boards proplen\n"); 1147 } else { 1148 floating = (int *)kmem_alloc(len, KM_SLEEP); 1149 rv = ddi_prop_op(DDI_DEV_T_ANY, rdip, 1150 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, 1151 "floating-boards", (caddr_t)floating, &len); 1152 if (rv != DDI_PROP_SUCCESS) { 1153 cmn_err(CE_WARN, "Cannot get floating-boards prop\n"); 1154 } else { 1155 drmach_domain.floating = 0; 1156 for (i = 0; i < len / sizeof (int); i++) { 1157 drmach_domain.floating |= (1 << floating[i]); 1158 } 1159 } 1160 kmem_free(floating, len); 1161 } 1162 drmach_domain.allow_dr = opl_check_dr_status(); 1163 1164 rdip = ddi_get_child(ddi_root_node()); 1165 do { 1166 int bnum; 1167 drmachid_t id; 1168 1169 bnum = -1; 1170 bnum = ddi_getprop(DDI_DEV_T_ANY, rdip, 1171 DDI_PROP_DONTPASS, OBP_BOARDNUM, -1); 1172 if (bnum == -1) 1173 continue; 1174 1175 if (drmach_array_get(drmach_boards, bnum, &id) == -1) { 1176 cmn_err(CE_WARN, "Device node 0x%p has" 1177 " invalid property value, %s=%d", 1178 rdip, OBP_BOARDNUM, bnum); 1179 goto error; 1180 } else if (id == NULL) { 1181 (void) drmach_board_new(bnum, 1); 1182 } 1183 } while ((rdip = ddi_get_next_sibling(rdip)) != NULL); 1184 1185 opl_hold_devtree(); 1186 1187 /* 1188 * Initialize the IKP feature. 1189 * 1190 * This can be done only after DR has acquired a hold on all the 1191 * device nodes that are interesting to IKP. 1192 */ 1193 if (opl_init_cfg() != 0) { 1194 cmn_err(CE_WARN, "DR - IKP initialization failed"); 1195 1196 opl_release_devtree(); 1197 1198 goto error; 1199 } 1200 1201 return (0); 1202 error: 1203 drmach_array_dispose(drmach_boards, drmach_board_dispose); 1204 rw_destroy(&drmach_boards_rwlock); 1205 return (ENXIO); 1206 } 1207 1208 static void 1209 drmach_fini(void) 1210 { 1211 rw_enter(&drmach_boards_rwlock, RW_WRITER); 1212 drmach_array_dispose(drmach_boards, drmach_board_dispose); 1213 drmach_boards = NULL; 1214 rw_exit(&drmach_boards_rwlock); 1215 1216 /* 1217 * Walk immediate children of the root devinfo node 1218 * releasing holds acquired on branches in drmach_init() 1219 */ 1220 1221 opl_release_devtree(); 1222 1223 rw_destroy(&drmach_boards_rwlock); 1224 } 1225 1226 /* 1227 * Each system board contains 2 Oberon PCI bridge and 1228 * 1 CMUCH. 1229 * Each oberon has 2 channels. 1230 * Each channel has 2 pci-ex leaf. 1231 * Each CMUCH has 1 pci bus. 1232 * 1233 * 1234 * Device Path: 1235 * /pci@<portid>,reg 1236 * 1237 * where 1238 * portid[10] = 0 1239 * portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel 1240 * 1241 * LLEAF_ID[9:8] = 0 1242 * LLEAF_ID[8:4] = LSB_ID[4:0] 1243 * LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon) 1244 * channel 4 is pcicmu 1245 * LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B) 1246 * 1247 * Properties: 1248 * name = pci 1249 * device_type = "pciex" 1250 * board# = LSBID 1251 * reg = int32 * 2, Oberon CSR space of the leaf and the UBC space 1252 * portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#) 1253 */ 1254 1255 static sbd_error_t * 1256 drmach_io_new(drmach_device_t *proto, drmachid_t *idp) 1257 { 1258 drmach_io_t *ip; 1259 1260 int portid; 1261 1262 portid = proto->portid; 1263 ASSERT(portid != -1); 1264 proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1); 1265 1266 ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP); 1267 bcopy(proto, &ip->dev, sizeof (ip->dev)); 1268 ip->dev.node = drmach_node_dup(proto->node); 1269 ip->dev.cm.isa = (void *)drmach_io_new; 1270 ip->dev.cm.dispose = drmach_io_dispose; 1271 ip->dev.cm.release = drmach_io_release; 1272 ip->dev.cm.status = drmach_io_status; 1273 ip->channel = (portid >> 1) & 0x7; 1274 ip->leaf = (portid & 0x1); 1275 1276 snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d", 1277 ip->dev.type, ip->dev.unum); 1278 1279 *idp = (drmachid_t)ip; 1280 return (NULL); 1281 } 1282 1283 1284 static void 1285 drmach_io_dispose(drmachid_t id) 1286 { 1287 drmach_io_t *self; 1288 1289 ASSERT(DRMACH_IS_IO_ID(id)); 1290 1291 self = id; 1292 if (self->dev.node) 1293 drmach_node_dispose(self->dev.node); 1294 1295 kmem_free(self, sizeof (*self)); 1296 } 1297 1298 /*ARGSUSED*/ 1299 sbd_error_t * 1300 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts) 1301 { 1302 drmach_board_t *bp = (drmach_board_t *)id; 1303 sbd_error_t *err = NULL; 1304 1305 /* allow status and ncm operations to always succeed */ 1306 if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) { 1307 return (NULL); 1308 } 1309 1310 /* check all other commands for the required option string */ 1311 1312 if ((opts->size > 0) && (opts->copts != NULL)) { 1313 1314 DRMACH_PR("platform options: %s\n", opts->copts); 1315 1316 if (strstr(opts->copts, "opldr") == NULL) { 1317 err = drerr_new(1, EOPL_SUPPORT, NULL); 1318 } 1319 } else { 1320 err = drerr_new(1, EOPL_SUPPORT, NULL); 1321 } 1322 1323 if (!err && id && DRMACH_IS_BOARD_ID(id)) { 1324 switch (cmd) { 1325 case SBD_CMD_TEST: 1326 case SBD_CMD_STATUS: 1327 case SBD_CMD_GETNCM: 1328 break; 1329 case SBD_CMD_CONNECT: 1330 if (bp->connected) 1331 err = drerr_new(0, ESBD_STATE, NULL); 1332 else if (!drmach_domain.allow_dr) 1333 err = drerr_new(1, EOPL_SUPPORT, 1334 NULL); 1335 break; 1336 case SBD_CMD_DISCONNECT: 1337 if (!bp->connected) 1338 err = drerr_new(0, ESBD_STATE, NULL); 1339 else if (!drmach_domain.allow_dr) 1340 err = drerr_new(1, EOPL_SUPPORT, 1341 NULL); 1342 break; 1343 default: 1344 if (!drmach_domain.allow_dr) 1345 err = drerr_new(1, EOPL_SUPPORT, 1346 NULL); 1347 break; 1348 1349 } 1350 } 1351 1352 return (err); 1353 } 1354 1355 /*ARGSUSED*/ 1356 sbd_error_t * 1357 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts) 1358 { 1359 return (NULL); 1360 } 1361 1362 sbd_error_t * 1363 drmach_board_assign(int bnum, drmachid_t *id) 1364 { 1365 sbd_error_t *err = NULL; 1366 1367 rw_enter(&drmach_boards_rwlock, RW_WRITER); 1368 1369 if (drmach_array_get(drmach_boards, bnum, id) == -1) { 1370 err = drerr_new(1, EOPL_BNUM, "%d", bnum); 1371 } else { 1372 drmach_board_t *bp; 1373 1374 if (*id) 1375 rw_downgrade(&drmach_boards_rwlock); 1376 1377 bp = *id; 1378 if (!(*id)) 1379 bp = *id = 1380 (drmachid_t)drmach_board_new(bnum, 0); 1381 bp->assigned = 1; 1382 } 1383 1384 rw_exit(&drmach_boards_rwlock); 1385 1386 return (err); 1387 } 1388 1389 /*ARGSUSED*/ 1390 sbd_error_t * 1391 drmach_board_connect(drmachid_t id, drmach_opts_t *opts) 1392 { 1393 drmach_board_t *obj = (drmach_board_t *)id; 1394 1395 if (!DRMACH_IS_BOARD_ID(id)) 1396 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1397 1398 if (opl_probe_sb(obj->bnum) != 0) 1399 return (drerr_new(1, EOPL_PROBE, NULL)); 1400 1401 (void) prom_attach_notice(obj->bnum); 1402 1403 drmach_setup_core_info(obj); 1404 1405 obj->connected = 1; 1406 1407 return (NULL); 1408 } 1409 1410 static int drmach_cache_flush_flag[NCPU]; 1411 1412 /*ARGSUSED*/ 1413 static void 1414 drmach_flush_cache(uint64_t id, uint64_t dummy) 1415 { 1416 extern void cpu_flush_ecache(void); 1417 1418 cpu_flush_ecache(); 1419 drmach_cache_flush_flag[id] = 0; 1420 } 1421 1422 static void 1423 drmach_flush_all() 1424 { 1425 cpuset_t xc_cpuset; 1426 int i; 1427 1428 xc_cpuset = cpu_ready_set; 1429 for (i = 0; i < NCPU; i++) { 1430 if (CPU_IN_SET(xc_cpuset, i)) { 1431 drmach_cache_flush_flag[i] = 1; 1432 xc_one(i, drmach_flush_cache, i, 0); 1433 while (drmach_cache_flush_flag[i]) { 1434 DELAY(1000); 1435 } 1436 } 1437 } 1438 } 1439 1440 static int 1441 drmach_disconnect_cpus(drmach_board_t *bp) 1442 { 1443 int i, bnum; 1444 1445 bnum = bp->bnum; 1446 1447 for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) { 1448 if (bp->cores[i].core_present) { 1449 if (bp->cores[i].core_started) 1450 return (-1); 1451 if (bp->cores[i].core_hotadded) { 1452 if (drmach_add_remove_cpu(bnum, i, HOTREMOVE_CPU)) { 1453 cmn_err(CE_WARN, 1454 "Failed to remove CMP %d on board %d\n", 1455 i, bnum); 1456 return (-1); 1457 } 1458 } 1459 } 1460 } 1461 return (0); 1462 } 1463 1464 /*ARGSUSED*/ 1465 sbd_error_t * 1466 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts) 1467 { 1468 drmach_board_t *obj; 1469 int rv = 0; 1470 sbd_error_t *err = NULL; 1471 1472 if (DRMACH_NULL_ID(id)) 1473 return (NULL); 1474 1475 if (!DRMACH_IS_BOARD_ID(id)) 1476 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1477 1478 obj = (drmach_board_t *)id; 1479 1480 if (drmach_disconnect_cpus(obj)) { 1481 err = drerr_new(1, EOPL_DEPROBE, obj->cm.name); 1482 return (err); 1483 } 1484 1485 rv = opl_unprobe_sb(obj->bnum); 1486 1487 if (rv == 0) { 1488 prom_detach_notice(obj->bnum); 1489 obj->connected = 0; 1490 1491 } else 1492 err = drerr_new(1, EOPL_DEPROBE, obj->cm.name); 1493 1494 return (err); 1495 } 1496 1497 static int 1498 drmach_get_portid(drmach_node_t *np) 1499 { 1500 int portid; 1501 char type[OBP_MAXPROPNAME]; 1502 1503 if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0) 1504 return (portid); 1505 1506 /* 1507 * Get the device_type property to see if we should 1508 * continue processing this node. 1509 */ 1510 if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0) 1511 return (-1); 1512 1513 if (strcmp(type, OPL_CPU_NODE) == 0) { 1514 /* 1515 * We return cpuid because it has no portid 1516 */ 1517 if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0) 1518 return (portid); 1519 } 1520 1521 return (-1); 1522 } 1523 1524 /* 1525 * This is a helper function to determine if a given 1526 * node should be considered for a dr operation according 1527 * to predefined dr type nodes and the node's name. 1528 * Formal Parameter : The name of a device node. 1529 * Return Value: -1, name does not map to a valid dr type. 1530 * A value greater or equal to 0, name is a valid dr type. 1531 */ 1532 static int 1533 drmach_name2type_idx(char *name) 1534 { 1535 int index, ntypes; 1536 1537 if (name == NULL) 1538 return (-1); 1539 1540 /* 1541 * Determine how many possible types are currently supported 1542 * for dr. 1543 */ 1544 ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]); 1545 1546 /* Determine if the node's name correspond to a predefined type. */ 1547 for (index = 0; index < ntypes; index++) { 1548 if (strcmp(drmach_name2type[index].name, name) == 0) 1549 /* The node is an allowed type for dr. */ 1550 return (index); 1551 } 1552 1553 /* 1554 * If the name of the node does not map to any of the 1555 * types in the array drmach_name2type then the node is not of 1556 * interest to dr. 1557 */ 1558 return (-1); 1559 } 1560 1561 /* 1562 * there is some complication on OPL: 1563 * - pseudo-mc nodes do not have portid property 1564 * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB# 1565 * - cmp has board# 1566 * - core and cpu nodes do not have portid and board# properties 1567 * starcat uses portid to derive the board# but that does not work 1568 * for us. starfire reads board# property to filter the devices. 1569 * That does not work either. So for these specific device, 1570 * we use specific hard coded methods to get the board# - 1571 * cpu: LSB# = CPUID[9:5] 1572 */ 1573 1574 static int 1575 drmach_board_find_devices_cb(drmach_node_walk_args_t *args) 1576 { 1577 drmach_node_t *node = args->node; 1578 drmach_board_cb_data_t *data = args->data; 1579 drmach_board_t *obj = data->obj; 1580 1581 int rv, portid; 1582 int bnum; 1583 drmachid_t id; 1584 drmach_device_t *device; 1585 char name[OBP_MAXDRVNAME]; 1586 1587 portid = drmach_get_portid(node); 1588 /* 1589 * core, cpu and pseudo-mc do not have portid 1590 * we use cpuid as the portid of the cpu node 1591 * for pseudo-mc, we do not use portid info. 1592 */ 1593 1594 rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME); 1595 if (rv) 1596 return (0); 1597 1598 1599 rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum)); 1600 1601 if (rv) { 1602 /* 1603 * cpu does not have board# property. We use 1604 * CPUID[9:5] 1605 */ 1606 if (strcmp("cpu", name) == 0) { 1607 bnum = (portid >> 5) & 0x1f; 1608 } else 1609 return (0); 1610 } 1611 1612 1613 if (bnum != obj->bnum) 1614 return (0); 1615 1616 if (drmach_name2type_idx(name) < 0) { 1617 return (0); 1618 } 1619 1620 /* 1621 * Create a device data structure from this node data. 1622 * The call may yield nothing if the node is not of interest 1623 * to drmach. 1624 */ 1625 data->err = drmach_device_new(node, obj, portid, &id); 1626 if (data->err) 1627 return (-1); 1628 else if (!id) { 1629 /* 1630 * drmach_device_new examined the node we passed in 1631 * and determined that it was one not of interest to 1632 * drmach. So, it is skipped. 1633 */ 1634 return (0); 1635 } 1636 1637 rv = drmach_array_set(obj->devices, data->ndevs++, id); 1638 if (rv) { 1639 data->err = DRMACH_INTERNAL_ERROR(); 1640 return (-1); 1641 } 1642 device = id; 1643 1644 data->err = (*data->found)(data->a, device->type, device->unum, id); 1645 return (data->err == NULL ? 0 : -1); 1646 } 1647 1648 sbd_error_t * 1649 drmach_board_find_devices(drmachid_t id, void *a, 1650 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t)) 1651 { 1652 drmach_board_t *bp = (drmach_board_t *)id; 1653 sbd_error_t *err; 1654 int max_devices; 1655 int rv; 1656 drmach_board_cb_data_t data; 1657 1658 1659 if (!DRMACH_IS_BOARD_ID(id)) 1660 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1661 1662 max_devices = MAX_CPU_UNITS_PER_BOARD; 1663 max_devices += MAX_MEM_UNITS_PER_BOARD; 1664 max_devices += MAX_IO_UNITS_PER_BOARD; 1665 1666 bp->devices = drmach_array_new(0, max_devices); 1667 1668 if (bp->tree == NULL) 1669 bp->tree = drmach_node_new(); 1670 1671 data.obj = bp; 1672 data.ndevs = 0; 1673 data.found = found; 1674 data.a = a; 1675 data.err = NULL; 1676 1677 rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb); 1678 if (rv == 0) 1679 err = NULL; 1680 else { 1681 drmach_array_dispose(bp->devices, drmach_device_dispose); 1682 bp->devices = NULL; 1683 1684 if (data.err) 1685 err = data.err; 1686 else 1687 err = DRMACH_INTERNAL_ERROR(); 1688 } 1689 1690 return (err); 1691 } 1692 1693 int 1694 drmach_board_lookup(int bnum, drmachid_t *id) 1695 { 1696 int rv = 0; 1697 1698 rw_enter(&drmach_boards_rwlock, RW_READER); 1699 if (drmach_array_get(drmach_boards, bnum, id)) { 1700 *id = 0; 1701 rv = -1; 1702 } 1703 rw_exit(&drmach_boards_rwlock); 1704 return (rv); 1705 } 1706 1707 sbd_error_t * 1708 drmach_board_name(int bnum, char *buf, int buflen) 1709 { 1710 snprintf(buf, buflen, "SB%d", bnum); 1711 return (NULL); 1712 } 1713 1714 sbd_error_t * 1715 drmach_board_poweroff(drmachid_t id) 1716 { 1717 drmach_board_t *bp; 1718 sbd_error_t *err; 1719 drmach_status_t stat; 1720 1721 if (DRMACH_NULL_ID(id)) 1722 return (NULL); 1723 1724 if (!DRMACH_IS_BOARD_ID(id)) 1725 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1726 bp = id; 1727 1728 err = drmach_board_status(id, &stat); 1729 1730 if (!err) { 1731 if (stat.configured || stat.busy) 1732 err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name); 1733 else { 1734 bp->powered = 0; 1735 } 1736 } 1737 return (err); 1738 } 1739 1740 sbd_error_t * 1741 drmach_board_poweron(drmachid_t id) 1742 { 1743 drmach_board_t *bp; 1744 1745 if (!DRMACH_IS_BOARD_ID(id)) 1746 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1747 bp = id; 1748 1749 bp->powered = 1; 1750 1751 return (NULL); 1752 } 1753 1754 static sbd_error_t * 1755 drmach_board_release(drmachid_t id) 1756 { 1757 if (!DRMACH_IS_BOARD_ID(id)) 1758 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1759 return (NULL); 1760 } 1761 1762 /*ARGSUSED*/ 1763 sbd_error_t * 1764 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force) 1765 { 1766 return (NULL); 1767 } 1768 1769 sbd_error_t * 1770 drmach_board_unassign(drmachid_t id) 1771 { 1772 drmach_board_t *bp; 1773 sbd_error_t *err; 1774 drmach_status_t stat; 1775 1776 if (DRMACH_NULL_ID(id)) 1777 return (NULL); 1778 1779 if (!DRMACH_IS_BOARD_ID(id)) { 1780 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1781 } 1782 bp = id; 1783 1784 rw_enter(&drmach_boards_rwlock, RW_WRITER); 1785 1786 err = drmach_board_status(id, &stat); 1787 if (err) { 1788 rw_exit(&drmach_boards_rwlock); 1789 return (err); 1790 } 1791 if (stat.configured || stat.busy) { 1792 err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name); 1793 } else { 1794 if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0) 1795 err = DRMACH_INTERNAL_ERROR(); 1796 else 1797 drmach_board_dispose(bp); 1798 } 1799 rw_exit(&drmach_boards_rwlock); 1800 return (err); 1801 } 1802 1803 /* 1804 * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id, 1805 * implementation #, etc 1806 */ 1807 1808 static sbd_error_t * 1809 drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp) 1810 { 1811 static void drmach_cpu_dispose(drmachid_t); 1812 static sbd_error_t *drmach_cpu_release(drmachid_t); 1813 static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *); 1814 1815 int portid; 1816 drmach_cpu_t *cp = NULL; 1817 1818 /* portid is CPUID of the node */ 1819 portid = proto->portid; 1820 ASSERT(portid != -1); 1821 1822 /* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */ 1823 proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) & 1824 (OPL_MAX_CMPID_PER_BOARD - 1)) + 1825 ((portid & (OPL_MAX_CPUID_PER_CMP - 1)) * 1826 (OPL_MAX_CMPID_PER_BOARD)); 1827 1828 cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP); 1829 bcopy(proto, &cp->dev, sizeof (cp->dev)); 1830 cp->dev.node = drmach_node_dup(proto->node); 1831 cp->dev.cm.isa = (void *)drmach_cpu_new; 1832 cp->dev.cm.dispose = drmach_cpu_dispose; 1833 cp->dev.cm.release = drmach_cpu_release; 1834 cp->dev.cm.status = drmach_cpu_status; 1835 1836 snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d", 1837 cp->dev.type, cp->dev.unum); 1838 1839 /* 1840 * CPU ID representation 1841 * CPUID[9:5] = SB# 1842 * CPUID[4:3] = Chip# 1843 * CPUID[2:1] = Core# (Only 2 core for OPL) 1844 * CPUID[0:0] = Strand# 1845 */ 1846 1847 /* 1848 * reg property of the strand contains strand ID 1849 * reg property of the parent node contains core ID 1850 * We should use them. 1851 */ 1852 cp->cpuid = portid; 1853 cp->sb = (portid >> 5) & 0x1f; 1854 cp->chipid = (portid >> 3) & 0x3; 1855 cp->coreid = (portid >> 1) & 0x3; 1856 cp->strandid = portid & 0x1; 1857 1858 *idp = (drmachid_t)cp; 1859 return (NULL); 1860 } 1861 1862 1863 static void 1864 drmach_cpu_dispose(drmachid_t id) 1865 { 1866 drmach_cpu_t *self; 1867 1868 ASSERT(DRMACH_IS_CPU_ID(id)); 1869 1870 self = id; 1871 if (self->dev.node) 1872 drmach_node_dispose(self->dev.node); 1873 1874 kmem_free(self, sizeof (*self)); 1875 } 1876 1877 static int 1878 drmach_cpu_start(struct cpu *cp) 1879 { 1880 int cpuid = cp->cpu_id; 1881 extern int restart_other_cpu(int); 1882 1883 ASSERT(MUTEX_HELD(&cpu_lock)); 1884 ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0); 1885 1886 cp->cpu_flags &= ~CPU_POWEROFF; 1887 1888 /* 1889 * NOTE: restart_other_cpu pauses cpus during the 1890 * slave cpu start. This helps to quiesce the 1891 * bus traffic a bit which makes the tick sync 1892 * routine in the prom more robust. 1893 */ 1894 DRMACH_PR("COLD START for cpu (%d)\n", cpuid); 1895 1896 restart_other_cpu(cpuid); 1897 1898 return (0); 1899 } 1900 1901 static sbd_error_t * 1902 drmach_cpu_release(drmachid_t id) 1903 { 1904 if (!DRMACH_IS_CPU_ID(id)) 1905 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1906 1907 return (NULL); 1908 } 1909 1910 static sbd_error_t * 1911 drmach_cpu_status(drmachid_t id, drmach_status_t *stat) 1912 { 1913 drmach_cpu_t *cp; 1914 drmach_device_t *dp; 1915 1916 ASSERT(DRMACH_IS_CPU_ID(id)); 1917 cp = (drmach_cpu_t *)id; 1918 dp = &cp->dev; 1919 1920 stat->assigned = dp->bp->assigned; 1921 stat->powered = dp->bp->powered; 1922 mutex_enter(&cpu_lock); 1923 stat->configured = (cpu_get(cp->cpuid) != NULL); 1924 mutex_exit(&cpu_lock); 1925 stat->busy = dp->busy; 1926 strncpy(stat->type, dp->type, sizeof (stat->type)); 1927 stat->info[0] = '\0'; 1928 1929 return (NULL); 1930 } 1931 1932 sbd_error_t * 1933 drmach_cpu_disconnect(drmachid_t id) 1934 { 1935 1936 if (!DRMACH_IS_CPU_ID(id)) 1937 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1938 1939 return (NULL); 1940 } 1941 1942 sbd_error_t * 1943 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid) 1944 { 1945 drmach_cpu_t *cpu; 1946 1947 if (!DRMACH_IS_CPU_ID(id)) 1948 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1949 cpu = (drmach_cpu_t *)id; 1950 1951 /* get from cpu directly on OPL */ 1952 *cpuid = cpu->cpuid; 1953 return (NULL); 1954 } 1955 1956 sbd_error_t * 1957 drmach_cpu_get_impl(drmachid_t id, int *ip) 1958 { 1959 drmach_device_t *cpu; 1960 drmach_node_t *np; 1961 drmach_node_t pp; 1962 int impl; 1963 char type[OBP_MAXPROPNAME]; 1964 1965 if (!DRMACH_IS_CPU_ID(id)) 1966 return (drerr_new(0, EOPL_INAPPROP, NULL)); 1967 1968 cpu = id; 1969 np = cpu->node; 1970 1971 if (np->get_parent(np, &pp) != 0) { 1972 return (DRMACH_INTERNAL_ERROR()); 1973 } 1974 1975 /* the parent should be core */ 1976 1977 if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) { 1978 return (drerr_new(0, EOPL_GETPROP, NULL)); 1979 } 1980 1981 if (strcmp(type, OPL_CORE_NODE) == 0) { 1982 if (pp.n_getprop(&pp, "implementation#", 1983 &impl, sizeof (impl)) != 0) { 1984 return (drerr_new(0, EOPL_GETPROP, NULL)); 1985 } 1986 } else { 1987 return (DRMACH_INTERNAL_ERROR()); 1988 } 1989 1990 *ip = impl; 1991 1992 return (NULL); 1993 } 1994 1995 sbd_error_t * 1996 drmach_get_dip(drmachid_t id, dev_info_t **dip) 1997 { 1998 drmach_device_t *dp; 1999 2000 if (!DRMACH_IS_DEVICE_ID(id)) 2001 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2002 dp = id; 2003 2004 *dip = dp->node->n_getdip(dp->node); 2005 return (NULL); 2006 } 2007 2008 sbd_error_t * 2009 drmach_io_is_attached(drmachid_t id, int *yes) 2010 { 2011 drmach_device_t *dp; 2012 dev_info_t *dip; 2013 int state; 2014 2015 if (!DRMACH_IS_IO_ID(id)) 2016 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2017 dp = id; 2018 2019 dip = dp->node->n_getdip(dp->node); 2020 if (dip == NULL) { 2021 *yes = 0; 2022 return (NULL); 2023 } 2024 2025 state = ddi_get_devstate(dip); 2026 *yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) || 2027 (state == DDI_DEVSTATE_UP)); 2028 2029 return (NULL); 2030 } 2031 2032 struct drmach_io_cb { 2033 char *name; /* name of the node */ 2034 int (*func)(dev_info_t *); 2035 int rv; 2036 dev_info_t *dip; 2037 }; 2038 2039 #define DRMACH_IO_POST_ATTACH 0 2040 #define DRMACH_IO_PRE_RELEASE 1 2041 2042 static int 2043 drmach_io_cb_check(dev_info_t *dip, void *arg) 2044 { 2045 struct drmach_io_cb *p = (struct drmach_io_cb *)arg; 2046 char name[OBP_MAXDRVNAME]; 2047 int len = OBP_MAXDRVNAME; 2048 2049 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 2050 DDI_PROP_DONTPASS, "name", 2051 (caddr_t)name, &len) != DDI_PROP_SUCCESS) { 2052 return (DDI_WALK_PRUNECHILD); 2053 } 2054 2055 if (strcmp(name, p->name) == 0) { 2056 ndi_hold_devi(dip); 2057 p->dip = dip; 2058 return (DDI_WALK_TERMINATE); 2059 } 2060 2061 return (DDI_WALK_CONTINUE); 2062 } 2063 2064 2065 static int 2066 drmach_console_ops(drmachid_t *id, int state) 2067 { 2068 drmach_io_t *obj = (drmach_io_t *)id; 2069 struct drmach_io_cb arg; 2070 int (*msudetp)(dev_info_t *); 2071 int (*msuattp)(dev_info_t *); 2072 dev_info_t *dip, *pdip; 2073 int circ; 2074 2075 /* 4 is pcicmu channel */ 2076 if (obj->channel != 4) 2077 return (0); 2078 2079 arg.name = "serial"; 2080 arg.func = NULL; 2081 if (state == DRMACH_IO_PRE_RELEASE) { 2082 msudetp = (int (*)(dev_info_t *)) 2083 modgetsymvalue("oplmsu_dr_detach", 0); 2084 if (msudetp != NULL) 2085 arg.func = msudetp; 2086 } else if (state == DRMACH_IO_POST_ATTACH) { 2087 msuattp = (int (*)(dev_info_t *)) 2088 modgetsymvalue("oplmsu_dr_attach", 0); 2089 if (msuattp != NULL) 2090 arg.func = msuattp; 2091 } else { 2092 return (0); 2093 } 2094 2095 if (arg.func == NULL) { 2096 return (0); 2097 } 2098 2099 arg.rv = 0; 2100 arg.dip = NULL; 2101 2102 dip = obj->dev.node->n_getdip(obj->dev.node); 2103 if (pdip = ddi_get_parent(dip)) { 2104 ndi_hold_devi(pdip); 2105 ndi_devi_enter(pdip, &circ); 2106 } else { 2107 /* this cannot happen unless something bad happens */ 2108 return (-1); 2109 } 2110 2111 ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg); 2112 2113 ndi_devi_exit(pdip, circ); 2114 ndi_rele_devi(pdip); 2115 2116 if (arg.dip) { 2117 arg.rv = (*arg.func)(arg.dip); 2118 ndi_rele_devi(arg.dip); 2119 } else { 2120 arg.rv = -1; 2121 } 2122 2123 return (arg.rv); 2124 } 2125 2126 sbd_error_t * 2127 drmach_io_pre_release(drmachid_t id) 2128 { 2129 int rv; 2130 2131 if (!DRMACH_IS_IO_ID(id)) 2132 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2133 2134 rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE); 2135 2136 if (rv != 0) 2137 cmn_err(CE_WARN, "IO callback failed in pre-release\n"); 2138 2139 return (NULL); 2140 } 2141 2142 static sbd_error_t * 2143 drmach_io_release(drmachid_t id) 2144 { 2145 if (!DRMACH_IS_IO_ID(id)) 2146 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2147 return (NULL); 2148 } 2149 2150 sbd_error_t * 2151 drmach_io_unrelease(drmachid_t id) 2152 { 2153 if (!DRMACH_IS_IO_ID(id)) 2154 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2155 return (NULL); 2156 } 2157 2158 /*ARGSUSED*/ 2159 sbd_error_t * 2160 drmach_io_post_release(drmachid_t id) 2161 { 2162 return (NULL); 2163 } 2164 2165 /*ARGSUSED*/ 2166 sbd_error_t * 2167 drmach_io_post_attach(drmachid_t id) 2168 { 2169 int rv; 2170 2171 if (!DRMACH_IS_IO_ID(id)) 2172 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2173 2174 rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH); 2175 2176 if (rv != 0) 2177 cmn_err(CE_WARN, "IO callback failed in post-attach\n"); 2178 2179 return (0); 2180 } 2181 2182 static sbd_error_t * 2183 drmach_io_status(drmachid_t id, drmach_status_t *stat) 2184 { 2185 drmach_device_t *dp; 2186 sbd_error_t *err; 2187 int configured; 2188 2189 ASSERT(DRMACH_IS_IO_ID(id)); 2190 dp = id; 2191 2192 err = drmach_io_is_attached(id, &configured); 2193 if (err) 2194 return (err); 2195 2196 stat->assigned = dp->bp->assigned; 2197 stat->powered = dp->bp->powered; 2198 stat->configured = (configured != 0); 2199 stat->busy = dp->busy; 2200 strncpy(stat->type, dp->type, sizeof (stat->type)); 2201 stat->info[0] = '\0'; 2202 2203 return (NULL); 2204 } 2205 2206 static sbd_error_t * 2207 drmach_mem_new(drmach_device_t *proto, drmachid_t *idp) 2208 { 2209 static void drmach_mem_dispose(drmachid_t); 2210 static sbd_error_t *drmach_mem_release(drmachid_t); 2211 static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *); 2212 dev_info_t *dip; 2213 int rv; 2214 2215 drmach_mem_t *mp; 2216 2217 rv = 0; 2218 2219 if ((proto->node->n_getproplen(proto->node, "mc-addr", &rv) < 0) || 2220 (rv <= 0)) { 2221 *idp = (drmachid_t)0; 2222 return (NULL); 2223 } 2224 2225 mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP); 2226 proto->unum = 0; 2227 2228 bcopy(proto, &mp->dev, sizeof (mp->dev)); 2229 mp->dev.node = drmach_node_dup(proto->node); 2230 mp->dev.cm.isa = (void *)drmach_mem_new; 2231 mp->dev.cm.dispose = drmach_mem_dispose; 2232 mp->dev.cm.release = drmach_mem_release; 2233 mp->dev.cm.status = drmach_mem_status; 2234 2235 snprintf(mp->dev.cm.name, 2236 sizeof (mp->dev.cm.name), "%s", mp->dev.type); 2237 2238 dip = mp->dev.node->n_getdip(mp->dev.node); 2239 if (drmach_setup_mc_info(dip, mp) != 0) { 2240 return (drerr_new(1, EOPL_MC_SETUP, NULL)); 2241 } 2242 2243 /* make sure we do not create memoryless nodes */ 2244 if (mp->nbytes == 0) { 2245 *idp = (drmachid_t)NULL; 2246 kmem_free(mp, sizeof (drmach_mem_t)); 2247 } else 2248 *idp = (drmachid_t)mp; 2249 2250 return (NULL); 2251 } 2252 2253 static void 2254 drmach_mem_dispose(drmachid_t id) 2255 { 2256 drmach_mem_t *mp; 2257 2258 ASSERT(DRMACH_IS_MEM_ID(id)); 2259 2260 2261 mp = id; 2262 2263 if (mp->dev.node) 2264 drmach_node_dispose(mp->dev.node); 2265 2266 if (mp->memlist) { 2267 memlist_delete(mp->memlist); 2268 mp->memlist = NULL; 2269 } 2270 2271 kmem_free(mp, sizeof (*mp)); 2272 } 2273 2274 sbd_error_t * 2275 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size) 2276 { 2277 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 2278 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 2279 int rv; 2280 2281 ASSERT(size != 0); 2282 2283 if (!DRMACH_IS_MEM_ID(id)) 2284 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2285 2286 kcage_range_lock(); 2287 rv = kcage_range_add(basepfn, npages, 1); 2288 kcage_range_unlock(); 2289 if (rv == ENOMEM) { 2290 cmn_err(CE_WARN, "%ld megabytes not available to kernel cage", 2291 (size == 0 ? 0 : size / MBYTE)); 2292 } else if (rv != 0) { 2293 /* catch this in debug kernels */ 2294 ASSERT(0); 2295 2296 cmn_err(CE_WARN, "unexpected kcage_range_add" 2297 " return value %d", rv); 2298 } 2299 2300 if (rv) { 2301 return (DRMACH_INTERNAL_ERROR()); 2302 } 2303 else 2304 return (NULL); 2305 } 2306 2307 sbd_error_t * 2308 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size) 2309 { 2310 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 2311 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 2312 int rv; 2313 2314 if (!DRMACH_IS_MEM_ID(id)) 2315 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2316 2317 if (size > 0) { 2318 kcage_range_lock(); 2319 rv = kcage_range_delete_post_mem_del(basepfn, npages); 2320 kcage_range_unlock(); 2321 if (rv != 0) { 2322 cmn_err(CE_WARN, 2323 "unexpected kcage_range_delete_post_mem_del" 2324 " return value %d", rv); 2325 return (DRMACH_INTERNAL_ERROR()); 2326 } 2327 } 2328 2329 return (NULL); 2330 } 2331 2332 sbd_error_t * 2333 drmach_mem_disable(drmachid_t id) 2334 { 2335 if (!DRMACH_IS_MEM_ID(id)) 2336 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2337 else { 2338 drmach_flush_all(); 2339 return (NULL); 2340 } 2341 } 2342 2343 sbd_error_t * 2344 drmach_mem_enable(drmachid_t id) 2345 { 2346 if (!DRMACH_IS_MEM_ID(id)) 2347 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2348 else 2349 return (NULL); 2350 } 2351 2352 sbd_error_t * 2353 drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem) 2354 { 2355 drmach_mem_t *mp; 2356 2357 if (!DRMACH_IS_MEM_ID(id)) 2358 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2359 2360 mp = (drmach_mem_t *)id; 2361 2362 /* 2363 * This is only used by dr to round up/down the memory 2364 * for copying. Our unit of memory isolation is 64 MB. 2365 */ 2366 2367 mem->mi_alignment_mask = (64 * 1024 * 1024 - 1); 2368 mem->mi_basepa = mp->base_pa; 2369 mem->mi_size = mp->nbytes; 2370 mem->mi_slice_size = mp->slice_size; 2371 2372 return (NULL); 2373 } 2374 2375 sbd_error_t * 2376 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa) 2377 { 2378 drmach_mem_t *mp; 2379 2380 if (!DRMACH_IS_MEM_ID(id)) 2381 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2382 2383 mp = (drmach_mem_t *)id; 2384 2385 *pa = mp->base_pa; 2386 return (NULL); 2387 } 2388 2389 sbd_error_t * 2390 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml) 2391 { 2392 drmach_mem_t *mem; 2393 #ifdef DEBUG 2394 int rv; 2395 #endif 2396 struct memlist *mlist; 2397 2398 if (!DRMACH_IS_MEM_ID(id)) 2399 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2400 2401 mem = (drmach_mem_t *)id; 2402 mlist = memlist_dup(mem->memlist); 2403 2404 #ifdef DEBUG 2405 /* 2406 * Make sure the incoming memlist doesn't already 2407 * intersect with what's present in the system (phys_install). 2408 */ 2409 memlist_read_lock(); 2410 rv = memlist_intersect(phys_install, mlist); 2411 memlist_read_unlock(); 2412 if (rv) { 2413 DRMACH_PR("Derived memlist intersects" 2414 " with phys_install\n"); 2415 memlist_dump(mlist); 2416 2417 DRMACH_PR("phys_install memlist:\n"); 2418 memlist_dump(phys_install); 2419 2420 memlist_delete(mlist); 2421 return (DRMACH_INTERNAL_ERROR()); 2422 } 2423 2424 DRMACH_PR("Derived memlist:"); 2425 memlist_dump(mlist); 2426 #endif 2427 *ml = mlist; 2428 2429 return (NULL); 2430 } 2431 2432 sbd_error_t * 2433 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes) 2434 { 2435 drmach_mem_t *mem; 2436 2437 if (!DRMACH_IS_MEM_ID(id)) 2438 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2439 2440 mem = (drmach_mem_t *)id; 2441 2442 *bytes = mem->slice_size; 2443 2444 return (NULL); 2445 } 2446 2447 2448 /* ARGSUSED */ 2449 processorid_t 2450 drmach_mem_cpu_affinity(drmachid_t id) 2451 { 2452 return (CPU_CURRENT); 2453 } 2454 2455 static sbd_error_t * 2456 drmach_mem_release(drmachid_t id) 2457 { 2458 if (!DRMACH_IS_MEM_ID(id)) 2459 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2460 return (NULL); 2461 } 2462 2463 static sbd_error_t * 2464 drmach_mem_status(drmachid_t id, drmach_status_t *stat) 2465 { 2466 drmach_mem_t *dp; 2467 uint64_t pa, slice_size; 2468 struct memlist *ml; 2469 2470 ASSERT(DRMACH_IS_MEM_ID(id)); 2471 dp = id; 2472 2473 /* get starting physical address of target memory */ 2474 pa = dp->base_pa; 2475 2476 /* round down to slice boundary */ 2477 slice_size = dp->slice_size; 2478 pa &= ~(slice_size - 1); 2479 2480 /* stop at first span that is in slice */ 2481 memlist_read_lock(); 2482 for (ml = phys_install; ml; ml = ml->next) 2483 if (ml->address >= pa && ml->address < pa + slice_size) 2484 break; 2485 memlist_read_unlock(); 2486 2487 stat->assigned = dp->dev.bp->assigned; 2488 stat->powered = dp->dev.bp->powered; 2489 stat->configured = (ml != NULL); 2490 stat->busy = dp->dev.busy; 2491 strncpy(stat->type, dp->dev.type, sizeof (stat->type)); 2492 stat->info[0] = '\0'; 2493 2494 return (NULL); 2495 } 2496 2497 2498 sbd_error_t * 2499 drmach_board_deprobe(drmachid_t id) 2500 { 2501 drmach_board_t *bp; 2502 2503 if (!DRMACH_IS_BOARD_ID(id)) 2504 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2505 2506 bp = id; 2507 2508 cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum); 2509 2510 if (bp->tree) { 2511 drmach_node_dispose(bp->tree); 2512 bp->tree = NULL; 2513 } 2514 if (bp->devices) { 2515 drmach_array_dispose(bp->devices, drmach_device_dispose); 2516 bp->devices = NULL; 2517 } 2518 2519 bp->boot_board = 0; 2520 2521 return (NULL); 2522 } 2523 2524 /*ARGSUSED*/ 2525 static sbd_error_t * 2526 drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts) 2527 { 2528 drmach_board_t *bp = (drmach_board_t *)id; 2529 sbd_error_t *err = NULL; 2530 int rv; 2531 2532 if (!DRMACH_IS_BOARD_ID(id)) 2533 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2534 2535 DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum); 2536 rv = opl_probe_sb(bp->bnum); 2537 if (rv != 0) { 2538 err = drerr_new(1, EOPL_PROBE, bp->cm.name); 2539 return (err); 2540 } 2541 return (err); 2542 } 2543 2544 /*ARGSUSED*/ 2545 static sbd_error_t * 2546 drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts) 2547 { 2548 drmach_board_t *bp; 2549 sbd_error_t *err = NULL; 2550 int rv; 2551 2552 if (!DRMACH_IS_BOARD_ID(id)) 2553 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2554 bp = (drmach_board_t *)id; 2555 2556 cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum); 2557 2558 rv = opl_unprobe_sb(bp->bnum); 2559 if (rv != 0) { 2560 err = drerr_new(1, EOPL_DEPROBE, bp->cm.name); 2561 } 2562 2563 return (err); 2564 } 2565 2566 2567 /*ARGSUSED*/ 2568 sbd_error_t * 2569 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts) 2570 { 2571 struct memlist *ml; 2572 uint64_t src_pa; 2573 uint64_t dst_pa; 2574 uint64_t dst; 2575 2576 dst_pa = va_to_pa(&dst); 2577 2578 memlist_read_lock(); 2579 for (ml = phys_install; ml; ml = ml->next) { 2580 uint64_t nbytes; 2581 2582 src_pa = ml->address; 2583 nbytes = ml->size; 2584 2585 while (nbytes != 0ull) { 2586 2587 /* copy 32 bytes at arc_pa to dst_pa */ 2588 bcopy32_il(src_pa, dst_pa); 2589 2590 /* increment by 32 bytes */ 2591 src_pa += (4 * sizeof (uint64_t)); 2592 2593 /* decrement by 32 bytes */ 2594 nbytes -= (4 * sizeof (uint64_t)); 2595 } 2596 } 2597 memlist_read_unlock(); 2598 2599 return (NULL); 2600 } 2601 2602 static struct { 2603 const char *name; 2604 sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts); 2605 } drmach_pt_arr[] = { 2606 { "readmem", drmach_pt_readmem }, 2607 { "ikprobe", drmach_pt_ikprobe }, 2608 { "ikdeprobe", drmach_pt_ikdeprobe }, 2609 2610 /* the following line must always be last */ 2611 { NULL, NULL } 2612 }; 2613 2614 /*ARGSUSED*/ 2615 sbd_error_t * 2616 drmach_passthru(drmachid_t id, drmach_opts_t *opts) 2617 { 2618 int i; 2619 sbd_error_t *err; 2620 2621 i = 0; 2622 while (drmach_pt_arr[i].name != NULL) { 2623 int len = strlen(drmach_pt_arr[i].name); 2624 2625 if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0) 2626 break; 2627 2628 i += 1; 2629 } 2630 2631 if (drmach_pt_arr[i].name == NULL) 2632 err = drerr_new(0, EOPL_UNKPTCMD, opts->copts); 2633 else 2634 err = (*drmach_pt_arr[i].handler)(id, opts); 2635 2636 return (err); 2637 } 2638 2639 sbd_error_t * 2640 drmach_release(drmachid_t id) 2641 { 2642 drmach_common_t *cp; 2643 2644 if (!DRMACH_IS_DEVICE_ID(id)) 2645 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2646 cp = id; 2647 2648 return (cp->release(id)); 2649 } 2650 2651 sbd_error_t * 2652 drmach_status(drmachid_t id, drmach_status_t *stat) 2653 { 2654 drmach_common_t *cp; 2655 sbd_error_t *err; 2656 2657 rw_enter(&drmach_boards_rwlock, RW_READER); 2658 2659 if (!DRMACH_IS_ID(id)) { 2660 rw_exit(&drmach_boards_rwlock); 2661 return (drerr_new(0, EOPL_NOTID, NULL)); 2662 } 2663 cp = (drmach_common_t *)id; 2664 err = cp->status(id, stat); 2665 2666 rw_exit(&drmach_boards_rwlock); 2667 2668 return (err); 2669 } 2670 2671 static sbd_error_t * 2672 drmach_i_status(drmachid_t id, drmach_status_t *stat) 2673 { 2674 drmach_common_t *cp; 2675 2676 if (!DRMACH_IS_ID(id)) 2677 return (drerr_new(0, EOPL_NOTID, NULL)); 2678 cp = id; 2679 2680 return (cp->status(id, stat)); 2681 } 2682 2683 /*ARGSUSED*/ 2684 sbd_error_t * 2685 drmach_unconfigure(drmachid_t id, int flags) 2686 { 2687 drmach_device_t *dp; 2688 dev_info_t *rdip, *fdip = NULL; 2689 char name[OBP_MAXDRVNAME]; 2690 int rv; 2691 2692 if (DRMACH_IS_CPU_ID(id)) 2693 return (NULL); 2694 2695 if (!DRMACH_IS_DEVICE_ID(id)) 2696 return (drerr_new(0, EOPL_INAPPROP, NULL)); 2697 2698 dp = id; 2699 2700 rdip = dp->node->n_getdip(dp->node); 2701 2702 ASSERT(rdip); 2703 2704 rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME); 2705 2706 if (rv) 2707 return (NULL); 2708 2709 /* 2710 * Note: FORCE flag is no longer necessary under devfs 2711 */ 2712 2713 ASSERT(e_ddi_branch_held(rdip)); 2714 if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) { 2715 sbd_error_t *err; 2716 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 2717 2718 /* 2719 * If non-NULL, fdip is returned held and must be released. 2720 */ 2721 if (fdip != NULL) { 2722 (void) ddi_pathname(fdip, path); 2723 ndi_rele_devi(fdip); 2724 } else { 2725 (void) ddi_pathname(rdip, path); 2726 } 2727 2728 err = drerr_new(1, EOPL_DRVFAIL, path); 2729 2730 kmem_free(path, MAXPATHLEN); 2731 2732 return (err); 2733 } 2734 2735 return (NULL); 2736 } 2737 2738 2739 int 2740 drmach_cpu_poweron(struct cpu *cp) 2741 { 2742 int bnum, cpuid, onb_core_num, strand_id; 2743 drmach_board_t *bp; 2744 2745 DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id); 2746 2747 cpuid = cp->cpu_id; 2748 bnum = LSB_ID(cpuid); 2749 onb_core_num = ON_BOARD_CORE_NUM(cpuid); 2750 strand_id = STRAND_ID(cpuid); 2751 bp = drmach_get_board_by_bnum(bnum); 2752 2753 ASSERT(bp); 2754 if (bp->cores[onb_core_num].core_hotadded == 0) { 2755 if (drmach_add_remove_cpu(bnum, onb_core_num, 2756 HOTADD_CPU) != 0) { 2757 cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n", 2758 onb_core_num, bnum); 2759 return (EIO); 2760 } 2761 } 2762 2763 ASSERT(MUTEX_HELD(&cpu_lock)); 2764 2765 if (drmach_cpu_start(cp) != 0) { 2766 if (bp->cores[onb_core_num].core_started == 0) { 2767 /* 2768 * we must undo the hotadd or no one will do that 2769 * If this fails, we will do this again in 2770 * drmach_board_disconnect. 2771 */ 2772 if (drmach_add_remove_cpu(bnum, onb_core_num, 2773 HOTREMOVE_CPU) != 0) { 2774 cmn_err(CE_WARN, "Failed to remove CMP %d " 2775 "on board %d\n", 2776 onb_core_num, bnum); 2777 } 2778 } 2779 return (EBUSY); 2780 } else { 2781 bp->cores[onb_core_num].core_started |= (1 << strand_id); 2782 return (0); 2783 } 2784 } 2785 2786 int 2787 drmach_cpu_poweroff(struct cpu *cp) 2788 { 2789 int rv = 0; 2790 processorid_t cpuid = cp->cpu_id; 2791 2792 DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id); 2793 2794 ASSERT(MUTEX_HELD(&cpu_lock)); 2795 2796 /* 2797 * Capture all CPUs (except for detaching proc) to prevent 2798 * crosscalls to the detaching proc until it has cleared its 2799 * bit in cpu_ready_set. 2800 * 2801 * The CPU's remain paused and the prom_mutex is known to be free. 2802 * This prevents the x-trap victim from blocking when doing prom 2803 * IEEE-1275 calls at a high PIL level. 2804 */ 2805 2806 promsafe_pause_cpus(); 2807 2808 /* 2809 * Quiesce interrupts on the target CPU. We do this by setting 2810 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to 2811 * prevent it from receiving cross calls and cross traps. 2812 * This prevents the processor from receiving any new soft interrupts. 2813 */ 2814 mp_cpu_quiesce(cp); 2815 2816 rv = prom_stopcpu_bycpuid(cpuid); 2817 if (rv == 0) 2818 cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF; 2819 2820 start_cpus(); 2821 2822 if (rv == 0) { 2823 int bnum, onb_core_num, strand_id; 2824 drmach_board_t *bp; 2825 2826 CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid); 2827 2828 bnum = LSB_ID(cpuid); 2829 onb_core_num = ON_BOARD_CORE_NUM(cpuid); 2830 strand_id = STRAND_ID(cpuid); 2831 bp = drmach_get_board_by_bnum(bnum); 2832 ASSERT(bp); 2833 2834 bp->cores[onb_core_num].core_started &= ~(1 << strand_id); 2835 if (bp->cores[onb_core_num].core_started == 0) { 2836 if (drmach_add_remove_cpu(bnum, onb_core_num, 2837 HOTREMOVE_CPU) != 0) { 2838 cmn_err(CE_WARN, 2839 "Failed to remove CMP %d LSB %d\n", 2840 onb_core_num, bnum); 2841 return (EIO); 2842 } 2843 } 2844 } 2845 2846 return (rv); 2847 } 2848 2849 /*ARGSUSED*/ 2850 int 2851 drmach_verify_sr(dev_info_t *dip, int sflag) 2852 { 2853 return (0); 2854 } 2855 2856 void 2857 drmach_suspend_last(void) 2858 { 2859 } 2860 2861 void 2862 drmach_resume_first(void) 2863 { 2864 } 2865 2866 /* 2867 * Log a DR sysevent. 2868 * Return value: 0 success, non-zero failure. 2869 */ 2870 int 2871 drmach_log_sysevent(int board, char *hint, int flag, int verbose) 2872 { 2873 sysevent_t *ev; 2874 sysevent_id_t eid; 2875 int rv, km_flag; 2876 sysevent_value_t evnt_val; 2877 sysevent_attr_list_t *evnt_attr_list = NULL; 2878 char attach_pnt[MAXNAMELEN]; 2879 2880 km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP; 2881 attach_pnt[0] = '\0'; 2882 if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) { 2883 rv = -1; 2884 goto logexit; 2885 } 2886 if (verbose) { 2887 DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n", 2888 attach_pnt, hint, flag, verbose); 2889 } 2890 2891 if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, 2892 SUNW_KERN_PUB"dr", km_flag)) == NULL) { 2893 rv = -2; 2894 goto logexit; 2895 } 2896 evnt_val.value_type = SE_DATA_TYPE_STRING; 2897 evnt_val.value.sv_string = attach_pnt; 2898 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, 2899 &evnt_val, km_flag)) != 0) 2900 goto logexit; 2901 2902 evnt_val.value_type = SE_DATA_TYPE_STRING; 2903 evnt_val.value.sv_string = hint; 2904 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, 2905 &evnt_val, km_flag)) != 0) { 2906 sysevent_free_attr(evnt_attr_list); 2907 goto logexit; 2908 } 2909 2910 (void) sysevent_attach_attributes(ev, evnt_attr_list); 2911 2912 /* 2913 * Log the event but do not sleep waiting for its 2914 * delivery. This provides insulation from syseventd. 2915 */ 2916 rv = log_sysevent(ev, SE_NOSLEEP, &eid); 2917 2918 logexit: 2919 if (ev) 2920 sysevent_free(ev); 2921 if ((rv != 0) && verbose) 2922 cmn_err(CE_WARN, 2923 "drmach_log_sysevent failed (rv %d) for %s %s\n", 2924 rv, attach_pnt, hint); 2925 2926 return (rv); 2927 } 2928 2929 #define OPL_DR_STATUS_PROP "dr-status" 2930 2931 static int 2932 opl_check_dr_status() 2933 { 2934 pnode_t node; 2935 int rtn, len; 2936 char *str; 2937 2938 node = prom_rootnode(); 2939 if (node == OBP_BADNODE) { 2940 return (1); 2941 } 2942 2943 len = prom_getproplen(node, OPL_DR_STATUS_PROP); 2944 if (len == -1) { 2945 /* 2946 * dr-status doesn't exist when DR is activated and 2947 * any warning messages aren't needed. 2948 */ 2949 return (1); 2950 } 2951 2952 str = (char *)kmem_zalloc(len+1, KM_SLEEP); 2953 rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str); 2954 kmem_free(str, len + 1); 2955 if (rtn == -1) { 2956 return (1); 2957 } else { 2958 return (0); 2959 } 2960 } 2961 2962 /* we are allocating memlist from TLB locked pages to avoid tlbmisses */ 2963 2964 static struct memlist * 2965 drmach_memlist_add_span(drmach_copy_rename_program_t *p, 2966 struct memlist *mlist, uint64_t base, uint64_t len) 2967 { 2968 struct memlist *ml, *tl, *nl; 2969 2970 if (len == 0ull) 2971 return (NULL); 2972 2973 if (mlist == NULL) { 2974 mlist = p->free_mlist; 2975 if (mlist == NULL) 2976 return (NULL); 2977 p->free_mlist = mlist->next; 2978 mlist->address = base; 2979 mlist->size = len; 2980 mlist->next = mlist->prev = NULL; 2981 2982 return (mlist); 2983 } 2984 2985 for (tl = ml = mlist; ml; tl = ml, ml = ml->next) { 2986 if (base < ml->address) { 2987 if ((base + len) < ml->address) { 2988 nl = p->free_mlist; 2989 if (nl == NULL) 2990 return (NULL); 2991 p->free_mlist = nl->next; 2992 nl->address = base; 2993 nl->size = len; 2994 nl->next = ml; 2995 if ((nl->prev = ml->prev) != NULL) 2996 nl->prev->next = nl; 2997 ml->prev = nl; 2998 if (mlist == ml) 2999 mlist = nl; 3000 } else { 3001 ml->size = MAX((base + len), 3002 (ml->address + ml->size)) - 3003 base; 3004 ml->address = base; 3005 } 3006 break; 3007 3008 } else if (base <= (ml->address + ml->size)) { 3009 ml->size = MAX((base + len), 3010 (ml->address + ml->size)) - 3011 MIN(ml->address, base); 3012 ml->address = MIN(ml->address, base); 3013 break; 3014 } 3015 } 3016 if (ml == NULL) { 3017 nl = p->free_mlist; 3018 if (nl == NULL) 3019 return (NULL); 3020 p->free_mlist = nl->next; 3021 nl->address = base; 3022 nl->size = len; 3023 nl->next = NULL; 3024 nl->prev = tl; 3025 tl->next = nl; 3026 } 3027 3028 return (mlist); 3029 } 3030 3031 /* 3032 * The routine performs the necessary memory COPY and MC adr SWITCH. 3033 * Both operations MUST be at the same "level" so that the stack is 3034 * maintained correctly between the copy and switch. The switch 3035 * portion implements a caching mechanism to guarantee the code text 3036 * is cached prior to execution. This is to guard against possible 3037 * memory access while the MC adr's are being modified. 3038 * 3039 * IMPORTANT: The _drmach_copy_rename_end() function must immediately 3040 * follow drmach_copy_rename_prog__relocatable() so that the correct 3041 * "length" of the drmach_copy_rename_prog__relocatable can be 3042 * calculated. This routine MUST be a LEAF function, i.e. it can 3043 * make NO function calls, primarily for two reasons: 3044 * 3045 * 1. We must keep the stack consistent across the "switch". 3046 * 2. Function calls are compiled to relative offsets, and 3047 * we execute this function we'll be executing it from 3048 * a copied version in a different area of memory, thus 3049 * the relative offsets will be bogus. 3050 * 3051 * Moreover, it must have the "__relocatable" suffix to inform DTrace 3052 * providers (and anything else, for that matter) that this 3053 * function's text is manually relocated elsewhere before it is 3054 * executed. That is, it cannot be safely instrumented with any 3055 * methodology that is PC-relative. 3056 */ 3057 3058 /* 3059 * We multiply this to system_clock_frequency so we 3060 * are setting a delay of fmem_timeout second for 3061 * the rename command. 3062 * 3063 * FMEM command itself should complete within 15 sec. 3064 * We add 2 more sec to be conservative. 3065 * 3066 * Note that there is also a SCF BUSY bit checking 3067 * in drmach_asm.s right before FMEM command is 3068 * issued. XSCF sets the SCF BUSY bit when the 3069 * other domain on the same PSB reboots and it 3070 * will not be able to service the FMEM command 3071 * within 15 sec. After setting the SCF BUSY 3072 * bit, XSCF will wait a while before servicing 3073 * other reboot command so there is no race 3074 * condition. 3075 */ 3076 3077 static int fmem_timeout = 17; 3078 3079 /* 3080 * The empirical data on some OPL system shows that 3081 * we can copy 250 MB per second. We set it to 3082 * 80 MB to be conservative. In normal case, 3083 * this timeout does not affect anything. 3084 */ 3085 3086 static int min_copy_size_per_sec = 80 * 1024 * 1024; 3087 3088 /* 3089 * This is the timeout value for the xcall synchronization 3090 * to get all the CPU ready to do the parallel copying. 3091 * Even on a fully loaded system, 10 sec. should be long 3092 * enough. 3093 */ 3094 3095 static int cpu_xcall_delay = 10; 3096 int drmach_disable_mcopy = 0; 3097 3098 /* 3099 * The following delay loop executes sleep instruction to yield the 3100 * CPU to other strands. If this is not done, some strand will tie 3101 * up the CPU in busy loops while the other strand cannot do useful 3102 * work. The copy procedure will take a much longer time without this. 3103 */ 3104 #define DR_DELAY_IL(ms, freq) \ 3105 { \ 3106 uint64_t start; \ 3107 uint64_t nstick; \ 3108 volatile uint64_t now; \ 3109 nstick = ((uint64_t)ms * freq)/1000; \ 3110 start = drmach_get_stick_il(); \ 3111 now = start; \ 3112 while ((now - start) <= nstick) { \ 3113 drmach_sleep_il(); \ 3114 now = drmach_get_stick_il(); \ 3115 } \ 3116 } 3117 3118 static int 3119 drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog, 3120 int cpuid) 3121 { 3122 struct memlist *ml; 3123 register int rtn; 3124 int i; 3125 register uint64_t curr, limit; 3126 extern uint64_t drmach_get_stick_il(); 3127 extern void membar_sync_il(); 3128 extern void flush_instr_mem_il(void*); 3129 extern void flush_windows_il(void); 3130 uint64_t copy_start; 3131 3132 /* 3133 * flush_windows is moved here to make sure all 3134 * registers used in the callers are flushed to 3135 * memory before the copy. 3136 * 3137 * If flush_windows() is called too early in the 3138 * calling function, the compiler might put some 3139 * data in the local registers after flush_windows(). 3140 * After FMA, if there is any fill trap, the registers 3141 * will contain stale data. 3142 */ 3143 3144 flush_windows_il(); 3145 3146 prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY; 3147 membar_sync_il(); 3148 3149 if (prog->data->cpuid == cpuid) { 3150 limit = drmach_get_stick_il(); 3151 limit += cpu_xcall_delay * system_clock_freq; 3152 for (i = 0; i < NCPU; i++) { 3153 if (CPU_IN_SET(prog->data->cpu_slave_set, i)) { 3154 /* wait for all CPU's to be ready */ 3155 for (;;) { 3156 if (prog->critical->stat[i] == 3157 FMEM_LOOP_COPY_READY) { 3158 break; 3159 } 3160 DR_DELAY_IL(1, prog->data->stick_freq); 3161 } 3162 curr = drmach_get_stick_il(); 3163 if (curr > limit) { 3164 prog->data->fmem_status.error = 3165 EOPL_FMEM_XC_TIMEOUT; 3166 return (EOPL_FMEM_XC_TIMEOUT); 3167 } 3168 } 3169 } 3170 prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY; 3171 membar_sync_il(); 3172 copy_start = drmach_get_stick_il(); 3173 } else { 3174 for (;;) { 3175 if (prog->data->fmem_status.stat == 3176 FMEM_LOOP_COPY_READY) { 3177 break; 3178 } 3179 if (prog->data->fmem_status.error) { 3180 prog->data->error[cpuid] = 3181 EOPL_FMEM_TERMINATE; 3182 return (EOPL_FMEM_TERMINATE); 3183 } 3184 DR_DELAY_IL(1, prog->data->stick_freq); 3185 } 3186 } 3187 3188 /* 3189 * DO COPY. 3190 */ 3191 if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) { 3192 for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->next) { 3193 uint64_t s_pa, t_pa; 3194 uint64_t nbytes; 3195 3196 s_pa = prog->data->s_copybasepa + ml->address; 3197 t_pa = prog->data->t_copybasepa + ml->address; 3198 nbytes = ml->size; 3199 3200 while (nbytes != 0ull) { 3201 /* If the master has detected error, we just bail out */ 3202 if (prog->data->fmem_status.error != ESBD_NOERROR) { 3203 prog->data->error[cpuid] = 3204 EOPL_FMEM_TERMINATE; 3205 return (EOPL_FMEM_TERMINATE); 3206 } 3207 /* 3208 * This copy does NOT use an ASI 3209 * that avoids the Ecache, therefore 3210 * the dst_pa addresses may remain 3211 * in our Ecache after the dst_pa 3212 * has been removed from the system. 3213 * A subsequent write-back to memory 3214 * will cause an ARB-stop because the 3215 * physical address no longer exists 3216 * in the system. Therefore we must 3217 * flush out local Ecache after we 3218 * finish the copy. 3219 */ 3220 3221 /* copy 32 bytes at src_pa to dst_pa */ 3222 bcopy32_il(s_pa, t_pa); 3223 3224 /* increment the counter to signal that we are alive */ 3225 prog->stat->nbytes[cpuid] += 32; 3226 3227 /* increment by 32 bytes */ 3228 s_pa += (4 * sizeof (uint64_t)); 3229 t_pa += (4 * sizeof (uint64_t)); 3230 3231 /* decrement by 32 bytes */ 3232 nbytes -= (4 * sizeof (uint64_t)); 3233 } 3234 } 3235 prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE; 3236 membar_sync_il(); 3237 } 3238 3239 /* 3240 * Since bcopy32_il() does NOT use an ASI to bypass 3241 * the Ecache, we need to flush our Ecache after 3242 * the copy is complete. 3243 */ 3244 flush_cache_il(); 3245 3246 /* 3247 * drmach_fmem_exec_script() 3248 */ 3249 if (prog->data->cpuid == cpuid) { 3250 uint64_t last, now; 3251 3252 limit = copy_start + prog->data->copy_delay; 3253 for (i = 0; i < NCPU; i++) { 3254 if (CPU_IN_SET(prog->data->cpu_slave_set, i)) { 3255 for (;;) { 3256 /* we get FMEM_LOOP_FMEM_READY in normal case */ 3257 if (prog->critical->stat[i] == 3258 FMEM_LOOP_FMEM_READY) { 3259 break; 3260 } 3261 /* got error traps */ 3262 if (prog->data->error[i] == 3263 EOPL_FMEM_COPY_ERROR) { 3264 prog->data->fmem_status.error = 3265 EOPL_FMEM_COPY_ERROR; 3266 return (EOPL_FMEM_COPY_ERROR); 3267 } 3268 /* if we have not reached limit, wait more */ 3269 curr = drmach_get_stick_il(); 3270 if (curr <= limit) 3271 continue; 3272 3273 prog->data->slowest_cpuid = i; 3274 prog->data->copy_wait_time = 3275 curr - copy_start; 3276 3277 /* now check if slave is alive */ 3278 last = prog->stat->nbytes[i]; 3279 3280 DR_DELAY_IL(1, prog->data->stick_freq); 3281 3282 now = prog->stat->nbytes[i]; 3283 if (now <= last) { 3284 /* no progress, perhaps just finished */ 3285 DR_DELAY_IL(1, prog->data->stick_freq); 3286 if (prog->critical->stat[i] == 3287 FMEM_LOOP_FMEM_READY) 3288 break; 3289 /* copy error */ 3290 if (prog->data->error[i] == 3291 EOPL_FMEM_COPY_ERROR) { 3292 prog->data->fmem_status.error = 3293 EOPL_FMEM_COPY_ERROR; 3294 return (EOPL_FMEM_COPY_ERROR); 3295 } 3296 prog->data->fmem_status.error = 3297 EOPL_FMEM_COPY_TIMEOUT; 3298 return (EOPL_FMEM_COPY_TIMEOUT); 3299 } 3300 } 3301 } 3302 } 3303 3304 prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY; 3305 prog->data->fmem_status.stat = FMEM_LOOP_FMEM_READY; 3306 3307 membar_sync_il(); 3308 flush_instr_mem_il((void*) (prog->critical)); 3309 /* 3310 * drmach_fmem_exec_script() 3311 */ 3312 rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE); 3313 return (rtn); 3314 } else { 3315 flush_instr_mem_il((void*) (prog->critical)); 3316 /* 3317 * drmach_fmem_loop_script() 3318 */ 3319 rtn = prog->critical->loop((void *)(prog->critical), 3320 PAGESIZE, (void *)&(prog->critical->stat[cpuid])); 3321 prog->data->error[cpuid] = rtn; 3322 /* slave thread does not care the rv */ 3323 return (0); 3324 } 3325 } 3326 3327 static void 3328 drmach_copy_rename_end(void) 3329 { 3330 /* 3331 * IMPORTANT: This function's location MUST be located immediately 3332 * following drmach_copy_rename_prog__relocatable to 3333 * accurately estimate its size. Note that this assumes 3334 * the compiler keeps these functions in the order in 3335 * which they appear :-o 3336 */ 3337 } 3338 3339 3340 static void 3341 drmach_setup_memlist(drmach_copy_rename_program_t *p) 3342 { 3343 struct memlist *ml; 3344 caddr_t buf; 3345 int nbytes, s; 3346 3347 nbytes = PAGESIZE; 3348 s = roundup(sizeof (struct memlist), sizeof (void *)); 3349 p->free_mlist = NULL; 3350 buf = p->memlist_buffer; 3351 while (nbytes >= sizeof (struct memlist)) { 3352 ml = (struct memlist *)buf; 3353 ml->next = p->free_mlist; 3354 p->free_mlist = ml; 3355 buf += s; 3356 nbytes -= s; 3357 } 3358 } 3359 3360 static void 3361 drmach_lock_critical(caddr_t va, caddr_t new_va) 3362 { 3363 tte_t tte; 3364 int i; 3365 3366 kpreempt_disable(); 3367 3368 for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) { 3369 vtag_flushpage(new_va, (uint64_t)ksfmmup); 3370 sfmmu_memtte(&tte, va_to_pfn(va), 3371 PROC_DATA|HAT_NOSYNC, TTE8K); 3372 tte.tte_intlo |= TTE_LCK_INT; 3373 sfmmu_dtlb_ld_kva(new_va, &tte); 3374 sfmmu_itlb_ld_kva(new_va, &tte); 3375 va += PAGESIZE; 3376 new_va += PAGESIZE; 3377 } 3378 } 3379 3380 static void 3381 drmach_unlock_critical(caddr_t va) 3382 { 3383 int i; 3384 3385 for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) { 3386 vtag_flushpage(va, (uint64_t)ksfmmup); 3387 va += PAGESIZE; 3388 } 3389 3390 kpreempt_enable(); 3391 } 3392 3393 sbd_error_t * 3394 drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id, 3395 struct memlist *c_ml, drmachid_t *pgm_id) 3396 { 3397 drmach_mem_t *s_mem; 3398 drmach_mem_t *t_mem; 3399 struct memlist *x_ml; 3400 uint64_t s_copybasepa, t_copybasepa; 3401 uint_t len; 3402 caddr_t bp, wp; 3403 int s_bd, t_bd, cpuid, active_cpus, i; 3404 uint64_t c_addr; 3405 size_t c_size, copy_sz, sz; 3406 extern void drmach_fmem_loop_script(); 3407 extern void drmach_fmem_loop_script_rtn(); 3408 extern int drmach_fmem_exec_script(); 3409 extern void drmach_fmem_exec_script_end(); 3410 sbd_error_t *err; 3411 drmach_copy_rename_program_t *prog = NULL; 3412 drmach_copy_rename_program_t *prog_kmem = NULL; 3413 void (*mc_suspend)(void); 3414 void (*mc_resume)(void); 3415 int (*scf_fmem_start)(int, int); 3416 int (*scf_fmem_end)(void); 3417 int (*scf_fmem_cancel)(void); 3418 uint64_t (*scf_get_base_addr)(void); 3419 3420 if (!DRMACH_IS_MEM_ID(s_id)) 3421 return (drerr_new(0, EOPL_INAPPROP, NULL)); 3422 if (!DRMACH_IS_MEM_ID(t_id)) 3423 return (drerr_new(0, EOPL_INAPPROP, NULL)); 3424 3425 for (i = 0; i < NCPU; i++) { 3426 int lsb_id, onb_core_num, strand_id; 3427 drmach_board_t *bp; 3428 3429 /* 3430 * this kind of CPU will spin in cache 3431 */ 3432 if (CPU_IN_SET(cpu_ready_set, i)) 3433 continue; 3434 3435 /* 3436 * Now check for any inactive CPU's that 3437 * have been hotadded. This can only occur in 3438 * error condition in drmach_cpu_poweron(). 3439 */ 3440 lsb_id = LSB_ID(i); 3441 onb_core_num = ON_BOARD_CORE_NUM(i); 3442 strand_id = STRAND_ID(i); 3443 bp = drmach_get_board_by_bnum(lsb_id); 3444 if (bp == NULL) 3445 continue; 3446 if (bp->cores[onb_core_num].core_hotadded & 3447 (1 << strand_id)) { 3448 if (!(bp->cores[onb_core_num].core_started & 3449 (1 << strand_id))) { 3450 return (drerr_new(1, EOPL_CPU_STATE, NULL)); 3451 } 3452 } 3453 } 3454 3455 mc_suspend = (void (*)(void)) 3456 modgetsymvalue("opl_mc_suspend", 0); 3457 mc_resume = (void (*)(void)) 3458 modgetsymvalue("opl_mc_resume", 0); 3459 3460 if (mc_suspend == NULL || mc_resume == NULL) { 3461 return (drerr_new(1, EOPL_MC_OPL, NULL)); 3462 } 3463 3464 scf_fmem_start = (int (*)(int, int)) 3465 modgetsymvalue("scf_fmem_start", 0); 3466 if (scf_fmem_start == NULL) { 3467 return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 3468 } 3469 scf_fmem_end = (int (*)(void)) 3470 modgetsymvalue("scf_fmem_end", 0); 3471 if (scf_fmem_end == NULL) { 3472 return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 3473 } 3474 scf_fmem_cancel = (int (*)(void)) 3475 modgetsymvalue("scf_fmem_cancel", 0); 3476 if (scf_fmem_cancel == NULL) { 3477 return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 3478 } 3479 scf_get_base_addr = (uint64_t (*)(void)) 3480 modgetsymvalue("scf_get_base_addr", 0); 3481 if (scf_get_base_addr == NULL) { 3482 return (drerr_new(1, EOPL_SCF_FMEM, NULL)); 3483 } 3484 s_mem = s_id; 3485 t_mem = t_id; 3486 3487 s_bd = s_mem->dev.bp->bnum; 3488 t_bd = t_mem->dev.bp->bnum; 3489 3490 /* calculate source and target base pa */ 3491 3492 s_copybasepa = s_mem->slice_base; 3493 t_copybasepa = t_mem->slice_base; 3494 3495 /* adjust copy memlist addresses to be relative to copy base pa */ 3496 x_ml = c_ml; 3497 while (x_ml != NULL) { 3498 x_ml->address -= s_copybasepa; 3499 x_ml = x_ml->next; 3500 } 3501 3502 /* 3503 * bp will be page aligned, since we're calling 3504 * kmem_zalloc() with an exact multiple of PAGESIZE. 3505 */ 3506 3507 prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc( 3508 DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP); 3509 3510 prog_kmem->prog = prog_kmem; 3511 3512 /* 3513 * To avoid MTLB hit, we allocate a new VM space and remap 3514 * the kmem_alloc buffer to that address. This solves 3515 * 2 problems we found: 3516 * - the kmem_alloc buffer can be just a chunk inside 3517 * a much larger, e.g. 4MB buffer and MTLB will occur 3518 * if there are both a 4MB and a 8K TLB mapping to 3519 * the same VA range. 3520 * - the kmem mapping got dropped into the TLB by other 3521 * strands, unintentionally. 3522 * Note that the pointers like data, critical, memlist_buffer, 3523 * and stat inside the copy rename structure are mapped to this 3524 * alternate VM space so we must make sure we lock the TLB mapping 3525 * whenever we access data pointed to by these pointers. 3526 */ 3527 3528 prog = prog_kmem->locked_prog = vmem_alloc(heap_arena, 3529 DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP); 3530 wp = bp = (caddr_t)prog; 3531 3532 /* Now remap prog_kmem to prog */ 3533 drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog); 3534 3535 /* All pointers in prog are based on the alternate mapping */ 3536 prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog + 3537 sizeof (drmach_copy_rename_program_t)), sizeof (void *)); 3538 3539 ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t)) 3540 <= ((uint64_t)prog + PAGESIZE)); 3541 3542 prog->critical = (drmach_copy_rename_critical_t *) 3543 (wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE); 3544 3545 prog->memlist_buffer = (caddr_t)(wp + 3546 DRMACH_FMEM_MLIST_PAGE * PAGESIZE); 3547 3548 prog->stat = (drmach_cr_stat_t *)(wp + 3549 DRMACH_FMEM_STAT_PAGE * PAGESIZE); 3550 3551 /* LINTED */ 3552 ASSERT(sizeof (drmach_cr_stat_t) 3553 <= ((DRMACH_FMEM_LOCKED_PAGES - DRMACH_FMEM_STAT_PAGE) 3554 * PAGESIZE)); 3555 3556 prog->critical->scf_reg_base = (uint64_t)-1; 3557 prog->critical->scf_td[0] = (s_bd & 0xff); 3558 prog->critical->scf_td[1] = (t_bd & 0xff); 3559 for (i = 2; i < 15; i++) { 3560 prog->critical->scf_td[i] = 0; 3561 } 3562 prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff); 3563 3564 bp = (caddr_t)prog->critical; 3565 len = sizeof (drmach_copy_rename_critical_t); 3566 wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *)); 3567 3568 len = (uint_t)((ulong_t)drmach_copy_rename_end - 3569 (ulong_t)drmach_copy_rename_prog__relocatable); 3570 3571 /* 3572 * We always leave 1K nop's to prevent the processor from 3573 * speculative execution that causes memory access 3574 */ 3575 wp = wp + len + 1024; 3576 3577 len = (uint_t)((ulong_t)drmach_fmem_exec_script_end - 3578 (ulong_t)drmach_fmem_exec_script); 3579 /* this is the entry point of the loop script */ 3580 wp = wp + len + 1024; 3581 3582 len = (uint_t)((ulong_t)drmach_fmem_exec_script - 3583 (ulong_t)drmach_fmem_loop_script); 3584 wp = wp + len + 1024; 3585 3586 /* now we make sure there is 1K extra */ 3587 3588 if ((wp - bp) > PAGESIZE) { 3589 err = drerr_new(1, EOPL_FMEM_SETUP, NULL); 3590 goto out; 3591 } 3592 3593 bp = (caddr_t)prog->critical; 3594 len = sizeof (drmach_copy_rename_critical_t); 3595 wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *)); 3596 3597 prog->critical->run = (int (*)())(wp); 3598 len = (uint_t)((ulong_t)drmach_copy_rename_end - 3599 (ulong_t)drmach_copy_rename_prog__relocatable); 3600 3601 bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len); 3602 3603 wp = (caddr_t)roundup((uint64_t)wp + len, 1024); 3604 3605 prog->critical->fmem = (int (*)())(wp); 3606 len = (int)((ulong_t)drmach_fmem_exec_script_end - 3607 (ulong_t)drmach_fmem_exec_script); 3608 bcopy((caddr_t)drmach_fmem_exec_script, wp, len); 3609 3610 len = (int)((ulong_t)drmach_fmem_exec_script_end - 3611 (ulong_t)drmach_fmem_exec_script); 3612 wp = (caddr_t)roundup((uint64_t)wp + len, 1024); 3613 3614 prog->critical->loop = (int (*)())(wp); 3615 len = (int)((ulong_t)drmach_fmem_exec_script - 3616 (ulong_t)drmach_fmem_loop_script); 3617 bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len); 3618 len = (int)((ulong_t)drmach_fmem_loop_script_rtn- 3619 (ulong_t)drmach_fmem_loop_script); 3620 prog->critical->loop_rtn = (void (*)()) (wp+len); 3621 3622 prog->data->fmem_status.error = ESBD_NOERROR; 3623 3624 /* now we are committed, call SCF, soft suspend mac patrol */ 3625 if ((*scf_fmem_start)(s_bd, t_bd)) { 3626 err = drerr_new(1, EOPL_SCF_FMEM_START, NULL); 3627 goto out; 3628 } 3629 prog->data->scf_fmem_end = scf_fmem_end; 3630 prog->data->scf_fmem_cancel = scf_fmem_cancel; 3631 prog->data->scf_get_base_addr = scf_get_base_addr; 3632 prog->data->fmem_status.op |= OPL_FMEM_SCF_START; 3633 3634 /* soft suspend mac patrol */ 3635 (*mc_suspend)(); 3636 prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND; 3637 prog->data->mc_resume = mc_resume; 3638 3639 prog->critical->inst_loop_ret = 3640 *(uint64_t *)(prog->critical->loop_rtn); 3641 3642 /* 3643 * 0x30800000 is op code "ba,a +0" 3644 */ 3645 3646 *(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000); 3647 3648 /* 3649 * set the value of SCF FMEM TIMEOUT 3650 */ 3651 prog->critical->delay = fmem_timeout * system_clock_freq; 3652 3653 prog->data->s_mem = (drmachid_t)s_mem; 3654 prog->data->t_mem = (drmachid_t)t_mem; 3655 3656 cpuid = CPU->cpu_id; 3657 prog->data->cpuid = cpuid; 3658 prog->data->cpu_ready_set = cpu_ready_set; 3659 prog->data->cpu_slave_set = cpu_ready_set; 3660 prog->data->slowest_cpuid = (processorid_t)-1; 3661 prog->data->copy_wait_time = 0; 3662 CPUSET_DEL(prog->data->cpu_slave_set, cpuid); 3663 3664 for (i = 0; i < NCPU; i++) { 3665 prog->data->cpu_ml[i] = NULL; 3666 } 3667 3668 active_cpus = 0; 3669 if (drmach_disable_mcopy) { 3670 active_cpus = 1; 3671 CPUSET_ADD(prog->data->cpu_copy_set, cpuid); 3672 } else { 3673 for (i = 0; i < NCPU; i++) { 3674 if (CPU_IN_SET(cpu_ready_set, i) && 3675 CPU_ACTIVE(cpu[i])) { 3676 CPUSET_ADD(prog->data->cpu_copy_set, i); 3677 active_cpus++; 3678 } 3679 } 3680 } 3681 3682 drmach_setup_memlist(prog); 3683 3684 x_ml = c_ml; 3685 sz = 0; 3686 while (x_ml != NULL) { 3687 sz += x_ml->size; 3688 x_ml = x_ml->next; 3689 } 3690 3691 copy_sz = sz/active_cpus; 3692 copy_sz = roundup(copy_sz, MMU_PAGESIZE4M); 3693 3694 while (sz > copy_sz*active_cpus) { 3695 copy_sz += MMU_PAGESIZE4M; 3696 } 3697 3698 prog->data->stick_freq = system_clock_freq; 3699 prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) * 3700 system_clock_freq; 3701 3702 x_ml = c_ml; 3703 c_addr = x_ml->address; 3704 c_size = x_ml->size; 3705 3706 for (i = 0; i < NCPU; i++) { 3707 prog->stat->nbytes[i] = 0; 3708 if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) { 3709 continue; 3710 } 3711 sz = copy_sz; 3712 3713 while (sz) { 3714 if (c_size > sz) { 3715 prog->data->cpu_ml[i] = 3716 drmach_memlist_add_span(prog, 3717 prog->data->cpu_ml[i], 3718 c_addr, sz); 3719 c_addr += sz; 3720 c_size -= sz; 3721 break; 3722 } else { 3723 sz -= c_size; 3724 prog->data->cpu_ml[i] = drmach_memlist_add_span( 3725 prog, prog->data->cpu_ml[i], 3726 c_addr, c_size); 3727 x_ml = x_ml->next; 3728 if (x_ml != NULL) { 3729 c_addr = x_ml->address; 3730 c_size = x_ml->size; 3731 } else { 3732 goto end; 3733 } 3734 } 3735 } 3736 } 3737 end: 3738 prog->data->s_copybasepa = s_copybasepa; 3739 prog->data->t_copybasepa = t_copybasepa; 3740 prog->data->c_ml = c_ml; 3741 *pgm_id = prog_kmem; 3742 3743 /* Unmap the alternate space. It will have to be remapped again */ 3744 drmach_unlock_critical((caddr_t)prog); 3745 return (NULL); 3746 out: 3747 if (prog != NULL) { 3748 drmach_unlock_critical((caddr_t)prog); 3749 vmem_free(heap_arena, prog, 3750 DRMACH_FMEM_LOCKED_PAGES * PAGESIZE); 3751 } 3752 if (prog_kmem != NULL) { 3753 kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE); 3754 } 3755 return (err); 3756 } 3757 3758 sbd_error_t * 3759 drmach_copy_rename_fini(drmachid_t id) 3760 { 3761 drmach_copy_rename_program_t *prog = id; 3762 sbd_error_t *err = NULL; 3763 int rv; 3764 uint_t fmem_error; 3765 3766 /* 3767 * Note that we have to delay calling SCF to find out the 3768 * status of the FMEM operation here because SCF cannot 3769 * respond while it is suspended. 3770 * This create a small window when we are sure about the 3771 * base address of the system board. 3772 * If there is any call to mc-opl to get memory unum, 3773 * mc-opl will return UNKNOWN as the unum. 3774 */ 3775 3776 /* 3777 * we have to remap again because all the pointer like data, 3778 * critical in prog are based on the alternate vmem space. 3779 */ 3780 (void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog); 3781 3782 if (prog->data->c_ml != NULL) 3783 memlist_delete(prog->data->c_ml); 3784 3785 if ((prog->data->fmem_status.op & 3786 (OPL_FMEM_SCF_START| OPL_FMEM_MC_SUSPEND)) != 3787 (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) { 3788 cmn_err(CE_PANIC, "drmach_copy_rename_fini: " 3789 "invalid op code %x\n", 3790 prog->data->fmem_status.op); 3791 } 3792 3793 fmem_error = prog->data->fmem_status.error; 3794 if (fmem_error != ESBD_NOERROR) { 3795 err = drerr_new(1, fmem_error, NULL); 3796 } 3797 3798 /* possible ops are SCF_START, MC_SUSPEND */ 3799 if (prog->critical->fmem_issued) { 3800 if (fmem_error != ESBD_NOERROR) { 3801 cmn_err(CE_PANIC, "Irrecoverable FMEM error %d\n", 3802 fmem_error); 3803 } 3804 rv = (*prog->data->scf_fmem_end)(); 3805 if (rv) { 3806 cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv); 3807 } 3808 /* 3809 * If we get here, rename is successful. 3810 * Do all the copy rename post processing. 3811 */ 3812 drmach_swap_pa((drmach_mem_t *)prog->data->s_mem, 3813 (drmach_mem_t *)prog->data->t_mem); 3814 } else { 3815 rv = (*prog->data->scf_fmem_cancel)(); 3816 if (rv) { 3817 cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x", rv); 3818 if (!err) 3819 err = drerr_new(1, EOPL_SCF_FMEM_CANCEL, 3820 "scf_fmem_cancel() failed. rv = 0x%x", rv); 3821 } 3822 } 3823 /* soft resume mac patrol */ 3824 (*prog->data->mc_resume)(); 3825 3826 drmach_unlock_critical((caddr_t)prog->locked_prog); 3827 3828 vmem_free(heap_arena, prog->locked_prog, 3829 DRMACH_FMEM_LOCKED_PAGES * PAGESIZE); 3830 kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE); 3831 return (err); 3832 } 3833 3834 /*ARGSUSED*/ 3835 static void 3836 drmach_copy_rename_slave(struct regs *rp, drmachid_t id) 3837 { 3838 drmach_copy_rename_program_t *prog = 3839 (drmach_copy_rename_program_t *)id; 3840 register int cpuid; 3841 extern void drmach_flush(); 3842 extern void membar_sync_il(); 3843 extern void drmach_flush_icache(); 3844 on_trap_data_t otd; 3845 3846 cpuid = CPU->cpu_id; 3847 3848 if (on_trap(&otd, OT_DATA_EC)) { 3849 no_trap(); 3850 prog->data->error[cpuid] = EOPL_FMEM_COPY_ERROR; 3851 prog->critical->stat[cpuid] = FMEM_LOOP_EXIT; 3852 drmach_flush_icache(); 3853 membar_sync_il(); 3854 return; 3855 } 3856 3857 3858 /* 3859 * jmp drmach_copy_rename_prog(). 3860 */ 3861 3862 drmach_flush(prog->critical, PAGESIZE); 3863 (void) prog->critical->run(prog, cpuid); 3864 drmach_flush_icache(); 3865 3866 no_trap(); 3867 3868 prog->critical->stat[cpuid] = FMEM_LOOP_EXIT; 3869 3870 membar_sync_il(); 3871 } 3872 3873 static void 3874 drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem) 3875 { 3876 uint64_t s_base, t_base; 3877 drmach_board_t *s_board, *t_board; 3878 struct memlist *ml; 3879 3880 s_board = s_mem->dev.bp; 3881 t_board = t_mem->dev.bp; 3882 if (s_board == NULL || t_board == NULL) { 3883 cmn_err(CE_PANIC, "Cannot locate source or target board\n"); 3884 return; 3885 } 3886 s_base = s_mem->slice_base; 3887 t_base = t_mem->slice_base; 3888 3889 s_mem->slice_base = t_base; 3890 s_mem->base_pa = (s_mem->base_pa - s_base) + t_base; 3891 3892 for (ml = s_mem->memlist; ml; ml = ml->next) { 3893 ml->address = ml->address - s_base + t_base; 3894 } 3895 3896 t_mem->slice_base = s_base; 3897 t_mem->base_pa = (t_mem->base_pa - t_base) + s_base; 3898 3899 for (ml = t_mem->memlist; ml; ml = ml->next) { 3900 ml->address = ml->address - t_base + s_base; 3901 } 3902 3903 /* 3904 * IKP has to update the sb-mem-ranges for mac patrol driver 3905 * when it resumes, it will re-read the sb-mem-range property 3906 * to get the new base address 3907 */ 3908 if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0) 3909 cmn_err(CE_PANIC, "Could not update device nodes\n"); 3910 } 3911 3912 void 3913 drmach_copy_rename(drmachid_t id) 3914 { 3915 drmach_copy_rename_program_t *prog_kmem = id; 3916 drmach_copy_rename_program_t *prog; 3917 cpuset_t cpuset; 3918 int cpuid; 3919 uint64_t inst; 3920 register int rtn; 3921 extern int in_sync; 3922 int old_in_sync; 3923 extern void drmach_sys_trap(); 3924 extern void drmach_flush(); 3925 extern void drmach_flush_icache(); 3926 extern uint64_t patch_inst(uint64_t *, uint64_t); 3927 on_trap_data_t otd; 3928 3929 3930 prog = prog_kmem->locked_prog; 3931 3932 3933 /* 3934 * We must immediately drop in the TLB because all pointers 3935 * are based on the alternate vmem space. 3936 */ 3937 3938 (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog); 3939 3940 /* 3941 * we call scf to get the base address here becuase if scf 3942 * has not been suspended yet, the active path can be changing and 3943 * sometimes it is not even mapped. We call the interface when 3944 * the OS has been quiesced. 3945 */ 3946 prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)(); 3947 3948 if (prog->critical->scf_reg_base == (uint64_t)-1 || 3949 prog->critical->scf_reg_base == NULL) { 3950 prog->data->fmem_status.error = EOPL_FMEM_SCF_ERR; 3951 drmach_unlock_critical((caddr_t)prog); 3952 return; 3953 } 3954 3955 cpuset = prog->data->cpu_ready_set; 3956 3957 for (cpuid = 0; cpuid < NCPU; cpuid++) { 3958 if (CPU_IN_SET(cpuset, cpuid)) { 3959 prog->critical->stat[cpuid] = FMEM_LOOP_START; 3960 prog->data->error[cpuid] = ESBD_NOERROR; 3961 } 3962 } 3963 3964 old_in_sync = in_sync; 3965 in_sync = 1; 3966 cpuid = CPU->cpu_id; 3967 3968 CPUSET_DEL(cpuset, cpuid); 3969 3970 for (cpuid = 0; cpuid < NCPU; cpuid++) { 3971 if (CPU_IN_SET(cpuset, cpuid)) { 3972 xc_one(cpuid, (xcfunc_t *)drmach_lock_critical, 3973 (uint64_t)prog_kmem, (uint64_t)prog); 3974 } 3975 } 3976 3977 cpuid = CPU->cpu_id; 3978 3979 xt_some(cpuset, (xcfunc_t *)drmach_sys_trap, 3980 (uint64_t)drmach_copy_rename_slave, 3981 (uint64_t)prog); 3982 xt_sync(cpuset); 3983 3984 if (on_trap(&otd, OT_DATA_EC)) { 3985 rtn = EOPL_FMEM_COPY_ERROR; 3986 drmach_flush_icache(); 3987 goto done; 3988 } 3989 3990 /* 3991 * jmp drmach_copy_rename_prog(). 3992 */ 3993 3994 drmach_flush(prog->critical, PAGESIZE); 3995 rtn = prog->critical->run(prog, cpuid); 3996 drmach_flush_icache(); 3997 3998 3999 done: 4000 no_trap(); 4001 if (rtn == EOPL_FMEM_HW_ERROR) { 4002 kpreempt_enable(); 4003 prom_panic("URGENT_ERROR_TRAP is " 4004 "detected during FMEM.\n"); 4005 } 4006 4007 /* 4008 * In normal case, all slave CPU's are still spinning in 4009 * the assembly code. The master has to patch the instruction 4010 * to get them out. 4011 * In error case, e.g. COPY_ERROR, some slave CPU's might 4012 * have aborted and already returned and sset LOOP_EXIT status. 4013 * Some CPU might still be copying. 4014 * In any case, some delay is necessary to give them 4015 * enough time to set the LOOP_EXIT status. 4016 */ 4017 4018 for (;;) { 4019 inst = patch_inst((uint64_t *)prog->critical->loop_rtn, 4020 prog->critical->inst_loop_ret); 4021 if (prog->critical->inst_loop_ret == inst) { 4022 break; 4023 } 4024 } 4025 4026 for (cpuid = 0; cpuid < NCPU; cpuid++) { 4027 uint64_t last, now; 4028 if (!CPU_IN_SET(cpuset, cpuid)) { 4029 continue; 4030 } 4031 last = prog->stat->nbytes[cpuid]; 4032 /* 4033 * Wait for all CPU to exit. 4034 * However we do not want an infinite loop 4035 * so we detect hangup situation here. 4036 * If the slave CPU is still copying data, 4037 * we will continue to wait. 4038 * In error cases, the master has already set 4039 * fmem_status.error to abort the copying. 4040 * 1 m.s delay for them to abort copying and 4041 * return to drmach_copy_rename_slave to set 4042 * FMEM_LOOP_EXIT status should be enough. 4043 */ 4044 for (;;) { 4045 if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT) 4046 break; 4047 drmach_sleep_il(); 4048 drv_usecwait(1000); 4049 now = prog->stat->nbytes[cpuid]; 4050 if (now <= last) { 4051 drv_usecwait(1000); 4052 if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT) 4053 break; 4054 cmn_err(CE_PANIC, 4055 "CPU %d hang during Copy Rename", cpuid); 4056 } 4057 last = now; 4058 } 4059 if (prog->data->error[cpuid] == EOPL_FMEM_HW_ERROR) { 4060 prom_panic("URGENT_ERROR_TRAP is " 4061 "detected during FMEM.\n"); 4062 } 4063 } 4064 4065 /* 4066 * This must be done after all strands have exit. 4067 * Removing the TLB entry will affect both strands 4068 * in the same core. 4069 */ 4070 4071 for (cpuid = 0; cpuid < NCPU; cpuid++) { 4072 if (CPU_IN_SET(cpuset, cpuid)) { 4073 xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical, 4074 (uint64_t)prog, 0); 4075 } 4076 } 4077 4078 in_sync = old_in_sync; 4079 4080 /* 4081 * we should unlock before the following lock to keep the kpreempt 4082 * count correct. 4083 */ 4084 (void) drmach_unlock_critical((caddr_t)prog); 4085 4086 /* 4087 * we must remap again. TLB might have been removed in above xcall. 4088 */ 4089 4090 (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog); 4091 4092 if (prog->data->fmem_status.error == ESBD_NOERROR) 4093 prog->data->fmem_status.error = rtn; 4094 4095 if (prog->data->copy_wait_time > 0) { 4096 DRMACH_PR("Unexpected long wait time %ld seconds " 4097 "during copy rename on CPU %d\n", 4098 prog->data->copy_wait_time/prog->data->stick_freq, 4099 prog->data->slowest_cpuid); 4100 } 4101 drmach_unlock_critical((caddr_t)prog); 4102 } 4103