]> git.saurik.com Git - apple/xnu.git/blob - osfmk/arm64/alternate_debugger.c
xnu-4570.51.1.tar.gz
[apple/xnu.git] / osfmk / arm64 / alternate_debugger.c
1 /*
2 * Copyright (c) 2012 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 #if ALTERNATE_DEBUGGER
30
31 /*
32
33 The alternate debugger feature is enabled by setting the boot arg "alternate_debugger_init"
34 to the size of memory that should be set aside for the debugger. The boot arg
35 "alternate_debugger_init_pages" is used to allocate more vmpages that the alternate debugger
36 may use to do additional VA->PA mappings. The boot-arg "alternate_debugger_pause_for_load_at_boot"
37 will halt the system so that the debugger can be loaded early in the boot cycle -- once the
38 alternate debugger code is loaded, a register must be set to a 1 to continue the boot process.
39
40 Here's an example:
41 nvram boot-arg="alternate_debugger_init=0x800000 alternate_debugger_init_pages=0x8000 alternate_debugger_pause_for_load_at_boot=1"
42
43 The low memory global lgAltDebugger will contain the address of the allocated memory for
44 the alternate debugger. On arm64, the address of this low memory global is 0xffffff8000002048.
45
46 At any point after the low memory global is non-zero, Astris may be used to halt the cpu
47 and load the alternate debugger:
48
49 If no alternate debugger is given, but alternate_debugger_init has been specified, and the
50 kernel debugger is entered, the string ">MT<" is printed and normal processing continues.
51
52 Anytime the alternate debugger is entered, the osversion string is modified to start with "ALT"
53 so that panic reports can clearly indicated that some kernel poking may have occurred, and
54 the panic should be weighted accordingly.
55
56 */
57
58 #include <arm64/alternate_debugger.h>
59
60 #include <kern/kalloc.h>
61 #include <arm64/lowglobals.h>
62 #include <arm/caches_internal.h>
63 #include <kern/cpu_data.h>
64 #include <arm/pmap.h>
65 #include <pexpert/pexpert.h>
66 #include <vm/vm_map.h>
67 #include <vm/vm_kern.h>
68 #include <libkern/version.h>
69
70 void kprintf(const char *fmt, ...);
71
72
73 static mach_vm_address_t alt_code;
74 static mach_vm_size_t alt_size;
75 static mach_vm_address_t alt_pages;
76 static mach_vm_size_t alt_pages_size;
77
78 typedef void (*t_putc_fn)(char c);
79 typedef void (*t_call_altdbg_fn)(mach_vm_size_t size, mach_vm_address_t pages, mach_vm_size_t pages_size, t_putc_fn putc_address );
80
81 // used as a temporary alternate debugger until another is loaded
82 extern void alternate_debugger_just_return(__unused mach_vm_size_t size, __unused mach_vm_address_t pages, __unused mach_vm_size_t pages_size, t_putc_fn putc_address);
83 extern void *alternate_debugger_just_return_end;
84
85 // public entry to the alternate debugger
86 void alternate_debugger_enter(void)
87 {
88 if ( alt_code != 0 ) {
89 disable_preemption();
90
91 printf("########## Going to call ALTERNATE DEBUGGER\n");
92
93 // make sure it isn't in the cache
94 assert((alt_size & 0xFFFFFFFF00000000) == 0);
95 flush_dcache(alt_code, (unsigned int)alt_size, 0);
96
97 // set the code to execute
98 pmap_protect(kernel_map->pmap, alt_code, alt_code+alt_size, VM_PROT_READ|VM_PROT_EXECUTE);
99
100 // black-spot the OS version for any panic reports that occur because of entering the alternate debugger
101 if ( *osversion ) {
102 memcpy(osversion, "ALT", 3); // Version set, stomp on the begining of it
103 } else {
104 strncpy(osversion, "ALT - Version Not Set Yet", OSVERSIZE);
105 }
106
107 kprintf("########## Calling ALTERNATE DEBUGGER (size %lld, pages 0x%llx, pages_size 0x%llx, putc %p\n", alt_size, alt_pages, alt_pages_size, &consdebug_putc_unbuffered);
108 ((t_call_altdbg_fn)alt_code)(alt_size, alt_pages, alt_pages_size, &consdebug_putc_unbuffered);
109 kprintf("########## Returned from calling ALTERNATE DEBUGGER\n");
110
111 enable_preemption();
112 }
113 }
114
115 // public entry to check boot args and init accordingly
116 void alternate_debugger_init(void)
117 {
118 // use the alternate debugger
119 if( PE_parse_boot_argn("alternate_debugger_init", (void*)&alt_size, sizeof(alt_size)) )
120 {
121 vm_offset_t alt_va = 0;
122
123 kprintf("########## ALTERNATE_DEBUGGER\n");
124
125 PE_parse_boot_argn("alternate_debugger_init_pages", (void*)&alt_pages_size, sizeof(alt_pages_size));
126
127 alt_size = vm_map_round_page(alt_size,
128 VM_MAP_PAGE_MASK(kernel_map));
129 alt_pages_size = vm_map_round_page(alt_pages_size,
130 VM_MAP_PAGE_MASK(kernel_map));
131
132 kern_return_t kr = KERN_SUCCESS;
133 kr = kmem_alloc_contig(kernel_map, &alt_va, alt_size, VM_MAP_PAGE_MASK(kernel_map), 0, 0, KMA_NOPAGEWAIT | KMA_KOBJECT | KMA_LOMEM, VM_KERN_MEMORY_DIAG);
134 if( kr != KERN_SUCCESS)
135 {
136 kprintf("########## ALTERNATE_DEBUGGER FAILED kmem_alloc_contig with %d\n", kr);
137 alt_va = 0;
138 }
139 else {
140 if ( alt_pages_size ) {
141 alt_pages = (vm_offset_t) kalloc((vm_size_t) alt_pages_size);
142 }
143 }
144
145 kprintf("########## Initializing ALTERNATE DEBUGGER : [alloc size 0x%llx @0x%lx] [pages_size 0x%llx @0x%llx] -- lowmem pointer at %p\n",
146 alt_size, alt_va, alt_pages_size, alt_pages, &lowGlo.lgAltDebugger );
147
148 if ( alt_va ) {
149 uintptr_t just_return_size = (uintptr_t)&alternate_debugger_just_return_end - (uintptr_t)&alternate_debugger_just_return;
150 assert(just_return_size <= alt_size); // alt_size is page-rounded, just_return_size should be much less than a page.
151 // install a simple return vector
152 memcpy((void*)alt_va, &alternate_debugger_just_return, just_return_size);
153
154 // code is ready, enable the pointers to it
155 lowGlo.lgAltDebugger = alt_code = alt_va;
156
157 #if 1
158 // DEBUG for BRING-UP testing
159 unsigned int alt_init_test;
160 if(PE_parse_boot_argn("alternate_debugger_pause_for_load_at_boot", &alt_init_test, sizeof(alt_init_test)) ) {
161
162 // debug!!
163 kprintf("########## Waiting for ALTERNATE DEBUGGER to load (in file %s).... to continue, set register to 1", __FILE__ );
164 volatile int ii = 0;
165 while(!ii)
166 ;
167 kprintf("\n");
168 alternate_debugger_enter();
169 }
170 #endif
171 }
172 }
173 }
174
175 #endif /* ALTERNATE_DEBUGGER */