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
ac_mapin(uint64_t pa,caddr_t va)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
ac_unmap(caddr_t va)91 ac_unmap(caddr_t va)
92 {
93 vtag_flushpage(va, (uint64_t)ksfmmup);
94 }
95
96 int
ac_mem_test_start(ac_cfga_pkt_t * pkt,int flag)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
ac_mem_test_stop(ac_cfga_pkt_t * pkt,int flag)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
ac_mem_test_stop_on_close(uint_t board,uint_t bank)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
ac_mem_test_read(ac_cfga_pkt_t * pkt,int flag)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
ac_mem_test_write(ac_cfga_pkt_t * pkt,int flag)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