]> git.saurik.com Git - apple/xnu.git/blame_incremental - osfmk/kdp/ml/i386/kdp_vm.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / osfmk / kdp / ml / i386 / kdp_vm.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2000 Apple Computer, 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#include <mach/mach_types.h>
29#include <mach/vm_attributes.h>
30#include <mach/vm_param.h>
31#include <libsa/types.h>
32
33#include <vm/vm_map.h>
34#include <i386/pmap.h>
35
36#include <kdp/kdp_core.h>
37#include <kdp/kdp_internal.h>
38#include <mach-o/loader.h>
39#include <mach/vm_map.h>
40#include <mach/vm_statistics.h>
41#include <mach/thread_status.h>
42#include <i386/thread.h>
43
44#include <vm/vm_protos.h>
45#include <vm/vm_kern.h>
46
47int kern_dump(void);
48int kdp_dump_trap(int type, x86_saved_state32_t *regs);
49
50typedef struct {
51 int flavor; /* the number for this flavor */
52 mach_msg_type_number_t count; /* count of ints in this flavor */
53} mythread_state_flavor_t;
54
55static mythread_state_flavor_t thread_flavor_array [] = {
56 {x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT}
57};
58
59static int kdp_mynum_flavors = 1;
60static int MAX_TSTATE_FLAVORS = 1;
61
62typedef struct {
63 vm_offset_t header;
64 int hoffset;
65 mythread_state_flavor_t *flavors;
66 int tstate_size;
67} tir_t;
68
69char command_buffer[512];
70
71static void
72kern_collectth_state(thread_t thread, tir_t *t)
73{
74 vm_offset_t header;
75 int hoffset, i ;
76 mythread_state_flavor_t *flavors;
77 struct thread_command *tc;
78 /*
79 * Fill in thread command structure.
80 */
81 header = t->header;
82 hoffset = t->hoffset;
83 flavors = t->flavors;
84
85 tc = (struct thread_command *) (header + hoffset);
86 tc->cmd = LC_THREAD;
87 tc->cmdsize = sizeof(struct thread_command) + t->tstate_size;
88 hoffset += sizeof(struct thread_command);
89 /*
90 * Follow with a struct thread_state_flavor and
91 * the appropriate thread state struct for each
92 * thread state flavor.
93 */
94 for (i = 0; i < kdp_mynum_flavors; i++) {
95 *(mythread_state_flavor_t *)(header+hoffset) =
96 flavors[i];
97 hoffset += sizeof(mythread_state_flavor_t);
98 /* Locate and obtain the non-volatile register context
99 * for this kernel thread. This should ideally be
100 * encapsulated in machine_thread_get_kern_state()
101 * but that routine appears to have been co-opted
102 * by CHUD to obtain pre-interrupt state.
103 */
104 if (flavors[i].flavor == x86_THREAD_STATE32) {
105 x86_thread_state32_t *tstate = (x86_thread_state32_t *) (header + hoffset);
106 vm_offset_t kstack;
107 bzero(tstate, x86_THREAD_STATE32_COUNT * sizeof(int));
108 if ((kstack = thread->kernel_stack) != 0){
109 struct x86_kernel_state *iks = STACK_IKS(kstack);
110 tstate->ebx = iks->k_ebx;
111 tstate->esp = iks->k_esp;
112 tstate->ebp = iks->k_ebp;
113 tstate->edi = iks->k_edi;
114 tstate->esi = iks->k_esi;
115 tstate->eip = iks->k_eip;
116 }
117 }
118 else if (machine_thread_get_kern_state(thread,
119 flavors[i].flavor, (thread_state_t) (header+hoffset),
120 &flavors[i].count) != KERN_SUCCESS)
121 printf ("Failure in machine_thread_get_kern_state()\n");
122 hoffset += flavors[i].count*sizeof(int);
123 }
124
125 t->hoffset = hoffset;
126}
127
128/* Intended to be called from the kernel trap handler if an unrecoverable fault
129 * occurs during a crashdump (which shouldn't happen since we validate mappings
130 * and so on). This should be reworked to attempt some form of recovery.
131 */
132int
133kdp_dump_trap(
134 int type,
135 __unused x86_saved_state32_t *saved_state)
136{
137 printf ("An unexpected trap (type %d) occurred during the system dump, terminating.\n", type);
138 kdp_send_crashdump_pkt (KDP_EOF, NULL, 0, ((void *) 0));
139 abort_panic_transfer();
140 kdp_flag &= ~KDP_PANIC_DUMP_ENABLED;
141 kdp_flag &= ~PANIC_CORE_ON_NMI;
142 kdp_flag &= ~PANIC_LOG_DUMP;
143
144 kdp_reset();
145
146 kdp_raise_exception(EXC_BAD_ACCESS, 0, 0, kdp.saved_state);
147 return( 0 );
148}
149
150int
151kern_dump(void)
152{
153 vm_map_t map;
154 unsigned int thread_count, segment_count;
155 unsigned int command_size = 0, header_size = 0, tstate_size = 0;
156
157 uint64_t hoffset = 0, foffset = 0, nfoffset = 0, max_header_size;
158 vm_offset_t header, txstart;
159 vm_address_t vmoffset;
160
161 struct mach_header *mh;
162 struct segment_command *sc;
163 vm_size_t size;
164 vm_prot_t prot = 0;
165 vm_prot_t maxprot = 0;
166 mythread_state_flavor_t flavors[MAX_TSTATE_FLAVORS];
167 vm_size_t nflavors;
168 vm_size_t i;
169 uint32_t nesting_depth = 0;
170 kern_return_t kret = 0;
171 struct vm_region_submap_info_64 vbr;
172 mach_msg_type_number_t vbrcount = 0;
173 tir_t tir1;
174
175 int error = 0;
176 int panic_error = 0;
177
178 map = kernel_map;
179
180 thread_count = 1;
181 segment_count = get_vmmap_entries(map);
182
183 printf("Kernel map has %d entries\n", segment_count);
184
185 nflavors = kdp_mynum_flavors;
186 bcopy((char *)thread_flavor_array,(char *) flavors,sizeof(thread_flavor_array));
187
188 for (i = 0; i < nflavors; i++)
189 tstate_size += sizeof(mythread_state_flavor_t) +
190 (flavors[i].count * sizeof(int));
191
192 command_size = (segment_count) *
193 sizeof(struct segment_command) +
194 thread_count * sizeof(struct thread_command) +
195 tstate_size * thread_count;
196
197 header_size = command_size + sizeof(struct mach_header);
198 header = (vm_offset_t) command_buffer;
199
200 /*
201 * Set up Mach-O header for currently executing 32 bit kernel.
202 */
203 printf ("Generated Mach-O header size was %d\n", header_size);
204
205 mh = (struct mach_header *) header;
206 mh->magic = MH_MAGIC;
207 mh->cputype = cpu_type();
208 mh->cpusubtype = cpu_subtype();
209 mh->filetype = MH_CORE;
210 mh->ncmds = segment_count + thread_count;
211 mh->sizeofcmds = command_size;
212 mh->flags = 0;
213
214 hoffset = sizeof(struct mach_header); /* offset into header */
215 foffset = round_page_32(header_size); /* offset into file */
216 /* Padding */
217 if ((foffset - header_size) < (4*sizeof(struct segment_command))) {
218 foffset += ((4*sizeof(struct segment_command)) - (foffset-header_size));
219 }
220
221 max_header_size = foffset;
222
223 vmoffset = VM_MIN_KERNEL_ADDRESS; /* offset into VM */
224
225 /* Transmit the Mach-O MH_CORE header, and seek forward past the
226 * area reserved for the segment and thread commands
227 * to begin data transmission
228 */
229
230 if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(nfoffset) , &nfoffset)) < 0) {
231 printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error);
232 error = panic_error;
233 goto out;
234 }
235
236 if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, sizeof(struct mach_header), (caddr_t) mh) < 0)) {
237 printf ("kdp_send_crashdump_data failed with error %d\n", panic_error);
238 error = panic_error;
239 goto out;
240 }
241
242 if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(foffset) , &foffset) < 0)) {
243 printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error);
244 error = panic_error;
245 goto out;
246 }
247 printf ("Transmitting kernel state:\n");
248
249 while ((segment_count > 0) || (kret == KERN_SUCCESS)) {
250 while (1) {
251
252 /*
253 * Get region information for next region.
254 */
255
256 vbrcount = VM_REGION_SUBMAP_INFO_COUNT_64;
257 if((kret = vm_region_recurse_64(map,
258 &vmoffset, &size, &nesting_depth,
259 (vm_region_recurse_info_t)&vbr,
260 &vbrcount)) != KERN_SUCCESS) {
261 break;
262 }
263
264 if(vbr.is_submap) {
265 nesting_depth++;
266 continue;
267 } else {
268 break;
269 }
270 }
271
272 if(kret != KERN_SUCCESS)
273 break;
274
275 prot = vbr.protection;
276 maxprot = vbr.max_protection;
277 /*
278 * Fill in segment command structure.
279 */
280
281 if (hoffset > max_header_size)
282 break;
283 sc = (struct segment_command *) (header);
284 sc->cmd = LC_SEGMENT;
285 sc->cmdsize = sizeof(struct segment_command);
286 sc->segname[0] = 0;
287 sc->vmaddr = vmoffset;
288 sc->vmsize = size;
289 sc->fileoff = (uint32_t) foffset;
290 sc->filesize = size;
291 sc->maxprot = maxprot;
292 sc->initprot = prot;
293 sc->nsects = 0;
294
295 if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(hoffset) , &hoffset)) < 0) {
296 printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error);
297 error = panic_error;
298 goto out;
299 }
300
301 if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, sizeof(struct segment_command) , (caddr_t) sc)) < 0) {
302 printf ("kdp_send_crashdump_data failed with error %d\n", panic_error);
303 error = panic_error;
304 goto out;
305 }
306
307 /* Do not transmit memory tagged VM_MEMORY_IOKIT - instead,
308 * seek past that region on the server - this creates a
309 * hole in the file.
310 */
311
312 if ((vbr.user_tag != VM_MEMORY_IOKIT)) {
313
314 if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(foffset) , &foffset)) < 0) {
315 printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error);
316 error = panic_error;
317 goto out;
318 }
319
320 txstart = vmoffset;
321
322 if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, size, (caddr_t) txstart)) < 0) {
323 printf ("kdp_send_crashdump_data failed with error %d\n", panic_error);
324 error = panic_error;
325 goto out;
326 }
327 }
328
329 hoffset += sizeof(struct segment_command);
330 foffset += size;
331 vmoffset += size;
332 segment_count--;
333 }
334 tir1.header = header;
335 tir1.hoffset = 0;
336 tir1.flavors = flavors;
337 tir1.tstate_size = tstate_size;
338
339 /* Now send out the LC_THREAD load command, with the thread information
340 * for the current activation.
341 * Note that the corefile can contain LC_SEGMENT commands with file
342 * offsets that point past the edge of the corefile, in the event that
343 * the last N VM regions were all I/O mapped or otherwise
344 * non-transferable memory, not followed by a normal VM region;
345 * i.e. there will be no hole that reaches to the end of the core file.
346 */
347 kern_collectth_state (current_thread(), &tir1);
348
349 if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(hoffset) , &hoffset)) < 0) {
350 printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error);
351 error = panic_error;
352 goto out;
353 }
354
355 if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, tir1.hoffset , (caddr_t) header)) < 0) {
356 printf ("kdp_send_crashdump_data failed with error %d\n", panic_error);
357 error = panic_error;
358 goto out;
359 }
360
361 /* last packet */
362 if ((panic_error = kdp_send_crashdump_pkt (KDP_EOF, NULL, 0, ((void *) 0))) < 0) {
363 printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error);
364 error = panic_error;
365 goto out;
366 }
367out:
368 return (error);
369}