]>
Commit | Line | Data |
---|---|---|
fe8ab488 A |
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 = (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((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 | bool 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 = (EfiMemoryRange *)(void *)((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 | } |