]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 A |
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. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | #include <mach/mach_types.h> | |
29 | #include <mach/vm_attributes.h> | |
30 | #include <mach/vm_param.h> | |
9bccf70c | 31 | #include <libsa/types.h> |
0c530ab8 A |
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 | ||
1c79356b A |
47 | unsigned kdp_vm_read( caddr_t, caddr_t, unsigned); |
48 | unsigned kdp_vm_write( caddr_t, caddr_t, unsigned); | |
0c530ab8 | 49 | |
2d21ac55 A |
50 | boolean_t kdp_read_io; |
51 | boolean_t kdp_trans_off; | |
52 | uint32_t kdp_src_high32; | |
0c530ab8 A |
53 | extern pmap_paddr_t avail_start, avail_end; |
54 | ||
55 | extern void bcopy_phys(addr64_t from, addr64_t to, int size); | |
56 | static addr64_t kdp_vtophys(pmap_t pmap, addr64_t va); | |
57 | ||
58 | pmap_t kdp_pmap = 0; | |
1c79356b | 59 | |
55e303ae A |
60 | unsigned int not_in_kdp = 1; /* Cleared when we begin to access vm functions in kdp */ |
61 | ||
0c530ab8 A |
62 | extern vm_offset_t sectTEXTB, sectDATAB, sectLINKB, sectPRELINKB; |
63 | extern int sectSizeTEXT, sectSizeDATA, sectSizeLINK, sectSizePRELINK; | |
64 | ||
65 | int kern_dump(void); | |
66 | int kdp_dump_trap(int type, x86_saved_state32_t *regs); | |
67 | ||
68 | typedef struct { | |
69 | int flavor; /* the number for this flavor */ | |
70 | mach_msg_type_number_t count; /* count of ints in this flavor */ | |
71 | } mythread_state_flavor_t; | |
72 | ||
73 | static mythread_state_flavor_t thread_flavor_array [] = { | |
74 | {x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT} | |
75 | }; | |
76 | ||
77 | static int kdp_mynum_flavors = 1; | |
78 | static int MAX_TSTATE_FLAVORS = 1; | |
79 | ||
80 | typedef struct { | |
81 | vm_offset_t header; | |
82 | int hoffset; | |
83 | mythread_state_flavor_t *flavors; | |
84 | int tstate_size; | |
85 | } tir_t; | |
86 | ||
87 | char command_buffer[512]; | |
88 | ||
89 | static addr64_t | |
90 | kdp_vtophys( | |
91 | pmap_t pmap, | |
92 | addr64_t va) | |
93 | { | |
94 | addr64_t pa; | |
95 | ppnum_t pp; | |
2d21ac55 | 96 | |
0c530ab8 A |
97 | pp = pmap_find_phys(pmap, va); |
98 | if(!pp) return 0; | |
99 | ||
100 | pa = ((addr64_t)pp << 12) | (va & 0x0000000000000FFFULL); | |
101 | return(pa); | |
102 | } | |
103 | ||
1c79356b A |
104 | /* |
105 | * | |
106 | */ | |
107 | unsigned kdp_vm_read( | |
108 | caddr_t src, | |
109 | caddr_t dst, | |
110 | unsigned len) | |
111 | { | |
0c530ab8 A |
112 | addr64_t cur_virt_src = (addr64_t)((unsigned int)src | (((uint64_t)kdp_src_high32) << 32)); |
113 | addr64_t cur_virt_dst = (addr64_t)((unsigned int)dst); | |
114 | addr64_t cur_phys_dst, cur_phys_src; | |
115 | unsigned resid = len; | |
116 | unsigned cnt = 0; | |
117 | pmap_t src_pmap = kernel_pmap; | |
118 | ||
119 | /* If a different pmap has been specified with kdp_pmap, use it to translate the | |
120 | * source (cur_virt_src); otherwise, the source is translated using the | |
121 | * kernel_pmap. | |
122 | */ | |
123 | if (kdp_pmap) | |
124 | src_pmap = kdp_pmap; | |
125 | ||
126 | while (resid != 0) { | |
127 | /* Translate, unless kdp_trans_off is set */ | |
128 | if (!kdp_trans_off) { | |
129 | if (!(cur_phys_src = kdp_vtophys(src_pmap, | |
130 | cur_virt_src))) | |
131 | goto exit; | |
132 | } | |
133 | else | |
134 | cur_phys_src = cur_virt_src; | |
135 | ||
136 | /* Always translate the destination buffer using the kernel_pmap */ | |
137 | if(!(cur_phys_dst = kdp_vtophys(kernel_pmap, cur_virt_dst))) | |
138 | goto exit; | |
139 | ||
2d21ac55 A |
140 | /* Validate physical page numbers unless kdp_read_io is set */ |
141 | if (kdp_read_io == FALSE) | |
0c530ab8 A |
142 | if (!pmap_valid_page(i386_btop(cur_phys_dst)) || !pmap_valid_page(i386_btop(cur_phys_src))) |
143 | goto exit; | |
144 | ||
145 | /* Get length left on page */ | |
146 | cnt = PAGE_SIZE - (cur_phys_src & PAGE_MASK); | |
147 | if (cnt > (PAGE_SIZE - (cur_phys_dst & PAGE_MASK))) | |
148 | cnt = PAGE_SIZE - (cur_phys_dst & PAGE_MASK); | |
149 | if (cnt > resid) | |
150 | cnt = resid; | |
151 | ||
152 | /* Do a physical copy */ | |
153 | bcopy_phys(cur_phys_src, cur_phys_dst, cnt); | |
154 | ||
155 | cur_virt_src += cnt; | |
156 | cur_virt_dst += cnt; | |
157 | resid -= cnt; | |
158 | } | |
159 | exit: | |
160 | return (len - resid); | |
1c79356b A |
161 | } |
162 | ||
163 | /* | |
164 | * | |
165 | */ | |
166 | unsigned kdp_vm_write( | |
167 | caddr_t src, | |
168 | caddr_t dst, | |
169 | unsigned len) | |
170 | { | |
0c530ab8 A |
171 | addr64_t cur_virt_src, cur_virt_dst; |
172 | addr64_t cur_phys_src, cur_phys_dst; | |
173 | unsigned resid, cnt, cnt_src, cnt_dst; | |
174 | ||
175 | #ifdef KDP_VM_WRITE_DEBUG | |
176 | printf("kdp_vm_write: src %x dst %x len %x - %08X %08X\n", src, dst, len, ((unsigned long *)src)[0], ((unsigned long *)src)[1]); | |
177 | #endif | |
178 | ||
179 | cur_virt_src = (addr64_t)((unsigned int)src); | |
180 | cur_virt_dst = (addr64_t)((unsigned int)dst); | |
181 | ||
182 | resid = len; | |
183 | ||
184 | while (resid != 0) { | |
185 | if ((cur_phys_dst = kdp_vtophys(kernel_pmap, cur_virt_dst)) == 0) | |
186 | goto exit; | |
187 | ||
188 | if ((cur_phys_src = kdp_vtophys(kernel_pmap, cur_virt_src)) == 0) | |
189 | goto exit; | |
190 | ||
191 | cnt_src = ((cur_phys_src + PAGE_SIZE) & (PAGE_MASK)) - cur_phys_src; | |
192 | cnt_dst = ((cur_phys_dst + PAGE_SIZE) & (PAGE_MASK)) - cur_phys_dst; | |
193 | ||
194 | if (cnt_src > cnt_dst) | |
195 | cnt = cnt_dst; | |
196 | else | |
197 | cnt = cnt_src; | |
198 | if (cnt > resid) | |
199 | cnt = resid; | |
200 | ||
201 | bcopy_phys(cur_phys_src, cur_phys_dst, cnt); /* Copy stuff over */ | |
202 | ||
203 | cur_virt_src +=cnt; | |
204 | cur_virt_dst +=cnt; | |
205 | resid -= cnt; | |
206 | } | |
207 | exit: | |
208 | return (len - resid); | |
4452a7af A |
209 | } |
210 | ||
0c530ab8 A |
211 | static void |
212 | kern_collectth_state(thread_t thread, tir_t *t) | |
4452a7af | 213 | { |
0c530ab8 A |
214 | vm_offset_t header; |
215 | int hoffset, i ; | |
216 | mythread_state_flavor_t *flavors; | |
217 | struct thread_command *tc; | |
218 | /* | |
219 | * Fill in thread command structure. | |
220 | */ | |
221 | header = t->header; | |
222 | hoffset = t->hoffset; | |
223 | flavors = t->flavors; | |
224 | ||
225 | tc = (struct thread_command *) (header + hoffset); | |
226 | tc->cmd = LC_THREAD; | |
227 | tc->cmdsize = sizeof(struct thread_command) + t->tstate_size; | |
228 | hoffset += sizeof(struct thread_command); | |
229 | /* | |
230 | * Follow with a struct thread_state_flavor and | |
231 | * the appropriate thread state struct for each | |
232 | * thread state flavor. | |
233 | */ | |
234 | for (i = 0; i < kdp_mynum_flavors; i++) { | |
235 | *(mythread_state_flavor_t *)(header+hoffset) = | |
236 | flavors[i]; | |
237 | hoffset += sizeof(mythread_state_flavor_t); | |
238 | /* Locate and obtain the non-volatile register context | |
239 | * for this kernel thread. This should ideally be | |
240 | * encapsulated in machine_thread_get_kern_state() | |
241 | * but that routine appears to have been co-opted | |
242 | * by CHUD to obtain pre-interrupt state. | |
243 | */ | |
244 | if (flavors[i].flavor == x86_THREAD_STATE32) { | |
245 | x86_thread_state32_t *tstate = (x86_thread_state32_t *) (header + hoffset); | |
246 | vm_offset_t kstack; | |
247 | bzero(tstate, x86_THREAD_STATE32_COUNT * sizeof(int)); | |
248 | if ((kstack = thread->kernel_stack) != 0){ | |
249 | struct x86_kernel_state32 *iks = STACK_IKS(kstack); | |
250 | tstate->ebx = iks->k_ebx; | |
251 | tstate->esp = iks->k_esp; | |
252 | tstate->ebp = iks->k_ebp; | |
253 | tstate->edi = iks->k_edi; | |
254 | tstate->esi = iks->k_esi; | |
255 | tstate->eip = iks->k_eip; | |
256 | } | |
257 | } | |
258 | else if (machine_thread_get_kern_state(thread, | |
259 | flavors[i].flavor, (thread_state_t) (header+hoffset), | |
260 | &flavors[i].count) != KERN_SUCCESS) | |
261 | printf ("Failure in machine_thread_get_kern_state()\n"); | |
262 | hoffset += flavors[i].count*sizeof(int); | |
263 | } | |
264 | ||
265 | t->hoffset = hoffset; | |
266 | } | |
267 | ||
268 | /* Intended to be called from the kernel trap handler if an unrecoverable fault | |
269 | * occurs during a crashdump (which shouldn't happen since we validate mappings | |
270 | * and so on). This should be reworked to attempt some form of recovery. | |
271 | */ | |
272 | int | |
273 | kdp_dump_trap( | |
274 | int type, | |
275 | __unused x86_saved_state32_t *saved_state) | |
276 | { | |
277 | printf ("An unexpected trap (type %d) occurred during the system dump, terminating.\n", type); | |
278 | kdp_send_crashdump_pkt (KDP_EOF, NULL, 0, ((void *) 0)); | |
279 | abort_panic_transfer(); | |
280 | kdp_flag &= ~KDP_PANIC_DUMP_ENABLED; | |
281 | kdp_flag &= ~PANIC_CORE_ON_NMI; | |
282 | kdp_flag &= ~PANIC_LOG_DUMP; | |
283 | ||
284 | kdp_reset(); | |
285 | ||
286 | kdp_raise_exception(EXC_BAD_ACCESS, 0, 0, kdp.saved_state); | |
287 | return( 0 ); | |
288 | } | |
289 | ||
290 | int | |
291 | kern_dump(void) | |
292 | { | |
293 | vm_map_t map; | |
294 | unsigned int thread_count, segment_count; | |
295 | unsigned int command_size = 0, header_size = 0, tstate_size = 0; | |
296 | unsigned int hoffset = 0, foffset = 0, nfoffset = 0, vmoffset = 0; | |
297 | unsigned int max_header_size = 0; | |
298 | vm_offset_t header; | |
299 | struct mach_header *mh; | |
300 | struct segment_command *sc; | |
301 | vm_size_t size; | |
302 | vm_prot_t prot = 0; | |
303 | vm_prot_t maxprot = 0; | |
304 | vm_inherit_t inherit = 0; | |
305 | mythread_state_flavor_t flavors[MAX_TSTATE_FLAVORS]; | |
306 | vm_size_t nflavors; | |
307 | vm_size_t i; | |
308 | uint32_t nesting_depth = 0; | |
309 | kern_return_t kret = 0; | |
310 | struct vm_region_submap_info_64 vbr; | |
311 | mach_msg_type_number_t vbrcount = 0; | |
312 | tir_t tir1; | |
313 | ||
314 | int error = 0; | |
315 | int panic_error = 0; | |
316 | unsigned int txstart = 0; | |
317 | unsigned int mach_section_count = 4; | |
318 | unsigned int num_sects_txed = 0; | |
319 | ||
320 | map = kernel_map; | |
321 | ||
322 | not_in_kdp = 0; /* Signal vm functions not to acquire locks */ | |
323 | ||
324 | thread_count = 1; | |
325 | segment_count = get_vmmap_entries(map); | |
326 | ||
327 | printf("Kernel map has %d entries\n", segment_count); | |
328 | ||
329 | nflavors = kdp_mynum_flavors; | |
330 | bcopy((char *)thread_flavor_array,(char *) flavors,sizeof(thread_flavor_array)); | |
331 | ||
332 | for (i = 0; i < nflavors; i++) | |
333 | tstate_size += sizeof(mythread_state_flavor_t) + | |
334 | (flavors[i].count * sizeof(int)); | |
335 | ||
336 | command_size = (segment_count + mach_section_count) * | |
337 | sizeof(struct segment_command) + | |
338 | thread_count * sizeof(struct thread_command) + | |
339 | tstate_size * thread_count; | |
340 | ||
341 | header_size = command_size + sizeof(struct mach_header); | |
342 | header = (vm_offset_t) command_buffer; | |
343 | ||
344 | /* | |
345 | * Set up Mach-O header for currently executing 32 bit kernel. | |
346 | */ | |
347 | printf ("Generated Mach-O header size was %d\n", header_size); | |
348 | ||
349 | mh = (struct mach_header *) header; | |
350 | mh->magic = MH_MAGIC; | |
351 | mh->cputype = cpu_type(); | |
352 | mh->cpusubtype = cpu_subtype(); | |
353 | mh->filetype = MH_CORE; | |
354 | mh->ncmds = segment_count + thread_count + mach_section_count; | |
355 | mh->sizeofcmds = command_size; | |
356 | mh->flags = 0; | |
357 | ||
358 | hoffset = sizeof(struct mach_header); /* offset into header */ | |
359 | foffset = round_page_32(header_size); /* offset into file */ | |
360 | /* Padding */ | |
361 | if ((foffset - header_size) < (4*sizeof(struct segment_command))) { | |
362 | foffset += ((4*sizeof(struct segment_command)) - (foffset-header_size)); | |
363 | } | |
364 | ||
365 | max_header_size = foffset; | |
366 | ||
367 | vmoffset = VM_MIN_ADDRESS; /* offset into VM */ | |
368 | ||
369 | /* Transmit the Mach-O MH_CORE header, and seek forward past the | |
370 | * area reserved for the segment and thread commands | |
371 | * to begin data transmission | |
372 | */ | |
373 | ||
374 | if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(nfoffset) , &nfoffset)) < 0) { | |
375 | printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); | |
376 | error = panic_error; | |
377 | goto out; | |
378 | } | |
379 | ||
380 | if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, sizeof(struct mach_header), (caddr_t) mh) < 0)) { | |
381 | printf ("kdp_send_crashdump_data failed with error %d\n", panic_error); | |
382 | error = panic_error; | |
383 | goto out; | |
384 | } | |
385 | ||
386 | if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(foffset) , &foffset) < 0)) { | |
387 | printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); | |
388 | error = panic_error; | |
389 | goto out; | |
390 | } | |
391 | printf ("Transmitting kernel state, please wait: "); | |
392 | ||
393 | while ((segment_count > 0) || (kret == KERN_SUCCESS)){ | |
394 | /* Check if we've transmitted all the kernel sections */ | |
395 | if (num_sects_txed == mach_section_count) { | |
396 | ||
397 | while (1) { | |
398 | ||
399 | /* | |
400 | * Get region information for next region. | |
401 | */ | |
402 | ||
403 | vbrcount = VM_REGION_SUBMAP_INFO_COUNT_64; | |
404 | if((kret = vm_region_recurse_64(map, | |
405 | &vmoffset, &size, &nesting_depth, | |
406 | (vm_region_recurse_info_t)&vbr, | |
407 | &vbrcount)) != KERN_SUCCESS) { | |
408 | break; | |
409 | } | |
410 | ||
411 | if(vbr.is_submap) { | |
412 | nesting_depth++; | |
413 | continue; | |
414 | } else { | |
415 | break; | |
416 | } | |
417 | } | |
418 | ||
419 | if(kret != KERN_SUCCESS) | |
420 | break; | |
421 | ||
422 | prot = vbr.protection; | |
423 | maxprot = vbr.max_protection; | |
424 | inherit = vbr.inheritance; | |
425 | } | |
426 | else | |
427 | { | |
428 | switch (num_sects_txed) { | |
429 | case 0: | |
430 | /* Transmit the kernel text section */ | |
431 | vmoffset = sectTEXTB; | |
432 | size = sectSizeTEXT; | |
433 | break; | |
434 | case 1: | |
435 | vmoffset = sectDATAB; | |
436 | size = sectSizeDATA; | |
437 | break; | |
438 | case 2: | |
439 | vmoffset = sectPRELINKB; | |
440 | size = sectSizePRELINK; | |
441 | break; | |
442 | case 3: | |
443 | vmoffset = sectLINKB; | |
444 | size = sectSizeLINK; | |
445 | break; | |
446 | } | |
447 | num_sects_txed++; | |
448 | } | |
449 | /* | |
450 | * Fill in segment command structure. | |
451 | */ | |
452 | ||
453 | if (hoffset > max_header_size) | |
454 | break; | |
455 | sc = (struct segment_command *) (header); | |
456 | sc->cmd = LC_SEGMENT; | |
457 | sc->cmdsize = sizeof(struct segment_command); | |
458 | sc->segname[0] = 0; | |
459 | sc->vmaddr = vmoffset; | |
460 | sc->vmsize = size; | |
461 | sc->fileoff = foffset; | |
462 | sc->filesize = size; | |
463 | sc->maxprot = maxprot; | |
464 | sc->initprot = prot; | |
465 | sc->nsects = 0; | |
466 | ||
467 | if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(hoffset) , &hoffset)) < 0) { | |
468 | printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); | |
469 | error = panic_error; | |
470 | goto out; | |
471 | } | |
472 | ||
473 | if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, sizeof(struct segment_command) , (caddr_t) sc)) < 0) { | |
474 | printf ("kdp_send_crashdump_data failed with error %d\n", panic_error); | |
475 | error = panic_error; | |
476 | goto out; | |
477 | } | |
478 | ||
479 | /* Do not transmit memory tagged VM_MEMORY_IOKIT - instead, | |
480 | * seek past that region on the server - this creates a | |
481 | * hole in the file. | |
482 | */ | |
483 | ||
484 | if ((vbr.user_tag != VM_MEMORY_IOKIT)) { | |
485 | ||
486 | if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(foffset) , &foffset)) < 0) { | |
487 | printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); | |
488 | error = panic_error; | |
489 | goto out; | |
490 | } | |
491 | ||
492 | txstart = vmoffset; | |
493 | ||
494 | if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, size, (caddr_t) txstart)) < 0) { | |
495 | printf ("kdp_send_crashdump_data failed with error %d\n", panic_error); | |
496 | error = panic_error; | |
497 | goto out; | |
498 | } | |
499 | } | |
500 | ||
501 | hoffset += sizeof(struct segment_command); | |
502 | foffset += size; | |
503 | vmoffset += size; | |
504 | segment_count--; | |
505 | } | |
506 | tir1.header = header; | |
507 | tir1.hoffset = 0; | |
508 | tir1.flavors = flavors; | |
509 | tir1.tstate_size = tstate_size; | |
510 | ||
511 | /* Now send out the LC_THREAD load command, with the thread information | |
512 | * for the current activation. | |
513 | * Note that the corefile can contain LC_SEGMENT commands with file | |
514 | * offsets that point past the edge of the corefile, in the event that | |
515 | * the last N VM regions were all I/O mapped or otherwise | |
516 | * non-transferable memory, not followed by a normal VM region; | |
517 | * i.e. there will be no hole that reaches to the end of the core file. | |
518 | */ | |
519 | kern_collectth_state (current_thread(), &tir1); | |
520 | ||
521 | if ((panic_error = kdp_send_crashdump_pkt (KDP_SEEK, NULL, sizeof(hoffset) , &hoffset)) < 0) { | |
522 | printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); | |
523 | error = panic_error; | |
524 | goto out; | |
525 | } | |
526 | ||
527 | if ((panic_error = kdp_send_crashdump_data (KDP_DATA, NULL, tir1.hoffset , (caddr_t) header)) < 0) { | |
528 | printf ("kdp_send_crashdump_data failed with error %d\n", panic_error); | |
529 | error = panic_error; | |
530 | goto out; | |
531 | } | |
532 | ||
533 | /* last packet */ | |
534 | if ((panic_error = kdp_send_crashdump_pkt (KDP_EOF, NULL, 0, ((void *) 0))) < 0) | |
535 | { | |
536 | printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); | |
537 | error = panic_error; | |
538 | goto out; | |
539 | } | |
540 | out: | |
541 | return (error); | |
55e303ae | 542 | } |