]>
Commit | Line | Data |
---|---|---|
fe8ab488 A |
1 | /* |
2 | * Copyright (c) 2014 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
0a7de745 | 5 | * |
fe8ab488 A |
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. | |
0a7de745 | 14 | * |
fe8ab488 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
fe8ab488 A |
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. | |
0a7de745 | 25 | * |
fe8ab488 A |
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 { | |
0a7de745 A |
40 | uint32_t magic1; |
41 | queue_chain_t chain; | |
42 | thread_t thread; | |
43 | panic_hook_fn_t hook_fn; | |
44 | uint32_t magic2; | |
fe8ab488 A |
45 | }; |
46 | ||
47 | typedef char check1_[sizeof(struct panic_hook) | |
0a7de745 | 48 | <= sizeof(panic_hook_t) ? 1 : -1]; |
fe8ab488 A |
49 | typedef char check2_[PAGE_SIZE == 4096 ? 1 : -1]; |
50 | ||
0a7de745 A |
51 | static hw_lock_data_t panic_hooks_lock; |
52 | static queue_head_t panic_hooks; | |
53 | static uint8_t panic_dump_buf[8192]; | |
fe8ab488 | 54 | |
0a7de745 A |
55 | #define PANIC_HOOK_MAGIC1 0x4A1C400C |
56 | #define PANIC_HOOK_MAGIC2 0xC004C1A4 | |
fe8ab488 | 57 | |
0a7de745 A |
58 | void |
59 | panic_hooks_init(void) | |
fe8ab488 A |
60 | { |
61 | hw_lock_init(&panic_hooks_lock); | |
62 | queue_init(&panic_hooks); | |
63 | } | |
64 | ||
0a7de745 A |
65 | void |
66 | panic_hook(panic_hook_t *hook_, panic_hook_fn_t hook_fn) | |
fe8ab488 A |
67 | { |
68 | struct panic_hook *hook = (struct panic_hook *)hook_; | |
69 | ||
0a7de745 A |
70 | hook->magic1 = PANIC_HOOK_MAGIC1; |
71 | hook->magic2 = PANIC_HOOK_MAGIC2; | |
72 | hook->hook_fn = hook_fn; | |
73 | hook->thread = current_thread(); | |
fe8ab488 | 74 | |
0a7de745 | 75 | hw_lock_lock(&panic_hooks_lock, LCK_GRP_NULL); |
fe8ab488 A |
76 | queue_enter(&panic_hooks, hook, struct panic_hook *, chain); |
77 | hw_lock_unlock(&panic_hooks_lock); | |
78 | } | |
79 | ||
0a7de745 A |
80 | void |
81 | panic_unhook(panic_hook_t *hook_) | |
fe8ab488 A |
82 | { |
83 | struct panic_hook *hook = (struct panic_hook *)hook_; | |
84 | ||
0a7de745 | 85 | hw_lock_lock(&panic_hooks_lock, LCK_GRP_NULL); |
fe8ab488 A |
86 | queue_remove(&panic_hooks, hook, struct panic_hook *, chain); |
87 | hw_lock_unlock(&panic_hooks_lock); | |
88 | } | |
89 | ||
0a7de745 A |
90 | void |
91 | panic_check_hook(void) | |
fe8ab488 A |
92 | { |
93 | struct panic_hook *hook; | |
94 | thread_t thread = current_thread(); | |
95 | uint32_t count = 0; | |
96 | ||
97 | queue_iterate(&panic_hooks, hook, struct panic_hook *, chain) { | |
98 | if (++count > 1024 | |
0a7de745 A |
99 | || !kvtophys((vm_offset_t)hook) |
100 | || !kvtophys((vm_offset_t)hook + sizeof(*hook) - 1) | |
101 | || hook->magic1 != PANIC_HOOK_MAGIC1 | |
102 | || hook->magic2 != PANIC_HOOK_MAGIC2 | |
103 | || !kvtophys((vm_offset_t)hook->hook_fn)) { | |
fe8ab488 | 104 | return; |
0a7de745 | 105 | } |
fe8ab488 A |
106 | |
107 | if (hook->thread == thread) { | |
108 | hook->hook_fn((panic_hook_t *)hook); | |
109 | return; | |
110 | } | |
111 | } | |
112 | } | |
113 | ||
114 | /* | |
115 | * addr should be page aligned and len should be multiple of page | |
116 | * size. This will currently only work if each page can be compressed | |
117 | * to no more than 4095 bytes. | |
118 | * | |
119 | * Remember the debug buffer isn't very big so don't try and dump too | |
120 | * much. | |
121 | */ | |
0a7de745 A |
122 | void |
123 | panic_dump_mem(const void *addr, int len) | |
fe8ab488 A |
124 | { |
125 | void *scratch = panic_dump_buf + 4096; | |
126 | ||
3e170ce0 | 127 | for (; len > 0; addr = (const uint8_t *)addr + PAGE_SIZE, len -= PAGE_SIZE) { |
0a7de745 | 128 | if (!kvtophys((vm_offset_t)addr)) { |
fe8ab488 | 129 | continue; |
0a7de745 | 130 | } |
fe8ab488 A |
131 | |
132 | // 4095 is multiple of 3 -- see below | |
3e170ce0 | 133 | int n = WKdm_compress_new((const WK_word *)addr, (WK_word *)(void *)panic_dump_buf, |
0a7de745 | 134 | scratch, 4095); |
fe8ab488 | 135 | |
0a7de745 | 136 | if (n == -1) { |
fe8ab488 | 137 | return; // Give up |
0a7de745 | 138 | } |
fe8ab488 A |
139 | kdb_log("%p: ", addr); |
140 | ||
141 | // Dump out base64 | |
142 | static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
0a7de745 | 143 | "abcdefghijklmnopqrstuvwxyz0123456789+/"; |
fe8ab488 A |
144 | |
145 | // Pad to multiple of 3 | |
146 | switch (n % 3) { | |
147 | case 1: | |
148 | panic_dump_buf[n++] = 0; | |
149 | case 2: | |
150 | panic_dump_buf[n++] = 0; | |
151 | } | |
152 | ||
153 | uint8_t *p = panic_dump_buf; | |
154 | while (n) { | |
155 | uint8_t c; | |
156 | ||
157 | c = p[0] >> 2; | |
158 | consdebug_log(base64_table[c]); | |
159 | ||
160 | c = (p[0] << 4 | p[1] >> 4) & 0x3f; | |
161 | consdebug_log(base64_table[c]); | |
162 | ||
163 | c = (p[1] << 2 | p[2] >> 6) & 0x3f; | |
164 | consdebug_log(base64_table[c]); | |
165 | ||
166 | c = p[2] & 0x3f; | |
167 | consdebug_log(base64_table[c]); | |
168 | ||
169 | p += 3; | |
170 | n -= 3; | |
171 | } | |
172 | ||
173 | consdebug_log('\n'); | |
174 | } | |
175 | } | |
176 | ||
0a7de745 A |
177 | boolean_t |
178 | panic_phys_range_before(const void *addr, uint64_t *pphys, | |
179 | panic_phys_range_t *range) | |
fe8ab488 A |
180 | { |
181 | *pphys = kvtophys((vm_offset_t)addr); | |
182 | ||
183 | const boot_args *args = PE_state.bootArgs; | |
184 | ||
0a7de745 | 185 | if (!kvtophys((vm_offset_t)args)) { |
fe8ab488 | 186 | return FALSE; |
0a7de745 | 187 | } |
fe8ab488 A |
188 | |
189 | const EfiMemoryRange *r = PHYSMAP_PTOV((uintptr_t)args->MemoryMap), *closest = NULL; | |
190 | const uint32_t size = args->MemoryMapDescriptorSize; | |
191 | const uint32_t count = args->MemoryMapSize / size; | |
192 | ||
0a7de745 | 193 | if (count > 1024) { // Sanity check |
fe8ab488 | 194 | return FALSE; |
0a7de745 | 195 | } |
fe8ab488 | 196 | |
3e170ce0 | 197 | for (uint32_t i = 0; i < count; ++i, r = (const EfiMemoryRange *)(const void *)((const uint8_t *)r + size)) { |
0a7de745 | 198 | if (r->PhysicalStart + r->NumberOfPages * PAGE_SIZE > *pphys) { |
fe8ab488 | 199 | continue; |
0a7de745 | 200 | } |
fe8ab488 | 201 | |
0a7de745 | 202 | if (!closest || r->PhysicalStart > closest->PhysicalStart) { |
fe8ab488 | 203 | closest = r; |
0a7de745 | 204 | } |
fe8ab488 A |
205 | } |
206 | ||
0a7de745 | 207 | if (!closest) { |
fe8ab488 | 208 | return FALSE; |
0a7de745 | 209 | } |
fe8ab488 | 210 | |
0a7de745 A |
211 | range->type = closest->Type; |
212 | range->phys_start = closest->PhysicalStart; | |
213 | range->len = closest->NumberOfPages * PAGE_SIZE; | |
fe8ab488 A |
214 | |
215 | return TRUE; | |
216 | } |