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