xref: /illumos-gate/usr/src/uts/sun4u/sunfire/io/ac_test.c (revision 1a5e258f)
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
ac_mapin(uint64_t pa,caddr_t va)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
ac_unmap(caddr_t va)8929949e86Sstevel ac_unmap(caddr_t va)
9029949e86Sstevel {
911e2e7a75Shuah 	vtag_flushpage(va, (uint64_t)ksfmmup);
9229949e86Sstevel }
9329949e86Sstevel 
9429949e86Sstevel int
ac_mem_test_start(ac_cfga_pkt_t * pkt,int flag)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
ac_mem_test_stop(ac_cfga_pkt_t * pkt,int flag)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
ac_mem_test_stop_on_close(uint_t board,uint_t bank)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
ac_mem_test_read(ac_cfga_pkt_t * pkt,int flag)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
ac_mem_test_write(ac_cfga_pkt_t * pkt,int flag)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