11341Sstevel /*
21341Sstevel * CDDL HEADER START
31341Sstevel *
41341Sstevel * The contents of this file are subject to the terms of the
51341Sstevel * Common Development and Distribution License (the "License").
61341Sstevel * You may not use this file except in compliance with the License.
71341Sstevel *
81341Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91341Sstevel * or http://www.opensolaris.org/os/licensing.
101341Sstevel * See the License for the specific language governing permissions
111341Sstevel * and limitations under the License.
121341Sstevel *
131341Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141341Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151341Sstevel * If applicable, add the following below this CDDL HEADER, with the
161341Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171341Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181341Sstevel *
191341Sstevel * CDDL HEADER END
201341Sstevel */
211341Sstevel
221341Sstevel /*
231341Sstevel * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
241341Sstevel * Use is subject to license terms.
251341Sstevel */
261341Sstevel
271341Sstevel #pragma ident "%Z%%M% %I% %E% SMI"
281341Sstevel
291341Sstevel #include <sys/types.h>
301341Sstevel #include <sys/conf.h>
311341Sstevel #include <sys/ddi.h>
321341Sstevel #include <sys/sunddi.h>
331341Sstevel #include <sys/ddi_impldefs.h>
341341Sstevel #include <sys/obpdefs.h>
351341Sstevel #include <sys/cmn_err.h>
361341Sstevel #include <sys/errno.h>
371341Sstevel #include <sys/kmem.h>
381341Sstevel #include <sys/vmem.h>
391341Sstevel #include <sys/debug.h>
401341Sstevel #include <sys/sysmacros.h>
411341Sstevel #include <sys/machsystm.h>
421341Sstevel #include <sys/machparam.h>
431341Sstevel #include <sys/modctl.h>
441341Sstevel #include <sys/atomic.h>
451341Sstevel #include <sys/fhc.h>
461341Sstevel #include <sys/ac.h>
471341Sstevel #include <sys/jtag.h>
481341Sstevel #include <sys/cpu_module.h>
491341Sstevel #include <sys/spitregs.h>
501341Sstevel #include <sys/vm.h>
511341Sstevel #include <vm/seg_kmem.h>
521341Sstevel #include <vm/hat_sfmmu.h>
531341Sstevel
541341Sstevel /* memory setup parameters */
551341Sstevel #define TEST_PAGESIZE MMU_PAGESIZE
561341Sstevel
571341Sstevel struct test_info {
581341Sstevel struct test_info *next; /* linked list of tests */
591341Sstevel struct ac_mem_info *mem_info;
601341Sstevel uint_t board;
611341Sstevel uint_t bank;
621341Sstevel caddr_t bufp; /* pointer to buffer page */
631341Sstevel caddr_t va; /* test target VA */
641341Sstevel ac_mem_test_start_t info;
651341Sstevel uint_t in_test; /* count of threads in test */
661341Sstevel };
671341Sstevel
681341Sstevel /* list of tests in progress (list protected test_mutex) */
691341Sstevel static struct test_info *test_base = NULL;
701341Sstevel static kmutex_t test_mutex;
711341Sstevel static int test_mutex_initialized = FALSE;
721341Sstevel
731341Sstevel static mem_test_handle_t mem_test_sequence_id = 0;
741341Sstevel
751341Sstevel void
ac_mapin(uint64_t pa,caddr_t va)761341Sstevel ac_mapin(uint64_t pa, caddr_t va)
771341Sstevel {
781341Sstevel pfn_t pfn;
791341Sstevel tte_t tte;
801341Sstevel
811341Sstevel pfn = pa >> MMU_PAGESHIFT;
821341Sstevel tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
831341Sstevel TTE_PFN_INTHI(pfn);
841341Sstevel tte.tte_intlo = TTE_PFN_INTLO(pfn) | TTE_CP_INT |
851341Sstevel TTE_PRIV_INT | TTE_LCK_INT | TTE_HWWR_INT;
86*2241Shuah sfmmu_dtlb_ld_kva(va, &tte);
871341Sstevel
881341Sstevel }
891341Sstevel
901341Sstevel void
ac_unmap(caddr_t va)911341Sstevel ac_unmap(caddr_t va)
921341Sstevel {
93*2241Shuah vtag_flushpage(va, (uint64_t)ksfmmup);
941341Sstevel }
951341Sstevel
961341Sstevel int
ac_mem_test_start(ac_cfga_pkt_t * pkt,int flag)971341Sstevel ac_mem_test_start(ac_cfga_pkt_t *pkt, int flag)
981341Sstevel {
991341Sstevel struct ac_soft_state *softsp;
1001341Sstevel struct ac_mem_info *mem_info;
1011341Sstevel struct bd_list *board;
1021341Sstevel struct test_info *test;
1031341Sstevel uint64_t decode;
1041341Sstevel
1051341Sstevel /* XXX if ac ever detaches... */
1061341Sstevel if (test_mutex_initialized == FALSE) {
1071341Sstevel mutex_init(&test_mutex, NULL, MUTEX_DEFAULT, NULL);
1081341Sstevel test_mutex_initialized = TRUE;
1091341Sstevel }
1101341Sstevel
1111341Sstevel /*
1121341Sstevel * Is the specified bank testable?
1131341Sstevel */
1141341Sstevel
1151341Sstevel board = fhc_bdlist_lock(pkt->softsp->board);
1161341Sstevel if (board == NULL || board->ac_softsp == NULL) {
1171341Sstevel fhc_bdlist_unlock();
1181341Sstevel AC_ERR_SET(pkt, AC_ERR_BD);
1191341Sstevel return (EINVAL);
1201341Sstevel }
1211341Sstevel ASSERT(pkt->softsp == board->ac_softsp);
1221341Sstevel
1231341Sstevel /* verify the board is of the correct type */
1241341Sstevel switch (board->sc.type) {
1251341Sstevel case CPU_BOARD:
1261341Sstevel case MEM_BOARD:
1271341Sstevel break;
1281341Sstevel default:
1291341Sstevel fhc_bdlist_unlock();
1301341Sstevel AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
1311341Sstevel return (EINVAL);
1321341Sstevel }
1331341Sstevel
1341341Sstevel /*
1351341Sstevel * Memory must be in the spare state to be testable.
1361341Sstevel * However, spare memory that is testing can't be tested
1371341Sstevel * again, instead return the current test info.
1381341Sstevel */
1391341Sstevel softsp = pkt->softsp;
1401341Sstevel mem_info = &softsp->bank[pkt->bank];
1411341Sstevel if (!MEM_BOARD_VISIBLE(board) ||
1421341Sstevel fhc_bd_busy(softsp->board) ||
1431341Sstevel mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
1441341Sstevel mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED) {
1451341Sstevel fhc_bdlist_unlock();
1461341Sstevel AC_ERR_SET(pkt, AC_ERR_BD_STATE);
1471341Sstevel return (EINVAL);
1481341Sstevel }
1491341Sstevel if (mem_info->busy) { /* oops, testing? */
1501341Sstevel /*
1511341Sstevel * find the test entry
1521341Sstevel */
1531341Sstevel ASSERT(test_mutex_initialized);
1541341Sstevel mutex_enter(&test_mutex);
1551341Sstevel for (test = test_base; test != NULL; test = test->next) {
1561341Sstevel if (test->board == softsp->board &&
1571341Sstevel test->bank == pkt->bank)
1581341Sstevel break;
1591341Sstevel }
1601341Sstevel if (test == NULL) {
1611341Sstevel mutex_exit(&test_mutex);
1621341Sstevel fhc_bdlist_unlock();
1631341Sstevel /* Not busy testing. */
1641341Sstevel AC_ERR_SET(pkt, AC_ERR_BD_STATE);
1651341Sstevel return (EINVAL);
1661341Sstevel }
1671341Sstevel
1681341Sstevel /*
1691341Sstevel * return the current test information to the new caller
1701341Sstevel */
1711341Sstevel if (ddi_copyout(&test->info, pkt->cmd_cfga.private,
1721341Sstevel sizeof (ac_mem_test_start_t), flag) != 0) {
1731341Sstevel mutex_exit(&test_mutex);
1741341Sstevel fhc_bdlist_unlock();
1751341Sstevel return (EFAULT); /* !broken user app */
1761341Sstevel }
1771341Sstevel mutex_exit(&test_mutex);
1781341Sstevel fhc_bdlist_unlock();
1791341Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_BK);
1801341Sstevel return (EBUSY); /* signal bank in use */
1811341Sstevel }
1821341Sstevel
1831341Sstevel /*
1841341Sstevel * at this point, we have an available bank to test.
1851341Sstevel * create a test buffer
1861341Sstevel */
1871341Sstevel test = kmem_zalloc(sizeof (struct test_info), KM_SLEEP);
1881341Sstevel test->va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
1891341Sstevel
1901341Sstevel /* fill in all the test info details now */
1911341Sstevel test->mem_info = mem_info;
1921341Sstevel test->board = softsp->board;
1931341Sstevel test->bank = pkt->bank;
1941341Sstevel test->bufp = kmem_alloc(TEST_PAGESIZE, KM_SLEEP);
1951341Sstevel test->info.handle = atomic_add_32_nv(&mem_test_sequence_id, 1);
1961341Sstevel (void) drv_getparm(PPID, (ulong_t *)(&(test->info.tester_pid)));
1971341Sstevel test->info.prev_condition = mem_info->condition;
1981341Sstevel test->info.page_size = TEST_PAGESIZE;
1991341Sstevel /* If Blackbird ever gets a variable line size, this will change. */
2001341Sstevel test->info.line_size = cpunodes[CPU->cpu_id].ecache_linesize;
2011341Sstevel decode = (pkt->bank == Bank0) ?
2021341Sstevel *softsp->ac_memdecode0 : *softsp->ac_memdecode1;
2031341Sstevel test->info.afar_base = GRP_REALBASE(decode);
2041341Sstevel test->info.bank_size = GRP_UK2SPAN(decode);
2051341Sstevel
2061341Sstevel /* return the information to the user */
2071341Sstevel if (ddi_copyout(&test->info, pkt->cmd_cfga.private,
2081341Sstevel sizeof (ac_mem_test_start_t), flag) != 0) {
2091341Sstevel
2101341Sstevel /* oh well, tear down the test now */
2111341Sstevel kmem_free(test->bufp, TEST_PAGESIZE);
2121341Sstevel vmem_free(heap_arena, test->va, PAGESIZE);
2131341Sstevel kmem_free(test, sizeof (struct test_info));
2141341Sstevel
2151341Sstevel fhc_bdlist_unlock();
2161341Sstevel return (EFAULT);
2171341Sstevel }
2181341Sstevel
2191341Sstevel mem_info->busy = TRUE;
2201341Sstevel
2211341Sstevel /* finally link us into the test database */
2221341Sstevel mutex_enter(&test_mutex);
2231341Sstevel test->next = test_base;
2241341Sstevel test_base = test;
2251341Sstevel mutex_exit(&test_mutex);
2261341Sstevel
2271341Sstevel fhc_bdlist_unlock();
2281341Sstevel
2291341Sstevel #ifdef DEBUG
2301341Sstevel cmn_err(CE_NOTE, "!memtest: start test[%u]: board %d, bank %d",
2311341Sstevel test->info.handle, test->board, test->bank);
2321341Sstevel #endif /* DEBUG */
2331341Sstevel return (DDI_SUCCESS);
2341341Sstevel }
2351341Sstevel
2361341Sstevel int
ac_mem_test_stop(ac_cfga_pkt_t * pkt,int flag)2371341Sstevel ac_mem_test_stop(ac_cfga_pkt_t *pkt, int flag)
2381341Sstevel {
2391341Sstevel struct test_info *test, **prev;
2401341Sstevel ac_mem_test_stop_t stop;
2411341Sstevel
2421341Sstevel /* get test result information */
2431341Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &stop,
2441341Sstevel sizeof (ac_mem_test_stop_t), flag) != 0)
2451341Sstevel return (EFAULT);
2461341Sstevel
2471341Sstevel /* bdlist protects all state changes... */
2481341Sstevel (void) fhc_bdlist_lock(-1);
2491341Sstevel
2501341Sstevel /* find the test */
2511341Sstevel mutex_enter(&test_mutex);
2521341Sstevel prev = &test_base;
2531341Sstevel for (test = test_base; test != NULL; test = test->next) {
2541341Sstevel if (test->info.handle == stop.handle)
2551341Sstevel break; /* found the test */
2561341Sstevel prev = &test->next;
2571341Sstevel }
2581341Sstevel if (test == NULL) {
2591341Sstevel mutex_exit(&test_mutex);
2601341Sstevel fhc_bdlist_unlock();
2611341Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST);
2621341Sstevel return (EINVAL);
2631341Sstevel }
2641341Sstevel
2651341Sstevel #ifdef DEBUG
2661341Sstevel cmn_err(CE_NOTE,
2671341Sstevel "!memtest: stop test[%u]: board %d, bank %d,"
2681341Sstevel " condition %d",
2691341Sstevel test->info.handle, test->board,
2701341Sstevel test->bank, stop.condition);
2711341Sstevel #endif /* DEBUG */
2721341Sstevel
2731341Sstevel /* first unlink us from the test list (to allow no more entries) */
2741341Sstevel *prev = test->next;
2751341Sstevel
2761341Sstevel /* then, wait for current tests to complete */
2771341Sstevel while (test->in_test != 0)
2781341Sstevel delay(1);
2791341Sstevel
2801341Sstevel mutex_exit(&test_mutex);
2811341Sstevel
2821341Sstevel /* clean up the test related allocations */
2831341Sstevel vmem_free(heap_arena, test->va, PAGESIZE);
2841341Sstevel kmem_free(test->bufp, TEST_PAGESIZE);
2851341Sstevel
2861341Sstevel /* update the bank condition accordingly */
2871341Sstevel test->mem_info->condition = stop.condition;
2881341Sstevel test->mem_info->status_change = ddi_get_time();
2891341Sstevel
2901341Sstevel test->mem_info->busy = FALSE;
2911341Sstevel
2921341Sstevel /* finally, delete the test element */
2931341Sstevel kmem_free(test, sizeof (struct test_info));
2941341Sstevel
2951341Sstevel fhc_bdlist_unlock();
2961341Sstevel
2971341Sstevel return (DDI_SUCCESS);
2981341Sstevel }
2991341Sstevel
3001341Sstevel void
ac_mem_test_stop_on_close(uint_t board,uint_t bank)3011341Sstevel ac_mem_test_stop_on_close(uint_t board, uint_t bank)
3021341Sstevel {
3031341Sstevel struct test_info *test, **prev;
3041341Sstevel sysc_cfga_cond_t condition = SYSC_CFGA_COND_UNKNOWN;
3051341Sstevel
3061341Sstevel /* bdlist protects all state changes... */
3071341Sstevel (void) fhc_bdlist_lock(-1);
3081341Sstevel
3091341Sstevel /* find the test */
3101341Sstevel mutex_enter(&test_mutex);
3111341Sstevel prev = &test_base;
3121341Sstevel for (test = test_base; test != NULL; test = test->next) {
3131341Sstevel if (test->board == board && test->bank == bank)
3141341Sstevel break; /* found the test */
3151341Sstevel prev = &test->next;
3161341Sstevel }
3171341Sstevel if (test == NULL) {
3181341Sstevel /* No test running, nothing to do. */
3191341Sstevel mutex_exit(&test_mutex);
3201341Sstevel fhc_bdlist_unlock();
3211341Sstevel return;
3221341Sstevel }
3231341Sstevel
3241341Sstevel #ifdef DEBUG
3251341Sstevel cmn_err(CE_NOTE, "!memtest: stop test[%u] on close: "
3261341Sstevel "board %d, bank %d, condition %d", test->info.handle,
3271341Sstevel test->board, test->bank, condition);
3281341Sstevel #endif /* DEBUG */
3291341Sstevel
3301341Sstevel /* first unlink us from the test list (to allow no more entries) */
3311341Sstevel *prev = test->next;
3321341Sstevel
3331341Sstevel ASSERT(test->in_test == 0);
3341341Sstevel
3351341Sstevel mutex_exit(&test_mutex);
3361341Sstevel
3371341Sstevel /* clean up the test related allocations */
3381341Sstevel vmem_free(heap_arena, test->va, PAGESIZE);
3391341Sstevel kmem_free(test->bufp, TEST_PAGESIZE);
3401341Sstevel
3411341Sstevel /* update the bank condition accordingly */
3421341Sstevel test->mem_info->condition = condition;
3431341Sstevel test->mem_info->status_change = ddi_get_time();
3441341Sstevel
3451341Sstevel test->mem_info->busy = FALSE;
3461341Sstevel
3471341Sstevel /* finally, delete the test element */
3481341Sstevel kmem_free(test, sizeof (struct test_info));
3491341Sstevel
3501341Sstevel fhc_bdlist_unlock();
3511341Sstevel }
3521341Sstevel
3531341Sstevel int
ac_mem_test_read(ac_cfga_pkt_t * pkt,int flag)3541341Sstevel ac_mem_test_read(ac_cfga_pkt_t *pkt, int flag)
3551341Sstevel {
3561341Sstevel struct test_info *test;
3571341Sstevel uint_t page_offset;
3581341Sstevel uint64_t page_pa;
3591341Sstevel uint_t pstate_save;
3601341Sstevel caddr_t src_va, dst_va;
3611341Sstevel uint64_t orig_err;
3621341Sstevel int retval = DDI_SUCCESS;
3631341Sstevel sunfire_processor_error_regs_t error_buf;
3641341Sstevel int error_found;
3651341Sstevel ac_mem_test_read_t t_read;
3661341Sstevel
3671341Sstevel #ifdef _MULTI_DATAMODEL
3681341Sstevel switch (ddi_model_convert_from(flag & FMODELS)) {
3691341Sstevel case DDI_MODEL_ILP32: {
3701341Sstevel ac_mem_test_read32_t t_read32;
3711341Sstevel
3721341Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_read32,
3731341Sstevel sizeof (ac_mem_test_read32_t), flag) != 0)
3741341Sstevel return (EFAULT);
3751341Sstevel t_read.handle = t_read32.handle;
3761341Sstevel t_read.page_buf = (void *)(uintptr_t)t_read32.page_buf;
3771341Sstevel t_read.address = t_read32.address;
3781341Sstevel t_read.error_buf = (sunfire_processor_error_regs_t *)
3791341Sstevel (uintptr_t)t_read32.error_buf;
3801341Sstevel break;
3811341Sstevel }
3821341Sstevel case DDI_MODEL_NONE:
3831341Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_read,
3841341Sstevel sizeof (ac_mem_test_read_t), flag) != 0)
3851341Sstevel return (EFAULT);
3861341Sstevel break;
3871341Sstevel }
3881341Sstevel #else /* _MULTI_DATAMODEL */
3891341Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_read,
3901341Sstevel sizeof (ac_mem_test_read_t), flag) != 0)
3911341Sstevel return (EFAULT);
3921341Sstevel #endif /* _MULTI_DATAMODEL */
3931341Sstevel
3941341Sstevel /* verify the handle */
3951341Sstevel mutex_enter(&test_mutex);
3961341Sstevel for (test = test_base; test != NULL; test = test->next) {
3971341Sstevel if (test->info.handle == t_read.handle)
3981341Sstevel break;
3991341Sstevel }
4001341Sstevel if (test == NULL) {
4011341Sstevel mutex_exit(&test_mutex);
4021341Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST);
4031341Sstevel return (EINVAL);
4041341Sstevel }
4051341Sstevel
4061341Sstevel /* bump the busy bit */
4071341Sstevel atomic_add_32(&test->in_test, 1);
4081341Sstevel mutex_exit(&test_mutex);
4091341Sstevel
4101341Sstevel /* verify the remaining parameters */
4111341Sstevel if ((t_read.address.page_num >=
4121341Sstevel test->info.bank_size / test->info.page_size) ||
4131341Sstevel (t_read.address.line_count == 0) ||
4141341Sstevel (t_read.address.line_count >
4151341Sstevel test->info.page_size / test->info.line_size) ||
4161341Sstevel (t_read.address.line_offset >=
4171341Sstevel test->info.page_size / test->info.line_size) ||
4181341Sstevel ((t_read.address.line_offset + t_read.address.line_count) >
4191341Sstevel test->info.page_size / test->info.line_size)) {
4201341Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST_PAR);
4211341Sstevel retval = EINVAL;
4221341Sstevel goto read_done;
4231341Sstevel }
4241341Sstevel
4251341Sstevel page_offset = t_read.address.line_offset * test->info.line_size;
4261341Sstevel page_pa = test->info.afar_base +
4271341Sstevel t_read.address.page_num * test->info.page_size;
4281341Sstevel dst_va = test->bufp + page_offset;
4291341Sstevel src_va = test->va + page_offset;
4301341Sstevel
4311341Sstevel /* time to go quiet */
4321341Sstevel kpreempt_disable();
4331341Sstevel
4341341Sstevel /* we need a va for the block instructions */
4351341Sstevel ac_mapin(page_pa, test->va);
4361341Sstevel
4371341Sstevel pstate_save = disable_vec_intr();
4381341Sstevel
4391341Sstevel /* disable errors */
4401341Sstevel orig_err = get_error_enable();
4411341Sstevel set_error_enable(orig_err & ~(EER_CEEN | EER_NCEEN));
4421341Sstevel
4431341Sstevel /* copy the data again (using our very special copy) */
4441341Sstevel ac_blkcopy(src_va, dst_va, t_read.address.line_count,
4451341Sstevel test->info.line_size);
4461341Sstevel
4471341Sstevel /* process errors (if any) */
4481341Sstevel error_buf.module_id = CPU->cpu_id;
4491341Sstevel get_asyncflt(&(error_buf.afsr));
4501341Sstevel get_asyncaddr(&(error_buf.afar));
4511341Sstevel get_udb_errors(&(error_buf.udbh_error_reg),
4521341Sstevel &(error_buf.udbl_error_reg));
4531341Sstevel
4541341Sstevel /*
4551341Sstevel * clean up after our no-error copy but before enabling ints.
4561341Sstevel * XXX what to do about other error types?
4571341Sstevel */
4581341Sstevel if (error_buf.afsr & (P_AFSR_CE | P_AFSR_UE)) {
4591341Sstevel extern void clr_datapath(void); /* XXX */
4601341Sstevel
4611341Sstevel clr_datapath();
4621341Sstevel set_asyncflt(error_buf.afsr);
4631341Sstevel retval = EIO;
4641341Sstevel error_found = TRUE;
4651341Sstevel } else {
4661341Sstevel error_found = FALSE;
4671341Sstevel }
4681341Sstevel
4691341Sstevel /* errors back on */
4701341Sstevel set_error_enable(orig_err);
4711341Sstevel
4721341Sstevel enable_vec_intr(pstate_save);
4731341Sstevel
4741341Sstevel /* tear down translation (who needs an mmu) */
4751341Sstevel ac_unmap(test->va);
4761341Sstevel
4771341Sstevel /* we're back! */
4781341Sstevel kpreempt_enable();
4791341Sstevel
4801341Sstevel /*
4811341Sstevel * If there was a data error, attempt to return the error_buf
4821341Sstevel * to the user.
4831341Sstevel */
4841341Sstevel if (error_found) {
4851341Sstevel if (ddi_copyout(&error_buf, t_read.error_buf,
4861341Sstevel sizeof (sunfire_processor_error_regs_t), flag) != 0) {
4871341Sstevel retval = EFAULT;
4881341Sstevel /* Keep going */
4891341Sstevel }
4901341Sstevel }
4911341Sstevel
4921341Sstevel /*
4931341Sstevel * Then, return the page to the user (always)
4941341Sstevel */
4951341Sstevel if (ddi_copyout(dst_va, (caddr_t)(t_read.page_buf) + page_offset,
4961341Sstevel t_read.address.line_count * test->info.line_size, flag) != 0) {
4971341Sstevel retval = EFAULT;
4981341Sstevel }
4991341Sstevel
5001341Sstevel read_done:
5011341Sstevel atomic_add_32(&test->in_test, -1);
5021341Sstevel return (retval);
5031341Sstevel }
5041341Sstevel
5051341Sstevel int
ac_mem_test_write(ac_cfga_pkt_t * pkt,int flag)5061341Sstevel ac_mem_test_write(ac_cfga_pkt_t *pkt, int flag)
5071341Sstevel {
5081341Sstevel struct test_info *test;
5091341Sstevel uint_t page_offset;
5101341Sstevel uint64_t page_pa;
5111341Sstevel uint_t pstate_save;
5121341Sstevel caddr_t src_va, dst_va;
5131341Sstevel int retval = DDI_SUCCESS;
5141341Sstevel ac_mem_test_write_t t_write;
5151341Sstevel
5161341Sstevel #ifdef _MULTI_DATAMODEL
5171341Sstevel switch (ddi_model_convert_from(flag & FMODELS)) {
5181341Sstevel case DDI_MODEL_ILP32: {
5191341Sstevel ac_mem_test_write32_t t_write32;
5201341Sstevel
5211341Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_write32,
5221341Sstevel sizeof (ac_mem_test_write32_t), flag) != 0)
5231341Sstevel return (EFAULT);
5241341Sstevel t_write.handle = t_write32.handle;
5251341Sstevel t_write.page_buf = (void *)(uintptr_t)t_write32.page_buf;
5261341Sstevel t_write.address = t_write32.address;
5271341Sstevel break;
5281341Sstevel }
5291341Sstevel case DDI_MODEL_NONE:
5301341Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_write,
5311341Sstevel sizeof (ac_mem_test_write_t), flag) != 0)
5321341Sstevel return (EFAULT);
5331341Sstevel break;
5341341Sstevel }
5351341Sstevel #else /* _MULTI_DATAMODEL */
5361341Sstevel if (ddi_copyin(pkt->cmd_cfga.private, &t_write,
5371341Sstevel sizeof (ac_mem_test_write_t), flag) != 0)
5381341Sstevel return (EFAULT);
5391341Sstevel #endif /* _MULTI_DATAMODEL */
5401341Sstevel
5411341Sstevel /* verify the handle */
5421341Sstevel mutex_enter(&test_mutex);
5431341Sstevel for (test = test_base; test != NULL; test = test->next) {
5441341Sstevel if (test->info.handle == t_write.handle)
5451341Sstevel break;
5461341Sstevel }
5471341Sstevel if (test == NULL) {
5481341Sstevel mutex_exit(&test_mutex);
5491341Sstevel return (EINVAL);
5501341Sstevel }
5511341Sstevel
5521341Sstevel /* bump the busy bit */
5531341Sstevel atomic_add_32(&test->in_test, 1);
5541341Sstevel mutex_exit(&test_mutex);
5551341Sstevel
5561341Sstevel /* verify the remaining parameters */
5571341Sstevel if ((t_write.address.page_num >=
5581341Sstevel test->info.bank_size / test->info.page_size) ||
5591341Sstevel (t_write.address.line_count == 0) ||
5601341Sstevel (t_write.address.line_count >
5611341Sstevel test->info.page_size / test->info.line_size) ||
5621341Sstevel (t_write.address.line_offset >=
5631341Sstevel test->info.page_size / test->info.line_size) ||
5641341Sstevel ((t_write.address.line_offset + t_write.address.line_count) >
5651341Sstevel test->info.page_size / test->info.line_size)) {
5661341Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_TEST_PAR);
5671341Sstevel retval = EINVAL;
5681341Sstevel goto write_done;
5691341Sstevel }
5701341Sstevel
5711341Sstevel page_offset = t_write.address.line_offset * test->info.line_size;
5721341Sstevel page_pa = test->info.afar_base +
5731341Sstevel t_write.address.page_num * test->info.page_size;
5741341Sstevel src_va = test->bufp + page_offset;
5751341Sstevel dst_va = test->va + page_offset;
5761341Sstevel
5771341Sstevel /* copy in the specified user data */
5781341Sstevel if (ddi_copyin((caddr_t)(t_write.page_buf) + page_offset, src_va,
5791341Sstevel t_write.address.line_count * test->info.line_size, flag) != 0) {
5801341Sstevel retval = EFAULT;
5811341Sstevel goto write_done;
5821341Sstevel }
5831341Sstevel
5841341Sstevel /* time to go quiet */
5851341Sstevel kpreempt_disable();
5861341Sstevel
5871341Sstevel /* we need a va for the block instructions */
5881341Sstevel ac_mapin(page_pa, test->va);
5891341Sstevel
5901341Sstevel pstate_save = disable_vec_intr();
5911341Sstevel
5921341Sstevel /* copy the data again (using our very special copy) */
5931341Sstevel ac_blkcopy(src_va, dst_va, t_write.address.line_count,
5941341Sstevel test->info.line_size);
5951341Sstevel
5961341Sstevel enable_vec_intr(pstate_save);
5971341Sstevel
5981341Sstevel /* tear down translation (who needs an mmu) */
5991341Sstevel ac_unmap(test->va);
6001341Sstevel
6011341Sstevel /* we're back! */
6021341Sstevel kpreempt_enable();
6031341Sstevel
6041341Sstevel write_done:
6051341Sstevel atomic_add_32(&test->in_test, -1);
6061341Sstevel return (retval);
6071341Sstevel }
608