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