X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..4452a7af2eac33dbad800bcc91f2399d62c18f53:/osfmk/kdp/kdp.c diff --git a/osfmk/kdp/kdp.c b/osfmk/kdp/kdp.c index a6e8abc71..00f50e82a 100644 --- a/osfmk/kdp/kdp.c +++ b/osfmk/kdp/kdp.c @@ -1,29 +1,29 @@ /* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ - */ -/* - * Copyright (c) 1993 NeXT Computer, Inc. All rights reserved. - * - * kdp.c -- Kernel Debugging Protocol. - * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include @@ -32,6 +32,15 @@ #include #include +#include + +#include /* bcopy */ + +#include +#include +#include +#include + int kdp_vm_read( caddr_t, caddr_t, unsigned int); int kdp_vm_write( caddr_t, caddr_t, unsigned int); @@ -45,27 +54,90 @@ int kdp_vm_write( caddr_t, caddr_t, unsigned int); #endif static kdp_dispatch_t - dispatch_table[KDP_TERMINATION - KDP_CONNECT + 1] = + dispatch_table[KDP_HOSTREBOOT - KDP_CONNECT +1] = { /* 0 */ kdp_connect, /* 1 */ kdp_disconnect, /* 2 */ kdp_hostinfo, -/* 3 */ kdp_regions, +/* 3 */ kdp_version, /* 4 */ kdp_maxbytes, /* 5 */ kdp_readmem, /* 6 */ kdp_writemem, /* 7 */ kdp_readregs, /* 8 */ kdp_writeregs, -/* 9 */ kdp_unknown, -/* A */ kdp_unknown, +/* 9 */ kdp_unknown, +/* A */ kdp_unknown, /* B */ kdp_suspend, /* C */ kdp_resumecpus, /* D */ kdp_unknown, -/* E */ kdp_unknown, +/* E */ kdp_unknown, +/* F */ kdp_breakpoint_set, +/*10 */ kdp_breakpoint_remove, +/*11 */ kdp_regions, +/*12 */ kdp_reattach, +/*13 */ kdp_reboot }; kdp_glob_t kdp; -int kdp_flag=0; + + +#define MAX_BREAKPOINTS 100 +#define KDP_MAX_BREAKPOINTS 100 + +#define BREAKPOINT_NOT_FOUND 101 +#define BREAKPOINT_ALREADY_SET 102 + +#define KDP_VERSION 10 + +typedef struct{ + unsigned int address; + unsigned int old_instruction; +} kdp_breakpoint_record_t; + +static kdp_breakpoint_record_t breakpoint_list[MAX_BREAKPOINTS]; +static unsigned int breakpoints_initialized = 0; + +int reattach_wait = 0; +int noresume_on_disconnect = 0; + +#define MAXCOMLEN 16 + +struct thread_snapshot { + uint32_t snapshot_magic; + thread_t thread_id; + int32_t state; + wait_queue_t wait_queue; + event64_t wait_event; + vm_offset_t kernel_stack; + vm_offset_t reserved_stack; + thread_continue_t continuation; + uint32_t nkern_frames; + char user64_p; + uint32_t nuser_frames; + int32_t pid; + char p_comm[MAXCOMLEN + 1]; +}; + +typedef struct thread_snapshot *thread_snapshot_t; + +extern int +machine_trace_thread(thread_t thread, uint32_t tracepos, uint32_t tracebound, int nframes, boolean_t user_p); +extern int +machine_trace_thread64(thread_t thread, uint32_t tracepos, uint32_t tracebound, int nframes, boolean_t user_p); +extern int +proc_pid(void *p); +extern void +proc_name(int pid, char *buf, int size); +extern void +kdp_snapshot_postflight(void); + +static int +pid_from_task(task_t task); + +int +kdp_stackshot(int pid, uint32_t tracebuf, uint32_t tracebuf_size, unsigned trace_options, uint32_t *pbytesTraced); + +extern char version[]; boolean_t kdp_packet( @@ -99,7 +171,7 @@ kdp_packet( } req = rd->hdr.request; - if (req < KDP_CONNECT || req > KDP_TERMINATION) { + if ((req < KDP_CONNECT) || (req > KDP_HOSTREBOOT)) { printf("kdp_packet bad request %x len %d seq %x key %x\n", rd->hdr.request, rd->hdr.len, rd->hdr.seq, rd->hdr.key); @@ -196,6 +268,11 @@ kdp_disconnect( kdp.is_halted = kdp.is_conn = FALSE; kdp.exception_seq = kdp.conn_seq = 0; + if (noresume_on_disconnect == 1) { + reattach_wait = 1; + noresume_on_disconnect = 0; + } + rp->hdr.is_reply = 1; rp->hdr.len = sizeof (*rp); @@ -207,6 +284,23 @@ kdp_disconnect( return (TRUE); } +static boolean_t +kdp_reattach( + kdp_pkt_t *pkt, + int *len, + unsigned short *reply_port +) +{ + kdp_reattach_req_t *rq = &pkt->reattach_req; + kdp_disconnect_reply_t *rp = &pkt->disconnect_reply; + + kdp.is_conn = TRUE; + kdp_disconnect(pkt, len, reply_port); + *reply_port = rq->req_reply_port; + reattach_wait = 1; + return (TRUE); +} + static boolean_t kdp_hostinfo( kdp_pkt_t *pkt, @@ -225,7 +319,7 @@ kdp_hostinfo( rp->hdr.len = sizeof (*rp); kdp_machine_hostinfo(&rp->hostinfo); - + *reply_port = kdp.reply_port; *len = rp->hdr.len; @@ -277,7 +371,7 @@ kdp_resumecpus( rp->hdr.len = sizeof (*rp); dprintf(("kdp_resumecpus %x\n", rq->cpu_mask)); - + kdp.is_halted = FALSE; *reply_port = kdp.reply_port; @@ -330,7 +424,9 @@ kdp_readmem( int plen = *len; kdp_readmem_reply_t *rp = &pkt->readmem_reply; int cnt; - +#if __i386__ + void *pversion = &version; +#endif if (plen < sizeof (*rq)) return (FALSE); @@ -343,7 +439,17 @@ kdp_readmem( unsigned int n = rq->nbytes; dprintf(("kdp_readmem addr %x size %d\n", rq->address, rq->nbytes)); - +#if __i386__ + /* XXX This is a hack to facilitate the "showversion" macro + * on i386, which is used to obtain the kernel version without + * symbols - a pointer to the version string should eventually + * be pinned at a fixed address when an equivalent of the + * VECTORS segment (loaded at a fixed load address, and contains + * a table) is implemented on x86, as with PPC. + */ + if (rq->address == (void *)0x501C) + rq->address = &pversion; +#endif cnt = kdp_vm_read((caddr_t)rq->address, (caddr_t)rp->data, rq->nbytes); rp->error = KDPERR_NO_ERROR; @@ -383,6 +489,42 @@ kdp_maxbytes( return (TRUE); } +static boolean_t +kdp_version( + kdp_pkt_t *pkt, + int *len, + unsigned short *reply_port +) +{ + kdp_version_req_t *rq = &pkt->version_req; + int plen = *len; + kdp_version_reply_t *rp = &pkt->version_reply; + kdp_region_t *r; + + if (plen < sizeof (*rq)) + return (FALSE); + + rp->hdr.is_reply = 1; + rp->hdr.len = sizeof (*rp); + + dprintf(("kdp_version\n")); + + rp->version = KDP_VERSION; +#ifdef __ppc__ + if (!(kdp_flag & KDP_BP_DIS)) + rp->feature = KDP_FEATURE_BP; + else + rp->feature = 0; +#else + rp->feature = 0; +#endif + + *reply_port = kdp.reply_port; + *len = rp->hdr.len; + + return (TRUE); +} + static boolean_t kdp_regions( kdp_pkt_t *pkt, @@ -472,3 +614,221 @@ kdp_readregs( return (TRUE); } + +static boolean_t +kdp_breakpoint_set( + kdp_pkt_t *pkt, + int *len, + unsigned short *reply_port +) +{ + kdp_breakpoint_req_t *rq = &pkt->breakpoint_req; + kdp_breakpoint_reply_t *rp = &pkt->breakpoint_reply; + int plen = *len; + int cnt, i; + unsigned int old_instruction = 0; + unsigned int breakinstr = kdp_ml_get_breakinsn(); + + if(breakpoints_initialized == 0) + { + for(i=0;(i < MAX_BREAKPOINTS); breakpoint_list[i].address=0, i++); + breakpoints_initialized++; + } + if (plen < sizeof (*rq)) + return (FALSE); + cnt = kdp_vm_read((caddr_t)rq->address, (caddr_t)(&old_instruction), sizeof(int)); + + if (old_instruction==breakinstr) + { + printf("A trap was already set at that address, not setting new breakpoint\n"); + rp->error = BREAKPOINT_ALREADY_SET; + + rp->hdr.is_reply = 1; + rp->hdr.len = sizeof (*rp); + *reply_port = kdp.reply_port; + *len = rp->hdr.len; + + return (TRUE); + } + + for(i=0;(i < MAX_BREAKPOINTS) && (breakpoint_list[i].address != 0); i++); + + if (i == MAX_BREAKPOINTS) + { + rp->error = KDP_MAX_BREAKPOINTS; + + rp->hdr.is_reply = 1; + rp->hdr.len = sizeof (*rp); + *reply_port = kdp.reply_port; + *len = rp->hdr.len; + + return (TRUE); + } + breakpoint_list[i].address = rq->address; + breakpoint_list[i].old_instruction = old_instruction; + + cnt = kdp_vm_write((caddr_t)&breakinstr, (caddr_t)rq->address, sizeof(&breakinstr)); + + rp->error = KDPERR_NO_ERROR; + rp->hdr.is_reply = 1; + rp->hdr.len = sizeof (*rp); + *reply_port = kdp.reply_port; + *len = rp->hdr.len; + + return (TRUE); +} + +static boolean_t +kdp_breakpoint_remove( + kdp_pkt_t *pkt, + int *len, + unsigned short *reply_port +) +{ + kdp_breakpoint_req_t *rq = &pkt->breakpoint_req; + kdp_breakpoint_reply_t *rp = &pkt->breakpoint_reply; + int plen = *len; + int cnt,i; + + if (plen < sizeof (*rq)) + return (FALSE); + + for(i=0;(i < MAX_BREAKPOINTS) && (breakpoint_list[i].address != rq->address); i++); + if (i == MAX_BREAKPOINTS) + { + rp->error = BREAKPOINT_NOT_FOUND; + rp->hdr.is_reply = 1; + rp->hdr.len = sizeof (*rp); + *reply_port = kdp.reply_port; + *len = rp->hdr.len; + + return (TRUE); /* Check if it needs to be FALSE in case of error */ + } + + breakpoint_list[i].address = 0; + cnt = kdp_vm_write((caddr_t)&(breakpoint_list[i].old_instruction), (caddr_t)rq->address, sizeof(int)); + rp->error = KDPERR_NO_ERROR; + rp->hdr.is_reply = 1; + rp->hdr.len = sizeof (*rp); + *reply_port = kdp.reply_port; + *len = rp->hdr.len; + + return (TRUE); +} + +boolean_t +kdp_remove_all_breakpoints() +{ + int i; + boolean_t breakpoint_found = FALSE; + + if (breakpoints_initialized) + { + for(i=0;i < MAX_BREAKPOINTS; i++) + { + if (breakpoint_list[i].address) + { + kdp_vm_write((caddr_t)&(breakpoint_list[i].old_instruction), (caddr_t)breakpoint_list[i].address, sizeof(int)); + breakpoint_found = TRUE; + breakpoint_list[i].address = 0; + } + } + if (breakpoint_found) + printf("kdp_remove_all_breakpoints: found extant breakpoints, removing them.\n"); + } + return breakpoint_found; +} + + +#define MAX_FRAMES 1000 + +static int pid_from_task(task_t task) +{ + int pid = -1; + + if (task->bsd_info) + pid = proc_pid(task->bsd_info); + + return pid; +} + +int +kdp_stackshot(int pid, uint32_t tracebuf, uint32_t tracebuf_size, unsigned trace_options, uint32_t *pbytesTraced) +{ + uint32_t tracepos = (uint32_t) tracebuf; + uint32_t tracebound = tracepos + tracebuf_size; + uint32_t tracebytes = 0; + int error = 0; + + processor_set_t pset = &default_pset; + task_t task = TASK_NULL; + thread_t thread = THREAD_NULL; + int nframes = trace_options; + thread_snapshot_t tsnap = NULL; + unsigned framesize = 2 * sizeof(vm_offset_t); + + if ((nframes <= 0) || nframes > MAX_FRAMES) + nframes = MAX_FRAMES; + + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + /* Trace everything, unless a process was specified */ + if ((pid == -1) || (pid == pid_from_task(task))) + queue_iterate(&task->threads, thread, thread_t, task_threads){ + if ((tracepos + 4 * sizeof(struct thread_snapshot)) > tracebound) { + error = -1; + goto error_exit; + } +/* Populate the thread snapshot header */ + tsnap = (thread_snapshot_t) tracepos; + tsnap->thread_id = thread; + tsnap->state = thread->state; + tsnap->wait_queue = thread->wait_queue; + tsnap->wait_event = thread->wait_event; + tsnap->kernel_stack = thread->kernel_stack; + tsnap->reserved_stack = thread->reserved_stack; + tsnap->continuation = thread->continuation; +/* Add the BSD process identifiers */ + if ((tsnap->pid = pid_from_task(task)) != -1) + proc_name(tsnap->pid, tsnap->p_comm, MAXCOMLEN + 1); + else + tsnap->p_comm[0] = '\0'; + + tsnap->snapshot_magic = 0xfeedface; + tracepos += sizeof(struct thread_snapshot); + +/* Call through to the machine specific trace routines + * Frames are added past the snapshot header. + */ + if (tsnap->kernel_stack != 0) + tracebytes = machine_trace_thread(thread, tracepos, tracebound, nframes, FALSE); + tsnap->nkern_frames = tracebytes/(2 * sizeof(vm_offset_t)); + tracepos += tracebytes; + tracebytes = 0; + tsnap->user64_p = 0; +/* Trace user stack, if any */ + if (thread->task->map != kernel_map) { +/* 64-bit task? */ + if (task_has_64BitAddr(thread->task)) { + tracebytes = machine_trace_thread64(thread, tracepos, tracebound, nframes, TRUE); + tsnap->user64_p = 1; + framesize = 2 * sizeof(addr64_t); + } + else { + tracebytes = machine_trace_thread(thread, tracepos, tracebound, nframes, TRUE); + framesize = 2 * sizeof(vm_offset_t); + } + } + tsnap->nuser_frames = tracebytes/framesize; + tracepos += tracebytes; + tracebytes = 0; + } + } + +error_exit: + /* Release stack snapshot wait indicator */ + kdp_snapshot_postflight(); + + *pbytesTraced = tracepos - tracebuf; + + return error; +}