xref: /onnv-gate/usr/src/uts/sun4u/sunfire/io/ac_test.c (revision 2241:592fbc504a44)
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