]> git.saurik.com Git - apple/xnu.git/blob - libkern/gen/OSDebug.cpp
aa0bffb5175002511d7c4a63e7f73c4bcf5960c7
[apple/xnu.git] / libkern / gen / OSDebug.cpp
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
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
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
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.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 // NOTE: This file is only c++ so I can get static initialisers going
32 #include <libkern/OSDebug.h>
33
34 #include <sys/cdefs.h>
35
36 #include <stdarg.h>
37 #include <mach/mach_types.h>
38 #include <mach/kmod.h>
39 #include <kern/lock.h>
40
41 #include <libkern/libkern.h> // From bsd's libkern directory
42 #include <mach/vm_param.h>
43
44 __BEGIN_DECLS
45 // From osmfk/kern/thread.h but considered to be private
46 extern vm_offset_t min_valid_stack_address(void);
47 extern vm_offset_t max_valid_stack_address(void);
48
49 // From osfmk/kmod.c
50 extern void kmod_dump_log(vm_offset_t *addr, unsigned int cnt);
51
52 extern addr64_t kvtophys(vm_offset_t va);
53 __END_DECLS
54
55 static mutex_t *sOSReportLock = mutex_alloc(0);
56
57 /* Report a message with a 4 entry backtrace - very slow */
58 void
59 OSReportWithBacktrace(const char *str, ...)
60 {
61 char buf[128];
62 void *bt[9];
63 const unsigned cnt = sizeof(bt) / sizeof(bt[0]);
64 va_list listp;
65
66 // Ignore the our and our callers stackframes, skipping frames 0 & 1
67 (void) OSBacktrace(bt, cnt);
68
69 va_start(listp, str);
70 vsnprintf(buf, sizeof(buf), str, listp);
71 va_end(listp);
72
73 mutex_lock(sOSReportLock);
74 {
75 printf("%s\nBacktrace %p %p %p %p %p %p %p\n",
76 buf, bt[2], bt[3], bt[4], bt[5], bt[6], bt[7], bt[8]);
77 kmod_dump_log((vm_offset_t *) &bt[2], cnt - 2);
78 }
79 mutex_unlock(sOSReportLock);
80 }
81
82 static vm_offset_t minstackaddr = min_valid_stack_address();
83 static vm_offset_t maxstackaddr = max_valid_stack_address();
84
85 #if __i386__
86 #define i386_RETURN_OFFSET 4
87
88 static unsigned int
89 i386_validate_stackptr(vm_offset_t stackptr)
90 {
91 /* Existence and alignment check
92 */
93 if (!stackptr || (stackptr & 0x3))
94 return 0;
95
96 /* Is a virtual->physical translation present?
97 */
98 if (!kvtophys(stackptr))
99 return 0;
100
101 /* Check if the return address lies on the same page;
102 * If not, verify that a translation exists.
103 */
104 if (((PAGE_SIZE - (stackptr & PAGE_MASK)) < i386_RETURN_OFFSET) &&
105 !kvtophys(stackptr + i386_RETURN_OFFSET))
106 return 0;
107 return 1;
108 }
109
110 static unsigned int
111 i386_validate_raddr(vm_offset_t raddr)
112 {
113 return ((raddr > VM_MIN_KERNEL_ADDRESS) &&
114 (raddr < VM_MAX_KERNEL_ADDRESS));
115 }
116 #endif
117
118 unsigned OSBacktrace(void **bt, unsigned maxAddrs)
119 {
120 unsigned frame;
121
122 #if __ppc__
123 vm_offset_t stackptr, stackptr_prev;
124 const vm_offset_t * const mem = (vm_offset_t *) 0;
125 unsigned i = 0;
126
127 __asm__ volatile("mflr %0" : "=r" (stackptr));
128 bt[i++] = (void *) stackptr;
129
130 __asm__ volatile("mr %0,r1" : "=r" (stackptr));
131 for ( ; i < maxAddrs; i++) {
132 // Validate we have a reasonable stackptr
133 if ( !(minstackaddr <= stackptr && stackptr < maxstackaddr)
134 || (stackptr & 3))
135 break;
136
137 stackptr_prev = stackptr;
138 stackptr = mem[stackptr_prev >> 2];
139 if ((stackptr_prev ^ stackptr) > 8 * 1024) // Sanity check
140 break;
141
142 vm_offset_t addr = mem[(stackptr >> 2) + 2];
143 if ((addr & 3) || (addr < 0x8000)) // More sanity checks
144 break;
145 bt[i] = (void *) addr;
146 }
147 frame = i;
148
149 for ( ; i < maxAddrs; i++)
150 bt[i] = (void *) 0;
151 #elif __i386__
152 #define SANE_i386_FRAME_SIZE 8*1024
153 vm_offset_t stackptr, stackptr_prev, raddr;
154 unsigned frame_index = 0;
155 /* Obtain current frame pointer */
156 __asm__ volatile("movl %%ebp, %0" : "=m" (stackptr));
157
158 if (!i386_validate_stackptr(stackptr))
159 goto pad;
160
161 raddr = *((vm_offset_t *) (stackptr + i386_RETURN_OFFSET));
162
163 if (!i386_validate_raddr(raddr))
164 goto pad;
165
166 bt[frame_index++] = (void *) raddr;
167
168 for ( ; frame_index < maxAddrs; frame_index++) {
169 stackptr_prev = stackptr;
170 stackptr = *((vm_offset_t *) stackptr_prev);
171
172 if (!i386_validate_stackptr(stackptr))
173 break;
174 /* Stack grows downwards */
175 if (stackptr < stackptr_prev)
176 break;
177
178 if ((stackptr_prev ^ stackptr) > SANE_i386_FRAME_SIZE)
179 break;
180
181 raddr = *((vm_offset_t *) (stackptr + i386_RETURN_OFFSET));
182
183 if (!i386_validate_raddr(raddr))
184 break;
185
186 bt[frame_index] = (void *) raddr;
187 }
188 pad:
189 frame = frame_index;
190
191 for ( ; frame_index < maxAddrs; frame_index++)
192 bt[frame_index] = (void *) 0;
193 #else
194 #error arch
195 #endif
196 return frame;
197 }