]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2000-2007 Apple Computer, Inc. All rights reserved. |
1c79356b | 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 | 27 | */ |
1c79356b A |
28 | |
29 | #include <mach/mach_types.h> | |
b0d623f7 | 30 | #include <sys/appleapiopts.h> |
1c79356b | 31 | #include <kern/debug.h> |
b7266188 | 32 | #include <uuid/uuid.h> |
1c79356b A |
33 | |
34 | #include <kdp/kdp_internal.h> | |
35 | #include <kdp/kdp_private.h> | |
7e4a7d39 | 36 | #include <kdp/kdp_core.h> |
b7266188 | 37 | #include <kdp/kdp_dyld.h> |
1c79356b | 38 | |
9bccf70c | 39 | #include <libsa/types.h> |
6d2010ae | 40 | #include <libkern/version.h> |
9bccf70c | 41 | |
91447636 A |
42 | #include <string.h> /* bcopy */ |
43 | ||
0c530ab8 A |
44 | #include <kern/processor.h> |
45 | #include <kern/thread.h> | |
6d2010ae | 46 | #include <kern/clock.h> |
0c530ab8 A |
47 | #include <vm/vm_map.h> |
48 | #include <vm/vm_kern.h> | |
49 | ||
1c79356b A |
50 | #define DO_ALIGN 1 /* align all packet data accesses */ |
51 | ||
52 | #define KDP_TEST_HARNESS 0 | |
53 | #if KDP_TEST_HARNESS | |
54 | #define dprintf(x) kprintf x | |
55 | #else | |
56 | #define dprintf(x) | |
57 | #endif | |
58 | ||
59 | static kdp_dispatch_t | |
b0d623f7 | 60 | dispatch_table[KDP_INVALID_REQUEST-KDP_CONNECT] = |
1c79356b A |
61 | { |
62 | /* 0 */ kdp_connect, | |
63 | /* 1 */ kdp_disconnect, | |
64 | /* 2 */ kdp_hostinfo, | |
9bccf70c | 65 | /* 3 */ kdp_version, |
1c79356b A |
66 | /* 4 */ kdp_maxbytes, |
67 | /* 5 */ kdp_readmem, | |
68 | /* 6 */ kdp_writemem, | |
69 | /* 7 */ kdp_readregs, | |
70 | /* 8 */ kdp_writeregs, | |
9bccf70c A |
71 | /* 9 */ kdp_unknown, |
72 | /* A */ kdp_unknown, | |
1c79356b A |
73 | /* B */ kdp_suspend, |
74 | /* C */ kdp_resumecpus, | |
75 | /* D */ kdp_unknown, | |
9bccf70c A |
76 | /* E */ kdp_unknown, |
77 | /* F */ kdp_breakpoint_set, | |
78 | /*10 */ kdp_breakpoint_remove, | |
79 | /*11 */ kdp_regions, | |
91447636 | 80 | /*12 */ kdp_reattach, |
b0d623f7 A |
81 | /*13 */ kdp_reboot, |
82 | /*14 */ kdp_readmem64, | |
83 | /*15 */ kdp_writemem64, | |
84 | /*16 */ kdp_breakpoint64_set, | |
85 | /*17 */ kdp_breakpoint64_remove, | |
86 | /*18 */ kdp_kernelversion, | |
87 | /*19 */ kdp_readphysmem64, | |
7e4a7d39 A |
88 | /*1A */ kdp_writephysmem64, |
89 | /*1B */ kdp_readioport, | |
90 | /*1C */ kdp_writeioport, | |
91 | /*1D */ kdp_readmsr64, | |
92 | /*1E */ kdp_writemsr64, | |
93 | /*1F */ kdp_dumpinfo, | |
1c79356b A |
94 | }; |
95 | ||
96 | kdp_glob_t kdp; | |
9bccf70c | 97 | |
9bccf70c | 98 | #define MAX_BREAKPOINTS 100 |
9bccf70c | 99 | |
b0d623f7 A |
100 | /* |
101 | * Version 11 of the KDP Protocol adds support for 64-bit wide memory | |
102 | * addresses (read/write and breakpoints) as well as a dedicated | |
103 | * kernelversion request. Version 12 adds read/writing of physical | |
7e4a7d39 | 104 | * memory with 64-bit wide memory addresses. |
b0d623f7 A |
105 | */ |
106 | #define KDP_VERSION 12 | |
9bccf70c A |
107 | |
108 | typedef struct{ | |
b0d623f7 A |
109 | mach_vm_address_t address; |
110 | uint32_t bytesused; | |
111 | uint8_t oldbytes[MAX_BREAKINSN_BYTES]; | |
9bccf70c A |
112 | } kdp_breakpoint_record_t; |
113 | ||
114 | static kdp_breakpoint_record_t breakpoint_list[MAX_BREAKPOINTS]; | |
115 | static unsigned int breakpoints_initialized = 0; | |
55e303ae | 116 | |
9bccf70c | 117 | int reattach_wait = 0; |
55e303ae | 118 | int noresume_on_disconnect = 0; |
2d21ac55 | 119 | extern unsigned int return_on_panic; |
1c79356b | 120 | |
0c530ab8 | 121 | typedef struct thread_snapshot *thread_snapshot_t; |
b7266188 | 122 | typedef struct task_snapshot *task_snapshot_t; |
0c530ab8 A |
123 | |
124 | extern int | |
2d21ac55 | 125 | machine_trace_thread(thread_t thread, char *tracepos, char *tracebound, int nframes, boolean_t user_p); |
0c530ab8 | 126 | extern int |
2d21ac55 | 127 | machine_trace_thread64(thread_t thread, char *tracepos, char *tracebound, int nframes, boolean_t user_p); |
0c530ab8 A |
128 | extern int |
129 | proc_pid(void *p); | |
130 | extern void | |
2d21ac55 A |
131 | proc_name_kdp(task_t task, char *buf, int size); |
132 | ||
0c530ab8 A |
133 | extern void |
134 | kdp_snapshot_postflight(void); | |
135 | ||
136 | static int | |
137 | pid_from_task(task_t task); | |
138 | ||
b0d623f7 A |
139 | kdp_error_t |
140 | kdp_set_breakpoint_internal( | |
141 | mach_vm_address_t address | |
142 | ); | |
143 | ||
144 | kdp_error_t | |
145 | kdp_remove_breakpoint_internal( | |
146 | mach_vm_address_t address | |
147 | ); | |
148 | ||
149 | ||
0c530ab8 | 150 | int |
b7266188 | 151 | kdp_stackshot(int pid, void *tracebuf, uint32_t tracebuf_size, uint32_t trace_flags, uint32_t dispatch_offset, uint32_t *pbytesTraced); |
0c530ab8 | 152 | |
b0d623f7 A |
153 | boolean_t kdp_copyin(pmap_t, uint64_t, void *, size_t); |
154 | extern void bcopy_phys(addr64_t, addr64_t, vm_size_t); | |
155 | ||
1c79356b A |
156 | boolean_t |
157 | kdp_packet( | |
158 | unsigned char *pkt, | |
159 | int *len, | |
160 | unsigned short *reply_port | |
161 | ) | |
162 | { | |
163 | static unsigned aligned_pkt[1538/sizeof(unsigned)+1]; // max ether pkt | |
164 | kdp_pkt_t *rd = (kdp_pkt_t *)&aligned_pkt; | |
2d21ac55 A |
165 | size_t plen = *len; |
166 | kdp_req_t req; | |
1c79356b A |
167 | boolean_t ret; |
168 | ||
169 | #if DO_ALIGN | |
170 | bcopy((char *)pkt, (char *)rd, sizeof(aligned_pkt)); | |
171 | #else | |
172 | rd = (kdp_pkt_t *)pkt; | |
173 | #endif | |
174 | if (plen < sizeof (rd->hdr) || rd->hdr.len != plen) { | |
2d21ac55 | 175 | printf("kdp_packet bad len pkt %lu hdr %d\n", plen, rd->hdr.len); |
1c79356b A |
176 | |
177 | return (FALSE); | |
178 | } | |
179 | ||
180 | if (rd->hdr.is_reply) { | |
181 | printf("kdp_packet reply recvd req %x seq %x\n", | |
182 | rd->hdr.request, rd->hdr.seq); | |
183 | ||
184 | return (FALSE); | |
185 | } | |
186 | ||
187 | req = rd->hdr.request; | |
b0d623f7 | 188 | if (req >= KDP_INVALID_REQUEST) { |
1c79356b A |
189 | printf("kdp_packet bad request %x len %d seq %x key %x\n", |
190 | rd->hdr.request, rd->hdr.len, rd->hdr.seq, rd->hdr.key); | |
191 | ||
192 | return (FALSE); | |
193 | } | |
194 | ||
195 | ret = ((*dispatch_table[req - KDP_CONNECT])(rd, len, reply_port)); | |
196 | #if DO_ALIGN | |
197 | bcopy((char *)rd, (char *) pkt, *len); | |
198 | #endif | |
199 | return ret; | |
200 | } | |
201 | ||
202 | static boolean_t | |
203 | kdp_unknown( | |
204 | kdp_pkt_t *pkt, | |
2d21ac55 A |
205 | __unused int *len, |
206 | __unused unsigned short *reply_port | |
1c79356b A |
207 | ) |
208 | { | |
209 | kdp_pkt_t *rd = (kdp_pkt_t *)pkt; | |
210 | ||
211 | printf("kdp_unknown request %x len %d seq %x key %x\n", | |
212 | rd->hdr.request, rd->hdr.len, rd->hdr.seq, rd->hdr.key); | |
213 | ||
214 | return (FALSE); | |
215 | } | |
216 | ||
217 | static boolean_t | |
218 | kdp_connect( | |
219 | kdp_pkt_t *pkt, | |
220 | int *len, | |
221 | unsigned short *reply_port | |
222 | ) | |
223 | { | |
224 | kdp_connect_req_t *rq = &pkt->connect_req; | |
2d21ac55 | 225 | size_t plen = *len; |
1c79356b | 226 | kdp_connect_reply_t *rp = &pkt->connect_reply; |
7e4a7d39 A |
227 | uint16_t rport, eport; |
228 | uint32_t key; | |
229 | uint8_t seq; | |
1c79356b A |
230 | |
231 | if (plen < sizeof (*rq)) | |
232 | return (FALSE); | |
233 | ||
234 | dprintf(("kdp_connect seq %x greeting %s\n", rq->hdr.seq, rq->greeting)); | |
235 | ||
7e4a7d39 A |
236 | rport = rq->req_reply_port; |
237 | eport = rq->exc_note_port; | |
238 | key = rq->hdr.key; | |
239 | seq = rq->hdr.seq; | |
1c79356b | 240 | if (kdp.is_conn) { |
7e4a7d39 A |
241 | if ((seq == kdp.conn_seq) && /* duplicate request */ |
242 | (rport == kdp.reply_port) && | |
243 | (eport == kdp.exception_port) && | |
244 | (key == kdp.session_key)) | |
1c79356b | 245 | rp->error = KDPERR_NO_ERROR; |
7e4a7d39 | 246 | else |
1c79356b A |
247 | rp->error = KDPERR_ALREADY_CONNECTED; |
248 | } | |
249 | else { | |
6d2010ae A |
250 | kdp.reply_port = rport; |
251 | kdp.exception_port = eport; | |
252 | kdp.is_conn = TRUE; | |
253 | kdp.conn_seq = seq; | |
7e4a7d39 A |
254 | kdp.session_key = key; |
255 | ||
1c79356b A |
256 | rp->error = KDPERR_NO_ERROR; |
257 | } | |
258 | ||
259 | rp->hdr.is_reply = 1; | |
260 | rp->hdr.len = sizeof (*rp); | |
261 | ||
7e4a7d39 | 262 | *reply_port = rport; |
1c79356b A |
263 | *len = rp->hdr.len; |
264 | ||
265 | if (current_debugger == KDP_CUR_DB) | |
266 | active_debugger=1; | |
267 | ||
268 | return (TRUE); | |
269 | } | |
270 | ||
271 | static boolean_t | |
272 | kdp_disconnect( | |
273 | kdp_pkt_t *pkt, | |
274 | int *len, | |
275 | unsigned short *reply_port | |
276 | ) | |
277 | { | |
278 | kdp_disconnect_req_t *rq = &pkt->disconnect_req; | |
2d21ac55 | 279 | size_t plen = *len; |
1c79356b A |
280 | kdp_disconnect_reply_t *rp = &pkt->disconnect_reply; |
281 | ||
282 | if (plen < sizeof (*rq)) | |
283 | return (FALSE); | |
284 | ||
285 | if (!kdp.is_conn) | |
286 | return (FALSE); | |
287 | ||
288 | dprintf(("kdp_disconnect\n")); | |
289 | ||
290 | *reply_port = kdp.reply_port; | |
291 | ||
292 | kdp.reply_port = kdp.exception_port = 0; | |
293 | kdp.is_halted = kdp.is_conn = FALSE; | |
294 | kdp.exception_seq = kdp.conn_seq = 0; | |
7e4a7d39 | 295 | kdp.session_key = 0; |
1c79356b | 296 | |
2d21ac55 A |
297 | if ((panicstr != NULL) && (return_on_panic == 0)) |
298 | reattach_wait = 1; | |
299 | ||
55e303ae A |
300 | if (noresume_on_disconnect == 1) { |
301 | reattach_wait = 1; | |
302 | noresume_on_disconnect = 0; | |
303 | } | |
304 | ||
1c79356b A |
305 | rp->hdr.is_reply = 1; |
306 | rp->hdr.len = sizeof (*rp); | |
307 | ||
308 | *len = rp->hdr.len; | |
309 | ||
310 | if (current_debugger == KDP_CUR_DB) | |
311 | active_debugger=0; | |
312 | ||
313 | return (TRUE); | |
314 | } | |
315 | ||
9bccf70c A |
316 | static boolean_t |
317 | kdp_reattach( | |
318 | kdp_pkt_t *pkt, | |
319 | int *len, | |
320 | unsigned short *reply_port | |
321 | ) | |
322 | { | |
323 | kdp_reattach_req_t *rq = &pkt->reattach_req; | |
9bccf70c A |
324 | |
325 | kdp.is_conn = TRUE; | |
326 | kdp_disconnect(pkt, len, reply_port); | |
327 | *reply_port = rq->req_reply_port; | |
328 | reattach_wait = 1; | |
329 | return (TRUE); | |
330 | } | |
331 | ||
1c79356b A |
332 | static boolean_t |
333 | kdp_hostinfo( | |
334 | kdp_pkt_t *pkt, | |
335 | int *len, | |
336 | unsigned short *reply_port | |
337 | ) | |
338 | { | |
339 | kdp_hostinfo_req_t *rq = &pkt->hostinfo_req; | |
2d21ac55 | 340 | size_t plen = *len; |
1c79356b A |
341 | kdp_hostinfo_reply_t *rp = &pkt->hostinfo_reply; |
342 | ||
343 | if (plen < sizeof (*rq)) | |
344 | return (FALSE); | |
345 | ||
b0d623f7 A |
346 | dprintf(("kdp_hostinfo\n")); |
347 | ||
1c79356b A |
348 | rp->hdr.is_reply = 1; |
349 | rp->hdr.len = sizeof (*rp); | |
350 | ||
351 | kdp_machine_hostinfo(&rp->hostinfo); | |
9bccf70c | 352 | |
1c79356b A |
353 | *reply_port = kdp.reply_port; |
354 | *len = rp->hdr.len; | |
355 | ||
356 | return (TRUE); | |
357 | } | |
358 | ||
b0d623f7 A |
359 | static boolean_t |
360 | kdp_kernelversion( | |
361 | kdp_pkt_t *pkt, | |
362 | int *len, | |
363 | unsigned short *reply_port | |
364 | ) | |
365 | { | |
366 | kdp_kernelversion_req_t *rq = &pkt->kernelversion_req; | |
367 | size_t plen = *len; | |
368 | kdp_kernelversion_reply_t *rp = &pkt->kernelversion_reply; | |
369 | size_t slen; | |
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_kernelversion\n")); | |
6d2010ae | 378 | slen = strlcpy(rp->version, kdp_kernelversion_string, MAX_KDP_DATA_SIZE); |
b0d623f7 A |
379 | |
380 | rp->hdr.len += slen + 1; /* strlcpy returns the amount copied with NUL */ | |
381 | ||
382 | *reply_port = kdp.reply_port; | |
383 | *len = rp->hdr.len; | |
384 | ||
385 | return (TRUE); | |
386 | } | |
387 | ||
1c79356b A |
388 | static boolean_t |
389 | kdp_suspend( | |
390 | kdp_pkt_t *pkt, | |
391 | int *len, | |
392 | unsigned short *reply_port | |
393 | ) | |
394 | { | |
395 | kdp_suspend_req_t *rq = &pkt->suspend_req; | |
2d21ac55 | 396 | size_t plen = *len; |
1c79356b A |
397 | kdp_suspend_reply_t *rp = &pkt->suspend_reply; |
398 | ||
399 | if (plen < sizeof (*rq)) | |
400 | return (FALSE); | |
401 | ||
402 | rp->hdr.is_reply = 1; | |
403 | rp->hdr.len = sizeof (*rp); | |
404 | ||
405 | dprintf(("kdp_suspend\n")); | |
406 | ||
407 | kdp.is_halted = TRUE; | |
408 | ||
409 | *reply_port = kdp.reply_port; | |
410 | *len = rp->hdr.len; | |
411 | ||
412 | return (TRUE); | |
413 | } | |
414 | ||
415 | static boolean_t | |
416 | kdp_resumecpus( | |
417 | kdp_pkt_t *pkt, | |
418 | int *len, | |
419 | unsigned short *reply_port | |
420 | ) | |
421 | { | |
422 | kdp_resumecpus_req_t *rq = &pkt->resumecpus_req; | |
2d21ac55 | 423 | size_t plen = *len; |
1c79356b A |
424 | kdp_resumecpus_reply_t *rp = &pkt->resumecpus_reply; |
425 | ||
426 | if (plen < sizeof (*rq)) | |
427 | return (FALSE); | |
428 | ||
429 | rp->hdr.is_reply = 1; | |
430 | rp->hdr.len = sizeof (*rp); | |
431 | ||
432 | dprintf(("kdp_resumecpus %x\n", rq->cpu_mask)); | |
9bccf70c | 433 | |
1c79356b A |
434 | kdp.is_halted = FALSE; |
435 | ||
436 | *reply_port = kdp.reply_port; | |
437 | *len = rp->hdr.len; | |
438 | ||
439 | return (TRUE); | |
440 | } | |
441 | ||
442 | static boolean_t | |
443 | kdp_writemem( | |
444 | kdp_pkt_t *pkt, | |
445 | int *len, | |
446 | unsigned short *reply_port | |
447 | ) | |
448 | { | |
449 | kdp_writemem_req_t *rq = &pkt->writemem_req; | |
2d21ac55 | 450 | size_t plen = *len; |
1c79356b | 451 | kdp_writemem_reply_t *rp = &pkt->writemem_reply; |
b0d623f7 | 452 | mach_vm_size_t cnt; |
1c79356b A |
453 | |
454 | if (plen < sizeof (*rq)) | |
455 | return (FALSE); | |
456 | ||
457 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
458 | rp->error = KDPERR_BAD_NBYTES; | |
459 | else { | |
460 | dprintf(("kdp_writemem addr %x size %d\n", rq->address, rq->nbytes)); | |
461 | ||
b0d623f7 | 462 | cnt = kdp_machine_vm_write((caddr_t)rq->data, (mach_vm_address_t)rq->address, rq->nbytes); |
1c79356b A |
463 | rp->error = KDPERR_NO_ERROR; |
464 | } | |
465 | ||
466 | rp->hdr.is_reply = 1; | |
467 | rp->hdr.len = sizeof (*rp); | |
468 | ||
469 | *reply_port = kdp.reply_port; | |
470 | *len = rp->hdr.len; | |
471 | ||
472 | return (TRUE); | |
473 | } | |
474 | ||
b0d623f7 A |
475 | static boolean_t |
476 | kdp_writemem64( | |
477 | kdp_pkt_t *pkt, | |
478 | int *len, | |
479 | unsigned short *reply_port | |
480 | ) | |
481 | { | |
482 | kdp_writemem64_req_t *rq = &pkt->writemem64_req; | |
483 | size_t plen = *len; | |
484 | kdp_writemem64_reply_t *rp = &pkt->writemem64_reply; | |
485 | mach_vm_size_t cnt; | |
486 | ||
487 | if (plen < sizeof (*rq)) | |
488 | return (FALSE); | |
489 | ||
490 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
491 | rp->error = KDPERR_BAD_NBYTES; | |
492 | else { | |
493 | dprintf(("kdp_writemem64 addr %llx size %d\n", rq->address, rq->nbytes)); | |
494 | ||
495 | cnt = kdp_machine_vm_write((caddr_t)rq->data, (mach_vm_address_t)rq->address, (mach_vm_size_t)rq->nbytes); | |
496 | rp->error = KDPERR_NO_ERROR; | |
497 | } | |
498 | ||
499 | rp->hdr.is_reply = 1; | |
500 | rp->hdr.len = sizeof (*rp); | |
501 | ||
502 | *reply_port = kdp.reply_port; | |
503 | *len = rp->hdr.len; | |
504 | ||
505 | return (TRUE); | |
506 | } | |
507 | ||
508 | static boolean_t | |
509 | kdp_writephysmem64( | |
510 | kdp_pkt_t *pkt, | |
511 | int *len, | |
512 | unsigned short *reply_port | |
513 | ) | |
514 | { | |
515 | kdp_writephysmem64_req_t *rq = &pkt->writephysmem64_req; | |
516 | size_t plen = *len; | |
517 | kdp_writephysmem64_reply_t *rp = &pkt->writephysmem64_reply; | |
518 | ||
519 | if (plen < sizeof (*rq)) | |
520 | return (FALSE); | |
521 | ||
522 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
523 | rp->error = KDPERR_BAD_NBYTES; | |
524 | else { | |
525 | dprintf(("kdp_writephysmem64 addr %llx size %d\n", rq->address, rq->nbytes)); | |
526 | kdp_machine_phys_write(rq, rq->data, rq->lcpu); | |
527 | rp->error = KDPERR_NO_ERROR; | |
528 | } | |
529 | ||
530 | rp->hdr.is_reply = 1; | |
531 | rp->hdr.len = sizeof (*rp); | |
532 | ||
533 | *reply_port = kdp.reply_port; | |
534 | *len = rp->hdr.len; | |
535 | ||
536 | return (TRUE); | |
537 | } | |
538 | ||
1c79356b A |
539 | static boolean_t |
540 | kdp_readmem( | |
541 | kdp_pkt_t *pkt, | |
542 | int *len, | |
543 | unsigned short *reply_port | |
544 | ) | |
545 | { | |
546 | kdp_readmem_req_t *rq = &pkt->readmem_req; | |
2d21ac55 | 547 | size_t plen = *len; |
1c79356b | 548 | kdp_readmem_reply_t *rp = &pkt->readmem_reply; |
b0d623f7 | 549 | mach_vm_size_t cnt; |
6d2010ae A |
550 | #if __i386__ |
551 | void *pversion = &kdp_kernelversion_string; | |
0c530ab8 | 552 | #endif |
b0d623f7 | 553 | |
1c79356b A |
554 | if (plen < sizeof (*rq)) |
555 | return (FALSE); | |
556 | ||
557 | rp->hdr.is_reply = 1; | |
558 | rp->hdr.len = sizeof (*rp); | |
559 | ||
560 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
561 | rp->error = KDPERR_BAD_NBYTES; | |
562 | else { | |
563 | unsigned int n = rq->nbytes; | |
564 | ||
2d21ac55 | 565 | dprintf(("kdp_readmem addr %x size %d\n", rq->address, n)); |
6d2010ae | 566 | #if __i386__ |
0c530ab8 | 567 | /* XXX This is a hack to facilitate the "showversion" macro |
6d2010ae | 568 | * on i386, which is used to obtain the kernel version without |
0c530ab8 A |
569 | * symbols - a pointer to the version string should eventually |
570 | * be pinned at a fixed address when an equivalent of the | |
571 | * VECTORS segment (loaded at a fixed load address, and contains | |
2d21ac55 A |
572 | * a table) is implemented on these architectures, as with PPC. |
573 | * N.B.: x86 now has a low global page, and the version indirection | |
574 | * is pinned at 0x201C. We retain the 0x501C address override | |
b0d623f7 A |
575 | * for compatibility. Future architectures should instead use |
576 | * the KDP_KERNELVERSION request. | |
0c530ab8 | 577 | */ |
b0d623f7 A |
578 | if (rq->address == 0x501C) |
579 | rq->address = (uintptr_t)&pversion; | |
0c530ab8 | 580 | #endif |
b0d623f7 | 581 | cnt = kdp_machine_vm_read((mach_vm_address_t)rq->address, (caddr_t)rp->data, n); |
1c79356b A |
582 | rp->error = KDPERR_NO_ERROR; |
583 | ||
584 | rp->hdr.len += cnt; | |
585 | } | |
586 | ||
587 | *reply_port = kdp.reply_port; | |
588 | *len = rp->hdr.len; | |
589 | ||
590 | return (TRUE); | |
591 | } | |
592 | ||
b0d623f7 A |
593 | static boolean_t |
594 | kdp_readmem64( | |
595 | kdp_pkt_t *pkt, | |
596 | int *len, | |
597 | unsigned short *reply_port | |
598 | ) | |
599 | { | |
600 | kdp_readmem64_req_t *rq = &pkt->readmem64_req; | |
601 | size_t plen = *len; | |
602 | kdp_readmem64_reply_t *rp = &pkt->readmem64_reply; | |
603 | mach_vm_size_t cnt; | |
604 | ||
605 | if (plen < sizeof (*rq)) | |
606 | return (FALSE); | |
607 | ||
608 | rp->hdr.is_reply = 1; | |
609 | rp->hdr.len = sizeof (*rp); | |
610 | ||
611 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
612 | rp->error = KDPERR_BAD_NBYTES; | |
613 | else { | |
614 | ||
615 | dprintf(("kdp_readmem64 addr %llx size %d\n", rq->address, rq->nbytes)); | |
616 | ||
617 | cnt = kdp_machine_vm_read((mach_vm_address_t)rq->address, (caddr_t)rp->data, rq->nbytes); | |
618 | rp->error = KDPERR_NO_ERROR; | |
619 | ||
620 | rp->hdr.len += cnt; | |
621 | } | |
622 | ||
623 | *reply_port = kdp.reply_port; | |
624 | *len = rp->hdr.len; | |
625 | ||
626 | return (TRUE); | |
627 | } | |
628 | ||
629 | static boolean_t | |
630 | kdp_readphysmem64( | |
631 | kdp_pkt_t *pkt, | |
632 | int *len, | |
633 | unsigned short *reply_port | |
634 | ) | |
635 | { | |
636 | kdp_readphysmem64_req_t *rq = &pkt->readphysmem64_req; | |
637 | size_t plen = *len; | |
638 | kdp_readphysmem64_reply_t *rp = &pkt->readphysmem64_reply; | |
639 | int cnt; | |
640 | ||
641 | if (plen < sizeof (*rq)) | |
642 | return (FALSE); | |
643 | ||
644 | rp->hdr.is_reply = 1; | |
645 | rp->hdr.len = sizeof (*rp); | |
646 | ||
647 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
648 | rp->error = KDPERR_BAD_NBYTES; | |
649 | else { | |
650 | ||
651 | dprintf(("kdp_readphysmem64 addr %llx size %d\n", rq->address, rq->nbytes)); | |
652 | ||
653 | cnt = (int)kdp_machine_phys_read(rq, rp->data, rq->lcpu); | |
654 | rp->error = KDPERR_NO_ERROR; | |
655 | ||
656 | rp->hdr.len += cnt; | |
657 | } | |
658 | ||
659 | *reply_port = kdp.reply_port; | |
660 | *len = rp->hdr.len; | |
661 | ||
662 | return (TRUE); | |
663 | } | |
664 | ||
1c79356b A |
665 | static boolean_t |
666 | kdp_maxbytes( | |
667 | kdp_pkt_t *pkt, | |
668 | int *len, | |
669 | unsigned short *reply_port | |
670 | ) | |
671 | { | |
672 | kdp_maxbytes_req_t *rq = &pkt->maxbytes_req; | |
2d21ac55 | 673 | size_t plen = *len; |
1c79356b A |
674 | kdp_maxbytes_reply_t *rp = &pkt->maxbytes_reply; |
675 | ||
676 | if (plen < sizeof (*rq)) | |
677 | return (FALSE); | |
678 | ||
679 | rp->hdr.is_reply = 1; | |
680 | rp->hdr.len = sizeof (*rp); | |
681 | ||
682 | dprintf(("kdp_maxbytes\n")); | |
683 | ||
684 | rp->max_bytes = MAX_KDP_DATA_SIZE; | |
685 | ||
686 | *reply_port = kdp.reply_port; | |
687 | *len = rp->hdr.len; | |
688 | ||
689 | return (TRUE); | |
690 | } | |
691 | ||
9bccf70c A |
692 | static boolean_t |
693 | kdp_version( | |
694 | kdp_pkt_t *pkt, | |
695 | int *len, | |
696 | unsigned short *reply_port | |
697 | ) | |
698 | { | |
699 | kdp_version_req_t *rq = &pkt->version_req; | |
2d21ac55 | 700 | size_t plen = *len; |
9bccf70c | 701 | kdp_version_reply_t *rp = &pkt->version_reply; |
9bccf70c A |
702 | |
703 | if (plen < sizeof (*rq)) | |
704 | return (FALSE); | |
705 | ||
706 | rp->hdr.is_reply = 1; | |
707 | rp->hdr.len = sizeof (*rp); | |
708 | ||
709 | dprintf(("kdp_version\n")); | |
710 | ||
711 | rp->version = KDP_VERSION; | |
9bccf70c A |
712 | if (!(kdp_flag & KDP_BP_DIS)) |
713 | rp->feature = KDP_FEATURE_BP; | |
714 | else | |
715 | rp->feature = 0; | |
b0d623f7 | 716 | |
9bccf70c A |
717 | *reply_port = kdp.reply_port; |
718 | *len = rp->hdr.len; | |
719 | ||
720 | return (TRUE); | |
721 | } | |
722 | ||
1c79356b A |
723 | static boolean_t |
724 | kdp_regions( | |
725 | kdp_pkt_t *pkt, | |
726 | int *len, | |
727 | unsigned short *reply_port | |
728 | ) | |
729 | { | |
730 | kdp_regions_req_t *rq = &pkt->regions_req; | |
2d21ac55 | 731 | size_t plen = *len; |
1c79356b A |
732 | kdp_regions_reply_t *rp = &pkt->regions_reply; |
733 | kdp_region_t *r; | |
734 | ||
735 | if (plen < sizeof (*rq)) | |
736 | return (FALSE); | |
737 | ||
738 | rp->hdr.is_reply = 1; | |
739 | rp->hdr.len = sizeof (*rp); | |
740 | ||
741 | dprintf(("kdp_regions\n")); | |
742 | ||
743 | r = rp->regions; | |
744 | rp->nregions = 0; | |
745 | ||
b0d623f7 | 746 | r->address = 0; |
1c79356b A |
747 | r->nbytes = 0xffffffff; |
748 | ||
749 | r->protection = VM_PROT_ALL; r++; rp->nregions++; | |
750 | ||
751 | rp->hdr.len += rp->nregions * sizeof (kdp_region_t); | |
752 | ||
753 | *reply_port = kdp.reply_port; | |
754 | *len = rp->hdr.len; | |
755 | ||
756 | return (TRUE); | |
757 | } | |
758 | ||
759 | static boolean_t | |
760 | kdp_writeregs( | |
761 | kdp_pkt_t *pkt, | |
762 | int *len, | |
763 | unsigned short *reply_port | |
764 | ) | |
765 | { | |
766 | kdp_writeregs_req_t *rq = &pkt->writeregs_req; | |
2d21ac55 | 767 | size_t plen = *len; |
1c79356b A |
768 | int size; |
769 | kdp_writeregs_reply_t *rp = &pkt->writeregs_reply; | |
770 | ||
771 | if (plen < sizeof (*rq)) | |
772 | return (FALSE); | |
773 | ||
b0d623f7 | 774 | size = rq->hdr.len - (unsigned)sizeof(kdp_hdr_t) - (unsigned)sizeof(unsigned int); |
1c79356b A |
775 | rp->error = kdp_machine_write_regs(rq->cpu, rq->flavor, rq->data, &size); |
776 | ||
777 | rp->hdr.is_reply = 1; | |
778 | rp->hdr.len = sizeof (*rp); | |
779 | ||
780 | *reply_port = kdp.reply_port; | |
781 | *len = rp->hdr.len; | |
782 | ||
783 | return (TRUE); | |
784 | } | |
785 | ||
786 | static boolean_t | |
787 | kdp_readregs( | |
788 | kdp_pkt_t *pkt, | |
789 | int *len, | |
790 | unsigned short *reply_port | |
791 | ) | |
792 | { | |
793 | kdp_readregs_req_t *rq = &pkt->readregs_req; | |
2d21ac55 | 794 | size_t plen = *len; |
1c79356b A |
795 | kdp_readregs_reply_t *rp = &pkt->readregs_reply; |
796 | int size; | |
797 | ||
798 | if (plen < sizeof (*rq)) | |
799 | return (FALSE); | |
800 | ||
801 | rp->hdr.is_reply = 1; | |
802 | rp->hdr.len = sizeof (*rp); | |
803 | ||
804 | rp->error = kdp_machine_read_regs(rq->cpu, rq->flavor, rp->data, &size); | |
805 | rp->hdr.len += size; | |
806 | ||
807 | *reply_port = kdp.reply_port; | |
808 | *len = rp->hdr.len; | |
809 | ||
810 | return (TRUE); | |
811 | } | |
9bccf70c | 812 | |
b0d623f7 A |
813 | |
814 | boolean_t | |
9bccf70c | 815 | kdp_breakpoint_set( |
b0d623f7 A |
816 | kdp_pkt_t *pkt, |
817 | int *len, | |
818 | unsigned short *reply_port | |
9bccf70c A |
819 | ) |
820 | { | |
b0d623f7 A |
821 | kdp_breakpoint_req_t *rq = &pkt->breakpoint_req; |
822 | kdp_breakpoint_reply_t *rp = &pkt->breakpoint_reply; | |
823 | size_t plen = *len; | |
824 | kdp_error_t kerr; | |
825 | ||
826 | if (plen < sizeof (*rq)) | |
827 | return (FALSE); | |
828 | ||
829 | dprintf(("kdp_breakpoint_set %x\n", rq->address)); | |
9bccf70c | 830 | |
b0d623f7 A |
831 | kerr = kdp_set_breakpoint_internal((mach_vm_address_t)rq->address); |
832 | ||
833 | rp->error = kerr; | |
834 | ||
835 | rp->hdr.is_reply = 1; | |
836 | rp->hdr.len = sizeof (*rp); | |
837 | *reply_port = kdp.reply_port; | |
838 | *len = rp->hdr.len; | |
839 | ||
840 | return (TRUE); | |
841 | } | |
9bccf70c | 842 | |
b0d623f7 A |
843 | boolean_t |
844 | kdp_breakpoint64_set( | |
845 | kdp_pkt_t *pkt, | |
846 | int *len, | |
847 | unsigned short *reply_port | |
848 | ) | |
849 | { | |
850 | kdp_breakpoint64_req_t *rq = &pkt->breakpoint64_req; | |
851 | kdp_breakpoint64_reply_t *rp = &pkt->breakpoint64_reply; | |
852 | size_t plen = *len; | |
853 | kdp_error_t kerr; | |
854 | ||
855 | if (plen < sizeof (*rq)) | |
856 | return (FALSE); | |
857 | ||
858 | dprintf(("kdp_breakpoint64_set %llx\n", rq->address)); | |
9bccf70c | 859 | |
b0d623f7 A |
860 | kerr = kdp_set_breakpoint_internal((mach_vm_address_t)rq->address); |
861 | ||
862 | rp->error = kerr; | |
863 | ||
864 | rp->hdr.is_reply = 1; | |
865 | rp->hdr.len = sizeof (*rp); | |
866 | *reply_port = kdp.reply_port; | |
867 | *len = rp->hdr.len; | |
868 | ||
869 | return (TRUE); | |
870 | } | |
9bccf70c | 871 | |
b0d623f7 A |
872 | boolean_t |
873 | kdp_breakpoint_remove( | |
874 | kdp_pkt_t *pkt, | |
875 | int *len, | |
876 | unsigned short *reply_port | |
877 | ) | |
878 | { | |
879 | kdp_breakpoint_req_t *rq = &pkt->breakpoint_req; | |
880 | kdp_breakpoint_reply_t *rp = &pkt->breakpoint_reply; | |
881 | size_t plen = *len; | |
882 | kdp_error_t kerr; | |
883 | if (plen < sizeof (*rq)) | |
884 | return (FALSE); | |
885 | ||
886 | dprintf(("kdp_breakpoint_remove %x\n", rq->address)); | |
9bccf70c | 887 | |
b0d623f7 A |
888 | kerr = kdp_remove_breakpoint_internal((mach_vm_address_t)rq->address); |
889 | ||
890 | rp->error = kerr; | |
891 | ||
892 | rp->hdr.is_reply = 1; | |
893 | rp->hdr.len = sizeof (*rp); | |
894 | *reply_port = kdp.reply_port; | |
895 | *len = rp->hdr.len; | |
896 | ||
897 | return (TRUE); | |
9bccf70c A |
898 | } |
899 | ||
b0d623f7 A |
900 | boolean_t |
901 | kdp_breakpoint64_remove( | |
902 | kdp_pkt_t *pkt, | |
903 | int *len, | |
904 | unsigned short *reply_port | |
9bccf70c A |
905 | ) |
906 | { | |
b0d623f7 A |
907 | kdp_breakpoint64_req_t *rq = &pkt->breakpoint64_req; |
908 | kdp_breakpoint64_reply_t *rp = &pkt->breakpoint64_reply; | |
909 | size_t plen = *len; | |
910 | kdp_error_t kerr; | |
911 | ||
912 | if (plen < sizeof (*rq)) | |
913 | return (FALSE); | |
914 | ||
915 | dprintf(("kdp_breakpoint64_remove %llx\n", rq->address)); | |
9bccf70c | 916 | |
b0d623f7 A |
917 | kerr = kdp_remove_breakpoint_internal((mach_vm_address_t)rq->address); |
918 | ||
919 | rp->error = kerr; | |
920 | ||
921 | rp->hdr.is_reply = 1; | |
922 | rp->hdr.len = sizeof (*rp); | |
923 | *reply_port = kdp.reply_port; | |
924 | *len = rp->hdr.len; | |
925 | ||
926 | return (TRUE); | |
927 | } | |
9bccf70c | 928 | |
9bccf70c | 929 | |
b0d623f7 A |
930 | kdp_error_t |
931 | kdp_set_breakpoint_internal( | |
932 | mach_vm_address_t address | |
933 | ) | |
934 | { | |
935 | ||
936 | uint8_t breakinstr[MAX_BREAKINSN_BYTES], oldinstr[MAX_BREAKINSN_BYTES]; | |
937 | uint32_t breakinstrsize = sizeof(breakinstr); | |
938 | mach_vm_size_t cnt; | |
939 | int i; | |
940 | ||
941 | kdp_machine_get_breakinsn(breakinstr, &breakinstrsize); | |
942 | ||
943 | if(breakpoints_initialized == 0) | |
944 | { | |
945 | for(i=0;(i < MAX_BREAKPOINTS); breakpoint_list[i].address=0, i++); | |
946 | breakpoints_initialized++; | |
9bccf70c | 947 | } |
b0d623f7 A |
948 | |
949 | cnt = kdp_machine_vm_read(address, (caddr_t)&oldinstr, (mach_vm_size_t)breakinstrsize); | |
950 | ||
951 | if (0 == memcmp(oldinstr, breakinstr, breakinstrsize)) { | |
952 | printf("A trap was already set at that address, not setting new breakpoint\n"); | |
953 | ||
954 | return KDPERR_BREAKPOINT_ALREADY_SET; | |
955 | } | |
956 | ||
957 | for(i=0;(i < MAX_BREAKPOINTS) && (breakpoint_list[i].address != 0); i++); | |
958 | ||
959 | if (i == MAX_BREAKPOINTS) { | |
960 | return KDPERR_MAX_BREAKPOINTS; | |
961 | } | |
962 | ||
963 | breakpoint_list[i].address = address; | |
964 | memcpy(breakpoint_list[i].oldbytes, oldinstr, breakinstrsize); | |
965 | breakpoint_list[i].bytesused = breakinstrsize; | |
966 | ||
967 | cnt = kdp_machine_vm_write((caddr_t)&breakinstr, address, breakinstrsize); | |
968 | ||
969 | return KDPERR_NO_ERROR; | |
970 | } | |
9bccf70c | 971 | |
b0d623f7 A |
972 | kdp_error_t |
973 | kdp_remove_breakpoint_internal( | |
974 | mach_vm_address_t address | |
975 | ) | |
976 | { | |
977 | mach_vm_size_t cnt; | |
978 | int i; | |
979 | ||
980 | for(i=0;(i < MAX_BREAKPOINTS) && (breakpoint_list[i].address != address); i++); | |
981 | ||
982 | if (i == MAX_BREAKPOINTS) | |
983 | { | |
984 | return KDPERR_BREAKPOINT_NOT_FOUND; | |
985 | } | |
986 | ||
987 | breakpoint_list[i].address = 0; | |
988 | cnt = kdp_machine_vm_write((caddr_t)&breakpoint_list[i].oldbytes, address, breakpoint_list[i].bytesused); | |
989 | ||
990 | return KDPERR_NO_ERROR; | |
9bccf70c A |
991 | } |
992 | ||
993 | boolean_t | |
2d21ac55 | 994 | kdp_remove_all_breakpoints(void) |
9bccf70c | 995 | { |
b0d623f7 A |
996 | int i; |
997 | boolean_t breakpoint_found = FALSE; | |
998 | ||
999 | if (breakpoints_initialized) | |
9bccf70c | 1000 | { |
b0d623f7 A |
1001 | for(i=0;i < MAX_BREAKPOINTS; i++) |
1002 | { | |
1003 | if (breakpoint_list[i].address) | |
1004 | { | |
1005 | kdp_machine_vm_write((caddr_t)&(breakpoint_list[i].oldbytes), (mach_vm_address_t)breakpoint_list[i].address, (mach_vm_size_t)breakpoint_list[i].bytesused); | |
1006 | breakpoint_found = TRUE; | |
1007 | breakpoint_list[i].address = 0; | |
1008 | } | |
1009 | } | |
1010 | ||
1011 | if (breakpoint_found) | |
1012 | printf("kdp_remove_all_breakpoints: found extant breakpoints, removing them.\n"); | |
9bccf70c | 1013 | } |
b0d623f7 | 1014 | return breakpoint_found; |
9bccf70c | 1015 | } |
0c530ab8 | 1016 | |
b0d623f7 A |
1017 | boolean_t |
1018 | kdp_reboot( | |
1019 | __unused kdp_pkt_t *pkt, | |
1020 | __unused int *len, | |
1021 | __unused unsigned short *reply_port | |
1022 | ) | |
1023 | { | |
1024 | dprintf(("kdp_reboot\n")); | |
1025 | ||
1026 | kdp_machine_reboot(); | |
1027 | ||
1028 | return (TRUE); // no, not really, we won't return | |
1029 | } | |
0c530ab8 A |
1030 | |
1031 | #define MAX_FRAMES 1000 | |
1032 | ||
1033 | static int pid_from_task(task_t task) | |
1034 | { | |
1035 | int pid = -1; | |
1036 | ||
1037 | if (task->bsd_info) | |
1038 | pid = proc_pid(task->bsd_info); | |
1039 | ||
1040 | return pid; | |
1041 | } | |
1042 | ||
b0d623f7 A |
1043 | boolean_t |
1044 | kdp_copyin(pmap_t p, uint64_t uaddr, void *dest, size_t size) { | |
1045 | size_t rem = size; | |
1046 | char *kvaddr = dest; | |
1047 | ||
1048 | while (rem) { | |
1049 | ppnum_t upn = pmap_find_phys(p, uaddr); | |
060df5ea | 1050 | uint64_t phys_src = ptoa_64(upn) | (uaddr & PAGE_MASK); |
b0d623f7 A |
1051 | uint64_t phys_dest = kvtophys((vm_offset_t)kvaddr); |
1052 | uint64_t src_rem = PAGE_SIZE - (phys_src & PAGE_MASK); | |
1053 | uint64_t dst_rem = PAGE_SIZE - (phys_dest & PAGE_MASK); | |
1054 | size_t cur_size = (uint32_t) MIN(src_rem, dst_rem); | |
1055 | cur_size = MIN(cur_size, rem); | |
1056 | ||
1057 | if (upn && pmap_valid_page(upn) && phys_dest) { | |
1058 | bcopy_phys(phys_src, phys_dest, cur_size); | |
1059 | } | |
1060 | else | |
1061 | break; | |
1062 | uaddr += cur_size; | |
1063 | kvaddr += cur_size; | |
1064 | rem -= cur_size; | |
1065 | } | |
1066 | return (rem == 0); | |
1067 | } | |
1068 | ||
6d2010ae A |
1069 | |
1070 | static void | |
1071 | kdp_mem_snapshot(struct mem_snapshot *mem_snap) | |
1072 | { | |
1073 | mem_snap->snapshot_magic = STACKSHOT_MEM_SNAPSHOT_MAGIC; | |
1074 | mem_snap->free_pages = vm_page_free_count; | |
1075 | mem_snap->active_pages = vm_page_active_count; | |
1076 | mem_snap->inactive_pages = vm_page_inactive_count; | |
1077 | mem_snap->purgeable_pages = vm_page_purgeable_count; | |
1078 | mem_snap->wired_pages = vm_page_wire_count; | |
1079 | mem_snap->speculative_pages = vm_page_speculative_count; | |
1080 | mem_snap->throttled_pages = vm_page_throttled_count; | |
1081 | } | |
1082 | ||
1083 | ||
1084 | /* | |
1085 | * Method for grabbing timer values safely, in the sense that no infinite loop will occur | |
1086 | * Certain flavors of the timer_grab function, which would seem to be the thing to use, | |
1087 | * can loop infinitely if called while the timer is in the process of being updated. | |
1088 | * Unfortunately, it is (rarely) possible to get inconsistent top and bottom halves of | |
1089 | * the timer using this method. This seems insoluble, since stackshot runs in a context | |
1090 | * where the timer might be half-updated, and has no way of yielding control just long | |
1091 | * enough to finish the update. | |
1092 | */ | |
1093 | ||
1094 | static uint64_t safe_grab_timer_value(struct timer *t) | |
1095 | { | |
1096 | #if defined(__LP64__) | |
1097 | return t->all_bits; | |
1098 | #else | |
1099 | uint64_t time = t->high_bits; /* endian independent grab */ | |
1100 | time = (time << 32) | t->low_bits; | |
1101 | return time; | |
1102 | #endif | |
1103 | } | |
1104 | ||
0c530ab8 | 1105 | int |
b7266188 | 1106 | kdp_stackshot(int pid, void *tracebuf, uint32_t tracebuf_size, uint32_t trace_flags, uint32_t dispatch_offset, uint32_t *pbytesTraced) |
0c530ab8 | 1107 | { |
2d21ac55 A |
1108 | char *tracepos = (char *) tracebuf; |
1109 | char *tracebound = tracepos + tracebuf_size; | |
0c530ab8 A |
1110 | uint32_t tracebytes = 0; |
1111 | int error = 0; | |
2d21ac55 | 1112 | |
0c530ab8 A |
1113 | task_t task = TASK_NULL; |
1114 | thread_t thread = THREAD_NULL; | |
0c530ab8 A |
1115 | thread_snapshot_t tsnap = NULL; |
1116 | unsigned framesize = 2 * sizeof(vm_offset_t); | |
2d21ac55 A |
1117 | struct task ctask; |
1118 | struct thread cthread; | |
6d2010ae A |
1119 | struct _vm_map cmap; |
1120 | struct pmap cpmap; | |
1121 | ||
1122 | queue_head_t *task_list = &tasks; | |
1123 | boolean_t is_active_list = TRUE; | |
b7266188 A |
1124 | |
1125 | boolean_t dispatch_p = ((trace_flags & STACKSHOT_GET_DQ) != 0); | |
1126 | boolean_t save_loadinfo_p = ((trace_flags & STACKSHOT_SAVE_LOADINFO) != 0); | |
0c530ab8 | 1127 | |
6d2010ae A |
1128 | if(trace_flags & STACKSHOT_GET_GLOBAL_MEM_STATS) { |
1129 | if(tracepos + sizeof(struct mem_snapshot) > tracebound) { | |
1130 | error = -1; | |
1131 | goto error_exit; | |
1132 | } | |
1133 | kdp_mem_snapshot((struct mem_snapshot *)tracepos); | |
1134 | tracepos += sizeof(struct mem_snapshot); | |
1135 | } | |
1136 | ||
1137 | walk_list: | |
1138 | queue_iterate(task_list, task, task_t, tasks) { | |
2d21ac55 A |
1139 | if ((task == NULL) || (ml_nofault_copy((vm_offset_t) task, (vm_offset_t) &ctask, sizeof(struct task)) != sizeof(struct task))) |
1140 | goto error_exit; | |
b7266188 | 1141 | |
060df5ea A |
1142 | int task_pid = pid_from_task(task); |
1143 | boolean_t task64 = task_has_64BitAddr(task); | |
1144 | ||
6d2010ae A |
1145 | if (!task->active) { |
1146 | /* | |
1147 | * Not interested in terminated tasks without threads, and | |
1148 | * at the moment, stackshot can't handle a task without a name. | |
1149 | */ | |
1150 | if (queue_empty(&task->threads) || task_pid == -1) { | |
1151 | continue; | |
1152 | } | |
1153 | } | |
1154 | ||
0c530ab8 | 1155 | /* Trace everything, unless a process was specified */ |
b7266188 A |
1156 | if ((pid == -1) || (pid == task_pid)) { |
1157 | task_snapshot_t task_snap; | |
6d2010ae A |
1158 | uint32_t uuid_info_count = 0; |
1159 | mach_vm_address_t uuid_info_addr = 0; | |
1160 | boolean_t have_map = (task->map != NULL) && | |
1161 | (ml_nofault_copy((vm_offset_t)(task->map), (vm_offset_t)&cmap, sizeof(struct _vm_map)) == sizeof(struct _vm_map)); | |
1162 | boolean_t have_pmap = have_map && (cmap.pmap != NULL) && | |
1163 | (ml_nofault_copy((vm_offset_t)(cmap.pmap), (vm_offset_t)&cpmap, sizeof(struct pmap)) == sizeof(struct pmap)); | |
1164 | ||
1165 | if (have_pmap && task->active && save_loadinfo_p && task_pid > 0) { | |
b7266188 A |
1166 | // Read the dyld_all_image_infos struct from the task memory to get UUID array count and location |
1167 | if (task64) { | |
1168 | struct dyld_all_image_infos64 task_image_infos; | |
6d2010ae A |
1169 | if (kdp_copyin(task->map->pmap, task->all_image_info_addr, &task_image_infos, sizeof(struct dyld_all_image_infos64))) { |
1170 | uuid_info_count = (uint32_t)task_image_infos.uuidArrayCount; | |
1171 | uuid_info_addr = task_image_infos.uuidArray; | |
1172 | } | |
b7266188 A |
1173 | } else { |
1174 | struct dyld_all_image_infos task_image_infos; | |
6d2010ae A |
1175 | if (kdp_copyin(task->map->pmap, task->all_image_info_addr, &task_image_infos, sizeof(struct dyld_all_image_infos))) { |
1176 | uuid_info_count = task_image_infos.uuidArrayCount; | |
1177 | uuid_info_addr = task_image_infos.uuidArray; | |
1178 | } | |
1179 | } | |
1180 | ||
1181 | // If we get a NULL uuid_info_addr (which can happen when we catch dyld in the middle of updating | |
1182 | // this data structure), we zero the uuid_info_count so that we won't even try to save load info | |
1183 | // for this task. | |
1184 | if (!uuid_info_addr) { | |
1185 | uuid_info_count = 0; | |
b7266188 | 1186 | } |
b7266188 A |
1187 | } |
1188 | ||
1189 | if (tracepos + sizeof(struct task_snapshot) > tracebound) { | |
1190 | error = -1; | |
1191 | goto error_exit; | |
1192 | } | |
1193 | ||
1194 | task_snap = (task_snapshot_t) tracepos; | |
1195 | task_snap->snapshot_magic = STACKSHOT_TASK_SNAPSHOT_MAGIC; | |
1196 | task_snap->pid = task_pid; | |
1197 | task_snap->nloadinfos = uuid_info_count; | |
1198 | /* Add the BSD process identifiers */ | |
1199 | if (task_pid != -1) | |
1200 | proc_name_kdp(task, task_snap->p_comm, sizeof(task_snap->p_comm)); | |
1201 | else | |
1202 | task_snap->p_comm[0] = '\0'; | |
1203 | task_snap->ss_flags = 0; | |
1204 | if (task64) | |
1205 | task_snap->ss_flags |= kUser64_p; | |
6d2010ae A |
1206 | if (!task->active) |
1207 | task_snap->ss_flags |= kTerminatedSnapshot; | |
1208 | ||
1209 | task_snap->suspend_count = task->suspend_count; | |
1210 | task_snap->task_size = have_pmap ? pmap_resident_count(task->map->pmap) : 0; | |
1211 | task_snap->faults = task->faults; | |
1212 | task_snap->pageins = task->pageins; | |
1213 | task_snap->cow_faults = task->cow_faults; | |
b7266188 | 1214 | |
6d2010ae A |
1215 | task_snap->user_time_in_terminated_threads = task->total_user_time; |
1216 | task_snap->system_time_in_terminated_threads = task->total_system_time; | |
b7266188 A |
1217 | tracepos += sizeof(struct task_snapshot); |
1218 | ||
1219 | if (task_pid > 0 && uuid_info_count > 0) { | |
1220 | uint32_t uuid_info_size = (uint32_t)(task64 ? sizeof(struct dyld_uuid_info64) : sizeof(struct dyld_uuid_info)); | |
1221 | uint32_t uuid_info_array_size = uuid_info_count * uuid_info_size; | |
1222 | ||
1223 | if (tracepos + uuid_info_array_size > tracebound) { | |
1224 | error = -1; | |
1225 | goto error_exit; | |
1226 | } | |
1227 | ||
1228 | // Copy in the UUID info array | |
6d2010ae A |
1229 | // It may be nonresident, in which case just fix up nloadinfos to 0 in the task_snap |
1230 | if (have_pmap && !kdp_copyin(task->map->pmap, uuid_info_addr, tracepos, uuid_info_array_size)) | |
1231 | task_snap->nloadinfos = 0; | |
1232 | else | |
1233 | tracepos += uuid_info_array_size; | |
b7266188 A |
1234 | } |
1235 | ||
0c530ab8 | 1236 | queue_iterate(&task->threads, thread, thread_t, task_threads){ |
2d21ac55 A |
1237 | if ((thread == NULL) || (ml_nofault_copy((vm_offset_t) thread, (vm_offset_t) &cthread, sizeof(struct thread)) != sizeof(struct thread))) |
1238 | goto error_exit; | |
b7266188 | 1239 | |
2d21ac55 | 1240 | if (((tracepos + 4 * sizeof(struct thread_snapshot)) > tracebound)) { |
0c530ab8 A |
1241 | error = -1; |
1242 | goto error_exit; | |
1243 | } | |
b7266188 | 1244 | /* Populate the thread snapshot header */ |
0c530ab8 | 1245 | tsnap = (thread_snapshot_t) tracepos; |
d41d1dae | 1246 | tsnap->thread_id = thread_tid(thread); |
0c530ab8 | 1247 | tsnap->state = thread->state; |
0c530ab8 | 1248 | tsnap->wait_event = thread->wait_event; |
b0d623f7 | 1249 | tsnap->continuation = (uint64_t) (uintptr_t) thread->continuation; |
6d2010ae A |
1250 | tsnap->user_time = safe_grab_timer_value(&thread->user_timer); |
1251 | tsnap->system_time = safe_grab_timer_value(&thread->system_timer); | |
b7266188 | 1252 | tsnap->snapshot_magic = STACKSHOT_THREAD_SNAPSHOT_MAGIC; |
0c530ab8 | 1253 | tracepos += sizeof(struct thread_snapshot); |
b0d623f7 A |
1254 | tsnap->ss_flags = 0; |
1255 | ||
6d2010ae | 1256 | if (dispatch_p && (task != kernel_task) && (task->active) && have_pmap) { |
b0d623f7 A |
1257 | uint64_t dqkeyaddr = thread_dispatchqaddr(thread); |
1258 | if (dqkeyaddr != 0) { | |
b0d623f7 A |
1259 | uint64_t dqaddr = 0; |
1260 | if (kdp_copyin(task->map->pmap, dqkeyaddr, &dqaddr, (task64 ? 8 : 4)) && (dqaddr != 0)) { | |
1261 | uint64_t dqserialnumaddr = dqaddr + dispatch_offset; | |
1262 | uint64_t dqserialnum = 0; | |
1263 | if (kdp_copyin(task->map->pmap, dqserialnumaddr, &dqserialnum, (task64 ? 8 : 4))) { | |
1264 | tsnap->ss_flags |= kHasDispatchSerial; | |
1265 | *(uint64_t *)tracepos = dqserialnum; | |
1266 | tracepos += 8; | |
1267 | } | |
1268 | } | |
1269 | } | |
1270 | } | |
0c530ab8 A |
1271 | /* Call through to the machine specific trace routines |
1272 | * Frames are added past the snapshot header. | |
1273 | */ | |
6d2010ae | 1274 | tracebytes = 0; |
b0d623f7 A |
1275 | if (thread->kernel_stack != 0) { |
1276 | #if defined(__LP64__) | |
b7266188 | 1277 | tracebytes = machine_trace_thread64(thread, tracepos, tracebound, MAX_FRAMES, FALSE); |
b0d623f7 A |
1278 | tsnap->ss_flags |= kKernel64_p; |
1279 | framesize = 16; | |
1280 | #else | |
b7266188 | 1281 | tracebytes = machine_trace_thread(thread, tracepos, tracebound, MAX_FRAMES, FALSE); |
b0d623f7 A |
1282 | framesize = 8; |
1283 | #endif | |
1284 | } | |
1285 | tsnap->nkern_frames = tracebytes/framesize; | |
0c530ab8 A |
1286 | tracepos += tracebytes; |
1287 | tracebytes = 0; | |
b7266188 | 1288 | /* Trace user stack, if any */ |
6d2010ae | 1289 | if (task->active && thread->task->map != kernel_map) { |
b0d623f7 | 1290 | /* 64-bit task? */ |
0c530ab8 | 1291 | if (task_has_64BitAddr(thread->task)) { |
b7266188 | 1292 | tracebytes = machine_trace_thread64(thread, tracepos, tracebound, MAX_FRAMES, TRUE); |
b0d623f7 A |
1293 | tsnap->ss_flags |= kUser64_p; |
1294 | framesize = 16; | |
0c530ab8 A |
1295 | } |
1296 | else { | |
b7266188 | 1297 | tracebytes = machine_trace_thread(thread, tracepos, tracebound, MAX_FRAMES, TRUE); |
b0d623f7 | 1298 | framesize = 8; |
0c530ab8 A |
1299 | } |
1300 | } | |
1301 | tsnap->nuser_frames = tracebytes/framesize; | |
1302 | tracepos += tracebytes; | |
1303 | tracebytes = 0; | |
1304 | } | |
b7266188 | 1305 | } |
0c530ab8 A |
1306 | } |
1307 | ||
6d2010ae A |
1308 | if (is_active_list) { |
1309 | is_active_list = FALSE; | |
1310 | task_list = &terminated_tasks; | |
1311 | goto walk_list; | |
1312 | } | |
1313 | ||
0c530ab8 A |
1314 | error_exit: |
1315 | /* Release stack snapshot wait indicator */ | |
1316 | kdp_snapshot_postflight(); | |
1317 | ||
b0d623f7 | 1318 | *pbytesTraced = (uint32_t)(tracepos - (char *) tracebuf); |
0c530ab8 A |
1319 | |
1320 | return error; | |
1321 | } | |
b0d623f7 A |
1322 | |
1323 | static boolean_t | |
1324 | kdp_readioport(kdp_pkt_t *pkt, | |
1325 | int *len, | |
1326 | unsigned short *reply_port | |
1327 | ) | |
1328 | { | |
1329 | kdp_readioport_req_t *rq = &pkt->readioport_req; | |
1330 | kdp_readioport_reply_t *rp = &pkt->readioport_reply; | |
1331 | size_t plen = *len; | |
1332 | ||
1333 | if (plen < sizeof (*rq)) | |
1334 | return (FALSE); | |
1335 | ||
1336 | rp->hdr.is_reply = 1; | |
1337 | rp->hdr.len = sizeof (*rp); | |
1338 | ||
1339 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
1340 | rp->error = KDPERR_BAD_NBYTES; | |
1341 | else { | |
1342 | #if KDP_TEST_HARNESS | |
1343 | uint16_t addr = rq->address; | |
1344 | #endif | |
1345 | uint16_t size = rq->nbytes; | |
1346 | dprintf(("kdp_readioport addr %x size %d\n", addr, size)); | |
1347 | ||
1348 | rp->error = kdp_machine_ioport_read(rq, rp->data, rq->lcpu); | |
1349 | if (rp->error == KDPERR_NO_ERROR) | |
1350 | rp->hdr.len += size; | |
1351 | } | |
1352 | ||
1353 | *reply_port = kdp.reply_port; | |
1354 | *len = rp->hdr.len; | |
1355 | ||
1356 | return (TRUE); | |
1357 | } | |
1358 | ||
1359 | static boolean_t | |
1360 | kdp_writeioport( | |
1361 | kdp_pkt_t *pkt, | |
1362 | int *len, | |
1363 | unsigned short *reply_port | |
1364 | ) | |
1365 | { | |
1366 | kdp_writeioport_req_t *rq = &pkt->writeioport_req; | |
1367 | kdp_writeioport_reply_t *rp = &pkt->writeioport_reply; | |
1368 | size_t plen = *len; | |
1369 | ||
1370 | if (plen < sizeof (*rq)) | |
1371 | return (FALSE); | |
1372 | ||
1373 | if (rq->nbytes > MAX_KDP_DATA_SIZE) | |
1374 | rp->error = KDPERR_BAD_NBYTES; | |
1375 | else { | |
1376 | dprintf(("kdp_writeioport addr %x size %d\n", rq->address, | |
1377 | rq->nbytes)); | |
1378 | ||
1379 | rp->error = kdp_machine_ioport_write(rq, rq->data, rq->lcpu); | |
1380 | } | |
1381 | ||
1382 | rp->hdr.is_reply = 1; | |
1383 | rp->hdr.len = sizeof (*rp); | |
1384 | ||
1385 | *reply_port = kdp.reply_port; | |
1386 | *len = rp->hdr.len; | |
1387 | ||
1388 | return (TRUE); | |
1389 | } | |
1390 | ||
1391 | static boolean_t | |
1392 | kdp_readmsr64(kdp_pkt_t *pkt, | |
1393 | int *len, | |
1394 | unsigned short *reply_port | |
1395 | ) | |
1396 | { | |
1397 | kdp_readmsr64_req_t *rq = &pkt->readmsr64_req; | |
1398 | kdp_readmsr64_reply_t *rp = &pkt->readmsr64_reply; | |
1399 | size_t plen = *len; | |
1400 | ||
1401 | if (plen < sizeof (*rq)) | |
1402 | return (FALSE); | |
1403 | ||
1404 | rp->hdr.is_reply = 1; | |
1405 | rp->hdr.len = sizeof (*rp); | |
1406 | ||
1407 | dprintf(("kdp_readmsr64 lcpu %x addr %x\n", rq->lcpu, rq->address)); | |
1408 | rp->error = kdp_machine_msr64_read(rq, rp->data, rq->lcpu); | |
1409 | if (rp->error == KDPERR_NO_ERROR) | |
1410 | rp->hdr.len += sizeof(uint64_t); | |
1411 | ||
1412 | *reply_port = kdp.reply_port; | |
1413 | *len = rp->hdr.len; | |
1414 | ||
1415 | return (TRUE); | |
1416 | } | |
1417 | ||
1418 | static boolean_t | |
1419 | kdp_writemsr64( | |
1420 | kdp_pkt_t *pkt, | |
1421 | int *len, | |
1422 | unsigned short *reply_port | |
1423 | ) | |
1424 | { | |
1425 | kdp_writemsr64_req_t *rq = &pkt->writemsr64_req; | |
1426 | kdp_writemsr64_reply_t *rp = &pkt->writemsr64_reply; | |
1427 | size_t plen = *len; | |
1428 | ||
1429 | if (plen < sizeof (*rq)) | |
1430 | return (FALSE); | |
1431 | ||
1432 | dprintf(("kdp_writemsr64 lcpu %x addr %x\n", rq->lcpu, rq->address)); | |
1433 | rp->error = kdp_machine_msr64_write(rq, rq->data, rq->lcpu); | |
1434 | ||
1435 | rp->hdr.is_reply = 1; | |
1436 | rp->hdr.len = sizeof (*rp); | |
1437 | ||
1438 | *reply_port = kdp.reply_port; | |
1439 | *len = rp->hdr.len; | |
1440 | ||
1441 | return (TRUE); | |
1442 | } | |
7e4a7d39 A |
1443 | |
1444 | static boolean_t | |
1445 | kdp_dumpinfo( | |
1446 | kdp_pkt_t *pkt, | |
1447 | int *len, | |
1448 | unsigned short *reply_port | |
1449 | ) | |
1450 | { | |
1451 | kdp_dumpinfo_req_t *rq = &pkt->dumpinfo_req; | |
1452 | kdp_dumpinfo_reply_t *rp = &pkt->dumpinfo_reply; | |
1453 | size_t plen = *len; | |
1454 | ||
1455 | if (plen < sizeof (*rq)) | |
1456 | return (FALSE); | |
1457 | ||
1458 | dprintf(("kdp_dumpinfo file=%s destip=%s routerip=%s\n", rq->name, rq->destip, rq->routerip)); | |
1459 | rp->hdr.is_reply = 1; | |
1460 | rp->hdr.len = sizeof (*rp); | |
1461 | ||
1462 | if ((rq->type & KDP_DUMPINFO_MASK) != KDP_DUMPINFO_GETINFO) { | |
1463 | kdp_set_dump_info(rq->type, rq->name, rq->destip, rq->routerip, | |
1464 | rq->port); | |
1465 | } | |
1466 | ||
1467 | /* gather some stats for reply */ | |
1468 | kdp_get_dump_info(&rp->type, rp->name, rp->destip, rp->routerip, | |
1469 | &rp->port); | |
1470 | ||
1471 | *reply_port = kdp.reply_port; | |
1472 | *len = rp->hdr.len; | |
1473 | ||
1474 | return (TRUE); | |
1475 | } |