]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2007 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 | ||
29 | #include <mach/mach_types.h> | |
30 | #include <kern/debug.h> | |
31 | ||
32 | #include <kdp/kdp_internal.h> | |
33 | #include <kdp/kdp_private.h> | |
34 | ||
35 | #include <libsa/types.h> | |
36 | ||
37 | #include <string.h> /* bcopy */ | |
38 | ||
39 | #include <kern/processor.h> | |
40 | #include <kern/thread.h> | |
41 | #include <vm/vm_map.h> | |
42 | #include <vm/vm_kern.h> | |
43 | ||
44 | int kdp_vm_read( caddr_t, caddr_t, unsigned int); | |
45 | int kdp_vm_write( caddr_t, caddr_t, unsigned int); | |
46 | ||
47 | #define DO_ALIGN 1 /* align all packet data accesses */ | |
48 | ||
49 | #define KDP_TEST_HARNESS 0 | |
50 | #if KDP_TEST_HARNESS | |
51 | #define dprintf(x) kprintf x | |
52 | #else | |
53 | #define dprintf(x) | |
54 | #endif | |
55 | ||
56 | static kdp_dispatch_t | |
57 | dispatch_table[KDP_HOSTREBOOT - KDP_CONNECT +1] = | |
58 | { | |
59 | /* 0 */ kdp_connect, | |
60 | /* 1 */ kdp_disconnect, | |
61 | /* 2 */ kdp_hostinfo, | |
62 | /* 3 */ kdp_version, | |
63 | /* 4 */ kdp_maxbytes, | |
64 | /* 5 */ kdp_readmem, | |
65 | /* 6 */ kdp_writemem, | |
66 | /* 7 */ kdp_readregs, | |
67 | /* 8 */ kdp_writeregs, | |
68 | /* 9 */ kdp_unknown, | |
69 | /* A */ kdp_unknown, | |
70 | /* B */ kdp_suspend, | |
71 | /* C */ kdp_resumecpus, | |
72 | /* D */ kdp_unknown, | |
73 | /* E */ kdp_unknown, | |
74 | /* F */ kdp_breakpoint_set, | |
75 | /*10 */ kdp_breakpoint_remove, | |
76 | /*11 */ kdp_regions, | |
77 | /*12 */ kdp_reattach, | |
78 | /*13 */ (kdp_dispatch_t)kdp_reboot | |
79 | }; | |
80 | ||
81 | kdp_glob_t kdp; | |
82 | ||
83 | ||
84 | #define MAX_BREAKPOINTS 100 | |
85 | #define KDP_MAX_BREAKPOINTS 100 | |
86 | ||
87 | #define BREAKPOINT_NOT_FOUND 101 | |
88 | #define BREAKPOINT_ALREADY_SET 102 | |
89 | ||
90 | #define KDP_VERSION 10 | |
91 | ||
92 | typedef struct{ | |
93 | unsigned int address; | |
94 | unsigned int old_instruction; | |
95 | } kdp_breakpoint_record_t; | |
96 | ||
97 | static kdp_breakpoint_record_t breakpoint_list[MAX_BREAKPOINTS]; | |
98 | static unsigned int breakpoints_initialized = 0; | |
99 | ||
100 | int reattach_wait = 0; | |
101 | int noresume_on_disconnect = 0; | |
102 | extern unsigned int return_on_panic; | |
103 | ||
104 | #define MAXCOMLEN 16 | |
105 | ||
106 | struct thread_snapshot { | |
107 | uint32_t snapshot_magic; | |
108 | thread_t thread_id; | |
109 | int32_t state; | |
110 | wait_queue_t wait_queue; | |
111 | event64_t wait_event; | |
112 | vm_offset_t kernel_stack; | |
113 | vm_offset_t reserved_stack; | |
114 | thread_continue_t continuation; | |
115 | uint32_t nkern_frames; | |
116 | char user64_p; | |
117 | uint32_t nuser_frames; | |
118 | int32_t pid; | |
119 | char p_comm[MAXCOMLEN + 1]; | |
120 | }; | |
121 | ||
122 | typedef struct thread_snapshot *thread_snapshot_t; | |
123 | ||
124 | extern int | |
125 | machine_trace_thread(thread_t thread, char *tracepos, char *tracebound, int nframes, boolean_t user_p); | |
126 | extern int | |
127 | machine_trace_thread64(thread_t thread, char *tracepos, char *tracebound, int nframes, boolean_t user_p); | |
128 | extern int | |
129 | proc_pid(void *p); | |
130 | extern void | |
131 | proc_name_kdp(task_t task, char *buf, int size); | |
132 | ||
133 | extern void | |
134 | kdp_snapshot_postflight(void); | |
135 | ||
136 | static int | |
137 | pid_from_task(task_t task); | |
138 | ||
139 | int | |
140 | kdp_stackshot(int pid, void *tracebuf, uint32_t tracebuf_size, unsigned trace_options, uint32_t *pbytesTraced); | |
141 | ||
142 | extern char version[]; | |
143 | ||
144 | boolean_t | |
145 | kdp_packet( | |
146 | unsigned char *pkt, | |
147 | int *len, | |
148 | unsigned short *reply_port | |
149 | ) | |
150 | { | |
151 | static unsigned aligned_pkt[1538/sizeof(unsigned)+1]; // max ether pkt | |
152 | kdp_pkt_t *rd = (kdp_pkt_t *)&aligned_pkt; | |
153 | size_t plen = *len; | |
154 | kdp_req_t req; | |
155 | boolean_t ret; | |
156 | ||
157 | #if DO_ALIGN | |
158 | bcopy((char *)pkt, (char *)rd, sizeof(aligned_pkt)); | |
159 | #else | |
160 | rd = (kdp_pkt_t *)pkt; | |
161 | #endif | |
162 | if (plen < sizeof (rd->hdr) || rd->hdr.len != plen) { | |
163 | printf("kdp_packet bad len pkt %lu hdr %d\n", plen, rd->hdr.len); | |
164 | ||
165 | return (FALSE); | |
166 | } | |
167 | ||
168 | if (rd->hdr.is_reply) { | |
169 | printf("kdp_packet reply recvd req %x seq %x\n", | |
170 | rd->hdr.request, rd->hdr.seq); | |
171 | ||
172 | return (FALSE); | |
173 | } | |
174 | ||
175 | req = rd->hdr.request; | |
176 | if (req > KDP_HOSTREBOOT) { | |
177 | printf("kdp_packet bad request %x len %d seq %x key %x\n", | |
178 | rd->hdr.request, rd->hdr.len, rd->hdr.seq, rd->hdr.key); | |
179 | ||
180 | return (FALSE); | |
181 | } | |
182 | ||
183 | ret = ((*dispatch_table[req - KDP_CONNECT])(rd, len, reply_port)); | |
184 | #if DO_ALIGN | |
185 | bcopy((char *)rd, (char *) pkt, *len); | |
186 | #endif | |
187 | return ret; | |
188 | } | |
189 | ||
190 | static boolean_t | |
191 | kdp_unknown( | |
192 | kdp_pkt_t *pkt, | |
193 | __unused int *len, | |
194 | __unused unsigned short *reply_port | |
195 | ) | |
196 | { | |
197 | kdp_pkt_t *rd = (kdp_pkt_t *)pkt; | |
198 | ||
199 | printf("kdp_unknown request %x len %d seq %x key %x\n", | |
200 | rd->hdr.request, rd->hdr.len, rd->hdr.seq, rd->hdr.key); | |
201 | ||
202 | return (FALSE); | |
203 | } | |
204 | ||
205 | static boolean_t | |
206 | kdp_connect( | |
207 | kdp_pkt_t *pkt, | |
208 | int *len, | |
209 | unsigned short *reply_port | |
210 | ) | |
211 | { | |
212 | kdp_connect_req_t *rq = &pkt->connect_req; | |
213 | size_t plen = *len; | |
214 | kdp_connect_reply_t *rp = &pkt->connect_reply; | |
215 | ||
216 | if (plen < sizeof (*rq)) | |
217 | return (FALSE); | |
218 | ||
219 | dprintf(("kdp_connect seq %x greeting %s\n", rq->hdr.seq, rq->greeting)); | |
220 | ||
221 | if (kdp.is_conn) { | |
222 | if (rq->hdr.seq == kdp.conn_seq) /* duplicate request */ | |
223 | rp->error = KDPERR_NO_ERROR; | |
224 | else | |
225 | rp->error = KDPERR_ALREADY_CONNECTED; | |
226 | } | |
227 | else { | |
228 | kdp.reply_port = rq->req_reply_port; | |
229 | kdp.exception_port = rq->exc_note_port; | |
230 | kdp.is_conn = TRUE; | |
231 | kdp.conn_seq = rq->hdr.seq; | |
232 | ||
233 | rp->error = KDPERR_NO_ERROR; | |
234 | } | |
235 | ||
236 | rp->hdr.is_reply = 1; | |
237 | rp->hdr.len = sizeof (*rp); | |
238 | ||
239 | *reply_port = kdp.reply_port; | |
240 | *len = rp->hdr.len; | |
241 | ||
242 | if (current_debugger == KDP_CUR_DB) | |
243 | active_debugger=1; | |
244 | ||
245 | return (TRUE); | |
246 | } | |
247 | ||
248 | static boolean_t | |
249 | kdp_disconnect( | |
250 | kdp_pkt_t *pkt, | |
251 | int *len, | |
252 | unsigned short *reply_port | |
253 | ) | |
254 | { | |
255 | kdp_disconnect_req_t *rq = &pkt->disconnect_req; | |
256 | size_t plen = *len; | |
257 | kdp_disconnect_reply_t *rp = &pkt->disconnect_reply; | |
258 | ||
259 | if (plen < sizeof (*rq)) | |
260 | return (FALSE); | |
261 | ||
262 | if (!kdp.is_conn) | |
263 | return (FALSE); | |
264 | ||
265 | dprintf(("kdp_disconnect\n")); | |
266 | ||
267 | *reply_port = kdp.reply_port; | |
268 | ||
269 | kdp.reply_port = kdp.exception_port = 0; | |
270 | kdp.is_halted = kdp.is_conn = FALSE; | |
271 | kdp.exception_seq = kdp.conn_seq = 0; | |
272 | ||
273 | if ((panicstr != NULL) && (return_on_panic == 0)) | |
274 | reattach_wait = 1; | |
275 | ||
276 | if (noresume_on_disconnect == 1) { | |
277 | reattach_wait = 1; | |
278 | noresume_on_disconnect = 0; | |
279 | } | |
280 | ||
281 | rp->hdr.is_reply = 1; | |
282 | rp->hdr.len = sizeof (*rp); | |
283 | ||
284 | *len = rp->hdr.len; | |
285 | ||
286 | if (current_debugger == KDP_CUR_DB) | |
287 | active_debugger=0; | |
288 | ||
289 | return (TRUE); | |
290 | } | |
291 | ||
292 | static boolean_t | |
293 | kdp_reattach( | |
294 | kdp_pkt_t *pkt, | |
295 | int *len, | |
296 | unsigned short *reply_port | |
297 | ) | |
298 | { | |
299 | kdp_reattach_req_t *rq = &pkt->reattach_req; | |
300 | ||
301 | kdp.is_conn = TRUE; | |
302 | kdp_disconnect(pkt, len, reply_port); | |
303 | *reply_port = rq->req_reply_port; | |
304 | reattach_wait = 1; | |
305 | return (TRUE); | |
306 | } | |
307 | ||
308 | static boolean_t | |
309 | kdp_hostinfo( | |
310 | kdp_pkt_t *pkt, | |
311 | int *len, | |
312 | unsigned short *reply_port | |
313 | ) | |
314 | { | |
315 | kdp_hostinfo_req_t *rq = &pkt->hostinfo_req; | |
316 | size_t plen = *len; | |
317 | kdp_hostinfo_reply_t *rp = &pkt->hostinfo_reply; | |
318 | ||
319 | if (plen < sizeof (*rq)) | |
320 | return (FALSE); | |
321 | ||
322 | rp->hdr.is_reply = 1; | |
323 | rp->hdr.len = sizeof (*rp); | |
324 | ||
325 | kdp_machine_hostinfo(&rp->hostinfo); | |
326 | ||
327 | *reply_port = kdp.reply_port; | |
328 | *len = rp->hdr.len; | |
329 | ||
330 | return (TRUE); | |
331 | } | |
332 | ||
333 | static boolean_t | |
334 | kdp_suspend( | |
335 | kdp_pkt_t *pkt, | |
336 | int *len, | |
337 | unsigned short *reply_port | |
338 | ) | |
339 | { | |
340 | kdp_suspend_req_t *rq = &pkt->suspend_req; | |
341 | size_t plen = *len; | |
342 | kdp_suspend_reply_t *rp = &pkt->suspend_reply; | |
343 | ||
344 | if (plen < sizeof (*rq)) | |
345 | return (FALSE); | |
346 | ||
347 | rp->hdr.is_reply = 1; | |
348 | rp->hdr.len = sizeof (*rp); | |
349 | ||
350 | dprintf(("kdp_suspend\n")); | |
351 | ||
352 | kdp.is_halted = TRUE; | |
353 | ||
354 | *reply_port = kdp.reply_port; | |
355 | *len = rp->hdr.len; | |
356 | ||
357 | return (TRUE); | |
358 | } | |
359 | ||
360 | static boolean_t | |
361 | kdp_resumecpus( | |
362 | kdp_pkt_t *pkt, | |
363 | int *len, | |
364 | unsigned short *reply_port | |
365 | ) | |
366 | { | |
367 | kdp_resumecpus_req_t *rq = &pkt->resumecpus_req; | |
368 | size_t plen = *len; | |
369 | kdp_resumecpus_reply_t *rp = &pkt->resumecpus_reply; | |
370 | ||
371 | if (plen < sizeof (*rq)) | |
372 | return (FALSE); | |
373 | ||
374 | rp->hdr.is_reply = 1; | |
375 | rp->hdr.len = sizeof (*rp); | |
376 | ||
377 | dprintf(("kdp_resumecpus %x\n", rq->cpu_mask)); | |
378 | ||
379 | kdp.is_halted = FALSE; | |
380 | ||
381 | *reply_port = kdp.reply_port; | |
382 | *len = rp->hdr.len; | |
383 | ||
384 | return (TRUE); | |
385 | } | |
386 | ||
387 | static boolean_t | |
388 | kdp_writemem( | |
389 | kdp_pkt_t *pkt, | |
390 | int *len, | |
391 | unsigned short *reply_port | |
392 | ) | |
393 | { | |
394 | kdp_writemem_req_t *rq = &pkt->writemem_req; | |
395 | size_t plen = *len; | |
396 | kdp_writemem_reply_t *rp = &pkt->writemem_reply; | |
397 | int cnt; | |
398 | ||
399 | if (plen < sizeof (*rq)) | |
400 | return (FALSE); | |
401 | ||
402 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
403 | rp->error = KDPERR_BAD_NBYTES; | |
404 | else { | |
405 | dprintf(("kdp_writemem addr %x size %d\n", rq->address, rq->nbytes)); | |
406 | ||
407 | cnt = kdp_vm_write((caddr_t)rq->data, (caddr_t)rq->address, rq->nbytes); | |
408 | rp->error = KDPERR_NO_ERROR; | |
409 | } | |
410 | ||
411 | rp->hdr.is_reply = 1; | |
412 | rp->hdr.len = sizeof (*rp); | |
413 | ||
414 | *reply_port = kdp.reply_port; | |
415 | *len = rp->hdr.len; | |
416 | ||
417 | return (TRUE); | |
418 | } | |
419 | ||
420 | static boolean_t | |
421 | kdp_readmem( | |
422 | kdp_pkt_t *pkt, | |
423 | int *len, | |
424 | unsigned short *reply_port | |
425 | ) | |
426 | { | |
427 | kdp_readmem_req_t *rq = &pkt->readmem_req; | |
428 | size_t plen = *len; | |
429 | kdp_readmem_reply_t *rp = &pkt->readmem_reply; | |
430 | int cnt; | |
431 | #if __i386__ || __arm__ | |
432 | void *pversion = &version; | |
433 | #endif | |
434 | if (plen < sizeof (*rq)) | |
435 | return (FALSE); | |
436 | ||
437 | rp->hdr.is_reply = 1; | |
438 | rp->hdr.len = sizeof (*rp); | |
439 | ||
440 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
441 | rp->error = KDPERR_BAD_NBYTES; | |
442 | else { | |
443 | unsigned int n = rq->nbytes; | |
444 | ||
445 | dprintf(("kdp_readmem addr %x size %d\n", rq->address, n)); | |
446 | #if __i386__ || __arm__ | |
447 | /* XXX This is a hack to facilitate the "showversion" macro | |
448 | * on i386/ARM, which is used to obtain the kernel version without | |
449 | * symbols - a pointer to the version string should eventually | |
450 | * be pinned at a fixed address when an equivalent of the | |
451 | * VECTORS segment (loaded at a fixed load address, and contains | |
452 | * a table) is implemented on these architectures, as with PPC. | |
453 | * N.B.: x86 now has a low global page, and the version indirection | |
454 | * is pinned at 0x201C. We retain the 0x501C address override | |
455 | * for compatibility. | |
456 | */ | |
457 | if (rq->address == (void *)0x501C) | |
458 | rq->address = &pversion; | |
459 | #endif | |
460 | cnt = kdp_vm_read((caddr_t)rq->address, (caddr_t)rp->data, n); | |
461 | rp->error = KDPERR_NO_ERROR; | |
462 | ||
463 | rp->hdr.len += cnt; | |
464 | } | |
465 | ||
466 | *reply_port = kdp.reply_port; | |
467 | *len = rp->hdr.len; | |
468 | ||
469 | return (TRUE); | |
470 | } | |
471 | ||
472 | static boolean_t | |
473 | kdp_maxbytes( | |
474 | kdp_pkt_t *pkt, | |
475 | int *len, | |
476 | unsigned short *reply_port | |
477 | ) | |
478 | { | |
479 | kdp_maxbytes_req_t *rq = &pkt->maxbytes_req; | |
480 | size_t plen = *len; | |
481 | kdp_maxbytes_reply_t *rp = &pkt->maxbytes_reply; | |
482 | ||
483 | if (plen < sizeof (*rq)) | |
484 | return (FALSE); | |
485 | ||
486 | rp->hdr.is_reply = 1; | |
487 | rp->hdr.len = sizeof (*rp); | |
488 | ||
489 | dprintf(("kdp_maxbytes\n")); | |
490 | ||
491 | rp->max_bytes = MAX_KDP_DATA_SIZE; | |
492 | ||
493 | *reply_port = kdp.reply_port; | |
494 | *len = rp->hdr.len; | |
495 | ||
496 | return (TRUE); | |
497 | } | |
498 | ||
499 | static boolean_t | |
500 | kdp_version( | |
501 | kdp_pkt_t *pkt, | |
502 | int *len, | |
503 | unsigned short *reply_port | |
504 | ) | |
505 | { | |
506 | kdp_version_req_t *rq = &pkt->version_req; | |
507 | size_t plen = *len; | |
508 | kdp_version_reply_t *rp = &pkt->version_reply; | |
509 | ||
510 | if (plen < sizeof (*rq)) | |
511 | return (FALSE); | |
512 | ||
513 | rp->hdr.is_reply = 1; | |
514 | rp->hdr.len = sizeof (*rp); | |
515 | ||
516 | dprintf(("kdp_version\n")); | |
517 | ||
518 | rp->version = KDP_VERSION; | |
519 | #if __ppc__ | |
520 | if (!(kdp_flag & KDP_BP_DIS)) | |
521 | rp->feature = KDP_FEATURE_BP; | |
522 | else | |
523 | rp->feature = 0; | |
524 | #else | |
525 | rp->feature = 0; | |
526 | #endif | |
527 | ||
528 | *reply_port = kdp.reply_port; | |
529 | *len = rp->hdr.len; | |
530 | ||
531 | return (TRUE); | |
532 | } | |
533 | ||
534 | static boolean_t | |
535 | kdp_regions( | |
536 | kdp_pkt_t *pkt, | |
537 | int *len, | |
538 | unsigned short *reply_port | |
539 | ) | |
540 | { | |
541 | kdp_regions_req_t *rq = &pkt->regions_req; | |
542 | size_t plen = *len; | |
543 | kdp_regions_reply_t *rp = &pkt->regions_reply; | |
544 | kdp_region_t *r; | |
545 | ||
546 | if (plen < sizeof (*rq)) | |
547 | return (FALSE); | |
548 | ||
549 | rp->hdr.is_reply = 1; | |
550 | rp->hdr.len = sizeof (*rp); | |
551 | ||
552 | dprintf(("kdp_regions\n")); | |
553 | ||
554 | r = rp->regions; | |
555 | rp->nregions = 0; | |
556 | ||
557 | r->address = NULL; | |
558 | r->nbytes = 0xffffffff; | |
559 | ||
560 | r->protection = VM_PROT_ALL; r++; rp->nregions++; | |
561 | ||
562 | rp->hdr.len += rp->nregions * sizeof (kdp_region_t); | |
563 | ||
564 | *reply_port = kdp.reply_port; | |
565 | *len = rp->hdr.len; | |
566 | ||
567 | return (TRUE); | |
568 | } | |
569 | ||
570 | static boolean_t | |
571 | kdp_writeregs( | |
572 | kdp_pkt_t *pkt, | |
573 | int *len, | |
574 | unsigned short *reply_port | |
575 | ) | |
576 | { | |
577 | kdp_writeregs_req_t *rq = &pkt->writeregs_req; | |
578 | size_t plen = *len; | |
579 | int size; | |
580 | kdp_writeregs_reply_t *rp = &pkt->writeregs_reply; | |
581 | ||
582 | if (plen < sizeof (*rq)) | |
583 | return (FALSE); | |
584 | ||
585 | size = rq->hdr.len - sizeof(kdp_hdr_t) - sizeof(unsigned int); | |
586 | rp->error = kdp_machine_write_regs(rq->cpu, rq->flavor, rq->data, &size); | |
587 | ||
588 | rp->hdr.is_reply = 1; | |
589 | rp->hdr.len = sizeof (*rp); | |
590 | ||
591 | *reply_port = kdp.reply_port; | |
592 | *len = rp->hdr.len; | |
593 | ||
594 | return (TRUE); | |
595 | } | |
596 | ||
597 | static boolean_t | |
598 | kdp_readregs( | |
599 | kdp_pkt_t *pkt, | |
600 | int *len, | |
601 | unsigned short *reply_port | |
602 | ) | |
603 | { | |
604 | kdp_readregs_req_t *rq = &pkt->readregs_req; | |
605 | size_t plen = *len; | |
606 | kdp_readregs_reply_t *rp = &pkt->readregs_reply; | |
607 | int size; | |
608 | ||
609 | if (plen < sizeof (*rq)) | |
610 | return (FALSE); | |
611 | ||
612 | rp->hdr.is_reply = 1; | |
613 | rp->hdr.len = sizeof (*rp); | |
614 | ||
615 | rp->error = kdp_machine_read_regs(rq->cpu, rq->flavor, rp->data, &size); | |
616 | rp->hdr.len += size; | |
617 | ||
618 | *reply_port = kdp.reply_port; | |
619 | *len = rp->hdr.len; | |
620 | ||
621 | return (TRUE); | |
622 | } | |
623 | ||
624 | static boolean_t | |
625 | kdp_breakpoint_set( | |
626 | kdp_pkt_t *pkt, | |
627 | int *len, | |
628 | unsigned short *reply_port | |
629 | ) | |
630 | { | |
631 | kdp_breakpoint_req_t *rq = &pkt->breakpoint_req; | |
632 | kdp_breakpoint_reply_t *rp = &pkt->breakpoint_reply; | |
633 | size_t plen = *len; | |
634 | int cnt, i; | |
635 | unsigned int old_instruction = 0; | |
636 | unsigned int breakinstr = kdp_ml_get_breakinsn(); | |
637 | ||
638 | if(breakpoints_initialized == 0) | |
639 | { | |
640 | for(i=0;(i < MAX_BREAKPOINTS); breakpoint_list[i].address=0, i++); | |
641 | breakpoints_initialized++; | |
642 | } | |
643 | if (plen < sizeof (*rq)) | |
644 | return (FALSE); | |
645 | cnt = kdp_vm_read((caddr_t)rq->address, (caddr_t)(&old_instruction), sizeof(int)); | |
646 | ||
647 | if (old_instruction==breakinstr) | |
648 | { | |
649 | printf("A trap was already set at that address, not setting new breakpoint\n"); | |
650 | rp->error = BREAKPOINT_ALREADY_SET; | |
651 | ||
652 | rp->hdr.is_reply = 1; | |
653 | rp->hdr.len = sizeof (*rp); | |
654 | *reply_port = kdp.reply_port; | |
655 | *len = rp->hdr.len; | |
656 | ||
657 | return (TRUE); | |
658 | } | |
659 | ||
660 | for(i=0;(i < MAX_BREAKPOINTS) && (breakpoint_list[i].address != 0); i++); | |
661 | ||
662 | if (i == MAX_BREAKPOINTS) | |
663 | { | |
664 | rp->error = KDP_MAX_BREAKPOINTS; | |
665 | ||
666 | rp->hdr.is_reply = 1; | |
667 | rp->hdr.len = sizeof (*rp); | |
668 | *reply_port = kdp.reply_port; | |
669 | *len = rp->hdr.len; | |
670 | ||
671 | return (TRUE); | |
672 | } | |
673 | breakpoint_list[i].address = rq->address; | |
674 | breakpoint_list[i].old_instruction = old_instruction; | |
675 | ||
676 | cnt = kdp_vm_write((caddr_t)&breakinstr, (caddr_t)rq->address, sizeof(&breakinstr)); | |
677 | ||
678 | rp->error = KDPERR_NO_ERROR; | |
679 | rp->hdr.is_reply = 1; | |
680 | rp->hdr.len = sizeof (*rp); | |
681 | *reply_port = kdp.reply_port; | |
682 | *len = rp->hdr.len; | |
683 | ||
684 | return (TRUE); | |
685 | } | |
686 | ||
687 | static boolean_t | |
688 | kdp_breakpoint_remove( | |
689 | kdp_pkt_t *pkt, | |
690 | int *len, | |
691 | unsigned short *reply_port | |
692 | ) | |
693 | { | |
694 | kdp_breakpoint_req_t *rq = &pkt->breakpoint_req; | |
695 | kdp_breakpoint_reply_t *rp = &pkt->breakpoint_reply; | |
696 | size_t plen = *len; | |
697 | int cnt,i; | |
698 | ||
699 | if (plen < sizeof (*rq)) | |
700 | return (FALSE); | |
701 | ||
702 | for(i=0;(i < MAX_BREAKPOINTS) && (breakpoint_list[i].address != rq->address); i++); | |
703 | if (i == MAX_BREAKPOINTS) | |
704 | { | |
705 | rp->error = BREAKPOINT_NOT_FOUND; | |
706 | rp->hdr.is_reply = 1; | |
707 | rp->hdr.len = sizeof (*rp); | |
708 | *reply_port = kdp.reply_port; | |
709 | *len = rp->hdr.len; | |
710 | ||
711 | return (TRUE); /* Check if it needs to be FALSE in case of error */ | |
712 | } | |
713 | ||
714 | breakpoint_list[i].address = 0; | |
715 | cnt = kdp_vm_write((caddr_t)&(breakpoint_list[i].old_instruction), (caddr_t)rq->address, sizeof(int)); | |
716 | rp->error = KDPERR_NO_ERROR; | |
717 | rp->hdr.is_reply = 1; | |
718 | rp->hdr.len = sizeof (*rp); | |
719 | *reply_port = kdp.reply_port; | |
720 | *len = rp->hdr.len; | |
721 | ||
722 | return (TRUE); | |
723 | } | |
724 | ||
725 | boolean_t | |
726 | kdp_remove_all_breakpoints(void) | |
727 | { | |
728 | int i; | |
729 | boolean_t breakpoint_found = FALSE; | |
730 | ||
731 | if (breakpoints_initialized) | |
732 | { | |
733 | for(i=0;i < MAX_BREAKPOINTS; i++) | |
734 | { | |
735 | if (breakpoint_list[i].address) | |
736 | { | |
737 | kdp_vm_write((caddr_t)&(breakpoint_list[i].old_instruction), (caddr_t)breakpoint_list[i].address, sizeof(int)); | |
738 | breakpoint_found = TRUE; | |
739 | breakpoint_list[i].address = 0; | |
740 | } | |
741 | } | |
742 | if (breakpoint_found) | |
743 | printf("kdp_remove_all_breakpoints: found extant breakpoints, removing them.\n"); | |
744 | } | |
745 | return breakpoint_found; | |
746 | } | |
747 | ||
748 | ||
749 | #define MAX_FRAMES 1000 | |
750 | ||
751 | static int pid_from_task(task_t task) | |
752 | { | |
753 | int pid = -1; | |
754 | ||
755 | if (task->bsd_info) | |
756 | pid = proc_pid(task->bsd_info); | |
757 | ||
758 | return pid; | |
759 | } | |
760 | ||
761 | int | |
762 | kdp_stackshot(int pid, void *tracebuf, uint32_t tracebuf_size, unsigned trace_options, uint32_t *pbytesTraced) | |
763 | { | |
764 | char *tracepos = (char *) tracebuf; | |
765 | char *tracebound = tracepos + tracebuf_size; | |
766 | uint32_t tracebytes = 0; | |
767 | int error = 0; | |
768 | ||
769 | task_t task = TASK_NULL; | |
770 | thread_t thread = THREAD_NULL; | |
771 | int nframes = trace_options; | |
772 | thread_snapshot_t tsnap = NULL; | |
773 | unsigned framesize = 2 * sizeof(vm_offset_t); | |
774 | struct task ctask; | |
775 | struct thread cthread; | |
776 | ||
777 | if ((nframes <= 0) || nframes > MAX_FRAMES) | |
778 | nframes = MAX_FRAMES; | |
779 | ||
780 | queue_iterate(&tasks, task, task_t, tasks) { | |
781 | if ((task == NULL) || (ml_nofault_copy((vm_offset_t) task, (vm_offset_t) &ctask, sizeof(struct task)) != sizeof(struct task))) | |
782 | goto error_exit; | |
783 | /* Trace everything, unless a process was specified */ | |
784 | if ((pid == -1) || (pid == pid_from_task(task))) | |
785 | queue_iterate(&task->threads, thread, thread_t, task_threads){ | |
786 | if ((thread == NULL) || (ml_nofault_copy((vm_offset_t) thread, (vm_offset_t) &cthread, sizeof(struct thread)) != sizeof(struct thread))) | |
787 | goto error_exit; | |
788 | if (((tracepos + 4 * sizeof(struct thread_snapshot)) > tracebound)) { | |
789 | error = -1; | |
790 | goto error_exit; | |
791 | } | |
792 | /* Populate the thread snapshot header */ | |
793 | tsnap = (thread_snapshot_t) tracepos; | |
794 | tsnap->thread_id = thread; | |
795 | tsnap->state = thread->state; | |
796 | tsnap->wait_queue = thread->wait_queue; | |
797 | tsnap->wait_event = thread->wait_event; | |
798 | tsnap->kernel_stack = thread->kernel_stack; | |
799 | tsnap->reserved_stack = thread->reserved_stack; | |
800 | tsnap->continuation = thread->continuation; | |
801 | /* Add the BSD process identifiers */ | |
802 | if ((tsnap->pid = pid_from_task(task)) != -1) | |
803 | proc_name_kdp(task, tsnap->p_comm, MAXCOMLEN + 1); | |
804 | else | |
805 | tsnap->p_comm[0] = '\0'; | |
806 | ||
807 | tsnap->snapshot_magic = 0xfeedface; | |
808 | tracepos += sizeof(struct thread_snapshot); | |
809 | ||
810 | /* Call through to the machine specific trace routines | |
811 | * Frames are added past the snapshot header. | |
812 | */ | |
813 | if (tsnap->kernel_stack != 0) | |
814 | tracebytes = machine_trace_thread(thread, tracepos, tracebound, nframes, FALSE); | |
815 | tsnap->nkern_frames = tracebytes/(2 * sizeof(vm_offset_t)); | |
816 | tracepos += tracebytes; | |
817 | tracebytes = 0; | |
818 | tsnap->user64_p = 0; | |
819 | /* Trace user stack, if any */ | |
820 | if (thread->task->map != kernel_map) { | |
821 | /* 64-bit task? */ | |
822 | if (task_has_64BitAddr(thread->task)) { | |
823 | tracebytes = machine_trace_thread64(thread, tracepos, tracebound, nframes, TRUE); | |
824 | tsnap->user64_p = 1; | |
825 | framesize = 2 * sizeof(addr64_t); | |
826 | } | |
827 | else { | |
828 | tracebytes = machine_trace_thread(thread, tracepos, tracebound, nframes, TRUE); | |
829 | framesize = 2 * sizeof(vm_offset_t); | |
830 | } | |
831 | } | |
832 | tsnap->nuser_frames = tracebytes/framesize; | |
833 | tracepos += tracebytes; | |
834 | tracebytes = 0; | |
835 | } | |
836 | } | |
837 | ||
838 | error_exit: | |
839 | /* Release stack snapshot wait indicator */ | |
840 | kdp_snapshot_postflight(); | |
841 | ||
842 | *pbytesTraced = tracepos - (char *) tracebuf; | |
843 | ||
844 | return error; | |
845 | } |