1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright (c) 2010, Intel Corporation. 28 * All rights reserved. 29 */ 30 31 /* 32 * DR memory support routines. 33 */ 34 35 #include <sys/note.h> 36 #include <sys/debug.h> 37 #include <sys/types.h> 38 #include <sys/errno.h> 39 #include <sys/param.h> 40 #include <sys/kmem.h> 41 #include <sys/kobj.h> 42 #include <sys/conf.h> 43 #include <sys/dditypes.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/sunndi.h> 47 #include <sys/ddi_impldefs.h> 48 #include <sys/ndi_impldefs.h> 49 #include <sys/sysmacros.h> 50 #include <sys/machsystm.h> 51 #include <sys/promif.h> 52 #include <sys/lgrp.h> 53 #include <sys/mem_config.h> 54 #include <vm/seg_kmem.h> 55 #include <vm/page.h> 56 57 #include <sys/dr.h> 58 #include <sys/dr_util.h> 59 #include <sys/drmach.h> 60 61 extern struct memlist *phys_install; 62 63 /* TODO: push this reference below drmach line */ 64 extern int kcage_on; 65 66 /* for the DR*INTERNAL_ERROR macros. see sys/dr.h. */ 67 static char *dr_ie_fmt = "dr_mem_acpi.c %d"; 68 69 static void dr_init_mem_unit_data(dr_mem_unit_t *mp); 70 71 /* 72 * dr_mem_unit_t.sbm_flags 73 */ 74 #define DR_MFLAG_RESERVED 0x01 /* mem unit reserved for delete */ 75 #define DR_MFLAG_SOURCE 0x02 /* source brd of copy/rename op */ 76 #define DR_MFLAG_TARGET 0x04 /* target brd of copy/rename op */ 77 #define DR_MFLAG_RELOWNER 0x20 /* memory release (delete) owner */ 78 #define DR_MFLAG_RELDONE 0x40 /* memory release (delete) done */ 79 80 /* helper macros */ 81 #define _ptob64(p) ((uint64_t)(p) << PAGESHIFT) 82 #define _b64top(b) ((pgcnt_t)((b) >> PAGESHIFT)) 83 84 static struct memlist * 85 dr_get_memlist(dr_mem_unit_t *mp) 86 { 87 struct memlist *mlist = NULL; 88 sbd_error_t *err; 89 static fn_t f = "dr_get_memlist"; 90 91 PR_MEM("%s for %s...\n", f, mp->sbm_cm.sbdev_path); 92 93 /* 94 * Return cached memlist, if present. 95 * This memlist will be present following an 96 * unconfigure (a.k.a: detach) of this memunit. 97 * It should only be used in the case were a configure 98 * is bringing this memunit back in without going 99 * through the disconnect and connect states. 100 */ 101 if (mp->sbm_mlist) { 102 PR_MEM("%s: found cached memlist\n", f); 103 104 mlist = memlist_dup(mp->sbm_mlist); 105 } else { 106 uint64_t basepa = _ptob64(mp->sbm_basepfn); 107 108 /* attempt to construct a memlist using phys_install */ 109 110 /* round down to slice base address */ 111 basepa &= ~mp->sbm_alignment_mask; 112 113 /* get a copy of phys_install to edit */ 114 memlist_read_lock(); 115 mlist = memlist_dup(phys_install); 116 memlist_read_unlock(); 117 118 /* trim lower irrelevant span */ 119 if (mlist) 120 mlist = memlist_del_span(mlist, 0ull, basepa); 121 122 /* trim upper irrelevant span */ 123 if (mlist) { 124 uint64_t endpa, toppa; 125 126 toppa = mp->sbm_slice_top; 127 endpa = _ptob64(physmax + 1); 128 if (endpa > toppa) 129 mlist = memlist_del_span( 130 mlist, toppa, 131 endpa - toppa); 132 } 133 134 if (mlist) { 135 /* successfully built a memlist */ 136 PR_MEM("%s: derived memlist from phys_install\n", f); 137 } 138 139 /* if no mlist yet, try platform layer */ 140 if (!mlist) { 141 err = drmach_mem_get_memlist( 142 mp->sbm_cm.sbdev_id, &mlist); 143 if (err) { 144 DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err); 145 mlist = NULL; /* paranoia */ 146 } 147 } 148 } 149 150 PR_MEM("%s: memlist for %s\n", f, mp->sbm_cm.sbdev_path); 151 PR_MEMLIST_DUMP(mlist); 152 153 return (mlist); 154 } 155 156 /*ARGSUSED*/ 157 void 158 dr_release_mem(dr_common_unit_t *cp) 159 { 160 } 161 162 void 163 dr_attach_mem(dr_handle_t *hp, dr_common_unit_t *cp) 164 { 165 dr_mem_unit_t *mp = (dr_mem_unit_t *)cp; 166 struct memlist *ml, *mc; 167 sbd_error_t *err; 168 static fn_t f = "dr_attach_mem"; 169 uint64_t dr_physmax; 170 171 PR_MEM("%s...\n", f); 172 173 dr_lock_status(hp->h_bd); 174 err = drmach_configure(cp->sbdev_id, 0); 175 dr_unlock_status(hp->h_bd); 176 if (err) { 177 DRERR_SET_C(&cp->sbdev_error, &err); 178 return; 179 } 180 181 ml = dr_get_memlist(mp); 182 183 /* Skip memory with address above plat_dr_physmax or kpm_size */ 184 dr_physmax = plat_dr_physmax ? ptob(plat_dr_physmax) : UINT64_MAX; 185 if (kpm_size < dr_physmax) 186 dr_physmax = kpm_size; 187 ml = memlist_del_span(ml, dr_physmax, UINT64_MAX - dr_physmax); 188 189 for (mc = ml; mc; mc = mc->ml_next) { 190 int rv; 191 sbd_error_t *err; 192 193 rv = kphysm_add_memory_dynamic( 194 (pfn_t)btop(mc->ml_address), 195 (pgcnt_t)btop(mc->ml_size)); 196 if (rv != KPHYSM_OK) { 197 /* 198 * translate kphysm error and 199 * store in devlist error 200 */ 201 switch (rv) { 202 case KPHYSM_ERESOURCE: 203 rv = ESBD_NOMEM; 204 break; 205 206 case KPHYSM_EFAULT: 207 rv = ESBD_FAULT; 208 break; 209 210 default: 211 rv = ESBD_INTERNAL; 212 break; 213 } 214 215 if (rv == ESBD_INTERNAL) { 216 DR_DEV_INTERNAL_ERROR(&mp->sbm_cm); 217 } else 218 dr_dev_err(CE_WARN, &mp->sbm_cm, rv); 219 break; 220 } 221 222 err = drmach_mem_add_span( 223 mp->sbm_cm.sbdev_id, mc->ml_address, mc->ml_size); 224 if (err) { 225 DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err); 226 break; 227 } 228 } 229 230 memlist_delete(ml); 231 dr_init_mem_unit_data(mp); 232 233 /* back out if configure failed */ 234 if (mp->sbm_cm.sbdev_error != NULL) { 235 dr_lock_status(hp->h_bd); 236 err = drmach_unconfigure(cp->sbdev_id, 0); 237 if (err) 238 sbd_err_clear(&err); 239 dr_unlock_status(hp->h_bd); 240 } 241 } 242 243 /*ARGSUSED*/ 244 void 245 dr_detach_mem(dr_handle_t *hp, dr_common_unit_t *cp) 246 { 247 } 248 249 /* 250 * This routine acts as a wrapper for kphysm_del_span_query in order to 251 * support potential memory holes in a board's physical address space. 252 * It calls kphysm_del_span_query for each node in a memlist and accumulates 253 * the results in *mp. 254 */ 255 static int 256 dr_del_mlist_query(struct memlist *mlist, memquery_t *mp) 257 { 258 int rv = 0; 259 260 if (mlist == NULL) 261 cmn_err(CE_WARN, "dr_del_mlist_query: mlist=NULL\n"); 262 263 mp->phys_pages = 0; 264 mp->managed = 0; 265 mp->nonrelocatable = 0; 266 mp->first_nonrelocatable = 0; 267 mp->last_nonrelocatable = 0; 268 269 return (rv); 270 } 271 272 /* 273 * NOTE: This routine is only partially smart about multiple 274 * mem-units. Need to make mem-status structure smart 275 * about them also. 276 */ 277 int 278 dr_mem_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp) 279 { 280 int m, mix; 281 memquery_t mq; 282 dr_board_t *bp; 283 dr_mem_unit_t *mp; 284 sbd_mem_stat_t *msp; 285 static fn_t f = "dr_mem_status"; 286 287 bp = hp->h_bd; 288 devset &= DR_DEVS_PRESENT(bp); 289 290 for (m = mix = 0; m < MAX_MEM_UNITS_PER_BOARD; m++) { 291 int rv; 292 sbd_error_t *err; 293 drmach_status_t pstat; 294 dr_mem_unit_t *p_mp; 295 296 if (DEVSET_IN_SET(devset, SBD_COMP_MEM, m) == 0) 297 continue; 298 299 mp = dr_get_mem_unit(bp, m); 300 301 if (mp->sbm_cm.sbdev_state == DR_STATE_EMPTY) { 302 /* present, but not fully initialized */ 303 continue; 304 } 305 306 if (mp->sbm_cm.sbdev_id == (drmachid_t)0) 307 continue; 308 309 /* fetch platform status */ 310 err = drmach_status(mp->sbm_cm.sbdev_id, &pstat); 311 if (err) { 312 DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err); 313 continue; 314 } 315 316 msp = &dsp->d_mem; 317 bzero((caddr_t)msp, sizeof (*msp)); 318 319 (void) strlcpy(msp->ms_cm.c_id.c_name, pstat.type, 320 sizeof (msp->ms_cm.c_id.c_name)); 321 msp->ms_cm.c_id.c_type = mp->sbm_cm.sbdev_type; 322 msp->ms_cm.c_id.c_unit = mp->sbm_cm.sbdev_unum; 323 msp->ms_cm.c_cond = mp->sbm_cm.sbdev_cond; 324 msp->ms_cm.c_busy = mp->sbm_cm.sbdev_busy | pstat.busy; 325 msp->ms_cm.c_time = mp->sbm_cm.sbdev_time; 326 msp->ms_cm.c_ostate = mp->sbm_cm.sbdev_ostate; 327 328 msp->ms_totpages = mp->sbm_npages; 329 msp->ms_basepfn = mp->sbm_basepfn; 330 msp->ms_pageslost = mp->sbm_pageslost; 331 msp->ms_cage_enabled = kcage_on; 332 333 if (mp->sbm_flags & DR_MFLAG_RESERVED) 334 p_mp = mp->sbm_peer; 335 else 336 p_mp = NULL; 337 338 if (p_mp == NULL) { 339 msp->ms_peer_is_target = 0; 340 msp->ms_peer_ap_id[0] = '\0'; 341 } else if (p_mp->sbm_flags & DR_MFLAG_RESERVED) { 342 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 343 char *minor; 344 345 /* 346 * b_dip doesn't have to be held for ddi_pathname() 347 * because the board struct (dr_board_t) will be 348 * destroyed before b_dip detaches. 349 */ 350 (void) ddi_pathname(bp->b_dip, path); 351 minor = strchr(p_mp->sbm_cm.sbdev_path, ':'); 352 353 (void) snprintf(msp->ms_peer_ap_id, 354 sizeof (msp->ms_peer_ap_id), "%s%s", 355 path, (minor == NULL) ? "" : minor); 356 357 kmem_free(path, MAXPATHLEN); 358 359 if (p_mp->sbm_flags & DR_MFLAG_TARGET) 360 msp->ms_peer_is_target = 1; 361 } 362 363 /* 364 * kphysm_del_span_query can report non-reloc pages = total 365 * pages for memory that is not yet configured 366 */ 367 if (mp->sbm_cm.sbdev_state != DR_STATE_UNCONFIGURED) { 368 struct memlist *ml; 369 370 ml = dr_get_memlist(mp); 371 rv = ml ? dr_del_mlist_query(ml, &mq) : -1; 372 memlist_delete(ml); 373 374 if (rv == KPHYSM_OK) { 375 msp->ms_managed_pages = mq.managed; 376 msp->ms_noreloc_pages = mq.nonrelocatable; 377 msp->ms_noreloc_first = 378 mq.first_nonrelocatable; 379 msp->ms_noreloc_last = 380 mq.last_nonrelocatable; 381 msp->ms_cm.c_sflags = 0; 382 if (mq.nonrelocatable && 383 drmach_copy_rename_need_suspend( 384 mp->sbm_cm.sbdev_id)) { 385 SBD_SET_SUSPEND(SBD_CMD_UNCONFIGURE, 386 msp->ms_cm.c_sflags); 387 } 388 } else { 389 PR_MEM("%s: kphysm_del_span_query() = %d\n", 390 f, rv); 391 } 392 } 393 394 /* 395 * Check source unit state during copy-rename 396 */ 397 if ((mp->sbm_flags & DR_MFLAG_SOURCE) && 398 (mp->sbm_cm.sbdev_state == DR_STATE_UNREFERENCED || 399 mp->sbm_cm.sbdev_state == DR_STATE_RELEASE)) 400 msp->ms_cm.c_ostate = SBD_STAT_CONFIGURED; 401 402 mix++; 403 dsp++; 404 } 405 406 return (mix); 407 } 408 409 /*ARGSUSED*/ 410 int 411 dr_pre_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 412 { 413 int err_flag = 0; 414 int d; 415 sbd_error_t *err; 416 static fn_t f = "dr_pre_attach_mem"; 417 418 PR_MEM("%s...\n", f); 419 420 for (d = 0; d < devnum; d++) { 421 dr_mem_unit_t *mp = (dr_mem_unit_t *)devlist[d]; 422 dr_state_t state; 423 424 cmn_err(CE_CONT, "OS configure %s", mp->sbm_cm.sbdev_path); 425 426 state = mp->sbm_cm.sbdev_state; 427 switch (state) { 428 case DR_STATE_UNCONFIGURED: 429 PR_MEM("%s: recovering from UNCONFIG for %s\n", 430 f, mp->sbm_cm.sbdev_path); 431 432 /* use memlist cached by dr_post_detach_mem_unit */ 433 ASSERT(mp->sbm_mlist != NULL); 434 PR_MEM("%s: re-configuring cached memlist for %s:\n", 435 f, mp->sbm_cm.sbdev_path); 436 PR_MEMLIST_DUMP(mp->sbm_mlist); 437 438 /* kphysm del handle should be have been freed */ 439 ASSERT((mp->sbm_flags & DR_MFLAG_RELOWNER) == 0); 440 441 /*FALLTHROUGH*/ 442 443 case DR_STATE_CONNECTED: 444 PR_MEM("%s: reprogramming mem hardware on %s\n", 445 f, mp->sbm_cm.sbdev_bp->b_path); 446 447 PR_MEM("%s: enabling %s\n", 448 f, mp->sbm_cm.sbdev_path); 449 450 err = drmach_mem_enable(mp->sbm_cm.sbdev_id); 451 if (err) { 452 DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err); 453 err_flag = 1; 454 } 455 break; 456 457 default: 458 dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_STATE); 459 err_flag = 1; 460 break; 461 } 462 463 /* exit for loop if error encountered */ 464 if (err_flag) 465 break; 466 } 467 468 return (err_flag ? -1 : 0); 469 } 470 471 /*ARGSUSED*/ 472 int 473 dr_post_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 474 { 475 int d; 476 static fn_t f = "dr_post_attach_mem"; 477 478 PR_MEM("%s...\n", f); 479 480 for (d = 0; d < devnum; d++) { 481 dr_mem_unit_t *mp = (dr_mem_unit_t *)devlist[d]; 482 struct memlist *mlist, *ml; 483 484 mlist = dr_get_memlist(mp); 485 486 /* 487 * Verify the memory really did successfully attach 488 * by checking for its existence in phys_install. 489 */ 490 memlist_read_lock(); 491 if (memlist_intersect(phys_install, mlist) == 0) { 492 memlist_read_unlock(); 493 494 DR_DEV_INTERNAL_ERROR(&mp->sbm_cm); 495 496 PR_MEM("%s: %s memlist not in phys_install", 497 f, mp->sbm_cm.sbdev_path); 498 499 memlist_delete(mlist); 500 continue; 501 } 502 memlist_read_unlock(); 503 504 for (ml = mlist; ml != NULL; ml = ml->ml_next) { 505 sbd_error_t *err; 506 507 err = drmach_mem_add_span( 508 mp->sbm_cm.sbdev_id, 509 ml->ml_address, 510 ml->ml_size); 511 if (err) 512 DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err); 513 } 514 515 memlist_delete(mlist); 516 517 /* 518 * Destroy cached memlist, if any. 519 * There will be a cached memlist in sbm_mlist if 520 * this board is being configured directly after 521 * an unconfigure. 522 * To support this transition, dr_post_detach_mem 523 * left a copy of the last known memlist in sbm_mlist. 524 * This memlist could differ from any derived from 525 * hardware if while this memunit was last configured 526 * the system detected and deleted bad pages from 527 * phys_install. The location of those bad pages 528 * will be reflected in the cached memlist. 529 */ 530 if (mp->sbm_mlist) { 531 memlist_delete(mp->sbm_mlist); 532 mp->sbm_mlist = NULL; 533 } 534 } 535 536 return (0); 537 } 538 539 /*ARGSUSED*/ 540 int 541 dr_pre_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 542 { 543 return (-1); 544 } 545 546 /*ARGSUSED*/ 547 int 548 dr_post_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 549 { 550 return (-1); 551 } 552 553 /* 554 * Successful return from this function will have the memory 555 * handle in bp->b_dev[..mem-unit...].sbm_memhandle allocated 556 * and waiting. This routine's job is to select the memory that 557 * actually has to be released (detached) which may not necessarily 558 * be the same memory node that came in in devlist[], 559 * i.e. a copy-rename is needed. 560 */ 561 /*ARGSUSED*/ 562 int 563 dr_pre_release_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 564 { 565 return (-1); 566 } 567 568 /*ARGSUSED*/ 569 void 570 dr_release_mem_done(dr_common_unit_t *cp) 571 { 572 } 573 574 /*ARGSUSED*/ 575 int 576 dr_disconnect_mem(dr_mem_unit_t *mp) 577 { 578 return (-1); 579 } 580 581 /*ARGSUSED*/ 582 int 583 dr_cancel_mem(dr_mem_unit_t *s_mp) 584 { 585 return (-1); 586 } 587 588 void 589 dr_init_mem_unit(dr_mem_unit_t *mp) 590 { 591 dr_state_t new_state; 592 593 if (DR_DEV_IS_ATTACHED(&mp->sbm_cm)) { 594 new_state = DR_STATE_CONFIGURED; 595 mp->sbm_cm.sbdev_cond = SBD_COND_OK; 596 } else if (DR_DEV_IS_PRESENT(&mp->sbm_cm)) { 597 new_state = DR_STATE_CONNECTED; 598 mp->sbm_cm.sbdev_cond = SBD_COND_OK; 599 } else if (mp->sbm_cm.sbdev_id != (drmachid_t)0) { 600 new_state = DR_STATE_OCCUPIED; 601 } else { 602 new_state = DR_STATE_EMPTY; 603 } 604 605 if (DR_DEV_IS_PRESENT(&mp->sbm_cm)) 606 dr_init_mem_unit_data(mp); 607 608 /* delay transition until fully initialized */ 609 dr_device_transition(&mp->sbm_cm, new_state); 610 } 611 612 static void 613 dr_init_mem_unit_data(dr_mem_unit_t *mp) 614 { 615 drmachid_t id = mp->sbm_cm.sbdev_id; 616 drmach_mem_info_t minfo; 617 sbd_error_t *err; 618 static fn_t f = "dr_init_mem_unit_data"; 619 620 PR_MEM("%s...\n", f); 621 622 /* a little sanity checking */ 623 ASSERT(mp->sbm_peer == NULL); 624 ASSERT(mp->sbm_flags == 0); 625 626 if (err = drmach_mem_get_info(id, &minfo)) { 627 DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err); 628 return; 629 } 630 mp->sbm_basepfn = _b64top(minfo.mi_basepa); 631 mp->sbm_npages = _b64top(minfo.mi_size); 632 mp->sbm_alignment_mask = minfo.mi_alignment_mask; 633 mp->sbm_slice_base = minfo.mi_slice_base; 634 mp->sbm_slice_top = minfo.mi_slice_top; 635 mp->sbm_slice_size = minfo.mi_slice_size; 636 637 PR_MEM("%s: %s (basepfn = 0x%lx, npgs = %ld)\n", 638 f, mp->sbm_cm.sbdev_path, mp->sbm_basepfn, mp->sbm_npages); 639 } 640