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