2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
36 #include <mach/boolean.h>
37 #include <vm/vm_map.h>
38 #include <kern/thread.h>
39 #include <kern/processor.h>
40 #include <kern/task.h>
42 #include <ppc/cpu_internal.h>
43 #include <ppc/exception.h>
44 #include <machine/asm.h>
45 #include <machine/db_machdep.h>
46 #include <machine/setjmp.h>
47 #include <mach/machine.h>
49 #include <ddb/db_access.h>
50 #include <ddb/db_sym.h>
51 #include <ddb/db_variables.h>
52 #include <ddb/db_command.h>
53 #include <ddb/db_task_thread.h>
54 #include <ddb/db_output.h>
56 extern jmp_buf_t
*db_recover
;
58 struct savearea ddb_null_kregs
;
60 extern vm_offset_t vm_min_inks_addr
; /* set by db_clone_symtabXXX */
62 #define DB_NUMARGS_MAX 5
65 #define INFIXEDSTACK(va) 0 \
67 #define INKERNELSTACK(va, th) 1
70 struct db_ppc_frame
*f_frame
;
76 uint32_t f_arg
[DB_NUMARGS_MAX
];
83 db_addr_t db_user_trap_symbol_value
= 0;
84 db_addr_t db_kernel_trap_symbol_value
= 0;
85 db_addr_t db_interrupt_symbol_value
= 0;
86 db_addr_t db_return_to_iret_symbol_value
= 0;
87 db_addr_t db_syscall_symbol_value
= 0;
88 boolean_t db_trace_symbols_found
= FALSE
;
90 extern int db_ppc_reg_value(
91 struct db_variable
* vp
,
94 db_var_aux_param_t ap
);
95 extern void db_find_trace_symbols(void);
96 extern int db_numargs(
97 struct db_ppc_frame
*fp
,
99 extern boolean_t
db_find_arg(
100 struct db_ppc_frame
*frame
,
105 extern void db_nextframe(
106 struct db_ppc_frame
**lfp
,
107 struct db_ppc_frame
**fp
,
110 thread_act_t thr_act
,
116 * Machine register set.
118 struct db_variable db_regs
[] = {
119 /* XXX "pc" is an alias to "srr0"... */
120 { "pc", &ddb_regs
.save_srr0
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
121 { "srr0", &ddb_regs
.save_srr0
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
122 { "srr1", &ddb_regs
.save_srr1
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
123 { "r0", &ddb_regs
.save_r0
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
124 { "r1", &ddb_regs
.save_r1
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
125 { "r2", &ddb_regs
.save_r2
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
126 { "r3", &ddb_regs
.save_r3
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
127 { "r4", &ddb_regs
.save_r4
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
128 { "r5", &ddb_regs
.save_r5
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
129 { "r6", &ddb_regs
.save_r6
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
130 { "r7", &ddb_regs
.save_r7
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
131 { "r8", &ddb_regs
.save_r8
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
132 { "r9", &ddb_regs
.save_r9
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
133 { "r10", &ddb_regs
.save_r10
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
134 { "r11", &ddb_regs
.save_r11
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
135 { "r12", &ddb_regs
.save_r12
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
136 { "r13", &ddb_regs
.save_r13
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
137 { "r14", &ddb_regs
.save_r14
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
138 { "r15", &ddb_regs
.save_r15
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
139 { "r16", &ddb_regs
.save_r16
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
140 { "r17", &ddb_regs
.save_r17
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
141 { "r18", &ddb_regs
.save_r18
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
142 { "r19", &ddb_regs
.save_r19
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
143 { "r20", &ddb_regs
.save_r20
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
144 { "r21", &ddb_regs
.save_r21
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
145 { "r22", &ddb_regs
.save_r22
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
146 { "r23", &ddb_regs
.save_r23
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
147 { "r24", &ddb_regs
.save_r24
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
148 { "r25", &ddb_regs
.save_r25
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
149 { "r26", &ddb_regs
.save_r26
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
150 { "r27", &ddb_regs
.save_r27
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
151 { "r28", &ddb_regs
.save_r28
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
152 { "r29", &ddb_regs
.save_r29
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
153 { "r30", &ddb_regs
.save_r30
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
154 { "r31", &ddb_regs
.save_r31
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
155 { "cr", &ddb_regs
.save_cr
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
156 { "xer", &ddb_regs
.save_xer
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
157 { "lr", &ddb_regs
.save_lr
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
158 { "ctr", &ddb_regs
.save_ctr
, db_ppc_reg_value
, 0, 0, 0, 0, TRUE
},
160 struct db_variable
*db_eregs
= db_regs
+ sizeof(db_regs
)/sizeof(db_regs
[0]);
164 struct db_variable
*vp
,
167 db_var_aux_param_t ap
)
170 db_expr_t null_reg
= 0;
173 register thread_act_t thr_act
= ap
->thr_act
;
176 if (db_option(ap
->modif
, 'u')) {
177 if (thr_act
== THR_ACT_NULL
) {
178 if ((thr_act
= current_thread()) == THR_ACT_NULL
)
179 db_error("no user registers\n");
181 if (thr_act
== current_thread()) {
182 if (IS_USER_TRAP((&ddb_regs
))) dp
= vp
->valuep
;
183 else if (INFIXEDSTACK(ddb_regs
.save_r1
))
184 db_error("cannot get/set user registers in nested interrupt\n");
188 if (thr_act
== THR_ACT_NULL
|| thr_act
== current_thread()) {
192 if (thr_act
->kernel_stack
) {
196 for (cpu
= 0; cpu
< real_ncpus
; cpu
++) {
197 if (cpu_to_processor(cpu
)->state
== PROCESSOR_RUNNING
&&
198 cpu_to_processor(cpu
)->active_thread
== thr_act
&&
199 PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
) {
201 dp
= (db_expr_t
)(((uint32_t)(PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
)) +
202 (((uint32_t) vp
->valuep
) -
203 (uint32_t) &ddb_regs
));
208 if (dp
== 0) dp
= &null_reg
;
211 /* only PC is valid */
212 if (vp
->valuep
== (int *) &ddb_regs
.save_srr0
) {
213 dp
= (int *)(&thr_act
->continuation
);
223 if (!db_option(ap
->modif
, 'u')) {
224 for (cpu
= 0; cpu
< real_ncpus
; cpu
++) {
225 if (cpu_to_processor(cpu
)->state
== PROCESSOR_RUNNING
&&
226 cpu_to_processor(cpu
)->active_thread
== thr_act
&&
227 PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
) {
228 dp
= (int *) (((int)(PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
)) +
229 (((int) vp
->valuep
) - (int) &ddb_regs
));
235 if (!thr_act
|| thr_act
->machine
.pcb
== 0) db_error("no pcb\n");
236 dp
= (int *)((int)thr_act
->machine
.pcb
+ ((int)vp
->valuep
- (int)&ddb_regs
));
240 if(vp
->valuep
== (int *) &ddb_regs
.save_cr
) { /* Is this the CR we are doing? */
241 dp32
= (uint32_t *)dp
; /* Make this easier */
242 if (flag
== DB_VAR_SET
) *dp32
= *valuep
;
243 else *valuep
= *dp32
;
245 else { /* Normal 64-bit registers */
246 if (flag
== DB_VAR_SET
) *dp
= *valuep
;
247 else *valuep
= *(unsigned long long *)dp
;
255 db_find_trace_symbols(void)
258 boolean_t found_some
;
261 if (db_value_of_name(CC_SYM_PREFIX
"thandler", &value
)) {
262 db_user_trap_symbol_value
= (db_addr_t
) value
;
265 if (db_value_of_name(CC_SYM_PREFIX
"thandler", &value
)) {
266 db_kernel_trap_symbol_value
= (db_addr_t
) value
;
269 if (db_value_of_name(CC_SYM_PREFIX
"ihandler", &value
)) {
270 db_interrupt_symbol_value
= (db_addr_t
) value
;
274 if (db_value_of_name(CC_SYM_PREFIX
"return_to_iret", &value
)) {
275 db_return_to_iret_symbol_value
= (db_addr_t
) value
;
279 if (db_value_of_name(CC_SYM_PREFIX
"thandler", &value
)) {
280 db_syscall_symbol_value
= (db_addr_t
) value
;
284 db_trace_symbols_found
= TRUE
;
289 struct db_ppc_frame
*fp
,
292 return (DB_NUMARGS_MAX
);
297 struct db_ppc_frame
*fp
,
311 db_find_task_sym_and_offset(calleepc
, &name
, &offset
, task
);
312 calleep
= calleepc
-offset
;
314 for (i
= 0; calleep
< calleepc
; i
++, calleep
++) {
315 if (!DB_CHECK_ACCESS((int) calleep
, 4, task
)) {
318 inst
= db_get_task_value(calleep
, 4, FALSE
, task
);
319 if ((inst
& 0xffff0000) == (0x907f0000 + (narg
<< 21)) ||
320 (inst
& 0xffff0000) == (0x90610000 + (narg
<< 21))) {
321 argp
= (db_addr_t
) &(fp
->f_arg
[narg
]);
331 * Figure out the next frame up in the call stack.
332 * For trap(), we print the address of the faulting instruction and
333 * proceed with the calling frame. We return the ip that faulted.
334 * If the trap was caused by jumping through a bogus pointer, then
335 * the next line in the backtrace will list some random function as
336 * being called. It should get the argument list correct, though.
337 * It might be possible to dig out from the next frame up the name
338 * of the function that faulted, but that could get hairy.
342 struct db_ppc_frame
**lfp
, /* in/out */
343 struct db_ppc_frame
**fp
, /* in/out */
344 db_addr_t
*ip
, /* out */
345 int frame_type
, /* in */
346 thread_act_t thr_act
,
347 db_addr_t linkpc
) /* in */
349 extern char * trap_type
[];
350 extern int TRAP_TYPES
;
352 struct savearea
*saved_regs
;
354 task_t task
= (thr_act
!= THR_ACT_NULL
)? thr_act
->task
: TASK_NULL
;
359 db_printf(">>>>> trap <<<<<\n");
364 db_printf(">>>>> interrupt <<<<<\n");
367 db_printf(">>>>> interrupt <<<<<\n");
371 if (thr_act
!= THR_ACT_NULL
&& thr_act
->machine
.pcb
) {
372 *ip
= (db_addr_t
) thr_act
->machine
.pcb
->save_srr0
;
373 *fp
= (struct db_ppc_frame
*) (thr_act
->machine
.pcb
->save_r1
);
376 /* falling down for unknown case */
380 if(!pmap_find_phys(kernel_pmap
, (addr64_t
)*fp
)) { /* Check if this is valid */
381 db_printf("Frame not mapped %08X\n",*fp
); /* Say not found */
382 *fp
= 0; /* Show not found */
383 break; /* Out of here */
388 db_get_task_value((int)&(*fp
)->f_frame
->f_retaddr
,
392 db_get_task_value((int)&(*fp
)->f_retaddr
,
396 *fp
= (struct db_ppc_frame
*)
397 db_get_task_value((int)&(*fp
)->f_frame
, 4, FALSE
, task
);
409 struct db_ppc_frame
*frame
, *lastframe
;
410 db_addr_t callpc
, linkpc
, lastcallpc
;
412 boolean_t kernel_only
= TRUE
;
413 boolean_t trace_thread
= FALSE
;
414 boolean_t trace_all_threads
= FALSE
;
419 thread_act_t th
, top_act
;
423 jmp_buf_t db_jmp_buf
;
424 queue_entry_t act_list
;
426 if (!db_trace_symbols_found
)
427 db_find_trace_symbols();
429 register char *cp
= modif
;
432 while ((c
= *cp
++) != 0) {
436 trace_all_threads
= TRUE
;
444 if (trace_all_threads
) {
445 if (!have_addr
&& !trace_thread
) {
448 act_list
= &(current_task()->threads
);
449 addr
= (db_expr_t
) queue_first(act_list
);
451 else if (trace_thread
) {
453 if (!db_check_act_address_valid((thread_act_t
)addr
)) {
454 if (db_lookup_task((task_t
)addr
) == -1)
456 act_list
= &(((task_t
)addr
)->threads
);
457 addr
= (db_expr_t
) queue_first(act_list
);
460 act_list
= &(((thread_act_t
)addr
)->task
->threads
);
461 thcount
= db_lookup_task_act(((thread_act_t
)addr
)->task
,
467 if (th
== THR_ACT_NULL
)
468 th
= current_thread();
469 if (th
== THR_ACT_NULL
) {
470 db_printf("no active thr_act\n");
474 act_list
= &th
->task
->threads
;
475 addr
= (db_expr_t
) queue_first(act_list
);
484 top_act
= THR_ACT_NULL
;
489 if (!have_addr
&& !trace_thread
) {
490 frame
= (struct db_ppc_frame
*)(ddb_regs
.save_r1
);
491 callpc
= (db_addr_t
)ddb_regs
.save_srr0
;
492 linkpc
= (db_addr_t
)ddb_regs
.save_lr
;
493 th
= current_thread();
494 task
= (th
!= THR_ACT_NULL
)? th
->task
: TASK_NULL
;
496 else if (trace_thread
) {
498 th
= (thread_act_t
) addr
;
499 if (!db_check_act_address_valid(th
))
504 if (th
== THR_ACT_NULL
)
505 th
= current_thread();
506 if (th
== THR_ACT_NULL
) {
507 db_printf("no active thread\n");
511 if (trace_all_threads
)
512 db_printf("---------- Thread 0x%x (#%d of %d) ----------\n",
513 addr
, thcount
, th
->task
->thread_count
);
520 if (th
== current_thread()) {
521 frame
= (struct db_ppc_frame
*)(ddb_regs
.save_r1
);
522 callpc
= (db_addr_t
)ddb_regs
.save_srr0
;
523 linkpc
= (db_addr_t
)ddb_regs
.save_lr
;
526 if (th
->machine
.pcb
== 0) {
527 db_printf("thread has no pcb\n");
530 if (th
->kernel_stack
== 0) {
531 register struct savearea
*pss
=
534 db_printf("Continuation ");
535 db_task_printsym((db_expr_t
)th
->continuation
,
538 frame
= (struct db_ppc_frame
*) (pss
->save_r1
);
539 callpc
= (db_addr_t
) (pss
->save_srr0
);
540 linkpc
= (db_addr_t
) (pss
->save_lr
);
545 for (cpu
= 0; cpu
< real_ncpus
; cpu
++) {
546 if (cpu_to_processor(cpu
)->state
== PROCESSOR_RUNNING
&&
547 cpu_to_processor(cpu
)->active_thread
== th
&&
548 PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
) {
552 if (top_act
!= THR_ACT_NULL
) {
554 * Trying to get the backtrace of an activation
555 * which is not the top_most one in the RPC chain:
556 * use the activation's pcb.
558 struct savearea
*pss
;
560 pss
= th
->machine
.pcb
;
561 frame
= (struct db_ppc_frame
*) (pss
->save_r1
);
562 callpc
= (db_addr_t
) (pss
->save_srr0
);
563 linkpc
= (db_addr_t
) (pss
->save_lr
);
565 if (cpu
== real_ncpus
) {
566 register struct savearea
*iks
;
569 iks
= th
->machine
.pcb
;
571 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
572 frame
= (struct db_ppc_frame
*) (iks
->save_r1
);
573 callpc
= (db_addr_t
) (iks
->save_lr
);
577 * The kernel stack has probably been
578 * paged out (swapped out activation).
581 if (r
== 2) /* 'q' from db_more() */
583 db_printf("<kernel stack (0x%x) error "
584 "(probably swapped out)>\n",
590 db_printf(">>>>> active on cpu %d <<<<<\n",
592 frame
= (struct db_ppc_frame
*)
593 (PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
->save_r1
);
594 callpc
= (db_addr_t
) PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
->save_srr0
;
595 linkpc
= (db_addr_t
) PerProcTable
[cpu
].ppe_vaddr
->db_saved_state
->save_lr
;
601 frame
= (struct db_ppc_frame
*)addr
;
602 th
= (db_default_act
)? db_default_act
: current_thread();
603 task
= (th
!= THR_ACT_NULL
)? th
->task
: TASK_NULL
;
604 if (frame
->f_frame
) {
605 callpc
= (db_addr_t
)db_get_task_value
606 ((int)&frame
->f_frame
->f_retaddr
,
607 4, FALSE
, (user_frame
) ? task
: 0);
608 callpc
= callpc
-sizeof(callpc
);
614 if (!INKERNELSTACK((unsigned)frame
, th
)) {
615 db_printf(">>>>> user space <<<<<\n");
622 lastcallpc
= (db_addr_t
) 0;
623 while (frame_count
-- && frame
!= 0) {
624 int narg
= DB_NUMARGS_MAX
;
628 db_addr_t call_func
= 0;
632 db_symbol_values(NULL
,
633 db_search_task_symbol_and_line(
634 callpc
, DB_STGY_XTRN
, &offset
, &filename
,
635 &linenum
, (user_frame
) ? task
: 0, &narg
),
636 &name
, (db_expr_t
*)&call_func
);
638 db_find_task_sym_and_offset(callpc
,
639 &name
, &off
, (user_frame
) ? task
: 0);
640 offset
= (db_expr_t
) off
;
643 if (user_frame
== 0) {
645 (call_func
== db_user_trap_symbol_value
||
646 call_func
== db_kernel_trap_symbol_value
)) {
649 } else if (call_func
&&
650 call_func
== db_interrupt_symbol_value
) {
651 frame_type
= INTERRUPT
;
653 } else if (call_func
&&
654 call_func
== db_syscall_symbol_value
) {
655 frame_type
= SYSCALL
;
660 if ((r
= _setjmp(db_recover
= &db_jmp_buf
))
663 narg
= db_numargs(frame
,
664 (user_frame
) ? task
: 0);
674 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
676 narg
= db_numargs(frame
,
677 (user_frame
) ? task
: 0);
685 if (name
== 0 || offset
> db_maxoff
) {
686 db_printf("[%08X]0x%08X(", frame
, callpc
);
688 db_printf("[%08X]%s", frame
, name
);
690 db_printf("+%llx", offset
);
694 narg
= db_numargs(frame
, (user_frame
) ? task
: 0);
696 for (arg
=0; arg
< narg
; arg
++) {
702 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
705 found
= db_find_arg(frame
, lastframe
->f_retaddr
,
706 (user_frame
) ? task
: 0, arg
, &argp
);
708 value
= db_get_task_value(argp
, 4, FALSE
,
709 (user_frame
) ? task
: 0);
712 if (r
== 2) /* 'q' from db_more() */
714 db_printf("... <stack error>)");
720 db_printf("%08X", value
);
723 argp
= argp
+ sizeof(argp
);
735 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
736 db_nextframe(&lastframe
, &frame
, &callpc
, frame_type
,
737 (user_frame
) ? th
: THR_ACT_NULL
, linkpc
);
738 callpc
= callpc
-sizeof(callpc
);
751 if (!INKERNELSTACK(lastframe
, th
) ||
752 !INKERNELSTACK((unsigned)frame
, th
))
754 if (user_frame
== 1) {
755 db_printf(">>>>> user space <<<<<\n");
760 if (frame
<= lastframe
) {
761 if ((INKERNELSTACK(lastframe
, th
) && !INKERNELSTACK(frame
, th
))) continue;
762 db_printf("Bad frame pointer: 0x%x\n", frame
);
768 if (trace_all_threads
) {
769 if (top_act
!= THR_ACT_NULL
)
771 th
= (thread_act_t
) queue_next(&th
->task_threads
);
772 if (! queue_end(act_list
, (queue_entry_t
) th
)) {
774 addr
= (db_expr_t
) th
;