129949e86Sstevel /* 229949e86Sstevel * CDDL HEADER START 329949e86Sstevel * 429949e86Sstevel * The contents of this file are subject to the terms of the 529949e86Sstevel * Common Development and Distribution License (the "License"). 629949e86Sstevel * You may not use this file except in compliance with the License. 729949e86Sstevel * 829949e86Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 929949e86Sstevel * or http://www.opensolaris.org/os/licensing. 1029949e86Sstevel * See the License for the specific language governing permissions 1129949e86Sstevel * and limitations under the License. 1229949e86Sstevel * 1329949e86Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1429949e86Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1529949e86Sstevel * If applicable, add the following below this CDDL HEADER, with the 1629949e86Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1729949e86Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1829949e86Sstevel * 1929949e86Sstevel * CDDL HEADER END 2029949e86Sstevel */ 2129949e86Sstevel 2229949e86Sstevel /* 2329949e86Sstevel * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 2429949e86Sstevel * Use is subject to license terms. 2529949e86Sstevel */ 2629949e86Sstevel 2729949e86Sstevel #include <sys/types.h> 2829949e86Sstevel #include <sys/conf.h> 2929949e86Sstevel #include <sys/ddi.h> 3029949e86Sstevel #include <sys/sunddi.h> 3129949e86Sstevel #include <sys/ddi_impldefs.h> 3229949e86Sstevel #include <sys/obpdefs.h> 3329949e86Sstevel #include <sys/cmn_err.h> 3429949e86Sstevel #include <sys/errno.h> 3529949e86Sstevel #include <sys/kmem.h> 3629949e86Sstevel #include <sys/vmem.h> 3729949e86Sstevel #include <sys/debug.h> 3829949e86Sstevel #include <sys/sysmacros.h> 3929949e86Sstevel #include <sys/machsystm.h> 4029949e86Sstevel #include <sys/machparam.h> 4129949e86Sstevel #include <sys/modctl.h> 4229949e86Sstevel #include <sys/atomic.h> 4329949e86Sstevel #include <sys/fhc.h> 4429949e86Sstevel #include <sys/ac.h> 4529949e86Sstevel #include <sys/jtag.h> 4629949e86Sstevel #include <sys/cpu_module.h> 4729949e86Sstevel #include <sys/spitregs.h> 4829949e86Sstevel #include <sys/vm.h> 4929949e86Sstevel #include <vm/seg_kmem.h> 5029949e86Sstevel #include <vm/hat_sfmmu.h> 5129949e86Sstevel 5229949e86Sstevel /* memory setup parameters */ 5329949e86Sstevel #define TEST_PAGESIZE MMU_PAGESIZE 5429949e86Sstevel 5529949e86Sstevel struct test_info { 5629949e86Sstevel struct test_info *next; /* linked list of tests */ 5729949e86Sstevel struct ac_mem_info *mem_info; 5829949e86Sstevel uint_t board; 5929949e86Sstevel uint_t bank; 6029949e86Sstevel caddr_t bufp; /* pointer to buffer page */ 6129949e86Sstevel caddr_t va; /* test target VA */ 6229949e86Sstevel ac_mem_test_start_t info; 6329949e86Sstevel uint_t in_test; /* count of threads in test */ 6429949e86Sstevel }; 6529949e86Sstevel 6629949e86Sstevel /* list of tests in progress (list protected test_mutex) */ 6729949e86Sstevel static struct test_info *test_base = NULL; 6829949e86Sstevel static kmutex_t test_mutex; 6929949e86Sstevel static int test_mutex_initialized = FALSE; 7029949e86Sstevel 7129949e86Sstevel static mem_test_handle_t mem_test_sequence_id = 0; 7229949e86Sstevel 7329949e86Sstevel void 7429949e86Sstevel ac_mapin(uint64_t pa, caddr_t va) 7529949e86Sstevel { 7629949e86Sstevel pfn_t pfn; 7729949e86Sstevel tte_t tte; 7829949e86Sstevel 7929949e86Sstevel pfn = pa >> MMU_PAGESHIFT; 8029949e86Sstevel tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) | 8129949e86Sstevel TTE_PFN_INTHI(pfn); 8229949e86Sstevel tte.tte_intlo = TTE_PFN_INTLO(pfn) | TTE_CP_INT | 8329949e86Sstevel TTE_PRIV_INT | TTE_LCK_INT | TTE_HWWR_INT; 841e2e7a75Shuah sfmmu_dtlb_ld_kva(va, &tte); 8529949e86Sstevel 8629949e86Sstevel } 8729949e86Sstevel 8829949e86Sstevel void 8929949e86Sstevel ac_unmap(caddr_t va) 9029949e86Sstevel { 911e2e7a75Shuah vtag_flushpage(va, (uint64_t)ksfmmup); 9229949e86Sstevel } 9329949e86Sstevel 9429949e86Sstevel int 9529949e86Sstevel ac_mem_test_start(ac_cfga_pkt_t *pkt, int flag) 9629949e86Sstevel { 9729949e86Sstevel struct ac_soft_state *softsp; 9829949e86Sstevel struct ac_mem_info *mem_info; 9929949e86Sstevel struct bd_list *board; 10029949e86Sstevel struct test_info *test; 10129949e86Sstevel uint64_t decode; 10229949e86Sstevel 10329949e86Sstevel /* XXX if ac ever detaches... */ 10429949e86Sstevel if (test_mutex_initialized == FALSE) { 10529949e86Sstevel mutex_init(&test_mutex, NULL, MUTEX_DEFAULT, NULL); 10629949e86Sstevel test_mutex_initialized = TRUE; 10729949e86Sstevel } 10829949e86Sstevel 10929949e86Sstevel /* 11029949e86Sstevel * Is the specified bank testable? 11129949e86Sstevel */ 11229949e86Sstevel 11329949e86Sstevel board = fhc_bdlist_lock(pkt->softsp->board); 11429949e86Sstevel if (board == NULL || board->ac_softsp == NULL) { 11529949e86Sstevel fhc_bdlist_unlock(); 11629949e86Sstevel AC_ERR_SET(pkt, AC_ERR_BD); 11729949e86Sstevel return (EINVAL); 11829949e86Sstevel } 11929949e86Sstevel ASSERT(pkt->softsp == board->ac_softsp); 12029949e86Sstevel 12129949e86Sstevel /* verify the board is of the correct type */ 12229949e86Sstevel switch (board->sc.type) { 12329949e86Sstevel case CPU_BOARD: 12429949e86Sstevel case MEM_BOARD: 12529949e86Sstevel break; 12629949e86Sstevel default: 12729949e86Sstevel fhc_bdlist_unlock(); 12829949e86Sstevel AC_ERR_SET(pkt, AC_ERR_BD_TYPE); 12929949e86Sstevel return (EINVAL); 13029949e86Sstevel } 13129949e86Sstevel 13229949e86Sstevel /* 13329949e86Sstevel * Memory must be in the spare state to be testable. 13429949e86Sstevel * However, spare memory that is testing can't be tested 13529949e86Sstevel * again, instead return the current test info. 13629949e86Sstevel */ 13729949e86Sstevel softsp = pkt->softsp; 13829949e86Sstevel mem_info = &softsp->bank[pkt->bank]; 13929949e86Sstevel if (!MEM_BOARD_VISIBLE(board) || 14029949e86Sstevel fhc_bd_busy(softsp->board) || 14129949e86Sstevel mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED || 14229949e86Sstevel mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED) { 14329949e86Sstevel fhc_bdlist_unlock(); 14429949e86Sstevel AC_ERR_SET(pkt, AC_ERR_BD_STATE); 14529949e86Sstevel return (EINVAL); 14629949e86Sstevel } 14729949e86Sstevel if (mem_info->busy) { /* oops, testing? */ 14829949e86Sstevel /* 14929949e86Sstevel * find the test entry 15029949e86Sstevel */ 15129949e86Sstevel ASSERT(test_mutex_initialized); 15229949e86Sstevel mutex_enter(&test_mutex); 15329949e86Sstevel for (test = test_base; test != NULL; test = test->next) { 15429949e86Sstevel if (test->board == softsp->board && 15529949e86Sstevel test->bank == pkt->bank) 15629949e86Sstevel break; 15729949e86Sstevel } 15829949e86Sstevel if (test == NULL) { 15929949e86Sstevel mutex_exit(&test_mutex); 16029949e86Sstevel fhc_bdlist_unlock(); 16129949e86Sstevel /* Not busy testing. */ 16229949e86Sstevel AC_ERR_SET(pkt, AC_ERR_BD_STATE); 16329949e86Sstevel return (EINVAL); 16429949e86Sstevel } 16529949e86Sstevel 16629949e86Sstevel /* 16729949e86Sstevel * return the current test information to the new caller 16829949e86Sstevel */ 16929949e86Sstevel if (ddi_copyout(&test->info, pkt->cmd_cfga.private, 17029949e86Sstevel sizeof (ac_mem_test_start_t), flag) != 0) { 17129949e86Sstevel mutex_exit(&test_mutex); 17229949e86Sstevel fhc_bdlist_unlock(); 17329949e86Sstevel return (EFAULT); /* !broken user app */ 17429949e86Sstevel } 17529949e86Sstevel mutex_exit(&test_mutex); 17629949e86Sstevel fhc_bdlist_unlock(); 17729949e86Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_BK); 17829949e86Sstevel return (EBUSY); /* signal bank in use */ 17929949e86Sstevel } 18029949e86Sstevel 18129949e86Sstevel /* 18229949e86Sstevel * at this point, we have an available bank to test. 18329949e86Sstevel * create a test buffer 18429949e86Sstevel */ 18529949e86Sstevel test = kmem_zalloc(sizeof (struct test_info), KM_SLEEP); 18629949e86Sstevel test->va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP); 18729949e86Sstevel 18829949e86Sstevel /* fill in all the test info details now */ 18929949e86Sstevel test->mem_info = mem_info; 19029949e86Sstevel test->board = softsp->board; 19129949e86Sstevel test->bank = pkt->bank; 19229949e86Sstevel test->bufp = kmem_alloc(TEST_PAGESIZE, KM_SLEEP); 193*1a5e258fSJosef 'Jeff' Sipek test->info.handle = atomic_inc_32_nv(&mem_test_sequence_id); 19429949e86Sstevel (void) drv_getparm(PPID, (ulong_t *)(&(test->info.tester_pid))); 19529949e86Sstevel test->info.prev_condition = mem_info->condition; 19629949e86Sstevel test->info.page_size = TEST_PAGESIZE; 19729949e86Sstevel /* If Blackbird ever gets a variable line size, this will change. */ 19829949e86Sstevel test->info.line_size = cpunodes[CPU->cpu_id].ecache_linesize; 19929949e86Sstevel decode = (pkt->bank == Bank0) ? 20029949e86Sstevel *softsp->ac_memdecode0 : *softsp->ac_memdecode1; 20129949e86Sstevel test->info.afar_base = GRP_REALBASE(decode); 20229949e86Sstevel test->info.bank_size = GRP_UK2SPAN(decode); 20329949e86Sstevel 20429949e86Sstevel /* return the information to the user */ 20529949e86Sstevel if (ddi_copyout(&test->info, pkt->cmd_cfga.private, 20629949e86Sstevel sizeof (ac_mem_test_start_t), flag) != 0) { 20729949e86Sstevel 20829949e86Sstevel /* oh well, tear down the test now */ 20929949e86Sstevel kmem_free(test->bufp, TEST_PAGESIZE); 21029949e86Sstevel vmem_free(heap_arena, test->va, PAGESIZE); 21129949e86Sstevel kmem_free(test, sizeof (struct test_info)); 21229949e86Sstevel 21329949e86Sstevel fhc_bdlist_unlock(); 21429949e86Sstevel return (EFAULT); 21529949e86Sstevel } 21629949e86Sstevel 21729949e86Sstevel mem_info->busy = TRUE; 21829949e86Sstevel 21929949e86Sstevel /* finally link us into the test database */ 22029949e86Sstevel mutex_enter(&test_mutex); 22129949e86Sstevel test->next = test_base; 22229949e86Sstevel test_base = test; 22329949e86Sstevel mutex_exit(&test_mutex); 22429949e86Sstevel 22529949e86Sstevel fhc_bdlist_unlock(); 22629949e86Sstevel 22729949e86Sstevel #ifdef DEBUG 22829949e86Sstevel cmn_err(CE_NOTE, "!memtest: start test[%u]: board %d, bank %d", 22929949e86Sstevel test->info.handle, test->board, test->bank); 23029949e86Sstevel #endif /* DEBUG */ 23129949e86Sstevel return (DDI_SUCCESS); 23229949e86Sstevel } 23329949e86Sstevel 23429949e86Sstevel int 23529949e86Sstevel ac_mem_test_stop(ac_cfga_pkt_t *pkt, int flag) 23629949e86Sstevel { 23729949e86Sstevel struct test_info *test, **prev; 23829949e86Sstevel ac_mem_test_stop_t stop; 23929949e86Sstevel 24029949e86Sstevel /* get test result information */ 24129949e86Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &stop, 24229949e86Sstevel sizeof (ac_mem_test_stop_t), flag) != 0) 24329949e86Sstevel return (EFAULT); 24429949e86Sstevel 24529949e86Sstevel /* bdlist protects all state changes... */ 24629949e86Sstevel (void) fhc_bdlist_lock(-1); 24729949e86Sstevel 24829949e86Sstevel /* find the test */ 24929949e86Sstevel mutex_enter(&test_mutex); 25029949e86Sstevel prev = &test_base; 25129949e86Sstevel for (test = test_base; test != NULL; test = test->next) { 25229949e86Sstevel if (test->info.handle == stop.handle) 25329949e86Sstevel break; /* found the test */ 25429949e86Sstevel prev = &test->next; 25529949e86Sstevel } 25629949e86Sstevel if (test == NULL) { 25729949e86Sstevel mutex_exit(&test_mutex); 25829949e86Sstevel fhc_bdlist_unlock(); 25929949e86Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST); 26029949e86Sstevel return (EINVAL); 26129949e86Sstevel } 26229949e86Sstevel 26329949e86Sstevel #ifdef DEBUG 26429949e86Sstevel cmn_err(CE_NOTE, 26529949e86Sstevel "!memtest: stop test[%u]: board %d, bank %d," 26629949e86Sstevel " condition %d", 26729949e86Sstevel test->info.handle, test->board, 26829949e86Sstevel test->bank, stop.condition); 26929949e86Sstevel #endif /* DEBUG */ 27029949e86Sstevel 27129949e86Sstevel /* first unlink us from the test list (to allow no more entries) */ 27229949e86Sstevel *prev = test->next; 27329949e86Sstevel 27429949e86Sstevel /* then, wait for current tests to complete */ 27529949e86Sstevel while (test->in_test != 0) 27629949e86Sstevel delay(1); 27729949e86Sstevel 27829949e86Sstevel mutex_exit(&test_mutex); 27929949e86Sstevel 28029949e86Sstevel /* clean up the test related allocations */ 28129949e86Sstevel vmem_free(heap_arena, test->va, PAGESIZE); 28229949e86Sstevel kmem_free(test->bufp, TEST_PAGESIZE); 28329949e86Sstevel 28429949e86Sstevel /* update the bank condition accordingly */ 28529949e86Sstevel test->mem_info->condition = stop.condition; 28629949e86Sstevel test->mem_info->status_change = ddi_get_time(); 28729949e86Sstevel 28829949e86Sstevel test->mem_info->busy = FALSE; 28929949e86Sstevel 29029949e86Sstevel /* finally, delete the test element */ 29129949e86Sstevel kmem_free(test, sizeof (struct test_info)); 29229949e86Sstevel 29329949e86Sstevel fhc_bdlist_unlock(); 29429949e86Sstevel 29529949e86Sstevel return (DDI_SUCCESS); 29629949e86Sstevel } 29729949e86Sstevel 29829949e86Sstevel void 29929949e86Sstevel ac_mem_test_stop_on_close(uint_t board, uint_t bank) 30029949e86Sstevel { 30129949e86Sstevel struct test_info *test, **prev; 30229949e86Sstevel sysc_cfga_cond_t condition = SYSC_CFGA_COND_UNKNOWN; 30329949e86Sstevel 30429949e86Sstevel /* bdlist protects all state changes... */ 30529949e86Sstevel (void) fhc_bdlist_lock(-1); 30629949e86Sstevel 30729949e86Sstevel /* find the test */ 30829949e86Sstevel mutex_enter(&test_mutex); 30929949e86Sstevel prev = &test_base; 31029949e86Sstevel for (test = test_base; test != NULL; test = test->next) { 31129949e86Sstevel if (test->board == board && test->bank == bank) 31229949e86Sstevel break; /* found the test */ 31329949e86Sstevel prev = &test->next; 31429949e86Sstevel } 31529949e86Sstevel if (test == NULL) { 31629949e86Sstevel /* No test running, nothing to do. */ 31729949e86Sstevel mutex_exit(&test_mutex); 31829949e86Sstevel fhc_bdlist_unlock(); 31929949e86Sstevel return; 32029949e86Sstevel } 32129949e86Sstevel 32229949e86Sstevel #ifdef DEBUG 32329949e86Sstevel cmn_err(CE_NOTE, "!memtest: stop test[%u] on close: " 32429949e86Sstevel "board %d, bank %d, condition %d", test->info.handle, 32529949e86Sstevel test->board, test->bank, condition); 32629949e86Sstevel #endif /* DEBUG */ 32729949e86Sstevel 32829949e86Sstevel /* first unlink us from the test list (to allow no more entries) */ 32929949e86Sstevel *prev = test->next; 33029949e86Sstevel 33129949e86Sstevel ASSERT(test->in_test == 0); 33229949e86Sstevel 33329949e86Sstevel mutex_exit(&test_mutex); 33429949e86Sstevel 33529949e86Sstevel /* clean up the test related allocations */ 33629949e86Sstevel vmem_free(heap_arena, test->va, PAGESIZE); 33729949e86Sstevel kmem_free(test->bufp, TEST_PAGESIZE); 33829949e86Sstevel 33929949e86Sstevel /* update the bank condition accordingly */ 34029949e86Sstevel test->mem_info->condition = condition; 34129949e86Sstevel test->mem_info->status_change = ddi_get_time(); 34229949e86Sstevel 34329949e86Sstevel test->mem_info->busy = FALSE; 34429949e86Sstevel 34529949e86Sstevel /* finally, delete the test element */ 34629949e86Sstevel kmem_free(test, sizeof (struct test_info)); 34729949e86Sstevel 34829949e86Sstevel fhc_bdlist_unlock(); 34929949e86Sstevel } 35029949e86Sstevel 35129949e86Sstevel int 35229949e86Sstevel ac_mem_test_read(ac_cfga_pkt_t *pkt, int flag) 35329949e86Sstevel { 35429949e86Sstevel struct test_info *test; 35529949e86Sstevel uint_t page_offset; 35629949e86Sstevel uint64_t page_pa; 35729949e86Sstevel uint_t pstate_save; 35829949e86Sstevel caddr_t src_va, dst_va; 35929949e86Sstevel uint64_t orig_err; 36029949e86Sstevel int retval = DDI_SUCCESS; 36129949e86Sstevel sunfire_processor_error_regs_t error_buf; 36229949e86Sstevel int error_found; 36329949e86Sstevel ac_mem_test_read_t t_read; 36429949e86Sstevel 36529949e86Sstevel #ifdef _MULTI_DATAMODEL 36629949e86Sstevel switch (ddi_model_convert_from(flag & FMODELS)) { 36729949e86Sstevel case DDI_MODEL_ILP32: { 36829949e86Sstevel ac_mem_test_read32_t t_read32; 36929949e86Sstevel 37029949e86Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_read32, 37129949e86Sstevel sizeof (ac_mem_test_read32_t), flag) != 0) 37229949e86Sstevel return (EFAULT); 37329949e86Sstevel t_read.handle = t_read32.handle; 37429949e86Sstevel t_read.page_buf = (void *)(uintptr_t)t_read32.page_buf; 37529949e86Sstevel t_read.address = t_read32.address; 37629949e86Sstevel t_read.error_buf = (sunfire_processor_error_regs_t *) 37729949e86Sstevel (uintptr_t)t_read32.error_buf; 37829949e86Sstevel break; 37929949e86Sstevel } 38029949e86Sstevel case DDI_MODEL_NONE: 38129949e86Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_read, 38229949e86Sstevel sizeof (ac_mem_test_read_t), flag) != 0) 38329949e86Sstevel return (EFAULT); 38429949e86Sstevel break; 38529949e86Sstevel } 38629949e86Sstevel #else /* _MULTI_DATAMODEL */ 38729949e86Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_read, 38829949e86Sstevel sizeof (ac_mem_test_read_t), flag) != 0) 38929949e86Sstevel return (EFAULT); 39029949e86Sstevel #endif /* _MULTI_DATAMODEL */ 39129949e86Sstevel 39229949e86Sstevel /* verify the handle */ 39329949e86Sstevel mutex_enter(&test_mutex); 39429949e86Sstevel for (test = test_base; test != NULL; test = test->next) { 39529949e86Sstevel if (test->info.handle == t_read.handle) 39629949e86Sstevel break; 39729949e86Sstevel } 39829949e86Sstevel if (test == NULL) { 39929949e86Sstevel mutex_exit(&test_mutex); 40029949e86Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST); 40129949e86Sstevel return (EINVAL); 40229949e86Sstevel } 40329949e86Sstevel 40429949e86Sstevel /* bump the busy bit */ 405*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&test->in_test); 40629949e86Sstevel mutex_exit(&test_mutex); 40729949e86Sstevel 40829949e86Sstevel /* verify the remaining parameters */ 40929949e86Sstevel if ((t_read.address.page_num >= 41029949e86Sstevel test->info.bank_size / test->info.page_size) || 41129949e86Sstevel (t_read.address.line_count == 0) || 41229949e86Sstevel (t_read.address.line_count > 41329949e86Sstevel test->info.page_size / test->info.line_size) || 41429949e86Sstevel (t_read.address.line_offset >= 41529949e86Sstevel test->info.page_size / test->info.line_size) || 41629949e86Sstevel ((t_read.address.line_offset + t_read.address.line_count) > 41729949e86Sstevel test->info.page_size / test->info.line_size)) { 41829949e86Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST_PAR); 41929949e86Sstevel retval = EINVAL; 42029949e86Sstevel goto read_done; 42129949e86Sstevel } 42229949e86Sstevel 42329949e86Sstevel page_offset = t_read.address.line_offset * test->info.line_size; 42429949e86Sstevel page_pa = test->info.afar_base + 42529949e86Sstevel t_read.address.page_num * test->info.page_size; 42629949e86Sstevel dst_va = test->bufp + page_offset; 42729949e86Sstevel src_va = test->va + page_offset; 42829949e86Sstevel 42929949e86Sstevel /* time to go quiet */ 43029949e86Sstevel kpreempt_disable(); 43129949e86Sstevel 43229949e86Sstevel /* we need a va for the block instructions */ 43329949e86Sstevel ac_mapin(page_pa, test->va); 43429949e86Sstevel 43529949e86Sstevel pstate_save = disable_vec_intr(); 43629949e86Sstevel 43729949e86Sstevel /* disable errors */ 43829949e86Sstevel orig_err = get_error_enable(); 43929949e86Sstevel set_error_enable(orig_err & ~(EER_CEEN | EER_NCEEN)); 44029949e86Sstevel 44129949e86Sstevel /* copy the data again (using our very special copy) */ 44229949e86Sstevel ac_blkcopy(src_va, dst_va, t_read.address.line_count, 44329949e86Sstevel test->info.line_size); 44429949e86Sstevel 44529949e86Sstevel /* process errors (if any) */ 44629949e86Sstevel error_buf.module_id = CPU->cpu_id; 44729949e86Sstevel get_asyncflt(&(error_buf.afsr)); 44829949e86Sstevel get_asyncaddr(&(error_buf.afar)); 44929949e86Sstevel get_udb_errors(&(error_buf.udbh_error_reg), 45029949e86Sstevel &(error_buf.udbl_error_reg)); 45129949e86Sstevel 45229949e86Sstevel /* 45329949e86Sstevel * clean up after our no-error copy but before enabling ints. 45429949e86Sstevel * XXX what to do about other error types? 45529949e86Sstevel */ 45629949e86Sstevel if (error_buf.afsr & (P_AFSR_CE | P_AFSR_UE)) { 45729949e86Sstevel extern void clr_datapath(void); /* XXX */ 45829949e86Sstevel 45929949e86Sstevel clr_datapath(); 46029949e86Sstevel set_asyncflt(error_buf.afsr); 46129949e86Sstevel retval = EIO; 46229949e86Sstevel error_found = TRUE; 46329949e86Sstevel } else { 46429949e86Sstevel error_found = FALSE; 46529949e86Sstevel } 46629949e86Sstevel 46729949e86Sstevel /* errors back on */ 46829949e86Sstevel set_error_enable(orig_err); 46929949e86Sstevel 47029949e86Sstevel enable_vec_intr(pstate_save); 47129949e86Sstevel 47229949e86Sstevel /* tear down translation (who needs an mmu) */ 47329949e86Sstevel ac_unmap(test->va); 47429949e86Sstevel 47529949e86Sstevel /* we're back! */ 47629949e86Sstevel kpreempt_enable(); 47729949e86Sstevel 47829949e86Sstevel /* 47929949e86Sstevel * If there was a data error, attempt to return the error_buf 48029949e86Sstevel * to the user. 48129949e86Sstevel */ 48229949e86Sstevel if (error_found) { 48329949e86Sstevel if (ddi_copyout(&error_buf, t_read.error_buf, 48429949e86Sstevel sizeof (sunfire_processor_error_regs_t), flag) != 0) { 48529949e86Sstevel retval = EFAULT; 48629949e86Sstevel /* Keep going */ 48729949e86Sstevel } 48829949e86Sstevel } 48929949e86Sstevel 49029949e86Sstevel /* 49129949e86Sstevel * Then, return the page to the user (always) 49229949e86Sstevel */ 49329949e86Sstevel if (ddi_copyout(dst_va, (caddr_t)(t_read.page_buf) + page_offset, 49429949e86Sstevel t_read.address.line_count * test->info.line_size, flag) != 0) { 49529949e86Sstevel retval = EFAULT; 49629949e86Sstevel } 49729949e86Sstevel 49829949e86Sstevel read_done: 499*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&test->in_test); 50029949e86Sstevel return (retval); 50129949e86Sstevel } 50229949e86Sstevel 50329949e86Sstevel int 50429949e86Sstevel ac_mem_test_write(ac_cfga_pkt_t *pkt, int flag) 50529949e86Sstevel { 50629949e86Sstevel struct test_info *test; 50729949e86Sstevel uint_t page_offset; 50829949e86Sstevel uint64_t page_pa; 50929949e86Sstevel uint_t pstate_save; 51029949e86Sstevel caddr_t src_va, dst_va; 51129949e86Sstevel int retval = DDI_SUCCESS; 51229949e86Sstevel ac_mem_test_write_t t_write; 51329949e86Sstevel 51429949e86Sstevel #ifdef _MULTI_DATAMODEL 51529949e86Sstevel switch (ddi_model_convert_from(flag & FMODELS)) { 51629949e86Sstevel case DDI_MODEL_ILP32: { 51729949e86Sstevel ac_mem_test_write32_t t_write32; 51829949e86Sstevel 51929949e86Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_write32, 52029949e86Sstevel sizeof (ac_mem_test_write32_t), flag) != 0) 52129949e86Sstevel return (EFAULT); 52229949e86Sstevel t_write.handle = t_write32.handle; 52329949e86Sstevel t_write.page_buf = (void *)(uintptr_t)t_write32.page_buf; 52429949e86Sstevel t_write.address = t_write32.address; 52529949e86Sstevel break; 52629949e86Sstevel } 52729949e86Sstevel case DDI_MODEL_NONE: 52829949e86Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_write, 52929949e86Sstevel sizeof (ac_mem_test_write_t), flag) != 0) 53029949e86Sstevel return (EFAULT); 53129949e86Sstevel break; 53229949e86Sstevel } 53329949e86Sstevel #else /* _MULTI_DATAMODEL */ 53429949e86Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_write, 53529949e86Sstevel sizeof (ac_mem_test_write_t), flag) != 0) 53629949e86Sstevel return (EFAULT); 53729949e86Sstevel #endif /* _MULTI_DATAMODEL */ 53829949e86Sstevel 53929949e86Sstevel /* verify the handle */ 54029949e86Sstevel mutex_enter(&test_mutex); 54129949e86Sstevel for (test = test_base; test != NULL; test = test->next) { 54229949e86Sstevel if (test->info.handle == t_write.handle) 54329949e86Sstevel break; 54429949e86Sstevel } 54529949e86Sstevel if (test == NULL) { 54629949e86Sstevel mutex_exit(&test_mutex); 54729949e86Sstevel return (EINVAL); 54829949e86Sstevel } 54929949e86Sstevel 55029949e86Sstevel /* bump the busy bit */ 551*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&test->in_test); 55229949e86Sstevel mutex_exit(&test_mutex); 55329949e86Sstevel 55429949e86Sstevel /* verify the remaining parameters */ 55529949e86Sstevel if ((t_write.address.page_num >= 55629949e86Sstevel test->info.bank_size / test->info.page_size) || 55729949e86Sstevel (t_write.address.line_count == 0) || 55829949e86Sstevel (t_write.address.line_count > 55929949e86Sstevel test->info.page_size / test->info.line_size) || 56029949e86Sstevel (t_write.address.line_offset >= 56129949e86Sstevel test->info.page_size / test->info.line_size) || 56229949e86Sstevel ((t_write.address.line_offset + t_write.address.line_count) > 56329949e86Sstevel test->info.page_size / test->info.line_size)) { 56429949e86Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST_PAR); 56529949e86Sstevel retval = EINVAL; 56629949e86Sstevel goto write_done; 56729949e86Sstevel } 56829949e86Sstevel 56929949e86Sstevel page_offset = t_write.address.line_offset * test->info.line_size; 57029949e86Sstevel page_pa = test->info.afar_base + 57129949e86Sstevel t_write.address.page_num * test->info.page_size; 57229949e86Sstevel src_va = test->bufp + page_offset; 57329949e86Sstevel dst_va = test->va + page_offset; 57429949e86Sstevel 57529949e86Sstevel /* copy in the specified user data */ 57629949e86Sstevel if (ddi_copyin((caddr_t)(t_write.page_buf) + page_offset, src_va, 57729949e86Sstevel t_write.address.line_count * test->info.line_size, flag) != 0) { 57829949e86Sstevel retval = EFAULT; 57929949e86Sstevel goto write_done; 58029949e86Sstevel } 58129949e86Sstevel 58229949e86Sstevel /* time to go quiet */ 58329949e86Sstevel kpreempt_disable(); 58429949e86Sstevel 58529949e86Sstevel /* we need a va for the block instructions */ 58629949e86Sstevel ac_mapin(page_pa, test->va); 58729949e86Sstevel 58829949e86Sstevel pstate_save = disable_vec_intr(); 58929949e86Sstevel 59029949e86Sstevel /* copy the data again (using our very special copy) */ 59129949e86Sstevel ac_blkcopy(src_va, dst_va, t_write.address.line_count, 59229949e86Sstevel test->info.line_size); 59329949e86Sstevel 59429949e86Sstevel enable_vec_intr(pstate_save); 59529949e86Sstevel 59629949e86Sstevel /* tear down translation (who needs an mmu) */ 59729949e86Sstevel ac_unmap(test->va); 59829949e86Sstevel 59929949e86Sstevel /* we're back! */ 60029949e86Sstevel kpreempt_enable(); 60129949e86Sstevel 60229949e86Sstevel write_done: 603*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&test->in_test); 60429949e86Sstevel return (retval); 60529949e86Sstevel } 606