xref: /xnu-8792.61.2/osfmk/i386/panic_hooks.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
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
panic_hooks_init(void)59 panic_hooks_init(void)
60 {
61 	hw_lock_init(&panic_hooks_lock);
62 	queue_init(&panic_hooks);
63 }
64 
65 void
panic_hook(panic_hook_t * hook_,panic_hook_fn_t hook_fn)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
panic_unhook(panic_hook_t * hook_)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
panic_check_hook(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
panic_dump_mem(const void * addr,int len)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
panic_phys_range_before(const void * addr,uint64_t * pphys,panic_phys_range_t * range)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 }
218