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.
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.
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]
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
30 #include <sys/sunddi.h>
31 #include <sys/ddi_impldefs.h>
32 #include <sys/obpdefs.h>
33 #include <sys/cmn_err.h>
34 #include <sys/errno.h>
37 #include <sys/debug.h>
38 #include <sys/sysmacros.h>
39 #include <sys/machsystm.h>
40 #include <sys/machparam.h>
41 #include <sys/modctl.h>
42 #include <sys/atomic.h>
46 #include <sys/cpu_module.h>
47 #include <sys/spitregs.h>
49 #include <vm/seg_kmem.h>
50 #include <vm/hat_sfmmu.h>
52 /* memory setup parameters */
53 #define TEST_PAGESIZE MMU_PAGESIZE
56 struct test_info
*next
; /* linked list of tests */
57 struct ac_mem_info
*mem_info
;
60 caddr_t bufp
; /* pointer to buffer page */
61 caddr_t va
; /* test target VA */
62 ac_mem_test_start_t info
;
63 uint_t in_test
; /* count of threads in test */
66 /* list of tests in progress (list protected test_mutex) */
67 static struct test_info
*test_base
= NULL
;
68 static kmutex_t test_mutex
;
69 static int test_mutex_initialized
= FALSE
;
71 static mem_test_handle_t mem_test_sequence_id
= 0;
74 ac_mapin(uint64_t pa
, caddr_t va
)
79 pfn
= pa
>> MMU_PAGESHIFT
;
80 tte
.tte_inthi
= TTE_VALID_INT
| TTE_SZ_INT(TTE8K
) |
82 tte
.tte_intlo
= TTE_PFN_INTLO(pfn
) | TTE_CP_INT
|
83 TTE_PRIV_INT
| TTE_LCK_INT
| TTE_HWWR_INT
;
84 sfmmu_dtlb_ld_kva(va
, &tte
);
91 vtag_flushpage(va
, (uint64_t)ksfmmup
);
95 ac_mem_test_start(ac_cfga_pkt_t
*pkt
, int flag
)
97 struct ac_soft_state
*softsp
;
98 struct ac_mem_info
*mem_info
;
99 struct bd_list
*board
;
100 struct test_info
*test
;
103 /* XXX if ac ever detaches... */
104 if (test_mutex_initialized
== FALSE
) {
105 mutex_init(&test_mutex
, NULL
, MUTEX_DEFAULT
, NULL
);
106 test_mutex_initialized
= TRUE
;
110 * Is the specified bank testable?
113 board
= fhc_bdlist_lock(pkt
->softsp
->board
);
114 if (board
== NULL
|| board
->ac_softsp
== NULL
) {
116 AC_ERR_SET(pkt
, AC_ERR_BD
);
119 ASSERT(pkt
->softsp
== board
->ac_softsp
);
121 /* verify the board is of the correct type */
122 switch (board
->sc
.type
) {
128 AC_ERR_SET(pkt
, AC_ERR_BD_TYPE
);
133 * Memory must be in the spare state to be testable.
134 * However, spare memory that is testing can't be tested
135 * again, instead return the current test info.
137 softsp
= pkt
->softsp
;
138 mem_info
= &softsp
->bank
[pkt
->bank
];
139 if (!MEM_BOARD_VISIBLE(board
) ||
140 fhc_bd_busy(softsp
->board
) ||
141 mem_info
->rstate
!= SYSC_CFGA_RSTATE_CONNECTED
||
142 mem_info
->ostate
!= SYSC_CFGA_OSTATE_UNCONFIGURED
) {
144 AC_ERR_SET(pkt
, AC_ERR_BD_STATE
);
147 if (mem_info
->busy
) { /* oops, testing? */
149 * find the test entry
151 ASSERT(test_mutex_initialized
);
152 mutex_enter(&test_mutex
);
153 for (test
= test_base
; test
!= NULL
; test
= test
->next
) {
154 if (test
->board
== softsp
->board
&&
155 test
->bank
== pkt
->bank
)
159 mutex_exit(&test_mutex
);
161 /* Not busy testing. */
162 AC_ERR_SET(pkt
, AC_ERR_BD_STATE
);
167 * return the current test information to the new caller
169 if (ddi_copyout(&test
->info
, pkt
->cmd_cfga
.private,
170 sizeof (ac_mem_test_start_t
), flag
) != 0) {
171 mutex_exit(&test_mutex
);
173 return (EFAULT
); /* !broken user app */
175 mutex_exit(&test_mutex
);
177 AC_ERR_SET(pkt
, AC_ERR_MEM_BK
);
178 return (EBUSY
); /* signal bank in use */
182 * at this point, we have an available bank to test.
183 * create a test buffer
185 test
= kmem_zalloc(sizeof (struct test_info
), KM_SLEEP
);
186 test
->va
= vmem_alloc(heap_arena
, PAGESIZE
, VM_SLEEP
);
188 /* fill in all the test info details now */
189 test
->mem_info
= mem_info
;
190 test
->board
= softsp
->board
;
191 test
->bank
= pkt
->bank
;
192 test
->bufp
= kmem_alloc(TEST_PAGESIZE
, KM_SLEEP
);
193 test
->info
.handle
= atomic_inc_32_nv(&mem_test_sequence_id
);
194 (void) drv_getparm(PPID
, (ulong_t
*)(&(test
->info
.tester_pid
)));
195 test
->info
.prev_condition
= mem_info
->condition
;
196 test
->info
.page_size
= TEST_PAGESIZE
;
197 /* If Blackbird ever gets a variable line size, this will change. */
198 test
->info
.line_size
= cpunodes
[CPU
->cpu_id
].ecache_linesize
;
199 decode
= (pkt
->bank
== Bank0
) ?
200 *softsp
->ac_memdecode0
: *softsp
->ac_memdecode1
;
201 test
->info
.afar_base
= GRP_REALBASE(decode
);
202 test
->info
.bank_size
= GRP_UK2SPAN(decode
);
204 /* return the information to the user */
205 if (ddi_copyout(&test
->info
, pkt
->cmd_cfga
.private,
206 sizeof (ac_mem_test_start_t
), flag
) != 0) {
208 /* oh well, tear down the test now */
209 kmem_free(test
->bufp
, TEST_PAGESIZE
);
210 vmem_free(heap_arena
, test
->va
, PAGESIZE
);
211 kmem_free(test
, sizeof (struct test_info
));
217 mem_info
->busy
= TRUE
;
219 /* finally link us into the test database */
220 mutex_enter(&test_mutex
);
221 test
->next
= test_base
;
223 mutex_exit(&test_mutex
);
228 cmn_err(CE_NOTE
, "!memtest: start test[%u]: board %d, bank %d",
229 test
->info
.handle
, test
->board
, test
->bank
);
231 return (DDI_SUCCESS
);
235 ac_mem_test_stop(ac_cfga_pkt_t
*pkt
, int flag
)
237 struct test_info
*test
, **prev
;
238 ac_mem_test_stop_t stop
;
240 /* get test result information */
241 if (ddi_copyin(pkt
->cmd_cfga
.private, &stop
,
242 sizeof (ac_mem_test_stop_t
), flag
) != 0)
245 /* bdlist protects all state changes... */
246 (void) fhc_bdlist_lock(-1);
249 mutex_enter(&test_mutex
);
251 for (test
= test_base
; test
!= NULL
; test
= test
->next
) {
252 if (test
->info
.handle
== stop
.handle
)
253 break; /* found the test */
257 mutex_exit(&test_mutex
);
259 AC_ERR_SET(pkt
, AC_ERR_MEM_TEST
);
265 "!memtest: stop test[%u]: board %d, bank %d,"
267 test
->info
.handle
, test
->board
,
268 test
->bank
, stop
.condition
);
271 /* first unlink us from the test list (to allow no more entries) */
274 /* then, wait for current tests to complete */
275 while (test
->in_test
!= 0)
278 mutex_exit(&test_mutex
);
280 /* clean up the test related allocations */
281 vmem_free(heap_arena
, test
->va
, PAGESIZE
);
282 kmem_free(test
->bufp
, TEST_PAGESIZE
);
284 /* update the bank condition accordingly */
285 test
->mem_info
->condition
= stop
.condition
;
286 test
->mem_info
->status_change
= ddi_get_time();
288 test
->mem_info
->busy
= FALSE
;
290 /* finally, delete the test element */
291 kmem_free(test
, sizeof (struct test_info
));
295 return (DDI_SUCCESS
);
299 ac_mem_test_stop_on_close(uint_t board
, uint_t bank
)
301 struct test_info
*test
, **prev
;
302 sysc_cfga_cond_t condition
= SYSC_CFGA_COND_UNKNOWN
;
304 /* bdlist protects all state changes... */
305 (void) fhc_bdlist_lock(-1);
308 mutex_enter(&test_mutex
);
310 for (test
= test_base
; test
!= NULL
; test
= test
->next
) {
311 if (test
->board
== board
&& test
->bank
== bank
)
312 break; /* found the test */
316 /* No test running, nothing to do. */
317 mutex_exit(&test_mutex
);
323 cmn_err(CE_NOTE
, "!memtest: stop test[%u] on close: "
324 "board %d, bank %d, condition %d", test
->info
.handle
,
325 test
->board
, test
->bank
, condition
);
328 /* first unlink us from the test list (to allow no more entries) */
331 ASSERT(test
->in_test
== 0);
333 mutex_exit(&test_mutex
);
335 /* clean up the test related allocations */
336 vmem_free(heap_arena
, test
->va
, PAGESIZE
);
337 kmem_free(test
->bufp
, TEST_PAGESIZE
);
339 /* update the bank condition accordingly */
340 test
->mem_info
->condition
= condition
;
341 test
->mem_info
->status_change
= ddi_get_time();
343 test
->mem_info
->busy
= FALSE
;
345 /* finally, delete the test element */
346 kmem_free(test
, sizeof (struct test_info
));
352 ac_mem_test_read(ac_cfga_pkt_t
*pkt
, int flag
)
354 struct test_info
*test
;
358 caddr_t src_va
, dst_va
;
360 int retval
= DDI_SUCCESS
;
361 sunfire_processor_error_regs_t error_buf
;
363 ac_mem_test_read_t t_read
;
365 #ifdef _MULTI_DATAMODEL
366 switch (ddi_model_convert_from(flag
& FMODELS
)) {
367 case DDI_MODEL_ILP32
: {
368 ac_mem_test_read32_t t_read32
;
370 if (ddi_copyin(pkt
->cmd_cfga
.private, &t_read32
,
371 sizeof (ac_mem_test_read32_t
), flag
) != 0)
373 t_read
.handle
= t_read32
.handle
;
374 t_read
.page_buf
= (void *)(uintptr_t)t_read32
.page_buf
;
375 t_read
.address
= t_read32
.address
;
376 t_read
.error_buf
= (sunfire_processor_error_regs_t
*)
377 (uintptr_t)t_read32
.error_buf
;
381 if (ddi_copyin(pkt
->cmd_cfga
.private, &t_read
,
382 sizeof (ac_mem_test_read_t
), flag
) != 0)
386 #else /* _MULTI_DATAMODEL */
387 if (ddi_copyin(pkt
->cmd_cfga
.private, &t_read
,
388 sizeof (ac_mem_test_read_t
), flag
) != 0)
390 #endif /* _MULTI_DATAMODEL */
392 /* verify the handle */
393 mutex_enter(&test_mutex
);
394 for (test
= test_base
; test
!= NULL
; test
= test
->next
) {
395 if (test
->info
.handle
== t_read
.handle
)
399 mutex_exit(&test_mutex
);
400 AC_ERR_SET(pkt
, AC_ERR_MEM_TEST
);
404 /* bump the busy bit */
405 atomic_inc_32(&test
->in_test
);
406 mutex_exit(&test_mutex
);
408 /* verify the remaining parameters */
409 if ((t_read
.address
.page_num
>=
410 test
->info
.bank_size
/ test
->info
.page_size
) ||
411 (t_read
.address
.line_count
== 0) ||
412 (t_read
.address
.line_count
>
413 test
->info
.page_size
/ test
->info
.line_size
) ||
414 (t_read
.address
.line_offset
>=
415 test
->info
.page_size
/ test
->info
.line_size
) ||
416 ((t_read
.address
.line_offset
+ t_read
.address
.line_count
) >
417 test
->info
.page_size
/ test
->info
.line_size
)) {
418 AC_ERR_SET(pkt
, AC_ERR_MEM_TEST_PAR
);
423 page_offset
= t_read
.address
.line_offset
* test
->info
.line_size
;
424 page_pa
= test
->info
.afar_base
+
425 t_read
.address
.page_num
* test
->info
.page_size
;
426 dst_va
= test
->bufp
+ page_offset
;
427 src_va
= test
->va
+ page_offset
;
429 /* time to go quiet */
432 /* we need a va for the block instructions */
433 ac_mapin(page_pa
, test
->va
);
435 pstate_save
= disable_vec_intr();
438 orig_err
= get_error_enable();
439 set_error_enable(orig_err
& ~(EER_CEEN
| EER_NCEEN
));
441 /* copy the data again (using our very special copy) */
442 ac_blkcopy(src_va
, dst_va
, t_read
.address
.line_count
,
443 test
->info
.line_size
);
445 /* process errors (if any) */
446 error_buf
.module_id
= CPU
->cpu_id
;
447 get_asyncflt(&(error_buf
.afsr
));
448 get_asyncaddr(&(error_buf
.afar
));
449 get_udb_errors(&(error_buf
.udbh_error_reg
),
450 &(error_buf
.udbl_error_reg
));
453 * clean up after our no-error copy but before enabling ints.
454 * XXX what to do about other error types?
456 if (error_buf
.afsr
& (P_AFSR_CE
| P_AFSR_UE
)) {
457 extern void clr_datapath(void); /* XXX */
460 set_asyncflt(error_buf
.afsr
);
468 set_error_enable(orig_err
);
470 enable_vec_intr(pstate_save
);
472 /* tear down translation (who needs an mmu) */
479 * If there was a data error, attempt to return the error_buf
483 if (ddi_copyout(&error_buf
, t_read
.error_buf
,
484 sizeof (sunfire_processor_error_regs_t
), flag
) != 0) {
491 * Then, return the page to the user (always)
493 if (ddi_copyout(dst_va
, (caddr_t
)(t_read
.page_buf
) + page_offset
,
494 t_read
.address
.line_count
* test
->info
.line_size
, flag
) != 0) {
499 atomic_dec_32(&test
->in_test
);
504 ac_mem_test_write(ac_cfga_pkt_t
*pkt
, int flag
)
506 struct test_info
*test
;
510 caddr_t src_va
, dst_va
;
511 int retval
= DDI_SUCCESS
;
512 ac_mem_test_write_t t_write
;
514 #ifdef _MULTI_DATAMODEL
515 switch (ddi_model_convert_from(flag
& FMODELS
)) {
516 case DDI_MODEL_ILP32
: {
517 ac_mem_test_write32_t t_write32
;
519 if (ddi_copyin(pkt
->cmd_cfga
.private, &t_write32
,
520 sizeof (ac_mem_test_write32_t
), flag
) != 0)
522 t_write
.handle
= t_write32
.handle
;
523 t_write
.page_buf
= (void *)(uintptr_t)t_write32
.page_buf
;
524 t_write
.address
= t_write32
.address
;
528 if (ddi_copyin(pkt
->cmd_cfga
.private, &t_write
,
529 sizeof (ac_mem_test_write_t
), flag
) != 0)
533 #else /* _MULTI_DATAMODEL */
534 if (ddi_copyin(pkt
->cmd_cfga
.private, &t_write
,
535 sizeof (ac_mem_test_write_t
), flag
) != 0)
537 #endif /* _MULTI_DATAMODEL */
539 /* verify the handle */
540 mutex_enter(&test_mutex
);
541 for (test
= test_base
; test
!= NULL
; test
= test
->next
) {
542 if (test
->info
.handle
== t_write
.handle
)
546 mutex_exit(&test_mutex
);
550 /* bump the busy bit */
551 atomic_inc_32(&test
->in_test
);
552 mutex_exit(&test_mutex
);
554 /* verify the remaining parameters */
555 if ((t_write
.address
.page_num
>=
556 test
->info
.bank_size
/ test
->info
.page_size
) ||
557 (t_write
.address
.line_count
== 0) ||
558 (t_write
.address
.line_count
>
559 test
->info
.page_size
/ test
->info
.line_size
) ||
560 (t_write
.address
.line_offset
>=
561 test
->info
.page_size
/ test
->info
.line_size
) ||
562 ((t_write
.address
.line_offset
+ t_write
.address
.line_count
) >
563 test
->info
.page_size
/ test
->info
.line_size
)) {
564 AC_ERR_SET(pkt
, AC_ERR_MEM_TEST_PAR
);
569 page_offset
= t_write
.address
.line_offset
* test
->info
.line_size
;
570 page_pa
= test
->info
.afar_base
+
571 t_write
.address
.page_num
* test
->info
.page_size
;
572 src_va
= test
->bufp
+ page_offset
;
573 dst_va
= test
->va
+ page_offset
;
575 /* copy in the specified user data */
576 if (ddi_copyin((caddr_t
)(t_write
.page_buf
) + page_offset
, src_va
,
577 t_write
.address
.line_count
* test
->info
.line_size
, flag
) != 0) {
582 /* time to go quiet */
585 /* we need a va for the block instructions */
586 ac_mapin(page_pa
, test
->va
);
588 pstate_save
= disable_vec_intr();
590 /* copy the data again (using our very special copy) */
591 ac_blkcopy(src_va
, dst_va
, t_write
.address
.line_count
,
592 test
->info
.line_size
);
594 enable_vec_intr(pstate_save
);
596 /* tear down translation (who needs an mmu) */
603 atomic_dec_32(&test
->in_test
);