1 | /* |
2 | * Copyright (c) 2014 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_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 License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | |
29 | #include "panic_hooks.h" |
30 | |
31 | #include <kern/queue.h> |
32 | #include <kern/locks.h> |
33 | #include <kern/thread.h> |
34 | #include <vm/WKdm_new.h> |
35 | #include <pexpert/boot.h> |
36 | |
37 | #include "pmap.h" |
38 | |
39 | struct panic_hook { |
40 | uint32_t magic1; |
41 | queue_chain_t chain; |
42 | thread_t thread; |
43 | panic_hook_fn_t hook_fn; |
44 | uint32_t magic2; |
45 | }; |
46 | |
47 | typedef char check1_[sizeof(struct panic_hook) |
48 | <= sizeof(panic_hook_t) ? 1 : -1]; |
49 | typedef char check2_[PAGE_SIZE == 4096 ? 1 : -1]; |
50 | |
51 | static hw_lock_data_t panic_hooks_lock; |
52 | static queue_head_t panic_hooks; |
53 | static uint8_t panic_dump_buf[8192]; |
54 | |
55 | #define PANIC_HOOK_MAGIC1 0x4A1C400C |
56 | #define PANIC_HOOK_MAGIC2 0xC004C1A4 |
57 | |
58 | void panic_hooks_init(void) |
59 | { |
60 | hw_lock_init(&panic_hooks_lock); |
61 | queue_init(&panic_hooks); |
62 | } |
63 | |
64 | void panic_hook(panic_hook_t *hook_, panic_hook_fn_t hook_fn) |
65 | { |
66 | struct panic_hook *hook = (struct panic_hook *)hook_; |
67 | |
68 | hook->magic1 = PANIC_HOOK_MAGIC1; |
69 | hook->magic2 = PANIC_HOOK_MAGIC2; |
70 | hook->hook_fn = hook_fn; |
71 | hook->thread = current_thread(); |
72 | |
73 | hw_lock_lock(&panic_hooks_lock); |
74 | queue_enter(&panic_hooks, hook, struct panic_hook *, chain); |
75 | hw_lock_unlock(&panic_hooks_lock); |
76 | } |
77 | |
78 | void panic_unhook(panic_hook_t *hook_) |
79 | { |
80 | struct panic_hook *hook = (struct panic_hook *)hook_; |
81 | |
82 | hw_lock_lock(&panic_hooks_lock); |
83 | queue_remove(&panic_hooks, hook, struct panic_hook *, chain); |
84 | hw_lock_unlock(&panic_hooks_lock); |
85 | } |
86 | |
87 | void panic_check_hook(void) |
88 | { |
89 | struct panic_hook *hook; |
90 | thread_t thread = current_thread(); |
91 | uint32_t count = 0; |
92 | |
93 | queue_iterate(&panic_hooks, hook, struct panic_hook *, chain) { |
94 | if (++count > 1024 |
95 | || !kvtophys((vm_offset_t)hook) |
96 | || !kvtophys((vm_offset_t)hook + sizeof (*hook) - 1) |
97 | || hook->magic1 != PANIC_HOOK_MAGIC1 |
98 | || hook->magic2 != PANIC_HOOK_MAGIC2 |
99 | || !kvtophys((vm_offset_t)hook->hook_fn)) |
100 | return; |
101 | |
102 | if (hook->thread == thread) { |
103 | hook->hook_fn((panic_hook_t *)hook); |
104 | return; |
105 | } |
106 | } |
107 | } |
108 | |
109 | /* |
110 | * addr should be page aligned and len should be multiple of page |
111 | * size. This will currently only work if each page can be compressed |
112 | * to no more than 4095 bytes. |
113 | * |
114 | * Remember the debug buffer isn't very big so don't try and dump too |
115 | * much. |
116 | */ |
117 | void panic_dump_mem(const void *addr, int len) |
118 | { |
119 | void *scratch = panic_dump_buf + 4096; |
120 | |
121 | for (; len > 0; addr = (const uint8_t *)addr + PAGE_SIZE, len -= PAGE_SIZE) { |
122 | if (!kvtophys((vm_offset_t)addr)) |
123 | continue; |
124 | |
125 | // 4095 is multiple of 3 -- see below |
126 | int n = WKdm_compress_new((const WK_word *)addr, (WK_word *)(void *)panic_dump_buf, |
127 | scratch, 4095); |
128 | |
129 | if (n == -1) |
130 | return; // Give up |
131 | |
132 | kdb_log("%p: " , addr); |
133 | |
134 | // Dump out base64 |
135 | static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
136 | "abcdefghijklmnopqrstuvwxyz0123456789+/" ; |
137 | |
138 | // Pad to multiple of 3 |
139 | switch (n % 3) { |
140 | case 1: |
141 | panic_dump_buf[n++] = 0; |
142 | case 2: |
143 | panic_dump_buf[n++] = 0; |
144 | } |
145 | |
146 | uint8_t *p = panic_dump_buf; |
147 | while (n) { |
148 | uint8_t c; |
149 | |
150 | c = p[0] >> 2; |
151 | consdebug_log(base64_table[c]); |
152 | |
153 | c = (p[0] << 4 | p[1] >> 4) & 0x3f; |
154 | consdebug_log(base64_table[c]); |
155 | |
156 | c = (p[1] << 2 | p[2] >> 6) & 0x3f; |
157 | consdebug_log(base64_table[c]); |
158 | |
159 | c = p[2] & 0x3f; |
160 | consdebug_log(base64_table[c]); |
161 | |
162 | p += 3; |
163 | n -= 3; |
164 | } |
165 | |
166 | consdebug_log('\n'); |
167 | } |
168 | } |
169 | |
170 | boolean_t panic_phys_range_before(const void *addr, uint64_t *pphys, |
171 | panic_phys_range_t *range) |
172 | { |
173 | *pphys = kvtophys((vm_offset_t)addr); |
174 | |
175 | const boot_args *args = PE_state.bootArgs; |
176 | |
177 | if (!kvtophys((vm_offset_t)args)) |
178 | return FALSE; |
179 | |
180 | const EfiMemoryRange *r = PHYSMAP_PTOV((uintptr_t)args->MemoryMap), *closest = NULL; |
181 | const uint32_t size = args->MemoryMapDescriptorSize; |
182 | const uint32_t count = args->MemoryMapSize / size; |
183 | |
184 | if (count > 1024) // Sanity check |
185 | return FALSE; |
186 | |
187 | for (uint32_t i = 0; i < count; ++i, r = (const EfiMemoryRange *)(const void *)((const uint8_t *)r + size)) { |
188 | if (r->PhysicalStart + r->NumberOfPages * PAGE_SIZE > *pphys) |
189 | continue; |
190 | |
191 | if (!closest || r->PhysicalStart > closest->PhysicalStart) |
192 | closest = r; |
193 | } |
194 | |
195 | if (!closest) |
196 | return FALSE; |
197 | |
198 | range->type = closest->Type; |
199 | range->phys_start = closest->PhysicalStart; |
200 | range->len = closest->NumberOfPages * PAGE_SIZE; |
201 | |
202 | return TRUE; |
203 | } |
204 | |