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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/systm.h> 31 #include <sys/ddi.h> 32 #include <sys/sunddi.h> 33 #include <sys/ddi_impldefs.h> 34 #include <sys/obpdefs.h> 35 #include <sys/errno.h> 36 #include <sys/kmem.h> 37 #include <sys/vmem.h> 38 #include <sys/debug.h> 39 #include <sys/sysmacros.h> 40 #include <sys/machsystm.h> 41 #include <sys/machparam.h> 42 #include <sys/modctl.h> 43 #include <sys/fhc.h> 44 #include <sys/ac.h> 45 #include <sys/vm.h> 46 #include <sys/cpu_module.h> 47 #include <vm/seg_kmem.h> 48 #include <vm/hat_sfmmu.h> 49 #include <sys/mem_config.h> 50 #include <sys/mem_cage.h> 51 52 extern ac_err_t ac_kpm_err_cvt(int); 53 54 int ac_del_clean = 0; 55 56 /* 57 * Default timeout, in seconds, for delete. 58 * Time is counted when no progress is being made. 59 */ 60 static int ac_del_timeout = 60; 61 62 #define DEL_PAGESIZE MMU_PAGESIZE 63 64 struct del_status { 65 struct del_status *next; 66 memhandle_t handle; 67 volatile int its_done; 68 int done_error; 69 kcondvar_t ac_del_cv; 70 int del_timeout; 71 int del_noprogress; 72 ac_err_t cancel_code; 73 timeout_id_t to_id; 74 pgcnt_t last_collected; 75 }; 76 static struct del_status *ac_del_list; 77 static kmutex_t ac_del_mutex; 78 79 static struct del_status * 80 ac_del_alloc_status() 81 { 82 struct del_status *dsp; 83 84 dsp = (struct del_status *)kmem_zalloc(sizeof (*dsp), KM_SLEEP); 85 mutex_enter(&ac_del_mutex); 86 dsp->next = ac_del_list; 87 ac_del_list = dsp; 88 mutex_exit(&ac_del_mutex); 89 90 return (dsp); 91 } 92 93 static void 94 ac_del_free_status(struct del_status *dsp) 95 { 96 struct del_status **dspp; 97 98 mutex_enter(&ac_del_mutex); 99 dspp = &ac_del_list; 100 while (*dspp != NULL) { 101 if (*dspp == dsp) 102 break; 103 dspp = &(*dspp)->next; 104 } 105 ASSERT(*dspp == dsp); 106 if (*dspp == dsp) { 107 *dspp = dsp->next; 108 } 109 mutex_exit(&ac_del_mutex); 110 kmem_free((void *)dsp, sizeof (*dsp)); 111 } 112 113 static void 114 del_comp(void *arg, int error) 115 { 116 struct del_status *dsp; 117 118 dsp = (struct del_status *)arg; 119 mutex_enter(&ac_del_mutex); 120 #ifdef DEBUG 121 { 122 struct del_status *adsp; 123 for (adsp = ac_del_list; adsp != NULL; adsp = adsp->next) { 124 if (adsp == dsp) 125 break; 126 } 127 ASSERT(adsp != NULL); 128 } 129 #endif /* DEBUG */ 130 dsp->its_done = 1; 131 dsp->done_error = error; 132 cv_signal(&dsp->ac_del_cv); 133 mutex_exit(&ac_del_mutex); 134 } 135 136 /*ARGSUSED*/ 137 static void 138 del_to_scan(void *arg) 139 { 140 struct del_status *dsp; 141 int do_cancel; 142 memdelstat_t dstat; 143 int err; 144 145 dsp = arg; 146 147 #ifdef DEBUG 148 { 149 struct del_status *adsp; 150 151 mutex_enter(&ac_del_mutex); 152 for (adsp = ac_del_list; adsp != NULL; adsp = adsp->next) { 153 if (adsp == dsp) 154 break; 155 } 156 ASSERT(adsp != NULL); 157 mutex_exit(&ac_del_mutex); 158 } 159 #endif /* DEBUG */ 160 do_cancel = 0; 161 err = kphysm_del_status(dsp->handle, &dstat); 162 mutex_enter(&ac_del_mutex); 163 if (dsp->its_done) { 164 mutex_exit(&ac_del_mutex); 165 return; 166 } 167 if ((err == KPHYSM_OK) && 168 (dsp->last_collected != dstat.collected)) { 169 dsp->del_noprogress = 0; 170 dsp->last_collected = dstat.collected; 171 } else { 172 dsp->del_noprogress++; 173 if (dsp->del_noprogress >= dsp->del_timeout) { 174 if (dsp->cancel_code == 0) 175 dsp->cancel_code = AC_ERR_TIMEOUT; 176 do_cancel = 1; 177 } 178 } 179 if (!do_cancel) 180 dsp->to_id = timeout(del_to_scan, arg, hz); 181 else 182 dsp->to_id = 0; 183 mutex_exit(&ac_del_mutex); 184 if (do_cancel) 185 (void) kphysm_del_cancel(dsp->handle); 186 } 187 188 static void 189 del_to_start(struct del_status *dsp) 190 { 191 if (dsp->del_timeout != 0) 192 dsp->to_id = timeout(del_to_scan, dsp, hz); 193 } 194 195 static void 196 del_to_stop(struct del_status *dsp) 197 { 198 timeout_id_t tid; 199 200 while ((tid = dsp->to_id) != 0) { 201 dsp->to_id = 0; 202 mutex_exit(&ac_del_mutex); 203 (void) untimeout(tid); 204 mutex_enter(&ac_del_mutex); 205 } 206 } 207 208 static int 209 ac_del_bank_add_span( 210 memhandle_t handle, 211 ac_cfga_pkt_t *pkt) 212 { 213 uint64_t decode; 214 uint64_t base_pa; 215 uint64_t bank_size; 216 pfn_t base; 217 pgcnt_t npgs; 218 int errs; 219 int ret; 220 struct ac_soft_state *asp = pkt->softsp; 221 uint_t ilv; 222 223 /* 224 * Cannot delete interleaved banks at the moment. 225 */ 226 ilv = (pkt->bank == Bank0) ? 227 INTLV0(*asp->ac_memctl) : INTLV1(*asp->ac_memctl); 228 if (ilv != 1) { 229 AC_ERR_SET(pkt, AC_ERR_MEM_DEINTLV); 230 return (EINVAL); 231 } 232 /* 233 * Determine the physical location of the selected bank 234 */ 235 decode = (pkt->bank == Bank0) ? 236 *asp->ac_memdecode0 : *asp->ac_memdecode1; 237 base_pa = GRP_REALBASE(decode); 238 bank_size = GRP_UK2SPAN(decode); 239 240 base = base_pa >> PAGESHIFT; 241 npgs = bank_size >> PAGESHIFT; 242 243 /* 244 * Delete the pages from the cage growth list. 245 */ 246 ret = kcage_range_delete(base, npgs); 247 if (ret != 0) { 248 /* TODO: Should this be a separate error? */ 249 AC_ERR_SET(pkt, AC_ERR_KPM_NONRELOC); 250 return (EINVAL); 251 } 252 253 /* 254 * Add to delete memory list. 255 */ 256 257 if ((errs = kphysm_del_span(handle, base, npgs)) != KPHYSM_OK) { 258 AC_ERR_SET(pkt, ac_kpm_err_cvt(errs)); 259 /* 260 * Restore the pages to the cage growth list. 261 * TODO: We should not unconditionally add back 262 * if we conditionally add at memory add time. 263 */ 264 errs = kcage_range_add(base, npgs, KCAGE_DOWN); 265 /* TODO: deal with error return. */ 266 if (errs != 0) { 267 AC_ERR_SET(pkt, ac_kpm_err_cvt(errs)); 268 cmn_err(CE_NOTE, "ac_del_bank_add_span(): " 269 "board %d, bank %d, " 270 "kcage_range_add() returned %d", 271 pkt->softsp->board, pkt->bank, errs); 272 } 273 return (EINVAL); 274 } 275 return (0); 276 } 277 278 static void 279 ac_del_bank_add_cage( 280 struct bd_list *del, 281 enum ac_bank_id bank) 282 { 283 uint64_t decode; 284 uint64_t base_pa; 285 uint64_t bank_size; 286 pfn_t base; 287 pgcnt_t npgs; 288 int errs; 289 struct ac_soft_state *asp = (struct ac_soft_state *)(del->ac_softsp); 290 291 /* 292 * Determine the physical location of the selected bank 293 */ 294 decode = (bank == Bank0) ? *asp->ac_memdecode0 : *asp->ac_memdecode1; 295 base_pa = GRP_REALBASE(decode); 296 bank_size = GRP_UK2SPAN(decode); 297 298 base = base_pa >> PAGESHIFT; 299 npgs = bank_size >> PAGESHIFT; 300 301 /* 302 * Restore the pages to the cage growth list. 303 * TODO: We should not unconditionally add back 304 * if we conditionally add at memory add time. 305 */ 306 errs = kcage_range_add(base, npgs, KCAGE_DOWN); 307 /* TODO: deal with error return. */ 308 if (errs != 0) 309 cmn_err(CE_NOTE, "ac_del_bank_add_cage(): " 310 "board %d, bank %d, " 311 "kcage_range_add() returned %d", 312 del->sc.board, bank, errs); 313 } 314 315 static int 316 ac_del_bank_run(struct del_status *dsp, ac_cfga_pkt_t *pkt) 317 { 318 int errs; 319 320 dsp->its_done = 0; 321 if ((errs = kphysm_del_start(dsp->handle, del_comp, (void *)dsp)) != 322 KPHYSM_OK) { 323 AC_ERR_SET(pkt, ac_kpm_err_cvt(errs)); 324 return (EINVAL); 325 } 326 /* Wait for it to complete. */ 327 mutex_enter(&ac_del_mutex); 328 del_to_start(dsp); 329 while (!dsp->its_done) { 330 if (!cv_wait_sig(&dsp->ac_del_cv, &ac_del_mutex)) { 331 if (dsp->cancel_code == 0) 332 dsp->cancel_code = AC_ERR_INTR; 333 mutex_exit(&ac_del_mutex); 334 errs = kphysm_del_cancel(dsp->handle); 335 mutex_enter(&ac_del_mutex); 336 if (errs != KPHYSM_OK) { 337 ASSERT(errs == KPHYSM_ENOTRUNNING); 338 } 339 break; 340 } 341 } 342 /* 343 * If the loop exited due to a signal, we must continue to wait 344 * using cv_wait() as the signal is pending until syscall exit. 345 */ 346 while (!dsp->its_done) { 347 cv_wait(&dsp->ac_del_cv, &ac_del_mutex); 348 } 349 if (dsp->done_error != KPHYSM_OK) { 350 AC_ERR_SET(pkt, ac_kpm_err_cvt(dsp->done_error)); 351 if ((dsp->done_error == KPHYSM_ECANCELLED) || 352 (dsp->done_error == KPHYSM_EREFUSED)) { 353 errs = EINTR; 354 if (dsp->cancel_code != 0) { 355 AC_ERR_SET(pkt, dsp->cancel_code); 356 } 357 } else { 358 errs = EINVAL; 359 } 360 } else 361 errs = 0; 362 del_to_stop(dsp); 363 mutex_exit(&ac_del_mutex); 364 365 return (errs); 366 } 367 368 369 /* 370 * set the memory to known state for debugging 371 */ 372 static void 373 ac_bank_write_pattern(struct bd_list *del, enum ac_bank_id bank) 374 { 375 uint64_t decode; 376 uint64_t base_pa; 377 uint64_t limit_pa; 378 uint64_t bank_size; 379 uint64_t current_pa; 380 caddr_t base_va; 381 caddr_t fill_buf; 382 struct ac_soft_state *asp = (struct ac_soft_state *)(del->ac_softsp); 383 int linesize; 384 385 /* 386 * Determine the physical location of the selected bank 387 */ 388 decode = (bank == Bank0) ? *asp->ac_memdecode0 : *asp->ac_memdecode1; 389 base_pa = GRP_REALBASE(decode); 390 bank_size = GRP_UK2SPAN(decode); 391 limit_pa = base_pa + bank_size; 392 linesize = cpunodes[CPU->cpu_id].ecache_linesize; 393 394 /* 395 * We need a page_va and a fill buffer for this operation 396 */ 397 base_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP); 398 fill_buf = kmem_zalloc(DEL_PAGESIZE, KM_SLEEP); 399 { 400 typedef uint32_t patt_t; 401 patt_t *bf, *bfe, patt; 402 403 bf = (patt_t *)fill_buf; 404 bfe = (patt_t *)((char *)fill_buf + DEL_PAGESIZE); 405 patt = 0xbeaddeed; 406 while (bf < bfe) 407 *bf++ = patt; 408 } 409 410 /* 411 * 'empty' the memory 412 */ 413 kpreempt_disable(); 414 for (current_pa = base_pa; current_pa < limit_pa; 415 current_pa += DEL_PAGESIZE) { 416 417 /* map current pa */ 418 ac_mapin(current_pa, base_va); 419 420 /* fill the target page */ 421 ac_blkcopy(fill_buf, base_va, 422 DEL_PAGESIZE/linesize, linesize); 423 424 /* tear down translation */ 425 ac_unmap(base_va); 426 } 427 kpreempt_enable(); 428 429 /* 430 * clean up temporary resources 431 */ 432 { 433 /* Distinguish the fill buf from memory deleted! */ 434 typedef uint32_t patt_t; 435 patt_t *bf, *bfe, patt; 436 437 bf = (patt_t *)fill_buf; 438 bfe = (patt_t *)((char *)fill_buf + DEL_PAGESIZE); 439 patt = 0xbeadfeed; 440 while (bf < bfe) 441 *bf++ = patt; 442 } 443 kmem_free(fill_buf, DEL_PAGESIZE); 444 vmem_free(heap_arena, base_va, PAGESIZE); 445 } 446 447 int 448 ac_del_memory(ac_cfga_pkt_t *pkt) 449 { 450 struct bd_list *board; 451 struct ac_mem_info *mem_info; 452 int busy_set; 453 struct del_status *dsp; 454 memdelstat_t dstat; 455 int retval; 456 int r_errs; 457 struct ac_soft_state *asp; 458 459 if (!kcage_on) { 460 static int cage_msg_done = 0; 461 462 if (!cage_msg_done) { 463 cage_msg_done = 1; 464 cmn_err(CE_NOTE, "ac: memory delete" 465 " refused: cage is off"); 466 } 467 AC_ERR_SET(pkt, ac_kpm_err_cvt(KPHYSM_ENONRELOC)); 468 return (EINVAL); 469 } 470 471 dsp = ac_del_alloc_status(); 472 if ((retval = kphysm_del_gethandle(&dsp->handle)) != KPHYSM_OK) { 473 ac_del_free_status(dsp); 474 AC_ERR_SET(pkt, ac_kpm_err_cvt(retval)); 475 return (EINVAL); 476 } 477 retval = 0; 478 busy_set = 0; 479 480 board = fhc_bdlist_lock(pkt->softsp->board); 481 if (board == NULL || board->ac_softsp == NULL) { 482 fhc_bdlist_unlock(); 483 AC_ERR_SET(pkt, AC_ERR_BD); 484 retval = EINVAL; 485 goto out; 486 } 487 ASSERT(pkt->softsp == board->ac_softsp); 488 asp = pkt->softsp; 489 490 /* verify the board is of the correct type */ 491 switch (board->sc.type) { 492 case CPU_BOARD: 493 case MEM_BOARD: 494 break; 495 default: 496 fhc_bdlist_unlock(); 497 AC_ERR_SET(pkt, AC_ERR_BD_TYPE); 498 retval = EINVAL; 499 goto out; 500 } 501 502 /* verify the memory condition is acceptable */ 503 mem_info = &asp->bank[pkt->bank]; 504 if (!MEM_BOARD_VISIBLE(board) || mem_info->busy || 505 fhc_bd_busy(pkt->softsp->board) || 506 mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED || 507 mem_info->ostate != SYSC_CFGA_OSTATE_CONFIGURED) { 508 fhc_bdlist_unlock(); 509 AC_ERR_SET(pkt, AC_ERR_BD_STATE); 510 retval = EINVAL; 511 goto out; 512 } 513 514 if ((dsp->del_timeout = pkt->cmd_cfga.arg) == -1) 515 dsp->del_timeout = ac_del_timeout; 516 517 /* 518 * at this point, we have an available bank to del. 519 * mark it busy and initiate the del function. 520 */ 521 mem_info->busy = TRUE; 522 fhc_bdlist_unlock(); 523 524 busy_set = 1; 525 526 retval = ac_del_bank_add_span(dsp->handle, pkt); 527 out: 528 if (retval != 0) { 529 r_errs = kphysm_del_release(dsp->handle); 530 ASSERT(r_errs == KPHYSM_OK); 531 532 if (busy_set) { 533 board = fhc_bdlist_lock(pkt->softsp->board); 534 ASSERT(board != NULL && board->ac_softsp != NULL); 535 536 ASSERT(board->sc.type == CPU_BOARD || 537 board->sc.type == MEM_BOARD); 538 ASSERT(asp == 539 (struct ac_soft_state *)(board->ac_softsp)); 540 mem_info = &asp->bank[pkt->bank]; 541 ASSERT(mem_info->busy != FALSE); 542 ASSERT(mem_info->ostate == SYSC_CFGA_OSTATE_CONFIGURED); 543 mem_info->busy = FALSE; 544 fhc_bdlist_unlock(); 545 } 546 547 ac_del_free_status(dsp); 548 return (retval); 549 } 550 551 (void) kphysm_del_status(dsp->handle, &dstat); 552 553 retval = ac_del_bank_run(dsp, pkt); 554 555 r_errs = kphysm_del_release(dsp->handle); 556 ASSERT(r_errs == KPHYSM_OK); 557 558 board = fhc_bdlist_lock(pkt->softsp->board); 559 ASSERT(board != NULL && board->ac_softsp != NULL); 560 561 ASSERT(board->sc.type == CPU_BOARD || board->sc.type == MEM_BOARD); 562 ASSERT(asp == (struct ac_soft_state *)(board->ac_softsp)); 563 mem_info = &asp->bank[pkt->bank]; 564 ASSERT(mem_info->busy != FALSE); 565 ASSERT(mem_info->ostate == SYSC_CFGA_OSTATE_CONFIGURED); 566 mem_info->busy = FALSE; 567 if (retval == 0) { 568 mem_info->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; 569 mem_info->status_change = ddi_get_time(); 570 571 if (ac_del_clean) { 572 /* DEBUG - set memory to known state */ 573 ac_bank_write_pattern(board, pkt->bank); 574 } 575 } else { 576 /* 577 * Restore the pages to the cage growth list. 578 */ 579 ac_del_bank_add_cage(board, pkt->bank); 580 } 581 fhc_bdlist_unlock(); 582 583 ac_del_free_status(dsp); 584 585 return (retval); 586 } 587