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