]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2014-2020 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 | |
59 | panic_hooks_init(void) | |
60 | { | |
61 | hw_lock_init(&panic_hooks_lock); | |
62 | queue_init(&panic_hooks); | |
63 | } | |
64 | ||
65 | void | |
66 | panic_hook(panic_hook_t *hook_, panic_hook_fn_t hook_fn) | |
67 | { | |
68 | struct panic_hook *hook = (struct panic_hook *)hook_; | |
69 | ||
70 | hook->magic1 = PANIC_HOOK_MAGIC1; | |
71 | hook->magic2 = PANIC_HOOK_MAGIC2; | |
72 | hook->hook_fn = hook_fn; | |
73 | hook->thread = current_thread(); | |
74 | ||
75 | hw_lock_lock(&panic_hooks_lock, LCK_GRP_NULL); | |
76 | queue_enter(&panic_hooks, hook, struct panic_hook *, chain); | |
77 | hw_lock_unlock(&panic_hooks_lock); | |
78 | } | |
79 | ||
80 | void | |
81 | panic_unhook(panic_hook_t *hook_) | |
82 | { | |
83 | struct panic_hook *hook = (struct panic_hook *)hook_; | |
84 | ||
85 | hw_lock_lock(&panic_hooks_lock, LCK_GRP_NULL); | |
86 | queue_remove(&panic_hooks, hook, struct panic_hook *, chain); | |
87 | hw_lock_unlock(&panic_hooks_lock); | |
88 | } | |
89 | ||
90 | void | |
91 | panic_check_hook(void) | |
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 | |
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)) { | |
104 | return; | |
105 | } | |
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 | */ | |
122 | void | |
123 | panic_dump_mem(const void *addr, int len) | |
124 | { | |
125 | void *scratch = panic_dump_buf + 4096; | |
126 | ||
127 | for (; len > 0; addr = (const uint8_t *)addr + PAGE_SIZE, len -= PAGE_SIZE) { | |
128 | if (!kvtophys((vm_offset_t)addr)) { | |
129 | continue; | |
130 | } | |
131 | ||
132 | // 4095 is multiple of 3 -- see below | |
133 | int n = WKdm_compress_new((const WK_word *)addr, (WK_word *)(void *)panic_dump_buf, | |
134 | scratch, 4095); | |
135 | ||
136 | if (n == -1) { | |
137 | return; // Give up | |
138 | } | |
139 | kdb_log("%p: ", addr); | |
140 | ||
141 | // Dump out base64 | |
142 | static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
143 | "abcdefghijklmnopqrstuvwxyz0123456789+/"; | |
144 | ||
145 | // Pad to multiple of 3 | |
146 | switch (n % 3) { | |
147 | case 1: | |
148 | panic_dump_buf[n++] = 0; | |
149 | OS_FALLTHROUGH; | |
150 | case 2: | |
151 | panic_dump_buf[n++] = 0; | |
152 | } | |
153 | ||
154 | uint8_t *p = panic_dump_buf; | |
155 | while (n) { | |
156 | uint8_t c; | |
157 | ||
158 | c = p[0] >> 2; | |
159 | consdebug_log(base64_table[c]); | |
160 | ||
161 | c = (p[0] << 4 | p[1] >> 4) & 0x3f; | |
162 | consdebug_log(base64_table[c]); | |
163 | ||
164 | c = (p[1] << 2 | p[2] >> 6) & 0x3f; | |
165 | consdebug_log(base64_table[c]); | |
166 | ||
167 | c = p[2] & 0x3f; | |
168 | consdebug_log(base64_table[c]); | |
169 | ||
170 | p += 3; | |
171 | n -= 3; | |
172 | } | |
173 | ||
174 | consdebug_log('\n'); | |
175 | } | |
176 | } | |
177 | ||
178 | boolean_t | |
179 | panic_phys_range_before(const void *addr, uint64_t *pphys, | |
180 | panic_phys_range_t *range) | |
181 | { | |
182 | *pphys = kvtophys((vm_offset_t)addr); | |
183 | ||
184 | const boot_args *args = PE_state.bootArgs; | |
185 | ||
186 | if (!kvtophys((vm_offset_t)args)) { | |
187 | return FALSE; | |
188 | } | |
189 | ||
190 | const EfiMemoryRange *r = PHYSMAP_PTOV((uintptr_t)args->MemoryMap), *closest = NULL; | |
191 | const uint32_t size = args->MemoryMapDescriptorSize; | |
192 | const uint32_t count = args->MemoryMapSize / size; | |
193 | ||
194 | if (count > 1024) { // Sanity check | |
195 | return FALSE; | |
196 | } | |
197 | ||
198 | for (uint32_t i = 0; i < count; ++i, r = (const EfiMemoryRange *)(const void *)((const uint8_t *)r + size)) { | |
199 | if (r->PhysicalStart + r->NumberOfPages * PAGE_SIZE > *pphys) { | |
200 | continue; | |
201 | } | |
202 | ||
203 | if (!closest || r->PhysicalStart > closest->PhysicalStart) { | |
204 | closest = r; | |
205 | } | |
206 | } | |
207 | ||
208 | if (!closest) { | |
209 | return FALSE; | |
210 | } | |
211 | ||
212 | range->type = closest->Type; | |
213 | range->phys_start = closest->PhysicalStart; | |
214 | range->len = closest->NumberOfPages * PAGE_SIZE; | |
215 | ||
216 | return TRUE; | |
217 | } |