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 #include <sys/types.h>
28 #include <sys/conf.h>
29 #include <sys/ddi.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>
35 #include <sys/kmem.h>
36 #include <sys/vmem.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>
43 #include <sys/fhc.h>
44 #include <sys/ac.h>
45 #include <sys/jtag.h>
46 #include <sys/cpu_module.h>
47 #include <sys/spitregs.h>
48 #include <sys/vm.h>
49 #include <vm/seg_kmem.h>
50 #include <vm/hat_sfmmu.h>
51
52 /* memory setup parameters */
53 #define TEST_PAGESIZE MMU_PAGESIZE
54
55 struct test_info {
56 struct test_info *next; /* linked list of tests */
57 struct ac_mem_info *mem_info;
58 uint_t board;
59 uint_t bank;
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 */
64 };
65
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;
70
71 static mem_test_handle_t mem_test_sequence_id = 0;
72
73 void
ac_mapin(uint64_t pa,caddr_t va)74 ac_mapin(uint64_t pa, caddr_t va)
75 {
76 pfn_t pfn;
77 tte_t tte;
78
79 pfn = pa >> MMU_PAGESHIFT;
80 tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
81 TTE_PFN_INTHI(pfn);
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);
85
86 }
87
88 void
ac_unmap(caddr_t va)89 ac_unmap(caddr_t va)
90 {
91 vtag_flushpage(va, (uint64_t)ksfmmup);
92 }
93
94 int
ac_mem_test_start(ac_cfga_pkt_t * pkt,int flag)95 ac_mem_test_start(ac_cfga_pkt_t *pkt, int flag)
96 {
97 struct ac_soft_state *softsp;
98 struct ac_mem_info *mem_info;
99 struct bd_list *board;
100 struct test_info *test;
101 uint64_t decode;
102
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;
107 }
108
109 /*
110 * Is the specified bank testable?
111 */
112
113 board = fhc_bdlist_lock(pkt->softsp->board);
114 if (board == NULL || board->ac_softsp == NULL) {
115 fhc_bdlist_unlock();
116 AC_ERR_SET(pkt, AC_ERR_BD);
117 return (EINVAL);
118 }
119 ASSERT(pkt->softsp == board->ac_softsp);
120
121 /* verify the board is of the correct type */
122 switch (board->sc.type) {
123 case CPU_BOARD:
124 case MEM_BOARD:
125 break;
126 default:
127 fhc_bdlist_unlock();
128 AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
129 return (EINVAL);
130 }
131
132 /*
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.
136 */
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) {
143 fhc_bdlist_unlock();
144 AC_ERR_SET(pkt, AC_ERR_BD_STATE);
145 return (EINVAL);
146 }
147 if (mem_info->busy) { /* oops, testing? */
148 /*
149 * find the test entry
150 */
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)
156 break;
157 }
158 if (test == NULL) {
159 mutex_exit(&test_mutex);
160 fhc_bdlist_unlock();
161 /* Not busy testing. */
162 AC_ERR_SET(pkt, AC_ERR_BD_STATE);
163 return (EINVAL);
164 }
165
166 /*
167 * return the current test information to the new caller
168 */
169 if (ddi_copyout(&test->info, pkt->cmd_cfga.private,
170 sizeof (ac_mem_test_start_t), flag) != 0) {
171 mutex_exit(&test_mutex);
172 fhc_bdlist_unlock();
173 return (EFAULT); /* !broken user app */
174 }
175 mutex_exit(&test_mutex);
176 fhc_bdlist_unlock();
177 AC_ERR_SET(pkt, AC_ERR_MEM_BK);
178 return (EBUSY); /* signal bank in use */
179 }
180
181 /*
182 * at this point, we have an available bank to test.
183 * create a test buffer
184 */
185 test = kmem_zalloc(sizeof (struct test_info), KM_SLEEP);
186 test->va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
187
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);
203
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) {
207
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));
212
213 fhc_bdlist_unlock();
214 return (EFAULT);
215 }
216
217 mem_info->busy = TRUE;
218
219 /* finally link us into the test database */
220 mutex_enter(&test_mutex);
221 test->next = test_base;
222 test_base = test;
223 mutex_exit(&test_mutex);
224
225 fhc_bdlist_unlock();
226
227 #ifdef DEBUG
228 cmn_err(CE_NOTE, "!memtest: start test[%u]: board %d, bank %d",
229 test->info.handle, test->board, test->bank);
230 #endif /* DEBUG */
231 return (DDI_SUCCESS);
232 }
233
234 int
ac_mem_test_stop(ac_cfga_pkt_t * pkt,int flag)235 ac_mem_test_stop(ac_cfga_pkt_t *pkt, int flag)
236 {
237 struct test_info *test, **prev;
238 ac_mem_test_stop_t stop;
239
240 /* get test result information */
241 if (ddi_copyin(pkt->cmd_cfga.private, &stop,
242 sizeof (ac_mem_test_stop_t), flag) != 0)
243 return (EFAULT);
244
245 /* bdlist protects all state changes... */
246 (void) fhc_bdlist_lock(-1);
247
248 /* find the test */
249 mutex_enter(&test_mutex);
250 prev = &test_base;
251 for (test = test_base; test != NULL; test = test->next) {
252 if (test->info.handle == stop.handle)
253 break; /* found the test */
254 prev = &test->next;
255 }
256 if (test == NULL) {
257 mutex_exit(&test_mutex);
258 fhc_bdlist_unlock();
259 AC_ERR_SET(pkt, AC_ERR_MEM_TEST);
260 return (EINVAL);
261 }
262
263 #ifdef DEBUG
264 cmn_err(CE_NOTE,
265 "!memtest: stop test[%u]: board %d, bank %d,"
266 " condition %d",
267 test->info.handle, test->board,
268 test->bank, stop.condition);
269 #endif /* DEBUG */
270
271 /* first unlink us from the test list (to allow no more entries) */
272 *prev = test->next;
273
274 /* then, wait for current tests to complete */
275 while (test->in_test != 0)
276 delay(1);
277
278 mutex_exit(&test_mutex);
279
280 /* clean up the test related allocations */
281 vmem_free(heap_arena, test->va, PAGESIZE);
282 kmem_free(test->bufp, TEST_PAGESIZE);
283
284 /* update the bank condition accordingly */
285 test->mem_info->condition = stop.condition;
286 test->mem_info->status_change = ddi_get_time();
287
288 test->mem_info->busy = FALSE;
289
290 /* finally, delete the test element */
291 kmem_free(test, sizeof (struct test_info));
292
293 fhc_bdlist_unlock();
294
295 return (DDI_SUCCESS);
296 }
297
298 void
ac_mem_test_stop_on_close(uint_t board,uint_t bank)299 ac_mem_test_stop_on_close(uint_t board, uint_t bank)
300 {
301 struct test_info *test, **prev;
302 sysc_cfga_cond_t condition = SYSC_CFGA_COND_UNKNOWN;
303
304 /* bdlist protects all state changes... */
305 (void) fhc_bdlist_lock(-1);
306
307 /* find the test */
308 mutex_enter(&test_mutex);
309 prev = &test_base;
310 for (test = test_base; test != NULL; test = test->next) {
311 if (test->board == board && test->bank == bank)
312 break; /* found the test */
313 prev = &test->next;
314 }
315 if (test == NULL) {
316 /* No test running, nothing to do. */
317 mutex_exit(&test_mutex);
318 fhc_bdlist_unlock();
319 return;
320 }
321
322 #ifdef DEBUG
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);
326 #endif /* DEBUG */
327
328 /* first unlink us from the test list (to allow no more entries) */
329 *prev = test->next;
330
331 ASSERT(test->in_test == 0);
332
333 mutex_exit(&test_mutex);
334
335 /* clean up the test related allocations */
336 vmem_free(heap_arena, test->va, PAGESIZE);
337 kmem_free(test->bufp, TEST_PAGESIZE);
338
339 /* update the bank condition accordingly */
340 test->mem_info->condition = condition;
341 test->mem_info->status_change = ddi_get_time();
342
343 test->mem_info->busy = FALSE;
344
345 /* finally, delete the test element */
346 kmem_free(test, sizeof (struct test_info));
347
348 fhc_bdlist_unlock();
349 }
350
351 int
ac_mem_test_read(ac_cfga_pkt_t * pkt,int flag)352 ac_mem_test_read(ac_cfga_pkt_t *pkt, int flag)
353 {
354 struct test_info *test;
355 uint_t page_offset;
356 uint64_t page_pa;
357 uint_t pstate_save;
358 caddr_t src_va, dst_va;
359 uint64_t orig_err;
360 int retval = DDI_SUCCESS;
361 sunfire_processor_error_regs_t error_buf;
362 int error_found;
363 ac_mem_test_read_t t_read;
364
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;
369
370 if (ddi_copyin(pkt->cmd_cfga.private, &t_read32,
371 sizeof (ac_mem_test_read32_t), flag) != 0)
372 return (EFAULT);
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;
378 break;
379 }
380 case DDI_MODEL_NONE:
381 if (ddi_copyin(pkt->cmd_cfga.private, &t_read,
382 sizeof (ac_mem_test_read_t), flag) != 0)
383 return (EFAULT);
384 break;
385 }
386 #else /* _MULTI_DATAMODEL */
387 if (ddi_copyin(pkt->cmd_cfga.private, &t_read,
388 sizeof (ac_mem_test_read_t), flag) != 0)
389 return (EFAULT);
390 #endif /* _MULTI_DATAMODEL */
391
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)
396 break;
397 }
398 if (test == NULL) {
399 mutex_exit(&test_mutex);
400 AC_ERR_SET(pkt, AC_ERR_MEM_TEST);
401 return (EINVAL);
402 }
403
404 /* bump the busy bit */
405 atomic_inc_32(&test->in_test);
406 mutex_exit(&test_mutex);
407
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);
419 retval = EINVAL;
420 goto read_done;
421 }
422
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;
428
429 /* time to go quiet */
430 kpreempt_disable();
431
432 /* we need a va for the block instructions */
433 ac_mapin(page_pa, test->va);
434
435 pstate_save = disable_vec_intr();
436
437 /* disable errors */
438 orig_err = get_error_enable();
439 set_error_enable(orig_err & ~(EER_CEEN | EER_NCEEN));
440
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);
444
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));
451
452 /*
453 * clean up after our no-error copy but before enabling ints.
454 * XXX what to do about other error types?
455 */
456 if (error_buf.afsr & (P_AFSR_CE | P_AFSR_UE)) {
457 extern void clr_datapath(void); /* XXX */
458
459 clr_datapath();
460 set_asyncflt(error_buf.afsr);
461 retval = EIO;
462 error_found = TRUE;
463 } else {
464 error_found = FALSE;
465 }
466
467 /* errors back on */
468 set_error_enable(orig_err);
469
470 enable_vec_intr(pstate_save);
471
472 /* tear down translation (who needs an mmu) */
473 ac_unmap(test->va);
474
475 /* we're back! */
476 kpreempt_enable();
477
478 /*
479 * If there was a data error, attempt to return the error_buf
480 * to the user.
481 */
482 if (error_found) {
483 if (ddi_copyout(&error_buf, t_read.error_buf,
484 sizeof (sunfire_processor_error_regs_t), flag) != 0) {
485 retval = EFAULT;
486 /* Keep going */
487 }
488 }
489
490 /*
491 * Then, return the page to the user (always)
492 */
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) {
495 retval = EFAULT;
496 }
497
498 read_done:
499 atomic_dec_32(&test->in_test);
500 return (retval);
501 }
502
503 int
ac_mem_test_write(ac_cfga_pkt_t * pkt,int flag)504 ac_mem_test_write(ac_cfga_pkt_t *pkt, int flag)
505 {
506 struct test_info *test;
507 uint_t page_offset;
508 uint64_t page_pa;
509 uint_t pstate_save;
510 caddr_t src_va, dst_va;
511 int retval = DDI_SUCCESS;
512 ac_mem_test_write_t t_write;
513
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;
518
519 if (ddi_copyin(pkt->cmd_cfga.private, &t_write32,
520 sizeof (ac_mem_test_write32_t), flag) != 0)
521 return (EFAULT);
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;
525 break;
526 }
527 case DDI_MODEL_NONE:
528 if (ddi_copyin(pkt->cmd_cfga.private, &t_write,
529 sizeof (ac_mem_test_write_t), flag) != 0)
530 return (EFAULT);
531 break;
532 }
533 #else /* _MULTI_DATAMODEL */
534 if (ddi_copyin(pkt->cmd_cfga.private, &t_write,
535 sizeof (ac_mem_test_write_t), flag) != 0)
536 return (EFAULT);
537 #endif /* _MULTI_DATAMODEL */
538
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)
543 break;
544 }
545 if (test == NULL) {
546 mutex_exit(&test_mutex);
547 return (EINVAL);
548 }
549
550 /* bump the busy bit */
551 atomic_inc_32(&test->in_test);
552 mutex_exit(&test_mutex);
553
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);
565 retval = EINVAL;
566 goto write_done;
567 }
568
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;
574
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) {
578 retval = EFAULT;
579 goto write_done;
580 }
581
582 /* time to go quiet */
583 kpreempt_disable();
584
585 /* we need a va for the block instructions */
586 ac_mapin(page_pa, test->va);
587
588 pstate_save = disable_vec_intr();
589
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);
593
594 enable_vec_intr(pstate_save);
595
596 /* tear down translation (who needs an mmu) */
597 ac_unmap(test->va);
598
599 /* we're back! */
600 kpreempt_enable();
601
602 write_done:
603 atomic_dec_32(&test->in_test);
604 return (retval);
605 }
606