]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/Diagnostics.c
xnu-792.6.22.tar.gz
[apple/xnu.git] / osfmk / ppc / Diagnostics.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * @OSF_FREE_COPYRIGHT@
24 */
25 /*
26 * @APPLE_FREE_COPYRIGHT@
27 */
28
29 /*
30 * Author: Bill Angell, Apple
31 * Date: 9/auht-aught
32 *
33 * Random diagnostics
34 */
35
36
37 #include <kern/machine.h>
38 #include <kern/processor.h>
39 #include <mach/machine.h>
40 #include <mach/processor_info.h>
41 #include <mach/mach_types.h>
42 #include <mach/boolean.h>
43 #include <kern/thread.h>
44 #include <kern/task.h>
45 #include <kern/ipc_kobject.h>
46 #include <mach/vm_param.h>
47 #include <ipc/port.h>
48 #include <ipc/ipc_entry.h>
49 #include <ipc/ipc_space.h>
50 #include <ipc/ipc_object.h>
51 #include <ipc/ipc_port.h>
52 #include <vm/vm_kern.h>
53 #include <vm/vm_map.h>
54 #include <vm/vm_page.h>
55 #include <vm/pmap.h>
56 #include <ppc/cpu_internal.h>
57 #include <ppc/exception.h>
58 #include <ppc/Firmware.h>
59 #include <ppc/low_trace.h>
60 #include <ppc/db_low_trace.h>
61 #include <ppc/mappings.h>
62 #include <ppc/pmap.h>
63 #include <ppc/mem.h>
64 #include <ppc/savearea.h>
65 #include <ppc/Diagnostics.h>
66 #include <pexpert/pexpert.h>
67 #include <console/video_console.h>
68 #include <ppc/trap.h>
69
70 extern struct vc_info vinfo;
71 extern uint32_t warFlags;
72 #define warDisMBpoff 0x80000000
73
74 kern_return_t testPerfTrap(int trapno, struct savearea *ss,
75 unsigned int dsisr, addr64_t dar);
76
77
78 int diagCall(struct savearea *save) {
79
80 union {
81 unsigned long long tbase;
82 unsigned int tb[2];
83 } ttt, adj;
84 natural_t tbu, tbu2, tbl;
85 struct per_proc_info *per_proc; /* Area for my per_proc address */
86 int cpu, ret, subc;
87 unsigned int tstrt, tend, temp, temp2, *baddr, oldwar;
88 addr64_t src, snk;
89 uint64_t scom, hid1, hid4, srrwrk, stat;
90 scomcomm sarea;
91 ipc_port_t port;
92 ipc_entry_t ientry;
93 processor_t prssr;
94 vm_address_t addrs;
95
96
97 if(!(dgWork.dgFlags & enaDiagSCs)) return 0; /* If not enabled, cause an exception */
98
99 switch(save->save_r3) { /* Select the routine */
100
101 /*
102 * Adjust the timebase for drift recovery testing
103 */
104 case dgAdjTB: /* Adjust the timebase */
105
106 adj.tb[0] = 0; /* Clear high part */
107 adj.tb[1] = save->save_r4; /* Set low order */
108 if(adj.tb[1] & 0x80000000) adj.tb[0] = 0xFFFFFFFF; /* Propagate sign bit */
109
110 do { /* Read current time */
111 asm volatile(" mftbu %0" : "=r" (tbu));
112 asm volatile(" mftb %0" : "=r" (tbl));
113 asm volatile(" mftbu %0" : "=r" (tbu2));
114 } while (tbu != tbu2);
115
116 ttt.tb[0] = tbu; /* Set high */
117 ttt.tb[1] = tbl; /* Set low */
118
119 ttt.tbase = ttt.tbase + adj.tbase; /* Increment or decrement the TB */
120
121 tbu = ttt.tb[0]; /* Save in regular variable */
122 tbl = ttt.tb[1]; /* Save in regular variable */
123
124 mttb(0); /* Set low to keep from ticking */
125 mttbu(tbu); /* Set adjusted high */
126 mttb(tbl); /* Set adjusted low */
127
128 return -1; /* Return no AST checking... */
129
130 /*
131 * Return physical address of a page
132 */
133 case dgLRA:
134
135 save->save_r3 = pmap_find_phys(current_thread()->map->pmap, save->save_r4); /* Get read address */
136
137 return -1; /* Return no AST checking... */
138
139 /*
140 * Copy physical to virtual
141 */
142 case dgpcpy:
143
144
145 #if 1
146 src = (save->save_r4 << 32) | (0x00000000FFFFFFFFULL & save->save_r5); /* Merge into 64-bit */
147 snk = (save->save_r6 << 32) | (0x00000000FFFFFFFFULL & save->save_r7); /* Merge into 64-bit */
148 save->save_r3 = copypv(src, snk, save->save_r8, save->save_r9); /* Copy the physical page */
149 #endif
150 return 1; /* Return and check for ASTs... */
151
152 /*
153 * Read/Write physical memory
154 */
155 case dgprw:
156
157 src = (save->save_r5 << 32) | (0x00000000FFFFFFFFULL & save->save_r6); /* Merge into 64-bit */
158
159 switch(save->save_r4) { /* Select the actual function */
160
161 case 0:
162 save->save_r3 = (uint64_t)ml_phys_read_byte((unsigned int)src);
163 break;
164
165 case 1:
166 save->save_r3 = (uint64_t)ml_phys_read_byte_64(src);
167 break;
168
169 case 2:
170 save->save_r3 = (uint64_t)ml_phys_read((unsigned int)src);
171 break;
172
173 case 3:
174 save->save_r3 = (uint64_t)ml_phys_read_64(src);
175 break;
176
177 case 4:
178 ml_phys_write_byte((unsigned int)src, (unsigned int)save->save_r7);
179 break;
180
181 case 5:
182 ml_phys_write_byte_64(src, (unsigned int)save->save_r7);
183 break;
184
185 case 6:
186 ml_phys_write((unsigned int)src, (unsigned int)save->save_r7);
187 break;
188
189 case 7:
190 ml_phys_write_64(src, (unsigned int)save->save_r7);
191 break;
192 }
193
194 return 1; /* Return and check for ASTs... */
195
196
197 /*
198 * Soft reset processor
199 */
200 case dgreset:
201
202 cpu = save->save_r4; /* Get the requested CPU number */
203
204 if(cpu >= MAX_CPUS) { /* Check for bogus cpu */
205 save->save_r3 = KERN_FAILURE; /* Set failure */
206 return 1;
207 }
208
209 per_proc = PerProcTable[cpu].ppe_vaddr; /* Point to the processor */
210 if(!per_proc->running) return KERN_FAILURE; /* It is not running */
211
212
213 (void)PE_cpu_start(per_proc->cpu_id,
214 per_proc->start_paddr, (vm_offset_t)per_proc);
215
216 save->save_r3 = KERN_SUCCESS; /* Set scuuess */
217
218 return 1; /* Return and check for ASTs... */
219
220 /*
221 * Force cache flush
222 */
223 case dgFlush:
224
225 cacheInit(); /* Blow cache */
226 return 1; /* Return and check for ASTs... */
227
228 /*
229 * various hack tests
230 */
231 case dgtest:
232
233 kprintf("Trying to hang\n");
234 baddr = (unsigned int)&baddr | 1; /* Make an odd address */
235 __asm__ volatile("lwarx r2,0,%0" : : "r" (baddr));
236 kprintf("Didn't hang\n");
237
238 return 1; /* Return and check for ASTs... */
239
240
241
242 /*
243 * Create a physical block map into the current task
244 * Don't bother to check for any errors.
245 * parms - vaddr, paddr, size, prot, attributes
246 */
247 case dgBMphys:
248
249 pmap_map_block(current_thread()->map->pmap, (addr64_t)save->save_r4, /* Map in the block */
250 save->save_r5, save->save_r6, save->save_r7, save->save_r8, 0);
251
252 return 1; /* Return and check for ASTs... */
253
254
255 /*
256 * Remove any mapping from the current task
257 * Don't bother to check for any errors.
258 * parms - vaddr
259 */
260 case dgUnMap:
261
262 (void)mapping_remove(current_thread()->map->pmap, save->save_r4); /* Remove mapping */
263 return 1; /* Return and check for ASTs... */
264
265
266 /*
267 * Allows direct control of alignment handling.
268 *
269 * The bottom bit of the parameter is used to set the control bit, enaNotifyEM.
270 */
271 case dgAlign:
272
273 temp = dgWork.dgFlags; /* Save the old values */
274
275 temp2 = (save->save_r4 & 1) << (31 - enaNotifyEMb); /* Move parms into flag format */
276 dgWork.dgFlags = (temp & ~enaNotifyEM) | temp2; /* Set the flag */
277
278 save->save_r3 = (temp >> (31 - enaNotifyEMb)) & 1; /* Return the original */
279
280 return 1; /* Return and check for ASTs... */
281
282 /*
283 * Return info for boot screen
284 */
285 case dgBootScreen:
286
287 ml_set_interrupts_enabled(1);
288 (void)copyout((char *)&vinfo, save->save_r4, sizeof(struct vc_info)); /* Copy out the video info */
289 ml_set_interrupts_enabled(0);
290 return 1; /* Return and check for ASTs... */
291
292 /*
293 * Don't return info for boot screen
294 */
295 case dgCPNull:
296
297 ml_set_interrupts_enabled(1);
298 (void)copyout((char *)&vinfo, save->save_r4, 0); /* Copy out nothing */
299 ml_set_interrupts_enabled(0);
300 return 1; /* Return and check for ASTs... */
301
302 /*
303 * Test machine check handler - only on 64-bit machines
304 */
305 case dgmck:
306 if(!(PerProcTable[0].ppe_vaddr->pf.Available & pf64Bit)) return 0; /* Leave if not correct machine */
307
308 fwEmMck(save->save_r4, save->save_r5, save->save_r6, save->save_r7, save->save_r8, save->save_r9); /* Start injecting */
309
310 return -1; /* Return and don't check for ASTs... */
311
312 /*
313 * Set 64-bit on or off - only on 64-bit machines
314 */
315 case dg64:
316 if(!(PerProcTable[0].ppe_vaddr->pf.Available & pf64Bit)) return 0; /* Leave if not correct machine */
317
318 srrwrk = save->save_srr1 >> 63; /* Save the old 64-bit bit */
319
320 save->save_srr1 = (save->save_srr1 & 0x7FFFFFFFFFFFFFFFULL) | (save->save_r4 << 63); /* Set the requested mode */
321 save->save_r3 = srrwrk; /* Return the old value */
322
323 task_clear_64BitAddr(current_thread()->task);
324 if((save->save_r4 & 1)) task_set_64BitAddr(current_thread()->task);
325
326 return -1; /* Return and don't check for ASTs... */
327
328 /*
329 * Test the probe read function
330 */
331
332 case dgProbeRead:
333
334 src = (save->save_r4 << 32) | (0x00000000FFFFFFFFULL & save->save_r5); /* Merge into 64-bit */
335 save->save_r3 = ml_probe_read_64(src, &temp); /* Try the address */
336 save->save_r4 = temp; /* Return the data */
337 return -1; /* Regurn and don't check for ASTs */
338
339 /*
340 * Do perf monitor stuff
341 */
342
343 case dgPerfMon:
344
345 setPmon(save->save_r4, save->save_r5); /* Go load up MMCR0 and MMCR1 */
346 return -1; /* Regurn and don't check for ASTs */
347
348 /*
349 * Map a page
350 * Don't bother to check for any errors.
351 * parms - vaddr, paddr, prot, attributes
352 */
353 case dgMapPage:
354
355 (void)mapping_make(current_thread()->map->pmap, /* Map in the page */
356 (addr64_t)(((save->save_r5 & 0xFFFFFFFF) << 32) | (save->save_r5 & 0xFFFFFFFF)), save->save_r6, 0, 1, VM_PROT_READ|VM_PROT_WRITE);
357
358 return -1; /* Return and check for ASTs... */
359
360 /*
361 * SCOM interface
362 * parms - pointer to scomcomm
363 */
364 case dgScom:
365
366 ret = copyin(save->save_r4, (void *)&sarea, sizeof(scomcomm)); /* Get the data */
367 if(ret) return 0; /* Copyin failed - return an exception */
368
369 sarea.scomstat = 0xFFFFFFFFFFFFFFFFULL; /* Clear status */
370 cpu = cpu_number(); /* Get us */
371
372 if((sarea.scomcpu < real_ncpus) && PerProcTable[sarea.scomcpu].ppe_vaddr->running) {
373 if(sarea.scomcpu == cpu) { /* Is it us? */
374 if(sarea.scomfunc) { /* Are we writing */
375 sarea.scomstat = ml_scom_write(sarea.scomreg, sarea.scomdata); /* Write scom */
376 }
377 else {
378 sarea.scomstat = ml_scom_read(sarea.scomreg, &sarea.scomdata); /* Read scom */
379 }
380 }
381 else { /* Otherwise, tell the other processor */
382 (void)cpu_signal(sarea.scomcpu, SIGPcpureq, CPRQscom ,(unsigned int)&sarea); /* Ask him to do this */
383 (void)hw_cpu_sync((unsigned long)&sarea.scomstat, LockTimeOut); /* Wait for the other processor to get its temperature */
384 }
385 }
386
387 ret = copyout((void *)&sarea, save->save_r4, sizeof(scomcomm)); /* Get the data */
388 if(ret) return 0; /* Copyin failed - return an exception */
389
390 return -1; /* Return and check for ASTs... */
391
392 /*
393 * Bind current thread to a processor. Parm is processor port. If port is 0, unbind.
394 */
395
396 case dgBind:
397
398 if(save->save_r4 == 0) { /* Are we unbinding? */
399 thread_bind(current_thread(), PROCESSOR_NULL); /* Unbind us */
400 save->save_r3 = KERN_SUCCESS; /* Set success */
401 return -1; /* Return and check asts */
402 }
403
404 ret = ipc_right_lookup_write(current_space(), (mach_port_name_t)save->save_r4,
405 &ientry); /* Look up the IPC entry */
406
407 if(ret != KERN_SUCCESS) { /* Couldn't find it */
408 save->save_r3 = ret; /* Pass back return */
409 return -1; /* Return and check asts */
410 }
411
412 port = (ipc_port_t)ientry->ie_object; /* Get the actual port */
413
414 if (!ip_active(port) || (ip_kotype(port) != IKOT_PROCESSOR)) { /* Active and a processor? */
415 is_write_unlock(current_space()); /* Unlock the space */
416 save->save_r3 = KERN_INVALID_ARGUMENT; /* This port is not a processor */
417 return -1; /* Return and check asts */
418 }
419
420 prssr = (processor_t)port->ip_kobject; /* Extract the processor */
421 is_write_unlock(current_space()); /* All done with the space now, unlock it */
422
423 /*
424 * The following probably isn't valid if a processor is in the processor going offline,
425 * but who cares, this is a diagnostic interface...
426 */
427
428 if(prssr->state == PROCESSOR_SHUTDOWN) { /* Are we trying to bind to an offline processor? */
429 save->save_r3 = KERN_INVALID_ARGUMENT; /* This processor is offline */
430 return -1; /* Return and check asts */
431 }
432
433 thread_bind(current_thread(), prssr); /* Bind us to the processor */
434 thread_block(THREAD_CONTINUE_NULL); /* Make it so */
435
436 save->save_r3 = KERN_SUCCESS; /* Set success */
437 return -1; /* Return and check asts */
438
439 /*
440 * Return per_proc for the named processor. Pass in a port. Returns per_proc or 0 if failure
441 */
442
443 case dgPproc:
444
445 ret = ipc_right_lookup_write(current_space(), (mach_port_name_t)save->save_r4,
446 &ientry); /* Look up the IPC entry */
447
448 if(ret != KERN_SUCCESS) { /* Couldn't find it */
449 save->save_r3 = 0; /* Pass back return */
450 return -1; /* Return and check asts */
451 }
452
453 port = (ipc_port_t)ientry->ie_object; /* Get the actualy port */
454
455 if (!ip_active(port) || (ip_kotype(port) != IKOT_PROCESSOR)) { /* Active and a processor? */
456 is_write_unlock(current_space()); /* Unlock the space */
457 save->save_r3 = 0; /* This port is not a processor */
458 return -1; /* Return and check asts */
459 }
460
461 prssr = (processor_t)port->ip_kobject; /* Extract the processor */
462 is_write_unlock(current_space()); /* All done with the space now, unlock it */
463
464 save->save_r3 = (uint64_t)PerProcTable[prssr->processor_data.slot_num].ppe_vaddr; /* Pass back ther per proc */
465 return -1; /* Return and check asts */
466
467 /*
468 * Allocate contiguous memory in the kernel. Pass in size, pass back vaddr or 0 for error
469 * Note that this must be explicitly released by the user. There is an "issue"
470 * if we try to allocate directly into the user: the contiguous area has a kernel wire
471 * on it. If we terminate, we will hang waiting for wire to be released. Ain't no
472 * way that will happen, so we do it in the kernel and make them release it. That way
473 * we will leak rather than hang.
474 *
475 */
476 case dgAcntg:
477
478 addrs = 0; /* Clear just in case */
479
480 ret = kmem_alloc_contig(kernel_map, &addrs, (vm_size_t)save->save_r4,
481 PAGE_MASK, 0); /* That which does not make us stronger, kills us... */
482 if(ret != KERN_SUCCESS) addrs = 0; /* Pass 0 if error */
483
484 save->save_r3 = (uint64_t)addrs; /* Pass back whatever */
485 return -1; /* Return and check for ASTs... */
486
487
488 /*
489 * Return physical address of a page in the kernel
490 */
491 case dgKlra:
492
493 save->save_r3 = pmap_find_phys(kernel_pmap, save->save_r4); /* Get read address */
494 return -1; /* Return no AST checking... */
495
496 /*
497 * Release kernel memory - intent is to release congiguous memory
498 */
499 case dgKfree:
500
501 kmem_free( kernel_map, (vm_address_t) save->save_r4, (vm_size_t)save->save_r5);
502 return -1; /* Return no AST checking... */
503
504
505 case dgWar: /* Set or reset workaround flags */
506
507 save->save_r3 = (uint32_t)warFlags; /* Get the old flags */
508 oldwar = warFlags; /* Remember the old war flags */
509
510 subc = (int32_t)save->save_r4; /* Extract the subcommand */
511 switch(subc) { /* Do what we need */
512 case 1: /* Replace all */
513 warFlags = (uint32_t)save->save_r5; /* Do them all */
514 break;
515
516 case 2: /* Turn on selected workarounds */
517 warFlags = warFlags | (uint32_t)save->save_r5;
518 break;
519
520 case 3: /* Turn off selected workarounds */
521 warFlags = warFlags & ~((uint32_t)save->save_r5);
522 break;
523
524 case 4: /* Start up selected workaround */
525 break;
526
527 case 5: /* Stop selected workaround */
528 break;
529
530 case 6: /* Reset specific workaround parameters to default */
531 break;
532
533 case 7: /* Set workaround parameters */
534 break;
535
536 default:
537
538 break;
539
540 }
541
542 save->save_r3 = oldwar; /* Pass back original */
543 return -1;
544
545
546 default: /* Handle invalid ones */
547 return 0; /* Return an exception */
548
549 }
550
551 };
552
553 kern_return_t testPerfTrap(int trapno, struct savearea *ss,
554 unsigned int dsisr, addr64_t dar) {
555
556 if(trapno != T_ALIGNMENT) return KERN_FAILURE;
557
558 kprintf("alignment exception at %08X, srr1 = %08X, dsisr = %08X, dar = %08X\n", ss->save_srr0,
559 ss->save_srr1, dsisr, dar);
560
561 return KERN_SUCCESS;
562
563 }
564