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 2006 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/conf.h>
  29 #include <sys/ddi.h>
  30 #include <sys/sunddi.h>
  31 #include <sys/ddi_impldefs.h>
  32 #include <sys/obpdefs.h>
  33 #include <sys/cmn_err.h>
  34 #include <sys/errno.h>
  35 #include <sys/kmem.h>
  36 #include <sys/vmem.h>
  37 #include <sys/debug.h>
  38 #include <sys/sysmacros.h>
  39 #include <sys/machsystm.h>
  40 #include <sys/machparam.h>
  41 #include <sys/modctl.h>
  42 #include <sys/atomic.h>
  43 #include <sys/fhc.h>
  44 #include <sys/ac.h>
  45 #include <sys/jtag.h>
  46 #include <sys/cpu_module.h>
  47 #include <sys/spitregs.h>
  48 #include <sys/vm.h>
  49 #include <vm/seg_kmem.h>
  50 #include <vm/hat_sfmmu.h>
  51 
  52 /* memory setup parameters */
  53 #define TEST_PAGESIZE   MMU_PAGESIZE
  54 
  55 struct test_info {
  56         struct test_info        *next;          /* linked list of tests */
  57         struct ac_mem_info      *mem_info;
  58         uint_t                  board;
  59         uint_t                  bank;
  60         caddr_t                 bufp;           /* pointer to buffer page */
  61         caddr_t                 va;             /* test target VA */
  62         ac_mem_test_start_t     info;
  63         uint_t                  in_test;        /* count of threads in test */
  64 };
  65 
  66 /* list of tests in progress (list protected test_mutex) */
  67 static struct test_info         *test_base = NULL;
  68 static kmutex_t                 test_mutex;
  69 static int                      test_mutex_initialized = FALSE;
  70 
  71 static mem_test_handle_t        mem_test_sequence_id = 0;
  72 
  73 void
  74 ac_mapin(uint64_t pa, caddr_t va)
  75 {
  76         pfn_t   pfn;
  77         tte_t   tte;
  78 
  79         pfn = pa >> MMU_PAGESHIFT;
  80         tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
  81             TTE_PFN_INTHI(pfn);
  82         tte.tte_intlo = TTE_PFN_INTLO(pfn) | TTE_CP_INT |
  83             TTE_PRIV_INT | TTE_LCK_INT | TTE_HWWR_INT;
  84         sfmmu_dtlb_ld_kva(va, &tte);
  85 
  86 }
  87 
  88 void
  89 ac_unmap(caddr_t va)
  90 {
  91         vtag_flushpage(va, (uint64_t)ksfmmup);
  92 }
  93 
  94 int
  95 ac_mem_test_start(ac_cfga_pkt_t *pkt, int flag)
  96 {
  97         struct ac_soft_state    *softsp;
  98         struct ac_mem_info      *mem_info;
  99         struct bd_list          *board;
 100         struct test_info        *test;
 101         uint64_t                decode;
 102 
 103         /* XXX if ac ever detaches... */
 104         if (test_mutex_initialized == FALSE) {
 105                 mutex_init(&test_mutex, NULL, MUTEX_DEFAULT, NULL);
 106                 test_mutex_initialized = TRUE;
 107         }
 108 
 109         /*
 110          * Is the specified bank testable?
 111          */
 112 
 113         board = fhc_bdlist_lock(pkt->softsp->board);
 114         if (board == NULL || board->ac_softsp == NULL) {
 115                 fhc_bdlist_unlock();
 116                 AC_ERR_SET(pkt, AC_ERR_BD);
 117                 return (EINVAL);
 118         }
 119         ASSERT(pkt->softsp == board->ac_softsp);
 120 
 121         /* verify the board is of the correct type */
 122         switch (board->sc.type) {
 123         case CPU_BOARD:
 124         case MEM_BOARD:
 125                 break;
 126         default:
 127                 fhc_bdlist_unlock();
 128                 AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
 129                 return (EINVAL);
 130         }
 131 
 132         /*
 133          * Memory must be in the spare state to be testable.
 134          * However, spare memory that is testing can't be tested
 135          * again, instead return the current test info.
 136          */
 137         softsp = pkt->softsp;
 138         mem_info = &softsp->bank[pkt->bank];
 139         if (!MEM_BOARD_VISIBLE(board) ||
 140             fhc_bd_busy(softsp->board) ||
 141             mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
 142             mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED) {
 143                 fhc_bdlist_unlock();
 144                 AC_ERR_SET(pkt, AC_ERR_BD_STATE);
 145                 return (EINVAL);
 146         }
 147         if (mem_info->busy) {        /* oops, testing? */
 148                 /*
 149                  * find the test entry
 150                  */
 151                 ASSERT(test_mutex_initialized);
 152                 mutex_enter(&test_mutex);
 153                 for (test = test_base; test != NULL; test = test->next) {
 154                         if (test->board == softsp->board &&
 155                             test->bank == pkt->bank)
 156                                 break;
 157                 }
 158                 if (test == NULL) {
 159                         mutex_exit(&test_mutex);
 160                         fhc_bdlist_unlock();
 161                         /* Not busy testing. */
 162                         AC_ERR_SET(pkt, AC_ERR_BD_STATE);
 163                         return (EINVAL);
 164                 }
 165 
 166                 /*
 167                  * return the current test information to the new caller
 168                  */
 169                 if (ddi_copyout(&test->info, pkt->cmd_cfga.private,
 170                     sizeof (ac_mem_test_start_t), flag) != 0) {
 171                         mutex_exit(&test_mutex);
 172                         fhc_bdlist_unlock();
 173                         return (EFAULT);                /* !broken user app */
 174                 }
 175                 mutex_exit(&test_mutex);
 176                 fhc_bdlist_unlock();
 177                 AC_ERR_SET(pkt, AC_ERR_MEM_BK);
 178                 return (EBUSY);                         /* signal bank in use */
 179         }
 180 
 181         /*
 182          * at this point, we have an available bank to test.
 183          * create a test buffer
 184          */
 185         test = kmem_zalloc(sizeof (struct test_info), KM_SLEEP);
 186         test->va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
 187 
 188         /* fill in all the test info details now */
 189         test->mem_info = mem_info;
 190         test->board = softsp->board;
 191         test->bank = pkt->bank;
 192         test->bufp = kmem_alloc(TEST_PAGESIZE, KM_SLEEP);
 193         test->info.handle = atomic_inc_32_nv(&mem_test_sequence_id);
 194         (void) drv_getparm(PPID, (ulong_t *)(&(test->info.tester_pid)));
 195         test->info.prev_condition = mem_info->condition;
 196         test->info.page_size = TEST_PAGESIZE;
 197         /* If Blackbird ever gets a variable line size, this will change. */
 198         test->info.line_size = cpunodes[CPU->cpu_id].ecache_linesize;
 199         decode = (pkt->bank == Bank0) ?
 200             *softsp->ac_memdecode0 : *softsp->ac_memdecode1;
 201         test->info.afar_base = GRP_REALBASE(decode);
 202         test->info.bank_size = GRP_UK2SPAN(decode);
 203 
 204         /* return the information to the user */
 205         if (ddi_copyout(&test->info, pkt->cmd_cfga.private,
 206             sizeof (ac_mem_test_start_t), flag) != 0) {
 207 
 208                 /* oh well, tear down the test now */
 209                 kmem_free(test->bufp, TEST_PAGESIZE);
 210                 vmem_free(heap_arena, test->va, PAGESIZE);
 211                 kmem_free(test, sizeof (struct test_info));
 212 
 213                 fhc_bdlist_unlock();
 214                 return (EFAULT);
 215         }
 216 
 217         mem_info->busy = TRUE;
 218 
 219         /* finally link us into the test database */
 220         mutex_enter(&test_mutex);
 221         test->next = test_base;
 222         test_base = test;
 223         mutex_exit(&test_mutex);
 224 
 225         fhc_bdlist_unlock();
 226 
 227 #ifdef DEBUG
 228         cmn_err(CE_NOTE, "!memtest: start test[%u]: board %d, bank %d",
 229                 test->info.handle, test->board, test->bank);
 230 #endif /* DEBUG */
 231         return (DDI_SUCCESS);
 232 }
 233 
 234 int
 235 ac_mem_test_stop(ac_cfga_pkt_t *pkt, int flag)
 236 {
 237         struct test_info *test, **prev;
 238         ac_mem_test_stop_t stop;
 239 
 240         /* get test result information */
 241         if (ddi_copyin(pkt->cmd_cfga.private, &stop,
 242             sizeof (ac_mem_test_stop_t), flag) != 0)
 243                 return (EFAULT);
 244 
 245         /* bdlist protects all state changes... */
 246         (void) fhc_bdlist_lock(-1);
 247 
 248         /* find the test */
 249         mutex_enter(&test_mutex);
 250         prev = &test_base;
 251         for (test = test_base; test != NULL; test = test->next) {
 252                 if (test->info.handle == stop.handle)
 253                         break;                  /* found the test */
 254                 prev = &test->next;
 255         }
 256         if (test == NULL) {
 257                 mutex_exit(&test_mutex);
 258                 fhc_bdlist_unlock();
 259                 AC_ERR_SET(pkt, AC_ERR_MEM_TEST);
 260                 return (EINVAL);
 261         }
 262 
 263 #ifdef DEBUG
 264         cmn_err(CE_NOTE,
 265                 "!memtest: stop test[%u]: board %d, bank %d,"
 266                 " condition %d",
 267                 test->info.handle, test->board,
 268                 test->bank, stop.condition);
 269 #endif /* DEBUG */
 270 
 271         /* first unlink us from the test list (to allow no more entries) */
 272         *prev = test->next;
 273 
 274         /* then, wait for current tests to complete */
 275         while (test->in_test != 0)
 276                 delay(1);
 277 
 278         mutex_exit(&test_mutex);
 279 
 280         /* clean up the test related allocations */
 281         vmem_free(heap_arena, test->va, PAGESIZE);
 282         kmem_free(test->bufp, TEST_PAGESIZE);
 283 
 284         /* update the bank condition accordingly */
 285         test->mem_info->condition = stop.condition;
 286         test->mem_info->status_change = ddi_get_time();
 287 
 288         test->mem_info->busy = FALSE;
 289 
 290         /* finally, delete the test element */
 291         kmem_free(test, sizeof (struct test_info));
 292 
 293         fhc_bdlist_unlock();
 294 
 295         return (DDI_SUCCESS);
 296 }
 297 
 298 void
 299 ac_mem_test_stop_on_close(uint_t board, uint_t bank)
 300 {
 301         struct test_info *test, **prev;
 302         sysc_cfga_cond_t condition = SYSC_CFGA_COND_UNKNOWN;
 303 
 304         /* bdlist protects all state changes... */
 305         (void) fhc_bdlist_lock(-1);
 306 
 307         /* find the test */
 308         mutex_enter(&test_mutex);
 309         prev = &test_base;
 310         for (test = test_base; test != NULL; test = test->next) {
 311                 if (test->board == board && test->bank == bank)
 312                         break;                  /* found the test */
 313                 prev = &test->next;
 314         }
 315         if (test == NULL) {
 316                 /* No test running, nothing to do. */
 317                 mutex_exit(&test_mutex);
 318                 fhc_bdlist_unlock();
 319                 return;
 320         }
 321 
 322 #ifdef DEBUG
 323         cmn_err(CE_NOTE, "!memtest: stop test[%u] on close: "
 324             "board %d, bank %d, condition %d", test->info.handle,
 325             test->board, test->bank, condition);
 326 #endif /* DEBUG */
 327 
 328         /* first unlink us from the test list (to allow no more entries) */
 329         *prev = test->next;
 330 
 331         ASSERT(test->in_test == 0);
 332 
 333         mutex_exit(&test_mutex);
 334 
 335         /* clean up the test related allocations */
 336         vmem_free(heap_arena, test->va, PAGESIZE);
 337         kmem_free(test->bufp, TEST_PAGESIZE);
 338 
 339         /* update the bank condition accordingly */
 340         test->mem_info->condition = condition;
 341         test->mem_info->status_change = ddi_get_time();
 342 
 343         test->mem_info->busy = FALSE;
 344 
 345         /* finally, delete the test element */
 346         kmem_free(test, sizeof (struct test_info));
 347 
 348         fhc_bdlist_unlock();
 349 }
 350 
 351 int
 352 ac_mem_test_read(ac_cfga_pkt_t *pkt, int flag)
 353 {
 354         struct test_info *test;
 355         uint_t page_offset;
 356         uint64_t page_pa;
 357         uint_t pstate_save;
 358         caddr_t src_va, dst_va;
 359         uint64_t orig_err;
 360         int retval = DDI_SUCCESS;
 361         sunfire_processor_error_regs_t error_buf;
 362         int error_found;
 363         ac_mem_test_read_t t_read;
 364 
 365 #ifdef _MULTI_DATAMODEL
 366         switch (ddi_model_convert_from(flag & FMODELS)) {
 367         case DDI_MODEL_ILP32: {
 368                 ac_mem_test_read32_t t_read32;
 369 
 370                 if (ddi_copyin(pkt->cmd_cfga.private, &t_read32,
 371                     sizeof (ac_mem_test_read32_t), flag) != 0)
 372                         return (EFAULT);
 373                 t_read.handle = t_read32.handle;
 374                 t_read.page_buf = (void *)(uintptr_t)t_read32.page_buf;
 375                 t_read.address = t_read32.address;
 376                 t_read.error_buf = (sunfire_processor_error_regs_t *)
 377                     (uintptr_t)t_read32.error_buf;
 378                 break;
 379         }
 380         case DDI_MODEL_NONE:
 381                 if (ddi_copyin(pkt->cmd_cfga.private, &t_read,
 382                     sizeof (ac_mem_test_read_t), flag) != 0)
 383                         return (EFAULT);
 384                 break;
 385         }
 386 #else /* _MULTI_DATAMODEL */
 387         if (ddi_copyin(pkt->cmd_cfga.private, &t_read,
 388             sizeof (ac_mem_test_read_t), flag) != 0)
 389                 return (EFAULT);
 390 #endif /* _MULTI_DATAMODEL */
 391 
 392         /* verify the handle */
 393         mutex_enter(&test_mutex);
 394         for (test = test_base; test != NULL; test = test->next) {
 395                 if (test->info.handle == t_read.handle)
 396                         break;
 397         }
 398         if (test == NULL) {
 399                 mutex_exit(&test_mutex);
 400                 AC_ERR_SET(pkt, AC_ERR_MEM_TEST);
 401                 return (EINVAL);
 402         }
 403 
 404         /* bump the busy bit */
 405         atomic_inc_32(&test->in_test);
 406         mutex_exit(&test_mutex);
 407 
 408         /* verify the remaining parameters */
 409         if ((t_read.address.page_num >=
 410             test->info.bank_size / test->info.page_size) ||
 411             (t_read.address.line_count == 0) ||
 412             (t_read.address.line_count >
 413             test->info.page_size / test->info.line_size) ||
 414             (t_read.address.line_offset >=
 415             test->info.page_size / test->info.line_size) ||
 416             ((t_read.address.line_offset + t_read.address.line_count) >
 417             test->info.page_size / test->info.line_size)) {
 418                 AC_ERR_SET(pkt, AC_ERR_MEM_TEST_PAR);
 419                 retval = EINVAL;
 420                 goto read_done;
 421         }
 422 
 423         page_offset = t_read.address.line_offset * test->info.line_size;
 424         page_pa = test->info.afar_base +
 425             t_read.address.page_num * test->info.page_size;
 426         dst_va = test->bufp + page_offset;
 427         src_va = test->va + page_offset;
 428 
 429         /* time to go quiet */
 430         kpreempt_disable();
 431 
 432         /* we need a va for the block instructions */
 433         ac_mapin(page_pa, test->va);
 434 
 435         pstate_save = disable_vec_intr();
 436 
 437         /* disable errors */
 438         orig_err = get_error_enable();
 439         set_error_enable(orig_err & ~(EER_CEEN | EER_NCEEN));
 440 
 441         /* copy the data again (using our very special copy) */
 442         ac_blkcopy(src_va, dst_va, t_read.address.line_count,
 443             test->info.line_size);
 444 
 445         /* process errors (if any) */
 446         error_buf.module_id = CPU->cpu_id;
 447         get_asyncflt(&(error_buf.afsr));
 448         get_asyncaddr(&(error_buf.afar));
 449         get_udb_errors(&(error_buf.udbh_error_reg),
 450             &(error_buf.udbl_error_reg));
 451 
 452         /*
 453          * clean up after our no-error copy but before enabling ints.
 454          * XXX what to do about other error types?
 455          */
 456         if (error_buf.afsr & (P_AFSR_CE | P_AFSR_UE)) {
 457                 extern void clr_datapath(void); /* XXX */
 458 
 459                 clr_datapath();
 460                 set_asyncflt(error_buf.afsr);
 461                 retval = EIO;
 462                 error_found = TRUE;
 463         } else {
 464                 error_found = FALSE;
 465         }
 466 
 467         /* errors back on */
 468         set_error_enable(orig_err);
 469 
 470         enable_vec_intr(pstate_save);
 471 
 472         /* tear down translation (who needs an mmu) */
 473         ac_unmap(test->va);
 474 
 475         /* we're back! */
 476         kpreempt_enable();
 477 
 478         /*
 479          * If there was a data error, attempt to return the error_buf
 480          * to the user.
 481          */
 482         if (error_found) {
 483                 if (ddi_copyout(&error_buf, t_read.error_buf,
 484                     sizeof (sunfire_processor_error_regs_t), flag) != 0) {
 485                         retval = EFAULT;
 486                         /* Keep going */
 487                 }
 488         }
 489 
 490         /*
 491          * Then, return the page to the user (always)
 492          */
 493         if (ddi_copyout(dst_va, (caddr_t)(t_read.page_buf) + page_offset,
 494             t_read.address.line_count * test->info.line_size, flag) != 0) {
 495                 retval = EFAULT;
 496         }
 497 
 498 read_done:
 499         atomic_dec_32(&test->in_test);
 500         return (retval);
 501 }
 502 
 503 int
 504 ac_mem_test_write(ac_cfga_pkt_t *pkt, int flag)
 505 {
 506         struct test_info *test;
 507         uint_t page_offset;
 508         uint64_t page_pa;
 509         uint_t pstate_save;
 510         caddr_t src_va, dst_va;
 511         int retval = DDI_SUCCESS;
 512         ac_mem_test_write_t t_write;
 513 
 514 #ifdef _MULTI_DATAMODEL
 515         switch (ddi_model_convert_from(flag & FMODELS)) {
 516         case DDI_MODEL_ILP32: {
 517                 ac_mem_test_write32_t t_write32;
 518 
 519                 if (ddi_copyin(pkt->cmd_cfga.private, &t_write32,
 520                     sizeof (ac_mem_test_write32_t), flag) != 0)
 521                         return (EFAULT);
 522                 t_write.handle = t_write32.handle;
 523                 t_write.page_buf = (void *)(uintptr_t)t_write32.page_buf;
 524                 t_write.address = t_write32.address;
 525                 break;
 526         }
 527         case DDI_MODEL_NONE:
 528                 if (ddi_copyin(pkt->cmd_cfga.private, &t_write,
 529                     sizeof (ac_mem_test_write_t), flag) != 0)
 530                         return (EFAULT);
 531                 break;
 532         }
 533 #else /* _MULTI_DATAMODEL */
 534         if (ddi_copyin(pkt->cmd_cfga.private, &t_write,
 535             sizeof (ac_mem_test_write_t), flag) != 0)
 536                 return (EFAULT);
 537 #endif /* _MULTI_DATAMODEL */
 538 
 539         /* verify the handle */
 540         mutex_enter(&test_mutex);
 541         for (test = test_base; test != NULL; test = test->next) {
 542                 if (test->info.handle == t_write.handle)
 543                         break;
 544         }
 545         if (test == NULL) {
 546                 mutex_exit(&test_mutex);
 547                 return (EINVAL);
 548         }
 549 
 550         /* bump the busy bit */
 551         atomic_inc_32(&test->in_test);
 552         mutex_exit(&test_mutex);
 553 
 554         /* verify the remaining parameters */
 555         if ((t_write.address.page_num >=
 556             test->info.bank_size / test->info.page_size) ||
 557             (t_write.address.line_count == 0) ||
 558             (t_write.address.line_count >
 559             test->info.page_size / test->info.line_size) ||
 560             (t_write.address.line_offset >=
 561             test->info.page_size / test->info.line_size) ||
 562             ((t_write.address.line_offset + t_write.address.line_count) >
 563             test->info.page_size / test->info.line_size)) {
 564                 AC_ERR_SET(pkt, AC_ERR_MEM_TEST_PAR);
 565                 retval = EINVAL;
 566                 goto write_done;
 567         }
 568 
 569         page_offset = t_write.address.line_offset * test->info.line_size;
 570         page_pa = test->info.afar_base +
 571             t_write.address.page_num * test->info.page_size;
 572         src_va = test->bufp + page_offset;
 573         dst_va = test->va + page_offset;
 574 
 575         /* copy in the specified user data */
 576         if (ddi_copyin((caddr_t)(t_write.page_buf) + page_offset, src_va,
 577             t_write.address.line_count * test->info.line_size, flag) != 0) {
 578                 retval = EFAULT;
 579                 goto write_done;
 580         }
 581 
 582         /* time to go quiet */
 583         kpreempt_disable();
 584 
 585         /* we need a va for the block instructions */
 586         ac_mapin(page_pa, test->va);
 587 
 588         pstate_save = disable_vec_intr();
 589 
 590         /* copy the data again (using our very special copy) */
 591         ac_blkcopy(src_va, dst_va, t_write.address.line_count,
 592             test->info.line_size);
 593 
 594         enable_vec_intr(pstate_save);
 595 
 596         /* tear down translation (who needs an mmu) */
 597         ac_unmap(test->va);
 598 
 599         /* we're back! */
 600         kpreempt_enable();
 601 
 602 write_done:
 603         atomic_dec_32(&test->in_test);
 604         return (retval);
 605 }