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