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
39struct 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
47typedef char check1_[sizeof(struct panic_hook)
48					 <= sizeof(panic_hook_t) ? 1 : -1];
49typedef char check2_[PAGE_SIZE == 4096 ? 1 : -1];
50
51static hw_lock_data_t 	panic_hooks_lock;
52static queue_head_t 	panic_hooks;
53static uint8_t		 	panic_dump_buf[8192];
54
55#define PANIC_HOOK_MAGIC1		0x4A1C400C
56#define PANIC_HOOK_MAGIC2		0xC004C1A4
57
58void panic_hooks_init(void)
59{
60	hw_lock_init(&panic_hooks_lock);
61	queue_init(&panic_hooks);
62}
63
64void 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
78void 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
87void 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 */
117void 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
170bool 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}
204