X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6601e61aa18bf4f09af135ff61fc7f4771d23b06..4d15aeb193b2c68f1d38666c317f8d3734f5f083:/osfmk/kdp/kdp_udp.c diff --git a/osfmk/kdp/kdp_udp.c b/osfmk/kdp/kdp_udp.c index 903054c96..2487c6071 100644 --- a/osfmk/kdp/kdp_udp.c +++ b/osfmk/kdp/kdp_udp.c @@ -1,24 +1,31 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2016 Apple 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@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ + /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. @@ -28,27 +35,58 @@ * Kernel Debugging Protocol UDP implementation. */ -#include #include #include #include #include #include +#include +#include #include #include +#include #include - #include +#if CONFIG_SERIAL_KDP +#include +#endif #include #include +#include /* kernel_map */ #include +#include + +#include + +/* we just want the link status flags, so undef KERNEL_PRIVATE for this + * header file. */ +#undef KERNEL_PRIVATE +#include +#define KERNEL_PRIVATE #include -#define DO_ALIGN 1 /* align all packet data accesses */ +#include +#include + +#include + +extern unsigned int not_in_kdp; +extern int kdp_snapshot; +extern void do_stackshot(void); + +#ifdef CONFIG_KDP_INTERACTIVE_DEBUGGING + +extern int inet_aton(const char *, struct kdp_in_addr *); /* in libkern */ +extern char *inet_ntoa_r(struct kdp_in_addr ina, char *buf, + size_t buflen); /* in libkern */ + +#define DO_ALIGN 1 /* align all packet data accesses */ +#define KDP_SERIAL_IPADDR 0xABADBABE /* IP address used for serial KDP */ +#define LINK_UP_STATUS (IFM_AVALID | IFM_ACTIVE) extern int kdp_getc(void); extern int reattach_wait; @@ -62,119 +100,372 @@ static u_short ip_id; /* ip packet ctr, for ids */ * Per RFC 768, August, 1980. */ #define UDP_TTL 60 /* deflt time to live for UDP packets */ -int udp_ttl = UDP_TTL; +static int udp_ttl = UDP_TTL; static unsigned char exception_seq; +struct kdp_ipovly { + uint32_t ih_next, ih_prev; /* for protocol sequence q's */ + u_char ih_x1; /* (unused) */ + u_char ih_pr; /* protocol */ + short ih_len; /* protocol length */ + struct kdp_in_addr ih_src; /* source internet address */ + struct kdp_in_addr ih_dst; /* destination internet address */ +}; + +struct kdp_udphdr { + u_short uh_sport; /* source port */ + u_short uh_dport; /* destination port */ + short uh_ulen; /* udp length */ + u_short uh_sum; /* udp checksum */ +}; + +struct kdp_udpiphdr { + struct kdp_ipovly ui_i; /* overlaid ip structure */ + struct kdp_udphdr ui_u; /* udp header */ +}; +#define ui_next ui_i.ih_next +#define ui_prev ui_i.ih_prev +#define ui_x1 ui_i.ih_x1 +#define ui_pr ui_i.ih_pr +#define ui_len ui_i.ih_len +#define ui_src ui_i.ih_src +#define ui_dst ui_i.ih_dst +#define ui_sport ui_u.uh_sport +#define ui_dport ui_u.uh_dport +#define ui_ulen ui_u.uh_ulen +#define ui_sum ui_u.uh_sum + +struct kdp_ip { + union { + uint32_t ip_w; + struct { + unsigned int +#ifdef __LITTLE_ENDIAN__ + ip_xhl:4, /* header length */ + ip_xv:4, /* version */ + ip_xtos:8, /* type of service */ + ip_xlen:16; /* total length */ +#endif +#ifdef __BIG_ENDIAN__ + ip_xv:4, /* version */ + ip_xhl:4, /* header length */ + ip_xtos:8, /* type of service */ + ip_xlen:16; /* total length */ +#endif + } ip_x; + } ip_vhltl; + u_short ip_id; /* identification */ + short ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct kdp_in_addr ip_src,ip_dst; /* source and dest address */ +}; +#define ip_v ip_vhltl.ip_x.ip_xv +#define ip_hl ip_vhltl.ip_x.ip_xhl +#define ip_tos ip_vhltl.ip_x.ip_xtos +#define ip_len ip_vhltl.ip_x.ip_xlen + +#define IPPROTO_UDP 17 +#define IPVERSION 4 + +#define ETHERTYPE_IP 0x0800 /* IP protocol */ + +/* + * Ethernet Address Resolution Protocol. + * + * See RFC 826 for protocol description. Structure below is adapted + * to resolving internet addresses. Field names used correspond to + * RFC 826. + */ + +#define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */ + +struct kdp_arphdr { + u_short ar_hrd; /* format of hardware address */ +#define ARPHRD_ETHER 1 /* ethernet hardware format */ +#define ARPHRD_FRELAY 15 /* frame relay hardware format */ + u_short ar_pro; /* format of protocol address */ + u_char ar_hln; /* length of hardware address */ + u_char ar_pln; /* length of protocol address */ + u_short ar_op; /* one of: */ +#define ARPOP_REQUEST 1 /* request to resolve address */ +#define ARPOP_REPLY 2 /* response to previous request */ +#define ARPOP_REVREQUEST 3 /* request protocol address given hardware */ +#define ARPOP_REVREPLY 4 /* response giving protocol address */ +#define ARPOP_INVREQUEST 8 /* request to identify peer */ +#define ARPOP_INVREPLY 9 /* response identifying peer */ +}; + +struct kdp_ether_arp { + struct kdp_arphdr ea_hdr; /* fixed-size header */ + u_char arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ + u_char arp_spa[4]; /* sender protocol address */ + u_char arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ + u_char arp_tpa[4]; /* target protocol address */ +}; +#define arp_hrd ea_hdr.ar_hrd +#define arp_pro ea_hdr.ar_pro +#define arp_hln ea_hdr.ar_hln +#define arp_pln ea_hdr.ar_pln +#define arp_op ea_hdr.ar_op + +#define ETHERMTU 1500 +#define ETHERHDRSIZE 14 +#define ETHERCRC 4 +#define KDP_MAXPACKET (ETHERHDRSIZE + ETHERMTU + ETHERCRC) + static struct { - unsigned char data[KDP_MAXPACKET]; - unsigned int off, len; - boolean_t input; + unsigned char data[KDP_MAXPACKET]; + unsigned int off, len; + boolean_t input; } pkt, saved_reply; +struct kdp_manual_pkt manual_pkt; + struct { - struct { - struct in_addr in; - struct ether_addr ea; - } loc; - struct { - struct in_addr in; - struct ether_addr ea; - } rmt; + struct { + struct kdp_in_addr in; + struct kdp_ether_addr ea; + } loc; + struct { + struct kdp_in_addr in; + struct kdp_ether_addr ea; + } rmt; } adr; -static char +static const char *exception_message[] = { - "Unknown", - "Memory access", /* EXC_BAD_ACCESS */ - "Failed instruction", /* EXC_BAD_INSTRUCTION */ - "Arithmetic", /* EXC_ARITHMETIC */ - "Emulation", /* EXC_EMULATION */ - "Software", /* EXC_SOFTWARE */ - "Breakpoint" /* EXC_BREAKPOINT */ + "Unknown", + "Memory access", /* EXC_BAD_ACCESS */ + "Failed instruction", /* EXC_BAD_INSTRUCTION */ + "Arithmetic", /* EXC_ARITHMETIC */ + "Emulation", /* EXC_EMULATION */ + "Software", /* EXC_SOFTWARE */ + "Breakpoint" /* EXC_BREAKPOINT */ }; -int kdp_flag = 0; +volatile int kdp_flag = 0; +boolean_t kdp_corezip_disabled = 0; -static kdp_send_t kdp_en_send_pkt = 0; -static kdp_receive_t kdp_en_recv_pkt = 0; +kdp_send_t kdp_en_send_pkt; +static kdp_receive_t kdp_en_recv_pkt; +static kdp_link_t kdp_en_linkstatus; +static kdp_mode_t kdp_en_setmode; +#if CONFIG_SERIAL_KDP +static void kdp_serial_send(void *rpkt, unsigned int rpkt_len); +#define KDP_SERIAL_ENABLED() (kdp_en_send_pkt == kdp_serial_send) +#else +#define KDP_SERIAL_ENABLED() (0) +#endif -static u_long kdp_current_ip_address = 0; -static struct ether_addr kdp_current_mac_address = {{0, 0, 0, 0, 0, 0}}; -static void *kdp_current_ifp = 0; +static uint32_t kdp_current_ip_address = 0; +static struct kdp_ether_addr kdp_current_mac_address = {{0, 0, 0, 0, 0, 0}}; +static void *kdp_current_ifp; static void kdp_handler( void *); -static unsigned int panic_server_ip = 0; -static unsigned int parsed_router_ip = 0; -static unsigned int router_ip = 0; -static unsigned int panicd_specified = 0; -static unsigned int router_specified = 0; +static uint32_t panic_server_ip = 0; +static uint32_t parsed_router_ip = 0; +static uint32_t router_ip = 0; +static uint32_t target_ip = 0; + +static boolean_t save_ip_in_nvram = FALSE; + +static volatile boolean_t panicd_specified = FALSE; +static boolean_t router_specified = FALSE; +static boolean_t corename_specified = FALSE; +static unsigned int panicd_port = CORE_REMOTE_PORT; + +static struct kdp_ether_addr etherbroadcastaddr = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; + +static struct kdp_ether_addr router_mac = {{0, 0, 0 , 0, 0, 0}}; +static struct kdp_ether_addr destination_mac = {{0, 0, 0 , 0, 0, 0}}; +static struct kdp_ether_addr temp_mac = {{0, 0, 0 , 0, 0, 0}}; +static struct kdp_ether_addr current_resolved_MAC = {{0, 0, 0 , 0, 0, 0}}; -static struct ether_addr router_mac = {{0, 0, 0 , 0, 0, 0}}; +static boolean_t flag_panic_dump_in_progress = FALSE; +static boolean_t flag_router_mac_initialized = FALSE; +static boolean_t flag_dont_abort_panic_dump = FALSE; -static u_char flag_panic_dump_in_progress = 0; -static u_char flag_router_mac_initialized = 0; +static boolean_t flag_arp_resolved = FALSE; static unsigned int panic_timeout = 100000; static unsigned int last_panic_port = CORE_REMOTE_PORT; -unsigned int SEGSIZE = 512; +#define KDP_THROTTLE_VALUE (10ULL * NSEC_PER_SEC) -static unsigned int PANIC_PKTSIZE = 518; +uint32_t kdp_crashdump_pkt_size = 512; +#define KDP_LARGE_CRASHDUMP_PKT_SIZE (1440 - 6 - sizeof(struct kdp_udpiphdr)) static char panicd_ip_str[20]; static char router_ip_str[20]; +static char corename_str[100]; static unsigned int panic_block = 0; -static volatile unsigned int kdp_trigger_core_dump = 0; -static volatile unsigned int flag_kdp_trigger_reboot = 0; +volatile unsigned int kdp_trigger_core_dump = 0; +__private_extern__ volatile unsigned int flag_kdp_trigger_reboot = 0; -extern unsigned int not_in_kdp; -extern int kdp_vm_read( caddr_t, caddr_t, unsigned int); +extern unsigned int disableConsoleOutput; + +extern void kdp_call(void); +extern boolean_t kdp_call_kdb(void); + +void * kdp_get_interface(void); +void kdp_set_gateway_mac(void *gatewaymac); +void kdp_set_ip_and_mac_addresses(struct kdp_in_addr *ipaddr, struct kdp_ether_addr *); +void kdp_set_interface(void *interface, const struct kdp_ether_addr *macaddr); + +void kdp_disable_arp(void); +static void kdp_arp_reply(struct kdp_ether_arp *); +static void kdp_process_arp_reply(struct kdp_ether_arp *); +static boolean_t kdp_arp_resolve(uint32_t, struct kdp_ether_addr *); + +static volatile unsigned kdp_reentry_deadline; + +static uint32_t kdp_crashdump_feature_mask = KDP_FEATURE_LARGE_CRASHDUMPS | KDP_FEATURE_LARGE_PKT_SIZE; +uint32_t kdp_feature_large_crashdumps, kdp_feature_large_pkt_size; + +char kdp_kernelversion_string[256]; + +static boolean_t gKDPDebug = FALSE; + +#if WITH_CONSISTENT_DBG +#include +extern volatile struct xnu_hw_shmem_dbg_command_info *hwsd_info; +#endif + +#define KDP_DEBUG(...) if (gKDPDebug) printf(__VA_ARGS__); + +#define SBLOCKSZ (2048) +uint64_t kdp_dump_start_time = 0; +uint64_t kdp_min_superblock_dump_time = ~1ULL; +uint64_t kdp_max_superblock_dump_time = 0; +uint64_t kdp_superblock_dump_time = 0; +uint64_t kdp_superblock_dump_start_time = 0; +static thread_call_t +kdp_timer_call; + +static void +kdp_ml_enter_debugger_wrapper(__unused void *param0, __unused void *param1) { + kdp_ml_enter_debugger(); +} + +static void +kdp_timer_callout_init(void) { + kdp_timer_call = thread_call_allocate(kdp_ml_enter_debugger_wrapper, NULL); +} + + +/* only send/receive data if the link is up */ +inline static void +wait_for_link(void) +{ + static int first = 0; + + if (!kdp_en_linkstatus) + return; + + while (((*kdp_en_linkstatus)() & LINK_UP_STATUS) != LINK_UP_STATUS) { + if (first) + continue; + + first = 1; + printf("Waiting for link to become available.\n"); + kprintf("Waiting for link to become available.\n"); + } +} + + +inline static void +kdp_send_data(void *packet, unsigned int len) +{ + wait_for_link(); + (*kdp_en_send_pkt)(packet, len); +} + + +inline static void +kdp_receive_data(void *packet, unsigned int *len, unsigned int timeout) +{ + wait_for_link(); + (*kdp_en_recv_pkt)(packet, len, timeout); +} + + +void +kdp_register_link(kdp_link_t link, kdp_mode_t mode) +{ + kdp_en_linkstatus = link; + kdp_en_setmode = mode; +} + +void +kdp_unregister_link(__unused kdp_link_t link, __unused kdp_mode_t mode) +{ + kdp_en_linkstatus = NULL; + kdp_en_setmode = NULL; +} void kdp_register_send_receive( kdp_send_t send, kdp_receive_t receive) { - unsigned int debug=0; + unsigned int debug = 0; - kdp_en_send_pkt = send; - kdp_en_recv_pkt = receive; + PE_parse_boot_argn("debug", &debug, sizeof (debug)); - debug_log_init(); - PE_parse_boot_arg("debug", &debug); + if (!debug) + return; + + kdp_en_send_pkt = send; + kdp_en_recv_pkt = receive; if (debug & DB_KDP_BP_DIS) kdp_flag |= KDP_BP_DIS; if (debug & DB_KDP_GETC_ENA) kdp_flag |= KDP_GETC_ENA; if (debug & DB_ARP) - kdp_flag |= KDP_ARP; + kdp_flag |= KDP_ARP; if (debug & DB_KERN_DUMP_ON_PANIC) - kdp_flag |= KDP_PANIC_DUMP_ENABLED; + kdp_flag |= KDP_PANIC_DUMP_ENABLED; if (debug & DB_KERN_DUMP_ON_NMI) - kdp_flag |= PANIC_CORE_ON_NMI; - + kdp_flag |= PANIC_CORE_ON_NMI; + if (debug & DB_DBG_POST_CORE) - kdp_flag |= DBG_POST_CORE; - + kdp_flag |= DBG_POST_CORE; + if (debug & DB_PANICLOG_DUMP) - kdp_flag |= PANIC_LOG_DUMP; - - if (PE_parse_boot_arg ("_panicd_ip", panicd_ip_str)) - panicd_specified = 1; - /* For the future, currently non-functional */ - if (PE_parse_boot_arg ("_router_ip", router_ip_str)) - router_specified = 1; + kdp_flag |= PANIC_LOG_DUMP; + + kdp_corezip_disabled = (0 != (debug & DB_DISABLE_GZIP_CORE)); + + if (PE_parse_boot_argn("_panicd_ip", panicd_ip_str, sizeof (panicd_ip_str))) + panicd_specified = TRUE; + + if ((debug & DB_REBOOT_POST_CORE) && (panicd_specified == TRUE)) + kdp_flag |= REBOOT_POST_CORE; + + if (PE_parse_boot_argn("_router_ip", router_ip_str, sizeof (router_ip_str))) + router_specified = TRUE; + + if (!PE_parse_boot_argn("panicd_port", &panicd_port, sizeof (panicd_port))) + panicd_port = CORE_REMOTE_PORT; + + if (PE_parse_boot_argn("_panicd_corename", &corename_str, sizeof (corename_str))) + corename_specified = TRUE; kdp_flag |= KDP_READY; - if (current_debugger == NO_CUR_DB) - current_debugger = KDP_CUR_DB; - if (halt_in_debugger) { + + current_debugger = KDP_CUR_DB; + if ((kdp_current_ip_address != 0) && halt_in_debugger) { kdp_call(); halt_in_debugger=0; } @@ -182,14 +473,22 @@ kdp_register_send_receive( void kdp_unregister_send_receive( - kdp_send_t send, - kdp_receive_t receive) + __unused kdp_send_t send, + __unused kdp_receive_t receive) { if (current_debugger == KDP_CUR_DB) current_debugger = NO_CUR_DB; kdp_flag &= ~KDP_READY; - kdp_en_send_pkt = NULL; - kdp_en_recv_pkt = NULL; + kdp_en_send_pkt = NULL; + kdp_en_recv_pkt = NULL; +} + +static void +kdp_schedule_debugger_reentry(unsigned interval) { + uint64_t deadline;; + + clock_interval_to_deadline(interval, 1000 * 1000, &deadline); + thread_call_enter_delayed(kdp_timer_call, deadline); } static void @@ -198,99 +497,103 @@ enaddr_copy( void *dst ) { - bcopy((char *)src, (char *)dst, sizeof (struct ether_addr)); + bcopy((char *)src, (char *)dst, sizeof (struct kdp_ether_addr)); } static unsigned short ip_sum( unsigned char *c, unsigned int hlen -) + ) { - unsigned int high, low, sum; + unsigned int high, low, sum; - high = low = 0; - while (hlen-- > 0) { - low += c[1] + c[3]; - high += c[0] + c[2]; + high = low = 0; + while (hlen-- > 0) { + low += c[1] + c[3]; + high += c[0] + c[2]; - c += sizeof (int); - } + c += sizeof (int); + } - sum = (high << 8) + low; - sum = (sum >> 16) + (sum & 65535); + sum = (high << 8) + low; + sum = (sum >> 16) + (sum & 65535); - return (sum > 65535 ? sum - 65535 : sum); + return (sum > 65535 ? sum - 65535 : sum); } static void kdp_reply( - unsigned short reply_port -) + unsigned short reply_port, + const boolean_t sideband + ) { - struct udpiphdr aligned_ui, *ui = &aligned_ui; - struct ip aligned_ip, *ip = &aligned_ip; - struct in_addr tmp_ipaddr; - struct ether_addr tmp_enaddr; - struct ether_header *eh; + struct kdp_udpiphdr aligned_ui, *ui = &aligned_ui; + struct kdp_ip aligned_ip, *ip = &aligned_ip; + struct kdp_in_addr tmp_ipaddr; + struct kdp_ether_addr tmp_enaddr; + struct kdp_ether_header *eh = NULL; - if (!pkt.input) - kdp_panic("kdp_reply"); + if (!pkt.input) + kdp_panic("kdp_reply"); - pkt.off -= sizeof (struct udpiphdr); + pkt.off -= (unsigned int)sizeof (struct kdp_udpiphdr); #if DO_ALIGN - bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); + bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); #else - ui = (struct udpiphdr *)&pkt.data[pkt.off]; + ui = (struct kdp_udpiphdr *)&pkt.data[pkt.off]; #endif - ui->ui_next = ui->ui_prev = 0; - ui->ui_x1 = 0; - ui->ui_pr = IPPROTO_UDP; - ui->ui_len = htons((u_short)pkt.len + sizeof (struct udphdr)); - tmp_ipaddr = ui->ui_src; - ui->ui_src = ui->ui_dst; - ui->ui_dst = tmp_ipaddr; - ui->ui_sport = htons(KDP_REMOTE_PORT); - ui->ui_dport = reply_port; - ui->ui_ulen = ui->ui_len; - ui->ui_sum = 0; + ui->ui_next = ui->ui_prev = 0; + ui->ui_x1 = 0; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = htons((u_short)pkt.len + sizeof (struct kdp_udphdr)); + tmp_ipaddr = ui->ui_src; + ui->ui_src = ui->ui_dst; + ui->ui_dst = tmp_ipaddr; + ui->ui_sport = htons(KDP_REMOTE_PORT); + ui->ui_dport = reply_port; + ui->ui_ulen = ui->ui_len; + ui->ui_sum = 0; #if DO_ALIGN - bcopy((char *)ui, (char *)&pkt.data[pkt.off], sizeof(*ui)); - bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); + bcopy((char *)ui, (char *)&pkt.data[pkt.off], sizeof(*ui)); + bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); #else - ip = (struct ip *)&pkt.data[pkt.off]; + ip = (struct kdp_ip *)&pkt.data[pkt.off]; #endif - ip->ip_len = htons(sizeof (struct udpiphdr) + pkt.len); - ip->ip_v = IPVERSION; - ip->ip_id = htons(ip_id++); - ip->ip_hl = sizeof (struct ip) >> 2; - ip->ip_ttl = udp_ttl; - ip->ip_sum = 0; - ip->ip_sum = htons(~ip_sum((unsigned char *)ip, ip->ip_hl)); + ip->ip_len = htons(sizeof (struct kdp_udpiphdr) + pkt.len); + ip->ip_v = IPVERSION; + ip->ip_id = htons(ip_id++); + ip->ip_hl = sizeof (struct kdp_ip) >> 2; + ip->ip_ttl = udp_ttl; + ip->ip_sum = 0; + ip->ip_sum = htons(~ip_sum((unsigned char *)ip, ip->ip_hl)); #if DO_ALIGN - bcopy((char *)ip, (char *)&pkt.data[pkt.off], sizeof(*ip)); + bcopy((char *)ip, (char *)&pkt.data[pkt.off], sizeof(*ip)); #endif - pkt.len += sizeof (struct udpiphdr); + pkt.len += (unsigned int)sizeof (struct kdp_udpiphdr); - pkt.off -= sizeof (struct ether_header); + pkt.off -= (unsigned int)sizeof (struct kdp_ether_header); - eh = (struct ether_header *)&pkt.data[pkt.off]; - enaddr_copy(eh->ether_shost, &tmp_enaddr); - enaddr_copy(eh->ether_dhost, eh->ether_shost); - enaddr_copy(&tmp_enaddr, eh->ether_dhost); - eh->ether_type = htons(ETHERTYPE_IP); + eh = (struct kdp_ether_header *)&pkt.data[pkt.off]; + enaddr_copy(eh->ether_shost, &tmp_enaddr); + enaddr_copy(eh->ether_dhost, eh->ether_shost); + enaddr_copy(&tmp_enaddr, eh->ether_dhost); + eh->ether_type = htons(ETHERTYPE_IP); - pkt.len += sizeof (struct ether_header); + pkt.len += (unsigned int)sizeof (struct kdp_ether_header); - // save reply for possible retransmission - bcopy((char *)&pkt, (char *)&saved_reply, sizeof(pkt)); + // save reply for possible retransmission + assert(pkt.len <= KDP_MAXPACKET); + if (!sideband) + bcopy((char *)&pkt, (char *)&saved_reply, sizeof(saved_reply)); - (*kdp_en_send_pkt)(&pkt.data[pkt.off], pkt.len); + kdp_send_data(&pkt.data[pkt.off], pkt.len); - // increment expected sequence number - exception_seq++; + // increment expected sequence number + if (!sideband) + exception_seq++; } static void @@ -298,133 +601,265 @@ kdp_send( unsigned short remote_port ) { - struct udpiphdr aligned_ui, *ui = &aligned_ui; - struct ip aligned_ip, *ip = &aligned_ip; - struct ether_header *eh; - - if (pkt.input) - kdp_panic("kdp_send"); + struct kdp_udpiphdr aligned_ui, *ui = &aligned_ui; + struct kdp_ip aligned_ip, *ip = &aligned_ip; + struct kdp_ether_header *eh; - pkt.off -= sizeof (struct udpiphdr); + if (pkt.input) + kdp_panic("kdp_send"); + + pkt.off -= (unsigned int)sizeof (struct kdp_udpiphdr); #if DO_ALIGN - bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); + bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); #else - ui = (struct udpiphdr *)&pkt.data[pkt.off]; + ui = (struct kdp_udpiphdr *)&pkt.data[pkt.off]; #endif - ui->ui_next = ui->ui_prev = 0; - ui->ui_x1 = 0; - ui->ui_pr = IPPROTO_UDP; - ui->ui_len = htons((u_short)pkt.len + sizeof (struct udphdr)); - ui->ui_src = adr.loc.in; - ui->ui_dst = adr.rmt.in; - ui->ui_sport = htons(KDP_REMOTE_PORT); - ui->ui_dport = remote_port; - ui->ui_ulen = ui->ui_len; - ui->ui_sum = 0; + ui->ui_next = ui->ui_prev = 0; + ui->ui_x1 = 0; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = htons((u_short)pkt.len + sizeof (struct kdp_udphdr)); + ui->ui_src = adr.loc.in; + ui->ui_dst = adr.rmt.in; + ui->ui_sport = htons(KDP_REMOTE_PORT); + ui->ui_dport = remote_port; + ui->ui_ulen = ui->ui_len; + ui->ui_sum = 0; #if DO_ALIGN - bcopy((char *)ui, (char *)&pkt.data[pkt.off], sizeof(*ui)); - bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); + bcopy((char *)ui, (char *)&pkt.data[pkt.off], sizeof(*ui)); + bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); #else - ip = (struct ip *)&pkt.data[pkt.off]; + ip = (struct kdp_ip *)&pkt.data[pkt.off]; #endif - ip->ip_len = htons(sizeof (struct udpiphdr) + pkt.len); - ip->ip_v = IPVERSION; - ip->ip_id = htons(ip_id++); - ip->ip_hl = sizeof (struct ip) >> 2; - ip->ip_ttl = udp_ttl; - ip->ip_sum = 0; - ip->ip_sum = htons(~ip_sum((unsigned char *)ip, ip->ip_hl)); + ip->ip_len = htons(sizeof (struct kdp_udpiphdr) + pkt.len); + ip->ip_v = IPVERSION; + ip->ip_id = htons(ip_id++); + ip->ip_hl = sizeof (struct kdp_ip) >> 2; + ip->ip_ttl = udp_ttl; + ip->ip_sum = 0; + ip->ip_sum = htons(~ip_sum((unsigned char *)ip, ip->ip_hl)); #if DO_ALIGN - bcopy((char *)ip, (char *)&pkt.data[pkt.off], sizeof(*ip)); + bcopy((char *)ip, (char *)&pkt.data[pkt.off], sizeof(*ip)); #endif - - pkt.len += sizeof (struct udpiphdr); - - pkt.off -= sizeof (struct ether_header); - - eh = (struct ether_header *)&pkt.data[pkt.off]; - enaddr_copy(&adr.loc.ea, eh->ether_shost); - enaddr_copy(&adr.rmt.ea, eh->ether_dhost); - eh->ether_type = htons(ETHERTYPE_IP); - - pkt.len += sizeof (struct ether_header); - (*kdp_en_send_pkt)(&pkt.data[pkt.off], pkt.len); + + pkt.len += (unsigned int)sizeof (struct kdp_udpiphdr); + + pkt.off -= (unsigned int)sizeof (struct kdp_ether_header); + + eh = (struct kdp_ether_header *)&pkt.data[pkt.off]; + enaddr_copy(&adr.loc.ea, eh->ether_shost); + enaddr_copy(&adr.rmt.ea, eh->ether_dhost); + eh->ether_type = htons(ETHERTYPE_IP); + + pkt.len += (unsigned int)sizeof (struct kdp_ether_header); + kdp_send_data(&pkt.data[pkt.off], pkt.len); } -/* We don't interpret this pointer, we just give it to the -bsd stack so it can decide when to set the MAC and IP info. */ + +inline static void +debugger_if_necessary(void) +{ + if ((current_debugger == KDP_CUR_DB) && halt_in_debugger) { + kdp_call(); + halt_in_debugger=0; + } +} + + +/* We don't interpret this pointer, we just give it to the bsd stack + so it can decide when to set the MAC and IP info. We'll + early initialize the MAC/IP info if we can so that we can use + KDP early in boot. These values may subsequently get over-written + when the interface gets initialized for real. +*/ void -kdp_set_interface(void *ifp) +kdp_set_interface(void *ifp, const struct kdp_ether_addr *macaddr) { + char kdpstr[80]; + struct kdp_in_addr addr = { 0 }; + unsigned int len; + kdp_current_ifp = ifp; + + if (PE_parse_boot_argn("kdp_ip_addr", kdpstr, sizeof(kdpstr))) { + /* look for a static ip address */ + if (inet_aton(kdpstr, &addr) == FALSE) + goto done; + + goto config_network; + } + + /* use saved ip address */ + save_ip_in_nvram = TRUE; + + len = sizeof(kdpstr); + if (PEReadNVRAMProperty("_kdp_ipstr", kdpstr, &len) == FALSE) + goto done; + + kdpstr[len < sizeof(kdpstr) ? len : sizeof(kdpstr) - 1] = '\0'; + if (inet_aton(kdpstr, &addr) == FALSE) + goto done; + +config_network: + kdp_current_ip_address = addr.s_addr; + if (macaddr) + kdp_current_mac_address = *macaddr; + + /* we can't drop into the debugger at this point because the + link will likely not be up. when getDebuggerLinkStatus() support gets + added to the appropriate network drivers, adding the + following will enable this capability: + debugger_if_necessary(); + */ +done: + return; } void * -kdp_get_interface() +kdp_get_interface(void) { return kdp_current_ifp; } void kdp_set_ip_and_mac_addresses( - struct in_addr *ipaddr, - struct ether_addr *macaddr) + struct kdp_in_addr *ipaddr, + struct kdp_ether_addr *macaddr) { - kdp_current_ip_address = ipaddr->s_addr; - kdp_current_mac_address = *macaddr; + static uint64_t last_time = (uint64_t) -1; + static uint64_t throttle_val = 0; + uint64_t cur_time; + char addr[16]; + + if (kdp_current_ip_address == ipaddr->s_addr) + goto done; + + /* don't replace if serial debugging is configured */ + if (!KDP_SERIAL_ENABLED() || + (kdp_current_ip_address != KDP_SERIAL_IPADDR)) { + kdp_current_mac_address = *macaddr; + kdp_current_ip_address = ipaddr->s_addr; + } + + if (save_ip_in_nvram == FALSE) + goto done; + + if (inet_ntoa_r(*ipaddr, addr, sizeof(addr)) == NULL) + goto done; + + /* throttle writes if needed */ + if (!throttle_val) + nanoseconds_to_absolutetime(KDP_THROTTLE_VALUE, &throttle_val); + + cur_time = mach_absolute_time(); + if (last_time == (uint64_t) -1 || + ((cur_time - last_time) > throttle_val)) { + PEWriteNVRAMProperty("_kdp_ipstr", addr, + (const unsigned int) strlen(addr)); + } + last_time = cur_time; + +done: + debugger_if_necessary(); } void kdp_set_gateway_mac(void *gatewaymac) { - router_mac = *(struct ether_addr *)gatewaymac; - flag_router_mac_initialized = 1; + router_mac = *(struct kdp_ether_addr *)gatewaymac; + flag_router_mac_initialized = TRUE; } -struct ether_addr +struct kdp_ether_addr kdp_get_mac_addr(void) { - return kdp_current_mac_address; + return kdp_current_mac_address; } unsigned int kdp_get_ip_address(void) { - return kdp_current_ip_address; + return (unsigned int)kdp_current_ip_address; +} + +void +kdp_disable_arp(void) +{ + kdp_flag &= ~(DB_ARP); +} + +static void +kdp_arp_dispatch(void) +{ + struct kdp_ether_arp aligned_ea, *ea = &aligned_ea; + unsigned arp_header_offset; + + arp_header_offset = (unsigned)sizeof(struct kdp_ether_header) + pkt.off; + memcpy((void *)ea, (void *)&pkt.data[arp_header_offset], sizeof(*ea)); + + switch(ntohs(ea->arp_op)) { + case ARPOP_REQUEST: + kdp_arp_reply(ea); + break; + case ARPOP_REPLY: + kdp_process_arp_reply(ea); + break; + default: + return; + } +} + +static void +kdp_process_arp_reply(struct kdp_ether_arp *ea) +{ + /* Are we interested in ARP replies? */ + if (flag_arp_resolved == TRUE) + return; + + /* Did we receive a reply from the right source? */ + if (((struct kdp_in_addr *)(ea->arp_spa))->s_addr != target_ip) + return; + + flag_arp_resolved = TRUE; + current_resolved_MAC = *(struct kdp_ether_addr *) (ea->arp_sha); + + return; } /* ARP responses are enabled when the DB_ARP bit of the debug boot arg - is set. A workaround if you don't want to reboot is to set - kdpDEBUGFlag &= DB_ARP when connected (but that certainly isn't a published - interface!) -*/ + * is set. + */ + static void -kdp_arp_reply(void) +kdp_arp_reply(struct kdp_ether_arp *ea) { - struct ether_header *eh; - struct ether_arp aligned_ea, *ea = &aligned_ea; + struct kdp_ether_header *eh; - struct in_addr isaddr, itaddr, myaddr; - struct ether_addr my_enaddr; + struct kdp_in_addr isaddr, itaddr, myaddr; + struct kdp_ether_addr my_enaddr; - eh = (struct ether_header *)&pkt.data[pkt.off]; - pkt.off += sizeof(struct ether_header); + eh = (struct kdp_ether_header *)&pkt.data[pkt.off]; + pkt.off += (unsigned int)sizeof(struct kdp_ether_header); - memcpy((void *)ea, (void *)&pkt.data[pkt.off],sizeof(*ea)); - if(ntohs(ea->arp_op) != ARPOP_REQUEST) return; myaddr.s_addr = kdp_get_ip_address(); my_enaddr = kdp_get_mac_addr(); - if (!(myaddr.s_addr) || !(my_enaddr.ether_addr_octet[1])) + if ((ntohl(myaddr.s_addr) == 0) || + ((my_enaddr.ether_addr_octet[0] & 0xff) == 0 + && (my_enaddr.ether_addr_octet[1] & 0xff) == 0 + && (my_enaddr.ether_addr_octet[2] & 0xff) == 0 + && (my_enaddr.ether_addr_octet[3] & 0xff) == 0 + && (my_enaddr.ether_addr_octet[4] & 0xff) == 0 + && (my_enaddr.ether_addr_octet[5] & 0xff) == 0 + )) return; (void)memcpy((void *)&isaddr, (void *)ea->arp_spa, sizeof (isaddr)); (void)memcpy((void *)&itaddr, (void *)ea->arp_tpa, sizeof (itaddr)); - + if (itaddr.s_addr == myaddr.s_addr) { (void)memcpy((void *)ea->arp_tha, (void *)ea->arp_sha, sizeof(ea->arp_sha)); (void)memcpy((void *)ea->arp_sha, (void *)&my_enaddr, sizeof(ea->arp_sha)); @@ -438,200 +873,342 @@ kdp_arp_reply(void) (void)memcpy(eh->ether_shost, &my_enaddr, sizeof(eh->ether_shost)); eh->ether_type = htons(ETHERTYPE_ARP); (void)memcpy(&pkt.data[pkt.off], ea, sizeof(*ea)); - pkt.off -= sizeof (struct ether_header); + pkt.off -= (unsigned int)sizeof (struct kdp_ether_header); /* pkt.len is still the length we want, ether_header+ether_arp */ - (*kdp_en_send_pkt)(&pkt.data[pkt.off], pkt.len); + kdp_send_data(&pkt.data[pkt.off], pkt.len); } } static void kdp_poll(void) { - struct ether_header *eh; - struct udpiphdr aligned_ui, *ui = &aligned_ui; - struct ip aligned_ip, *ip = &aligned_ip; - static int msg_printed; - + struct kdp_ether_header *eh = NULL; + struct kdp_udpiphdr aligned_ui, *ui = &aligned_ui; + struct kdp_ip aligned_ip, *ip = &aligned_ip; + static int msg_printed; - if (pkt.input) - kdp_panic("kdp_poll"); + if (pkt.input) + kdp_panic("kdp_poll"); - if (!kdp_en_recv_pkt || !kdp_en_send_pkt) { - if( msg_printed == 0) { - msg_printed = 1; - printf("kdp_poll: no debugger device\n"); + if (!kdp_en_recv_pkt || !kdp_en_send_pkt) { + if( msg_printed == 0) { + msg_printed = 1; + printf("kdp_poll: no debugger device\n"); + } + return; } - return; - } - pkt.off = pkt.len = 0; - (*kdp_en_recv_pkt)(pkt.data, &pkt.len, 3/* ms */); - - if (pkt.len == 0) - return; + pkt.off = pkt.len = 0; + kdp_receive_data(pkt.data, &pkt.len, 3/* ms */); - if (pkt.len >= sizeof(struct ether_header)) - { - eh = (struct ether_header *)&pkt.data[pkt.off]; - - if (kdp_flag & KDP_ARP) - { - if (ntohs(eh->ether_type) == ETHERTYPE_ARP) - { - kdp_arp_reply(); + if (pkt.len == 0) return; - } - } - } - if (pkt.len < (sizeof (struct ether_header) + sizeof (struct udpiphdr))) - return; + if (pkt.len >= sizeof(struct kdp_ether_header)) { + eh = (struct kdp_ether_header *)&pkt.data[pkt.off]; + + if (kdp_flag & KDP_ARP) { + if (ntohs(eh->ether_type) == ETHERTYPE_ARP) { + kdp_arp_dispatch(); + return; + } + } + } - pkt.off += sizeof (struct ether_header); - if (ntohs(eh->ether_type) != ETHERTYPE_IP) { - return; - } + if (pkt.len < (sizeof (struct kdp_ether_header) + sizeof (struct kdp_udpiphdr))) + return; + + pkt.off += (unsigned int)sizeof (struct kdp_ether_header); + if (ntohs(eh->ether_type) != ETHERTYPE_IP) { + return; + } #if DO_ALIGN - bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); - bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); + bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); + bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); #else - ui = (struct udpiphdr *)&pkt.data[pkt.off]; - ip = (struct ip *)&pkt.data[pkt.off]; + ui = (struct kdp_udpiphdr *)&pkt.data[pkt.off]; + ip = (struct kdp_ip *)&pkt.data[pkt.off]; #endif - pkt.off += sizeof (struct udpiphdr); - if (ui->ui_pr != IPPROTO_UDP) { - return; - } + pkt.off += (unsigned int)sizeof (struct kdp_udpiphdr); + if (ui->ui_pr != IPPROTO_UDP) { + return; + } - if (ip->ip_hl > (sizeof (struct ip) >> 2)) { - return; - } + if (ip->ip_hl > (sizeof (struct kdp_ip) >> 2)) { + return; + } - if (ntohs(ui->ui_dport) != KDP_REMOTE_PORT) { - if (CORE_REMOTE_PORT == (ntohs(ui->ui_dport)) && - flag_panic_dump_in_progress) { - last_panic_port = ui->ui_sport; + if (ntohs(ui->ui_dport) != KDP_REMOTE_PORT) { + if (panicd_port == (ntohs(ui->ui_dport)) && + flag_panic_dump_in_progress) { + last_panic_port = ui->ui_sport; + } + else + return; } - else - return; - } - /* If we receive a kernel debugging packet whilst a - * core dump is in progress, abort the transfer and - * enter the debugger. - */ - else - if (flag_panic_dump_in_progress) - { - abort_panic_transfer(); - return; + /* If we receive a kernel debugging packet whilst a + * core dump is in progress, abort the transfer and + * enter the debugger if not told otherwise. + */ + else + if (flag_panic_dump_in_progress) { + if (!flag_dont_abort_panic_dump) { + abort_panic_transfer(); + } + return; + } + + if (!kdp.is_conn && !flag_panic_dump_in_progress) { + enaddr_copy(eh->ether_dhost, &adr.loc.ea); + adr.loc.in = ui->ui_dst; + + enaddr_copy(eh->ether_shost, &adr.rmt.ea); + adr.rmt.in = ui->ui_src; } - if (!kdp.is_conn && !flag_panic_dump_in_progress) { - enaddr_copy(eh->ether_dhost, &adr.loc.ea); - adr.loc.in = ui->ui_dst; + /* + * Calculate kdp packet length. + */ + pkt.len = ntohs((u_short)ui->ui_ulen) - (unsigned int)sizeof (struct kdp_udphdr); + pkt.input = TRUE; +} + - enaddr_copy(eh->ether_shost, &adr.rmt.ea); - adr.rmt.in = ui->ui_src; - } +/* Create and transmit an ARP resolution request for the target IP address. + * This is modeled on ether_inet_arp()/RFC 826. + */ - /* - * Calculate kdp packet length. - */ - pkt.len = ntohs((u_short)ui->ui_ulen) - sizeof (struct udphdr); - pkt.input = TRUE; +static void +transmit_ARP_request(uint32_t ip_addr) +{ + struct kdp_ether_header *eh = (struct kdp_ether_header *) &pkt.data[0]; + struct kdp_ether_arp *ea = (struct kdp_ether_arp *) &pkt.data[sizeof(struct kdp_ether_header)]; + + KDP_DEBUG("Transmitting ARP request\n"); + /* Populate the ether_header */ + eh->ether_type = htons(ETHERTYPE_ARP); + enaddr_copy(&kdp_current_mac_address, eh->ether_shost); + enaddr_copy(ðerbroadcastaddr, eh->ether_dhost); + + /* Populate the ARP header */ + ea->arp_pro = htons(ETHERTYPE_IP); + ea->arp_hln = sizeof(ea->arp_sha); + ea->arp_pln = sizeof(ea->arp_spa); + ea->arp_hrd = htons(ARPHRD_ETHER); + ea->arp_op = htons(ARPOP_REQUEST); + + /* Target fields */ + enaddr_copy(ðerbroadcastaddr, ea->arp_tha); + memcpy(ea->arp_tpa, (void *) &ip_addr, sizeof(ip_addr)); + + /* Source fields */ + enaddr_copy(&kdp_current_mac_address, ea->arp_sha); + memcpy(ea->arp_spa, (void *) &kdp_current_ip_address, sizeof(kdp_current_ip_address)); + + pkt.off = 0; + pkt.len = sizeof(struct kdp_ether_header) + sizeof(struct kdp_ether_arp); + /* Transmit */ + kdp_send_data(&pkt.data[pkt.off], pkt.len); +} + +static boolean_t +kdp_arp_resolve(uint32_t arp_target_ip, struct kdp_ether_addr *resolved_MAC) +{ + int poll_count = 256; /* ~770 ms modulo broadcast/delayed traffic? */ + char tretries = 0; + +#define NUM_ARP_TX_RETRIES 5 + + target_ip = arp_target_ip; + flag_arp_resolved = FALSE; + +TRANSMIT_RETRY: + pkt.off = pkt.len = 0; + + tretries++; + + if (tretries >= NUM_ARP_TX_RETRIES) { + return FALSE; + } + + KDP_DEBUG("ARP TX attempt #%d \n", tretries); + + transmit_ARP_request(arp_target_ip); + + while (!pkt.input && !flag_arp_resolved && flag_panic_dump_in_progress && --poll_count) { + kdp_poll(); + } + + if (flag_arp_resolved) { + *resolved_MAC = current_resolved_MAC; + return TRUE; + } + + if (!flag_panic_dump_in_progress || pkt.input) { /* we received a debugging packet, bail*/ + printf("Received a debugger packet,transferring control to debugger\n"); + /* Indicate that we should wait in the debugger when we return */ + kdp_flag |= DBG_POST_CORE; + pkt.input = FALSE; + return FALSE; + } else { /* We timed out */ + if (0 == poll_count) { + poll_count = 256; + goto TRANSMIT_RETRY; + } + } + return FALSE; } static void kdp_handler( - void *saved_state + void *saved_state ) { - unsigned short reply_port; - kdp_hdr_t aligned_hdr, *hdr = &aligned_hdr; + unsigned short reply_port; + kdp_hdr_t aligned_hdr, *hdr = &aligned_hdr; + kdp.saved_state = saved_state; // see comment in kdp_raise_exception - kdp.saved_state = saved_state; // see comment in kdp_raise_exception + do { + while (!pkt.input) + kdp_poll(); - do { - while (!pkt.input) - kdp_poll(); - #if DO_ALIGN - bcopy((char *)&pkt.data[pkt.off], (char *)hdr, sizeof(*hdr)); + bcopy((char *)&pkt.data[pkt.off], (char *)hdr, sizeof(*hdr)); #else - hdr = (kdp_hdr_t *)&pkt.data[pkt.off]; + hdr = (kdp_hdr_t *)&pkt.data[pkt.off]; #endif - // ignore replies -- we're not expecting them anyway. - if (hdr->is_reply) { - goto again; - } - - if (hdr->request == KDP_REATTACH) - exception_seq = hdr->seq; - - // check for retransmitted request - if (hdr->seq == (exception_seq - 1)) { - /* retransmit last reply */ - (*kdp_en_send_pkt)(&saved_reply.data[saved_reply.off], - saved_reply.len); - goto again; - } else if (hdr->seq != exception_seq) { - printf("kdp: bad sequence %d (want %d)\n", - hdr->seq, exception_seq); - goto again; - } - - if (kdp_packet((unsigned char*)&pkt.data[pkt.off], - (int *)&pkt.len, - (unsigned short *)&reply_port)) { - kdp_reply(reply_port); - } + // ignore replies -- we're not expecting them anyway. + if (hdr->is_reply) { + goto again; + } + + if (hdr->request == KDP_REATTACH) + exception_seq = hdr->seq; + + // check for retransmitted request + if (hdr->seq == (exception_seq - 1)) { + /* retransmit last reply */ + kdp_send_data(&saved_reply.data[saved_reply.off], + saved_reply.len); + goto again; + } else if ((hdr->seq != exception_seq) && + (hdr->request != KDP_CONNECT)) { + printf("kdp: bad sequence %d (want %d)\n", + hdr->seq, exception_seq); + goto again; + } + + /* This is a manual side-channel to the main KDP protocol. + * A client like GDB/kgmacros can manually construct + * a request, set the input flag, issue a dummy KDP request, + * and then manually collect the result + */ + if (manual_pkt.input) { + kdp_hdr_t *manual_hdr = (kdp_hdr_t *)&manual_pkt.data; + unsigned short manual_port_unused = 0; + if (!manual_hdr->is_reply) { + /* process */ + int packet_length = manual_pkt.len; + kdp_packet((unsigned char *)&manual_pkt.data, + &packet_length, + &manual_port_unused); + manual_pkt.len = packet_length; + } + manual_pkt.input = 0; + } + + if (kdp_packet((unsigned char*)&pkt.data[pkt.off], + (int *)&pkt.len, + (unsigned short *)&reply_port)) { + boolean_t sideband = FALSE; + + /* if it's an already connected error message, + * send a sideband reply for that. for successful connects, + * make sure the sequence number is correct. */ + if (hdr->request == KDP_CONNECT) { + kdp_connect_reply_t *rp = + (kdp_connect_reply_t *) &pkt.data[pkt.off]; + kdp_error_t err = rp->error; + + if (err == KDPERR_NO_ERROR) { + exception_seq = hdr->seq; + } else if (err == KDPERR_ALREADY_CONNECTED) { + sideband = TRUE; + } + } + + kdp_reply(reply_port, sideband); + } again: - pkt.input = FALSE; - } while (kdp.is_halted); + pkt.input = FALSE; + } while (kdp.is_halted); } static void kdp_connection_wait(void) { unsigned short reply_port; - boolean_t kdp_call_kdb(); - struct ether_addr kdp_mac_addr = kdp_get_mac_addr(); + struct kdp_ether_addr kdp_mac_addr = kdp_get_mac_addr(); unsigned int ip_addr = ntohl(kdp_get_ip_address()); - printf( "ethernet MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", - kdp_mac_addr.ether_addr_octet[0] & 0xff, - kdp_mac_addr.ether_addr_octet[1] & 0xff, - kdp_mac_addr.ether_addr_octet[2] & 0xff, - kdp_mac_addr.ether_addr_octet[3] & 0xff, - kdp_mac_addr.ether_addr_octet[4] & 0xff, - kdp_mac_addr.ether_addr_octet[5] & 0xff); - - printf( "ip address: %d.%d.%d.%d\n", - (ip_addr & 0xff000000) >> 24, - (ip_addr & 0xff0000) >> 16, - (ip_addr & 0xff00) >> 8, - (ip_addr & 0xff)); + /* + * Do both a printf() and a kprintf() of the MAC and IP so that + * they will print out on headless machines but not be added to + * the panic.log + */ + + if (KDP_SERIAL_ENABLED()) { + printf("Using serial KDP.\n"); + kprintf("Using serial KDP.\n"); + } else { + printf("ethernet MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + kdp_mac_addr.ether_addr_octet[0] & 0xff, + kdp_mac_addr.ether_addr_octet[1] & 0xff, + kdp_mac_addr.ether_addr_octet[2] & 0xff, + kdp_mac_addr.ether_addr_octet[3] & 0xff, + kdp_mac_addr.ether_addr_octet[4] & 0xff, + kdp_mac_addr.ether_addr_octet[5] & 0xff); + + kprintf("ethernet MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + kdp_mac_addr.ether_addr_octet[0] & 0xff, + kdp_mac_addr.ether_addr_octet[1] & 0xff, + kdp_mac_addr.ether_addr_octet[2] & 0xff, + kdp_mac_addr.ether_addr_octet[3] & 0xff, + kdp_mac_addr.ether_addr_octet[4] & 0xff, + kdp_mac_addr.ether_addr_octet[5] & 0xff); + + printf("ip address: %d.%d.%d.%d\n", + (ip_addr & 0xff000000) >> 24, + (ip_addr & 0xff0000) >> 16, + (ip_addr & 0xff00) >> 8, + (ip_addr & 0xff)); + + kprintf("ip address: %d.%d.%d.%d\n", + (ip_addr & 0xff000000) >> 24, + (ip_addr & 0xff0000) >> 16, + (ip_addr & 0xff00) >> 8, + (ip_addr & 0xff)); + } printf("\nWaiting for remote debugger connection.\n"); + kprintf("\nWaiting for remote debugger connection.\n"); + if (reattach_wait == 0) { - if((kdp_flag & KDP_GETC_ENA) && (0 != kdp_getc())) - { + if((kdp_flag & KDP_GETC_ENA) && (0 != kdp_getc())) { printf("Options..... Type\n"); printf("------------ ----\n"); printf("continue.... 'c'\n"); printf("reboot...... 'r'\n"); -#if MACH_KDB - printf("enter kdb... 'k'\n"); -#endif } - } else + } else { reattach_wait = 0; + } exception_seq = 0; @@ -646,17 +1223,9 @@ kdp_connection_wait(void) return; case 'r': printf("Rebooting...\n"); - kdp_reboot(); + kdp_machine_reboot(); break; -#if MACH_KDB - case 'k': - printf("calling kdb...\n"); - if (kdp_call_kdb()) - return; - else - printf("not implemented...\n"); -#endif - default: + default: break; } } @@ -669,15 +1238,15 @@ kdp_connection_wait(void) hdr = (kdp_hdr_t *)&pkt.data[pkt.off]; #endif if (hdr->request == KDP_HOSTREBOOT) { - kdp_reboot(); + kdp_machine_reboot(); /* should not return! */ } if (((hdr->request == KDP_CONNECT) || (hdr->request == KDP_REATTACH)) && !hdr->is_reply && (hdr->seq == exception_seq)) { - if (kdp_packet((unsigned char *)&pkt.data[pkt.off], - (int *)&pkt.len, + if (kdp_packet((unsigned char *)&pkt.data[pkt.off], + (int *)&pkt.len, (unsigned short *)&reply_port)) - kdp_reply(reply_port); + kdp_reply(reply_port, FALSE); if (hdr->request == KDP_REATTACH) { reattach_wait = 0; hdr->request=KDP_DISCONNECT; @@ -691,163 +1260,176 @@ kdp_connection_wait(void) if (current_debugger == KDP_CUR_DB) active_debugger=1; printf("Connected to remote debugger.\n"); + kprintf("Connected to remote debugger.\n"); } static void kdp_send_exception( - unsigned int exception, - unsigned int code, - unsigned int subcode + unsigned int exception, + unsigned int code, + unsigned int subcode ) { - unsigned short remote_port; - unsigned int timeout_count = 100; - unsigned int poll_timeout; - - do { - pkt.off = sizeof (struct ether_header) + sizeof (struct udpiphdr); - kdp_exception((unsigned char *)&pkt.data[pkt.off], - (int *)&pkt.len, - (unsigned short *)&remote_port, - (unsigned int)exception, - (unsigned int)code, - (unsigned int)subcode); - - kdp_send(remote_port); - - poll_timeout = 50; - while(!pkt.input && poll_timeout) - { - kdp_poll(); - poll_timeout--; - } + unsigned short remote_port; + unsigned int timeout_count = 100; + unsigned int poll_timeout; + + do { + pkt.off = sizeof (struct kdp_ether_header) + sizeof (struct kdp_udpiphdr); + kdp_exception((unsigned char *)&pkt.data[pkt.off], + (int *)&pkt.len, + (unsigned short *)&remote_port, + (unsigned int)exception, + (unsigned int)code, + (unsigned int)subcode); + + kdp_send(remote_port); + + poll_timeout = 50; + while (!pkt.input && poll_timeout) { + kdp_poll(); + poll_timeout--; + } + + if (pkt.input) { + if (!kdp_exception_ack(&pkt.data[pkt.off], pkt.len)) { + pkt.input = FALSE; + } + } - if (pkt.input) { - if (!kdp_exception_ack(&pkt.data[pkt.off], pkt.len)) { pkt.input = FALSE; - } - } - pkt.input = FALSE; + if (kdp.exception_ack_needed) + kdp_us_spin(250000); - if (kdp.exception_ack_needed) - kdp_us_spin(250000); + } while (kdp.exception_ack_needed && timeout_count--); - } while (kdp.exception_ack_needed && timeout_count--); - - if (kdp.exception_ack_needed) { - // give up & disconnect - printf("kdp: exception ack timeout\n"); - if (current_debugger == KDP_CUR_DB) - active_debugger=0; - kdp_reset(); - } + if (kdp.exception_ack_needed) { + // give up & disconnect + printf("kdp: exception ack timeout\n"); + if (current_debugger == KDP_CUR_DB) + active_debugger=0; + kdp_reset(); + } } -void -kdp_raise_exception( - unsigned int exception, - unsigned int code, - unsigned int subcode, - void *saved_state -) +static void +kdp_debugger_loop( + unsigned int exception, + unsigned int code, + unsigned int subcode, + void *saved_state) { - int index; + int index; + + if (saved_state == 0) + printf("kdp_raise_exception with NULL state\n"); + + index = exception; + if (exception != EXC_BREAKPOINT) { + if (exception > EXC_BREAKPOINT || exception < EXC_BAD_ACCESS) { + index = 0; + } + printf("%s exception (%x,%x,%x)\n", + exception_message[index], + exception, code, subcode); + } - extern unsigned int disableConsoleOutput; + kdp_sync_cache(); + + /* XXX WMG it seems that sometimes it doesn't work to let kdp_handler + * do this. I think the client and the host can get out of sync. + */ + kdp.saved_state = saved_state; + kdp.kdp_cpu = cpu_number(); + kdp.kdp_thread = current_thread(); + + if (kdp_en_setmode) + (*kdp_en_setmode)(TRUE); /* enabling link mode */ + + if (pkt.input) + kdp_panic("kdp_raise_exception"); + + if (((kdp_flag & KDP_PANIC_DUMP_ENABLED) + || (kdp_flag & PANIC_LOG_DUMP) + || kdp_has_polled_corefile()) + && (panicstr != (char *) 0)) { + kdp_panic_dump(); + if (kdp_flag & REBOOT_POST_CORE) + kdp_machine_reboot(); + } else { + if ((kdp_flag & PANIC_CORE_ON_NMI) && (panicstr == (char *) 0) + && !kdp.is_conn) { + + disable_debug_output = disableConsoleOutput = FALSE; + kdp_panic_dump(); + if (kdp_flag & REBOOT_POST_CORE) + kdp_machine_reboot(); + + if (!(kdp_flag & DBG_POST_CORE)) + goto exit_debugger_loop; + } + } + +again: + if (!kdp.is_conn) { + kdp_connection_wait(); + } else { + kdp_send_exception(exception, code, subcode); + if (kdp.exception_ack_needed) { + kdp.exception_ack_needed = FALSE; + kdp_remove_all_breakpoints(); + printf("Remote debugger disconnected.\n"); + } + } - disable_preemption(); + if (kdp.is_conn) { + kdp.is_halted = TRUE; /* XXX */ + kdp_handler(saved_state); + if (!kdp.is_conn) + { + kdp_remove_all_breakpoints(); + printf("Remote debugger disconnected.\n"); + } + } + /* Allow triggering a panic core dump when connected to the machine + * Continuing after setting kdp_trigger_core_dump should do the + * trick. + */ + + if (1 == kdp_trigger_core_dump) { + kdp_flag |= KDP_PANIC_DUMP_ENABLED; + kdp_panic_dump(); + if (kdp_flag & REBOOT_POST_CORE) + kdp_machine_reboot(); + kdp_trigger_core_dump = 0; + } - if (saved_state == 0) - printf("kdp_raise_exception with NULL state\n"); + /* Trigger a reboot if the user has set this flag through the + * debugger.Ideally, this would be done through the HOSTREBOOT packet + * in the protocol,but that will need gdb support,and when it's + * available, it should work automatically. + */ + if (1 == flag_kdp_trigger_reboot) { + kdp_machine_reboot(); + /* If we're still around, reset the flag */ + flag_kdp_trigger_reboot = 0; + } - index = exception; - if (exception != EXC_BREAKPOINT) { - if (exception > EXC_BREAKPOINT || exception < EXC_BAD_ACCESS) { - index = 0; + if (kdp_reentry_deadline) { + kdp_schedule_debugger_reentry(kdp_reentry_deadline); + printf("Debugger re-entry scheduled in %d milliseconds\n", kdp_reentry_deadline); + kdp_reentry_deadline = 0; } - printf("%s exception (%x,%x,%x)\n", - exception_message[index], - exception, code, subcode); - } - - kdp_sync_cache(); - - /* XXX WMG it seems that sometimes it doesn't work to let kdp_handler - * do this. I think the client and the host can get out of sync. - */ - kdp.saved_state = saved_state; - - if (pkt.input) - kdp_panic("kdp_raise_exception"); - - if (((kdp_flag & KDP_PANIC_DUMP_ENABLED) || (kdp_flag & PANIC_LOG_DUMP)) - && (panicstr != (char *) 0)) { - - kdp_panic_dump(); - - } - else - if ((kdp_flag & PANIC_CORE_ON_NMI) && (panicstr == (char *) 0) && - !kdp.is_conn) { - - disableDebugOuput = disableConsoleOutput = FALSE; - kdp_panic_dump(); - - if (!(kdp_flag & DBG_POST_CORE)) - goto exit_raise_exception; - } - - again: - if (!kdp.is_conn) - kdp_connection_wait(); - else { - kdp_send_exception(exception, code, subcode); - if (kdp.exception_ack_needed) { - kdp.exception_ack_needed = FALSE; - kdp_remove_all_breakpoints(); - printf("Remote debugger disconnected.\n"); - } - } - - if (kdp.is_conn) { - kdp.is_halted = TRUE; /* XXX */ - kdp_handler(saved_state); - if (!kdp.is_conn) - { - kdp_remove_all_breakpoints(); - printf("Remote debugger disconnected.\n"); - } - } - /* Allow triggering a panic core dump when connected to the machine - * Continuing after setting kdp_trigger_core_dump should do the - * trick. - */ - - if (1 == kdp_trigger_core_dump) { - kdp_flag &= ~PANIC_LOG_DUMP; - kdp_flag |= KDP_PANIC_DUMP_ENABLED; - kdp_panic_dump(); - } - -/* Trigger a reboot if the user has set this flag through the - * debugger.Ideally, this would be done through the HOSTREBOOT packet - * in the protocol,but that will need gdb support,and when it's - * available, it should work automatically. - */ - if (1 == flag_kdp_trigger_reboot) { - kdp_reboot(); - /* If we're still around, reset the flag */ - flag_kdp_trigger_reboot = 0; - } - - kdp_sync_cache(); - - if (reattach_wait == 1) - goto again; - -exit_raise_exception: - enable_preemption(); + + kdp_sync_cache(); + + if (reattach_wait == 1) + goto again; + +exit_debugger_loop: + if (kdp_en_setmode) + (*kdp_en_setmode)(FALSE); /* link cleanup */ } void @@ -856,250 +1438,293 @@ kdp_reset(void) kdp.reply_port = kdp.exception_port = 0; kdp.is_halted = kdp.is_conn = FALSE; kdp.exception_seq = kdp.conn_seq = 0; + kdp.session_key = 0; + pkt.input = manual_pkt.input = FALSE; + pkt.len = pkt.off = manual_pkt.len = 0; } struct corehdr * create_panic_header(unsigned int request, const char *corename, - unsigned length, unsigned int block) + unsigned length, unsigned int block) { - struct udpiphdr aligned_ui, *ui = &aligned_ui; - struct ip aligned_ip, *ip = &aligned_ip; - struct ether_header *eh; - struct corehdr *coreh; - const char *mode = "octet"; - char modelen = strlen(mode); + struct kdp_udpiphdr aligned_ui, *ui = &aligned_ui; + struct kdp_ip aligned_ip, *ip = &aligned_ip; + struct kdp_ether_header *eh; + struct corehdr *coreh; + const char *mode = "octet"; + char modelen = strlen(mode) + 1; + + size_t fmask_size = sizeof(KDP_FEATURE_MASK_STRING) + sizeof(kdp_crashdump_feature_mask); - pkt.off = sizeof (struct ether_header); - pkt.len = length + ((request == KDP_WRQ) ? modelen : 0) + - (corename ? strlen(corename): 0) + sizeof(struct corehdr); + pkt.off = sizeof (struct kdp_ether_header); + pkt.len = (unsigned int)(length + ((request == KDP_WRQ) ? modelen + fmask_size : 0) + + (corename ? (strlen(corename) + 1 ): 0) + sizeof(struct corehdr)); #if DO_ALIGN - bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); + bcopy((char *)&pkt.data[pkt.off], (char *)ui, sizeof(*ui)); #else - ui = (struct udpiphdr *)&pkt.data[pkt.off]; + ui = (struct kdp_udpiphdr *)&pkt.data[pkt.off]; #endif - ui->ui_next = ui->ui_prev = 0; - ui->ui_x1 = 0; - ui->ui_pr = IPPROTO_UDP; - ui->ui_len = htons((u_short)pkt.len + sizeof (struct udphdr)); - ui->ui_src.s_addr = htonl(kdp_current_ip_address); - ui->ui_dst.s_addr = panic_server_ip; - ui->ui_sport = htons(CORE_REMOTE_PORT); - ui->ui_dport = ((request == KDP_WRQ) ? htons(CORE_REMOTE_PORT) : last_panic_port); - ui->ui_ulen = ui->ui_len; - ui->ui_sum = 0; + ui->ui_next = ui->ui_prev = 0; + ui->ui_x1 = 0; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = htons((u_short)pkt.len + sizeof (struct kdp_udphdr)); + ui->ui_src.s_addr = (uint32_t)kdp_current_ip_address; + /* Already in network byte order via inet_aton() */ + ui->ui_dst.s_addr = panic_server_ip; + ui->ui_sport = htons(panicd_port); + ui->ui_dport = ((request == KDP_WRQ) ? htons(panicd_port) : last_panic_port); + ui->ui_ulen = ui->ui_len; + ui->ui_sum = 0; #if DO_ALIGN - bcopy((char *)ui, (char *)&pkt.data[pkt.off], sizeof(*ui)); - bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); + bcopy((char *)ui, (char *)&pkt.data[pkt.off], sizeof(*ui)); + bcopy((char *)&pkt.data[pkt.off], (char *)ip, sizeof(*ip)); #else - ip = (struct ip *)&pkt.data[pkt.off]; + ip = (struct kdp_ip *)&pkt.data[pkt.off]; #endif - ip->ip_len = htons(sizeof (struct udpiphdr) + pkt.len); - ip->ip_v = IPVERSION; - ip->ip_id = htons(ip_id++); - ip->ip_hl = sizeof (struct ip) >> 2; - ip->ip_ttl = udp_ttl; - ip->ip_sum = 0; - ip->ip_sum = htons(~ip_sum((unsigned char *)ip, ip->ip_hl)); + ip->ip_len = htons(sizeof (struct kdp_udpiphdr) + pkt.len); + ip->ip_v = IPVERSION; + ip->ip_id = htons(ip_id++); + ip->ip_hl = sizeof (struct kdp_ip) >> 2; + ip->ip_ttl = udp_ttl; + ip->ip_sum = 0; + ip->ip_sum = htons(~ip_sum((unsigned char *)ip, ip->ip_hl)); #if DO_ALIGN - bcopy((char *)ip, (char *)&pkt.data[pkt.off], sizeof(*ip)); + bcopy((char *)ip, (char *)&pkt.data[pkt.off], sizeof(*ip)); #endif - pkt.len += sizeof (struct udpiphdr); + pkt.len += (unsigned int)sizeof (struct kdp_udpiphdr); - pkt.off += sizeof (struct udpiphdr); + pkt.off += (unsigned int)sizeof (struct kdp_udpiphdr); - coreh = (struct corehdr *) &pkt.data[pkt.off]; - coreh->th_opcode = htons((u_short)request); + coreh = (struct corehdr *) &pkt.data[pkt.off]; + coreh->th_opcode = htons((u_short)request); - if (request == KDP_WRQ) - { - register char *cp; - - cp = coreh->th_u.tu_rpl; - strcpy (cp, corename); - cp += strlen(corename); - *cp++ = '\0'; - strcpy (cp, mode); - cp+= modelen; - *cp++ = '\0'; - } - else - { - coreh->th_block = htonl((unsigned int) block); - } - - pkt.off -= sizeof (struct udpiphdr); - pkt.off -= sizeof (struct ether_header); - - eh = (struct ether_header *)&pkt.data[pkt.off]; - enaddr_copy(&kdp_current_mac_address, eh->ether_shost); - enaddr_copy(&router_mac, eh->ether_dhost); - eh->ether_type = htons(ETHERTYPE_IP); + if (request == KDP_WRQ) { + char *cp; + + cp = coreh->th_u.tu_rpl; + cp += strlcpy (cp, corename, KDP_MAXPACKET); + *cp++ = '\0'; + cp += strlcpy (cp, mode, KDP_MAXPACKET - strlen(corename)); + *cp++ = '\0'; + cp += strlcpy(cp, KDP_FEATURE_MASK_STRING, sizeof(KDP_FEATURE_MASK_STRING)); + *cp++ = '\0'; /* Redundant */ + bcopy(&kdp_crashdump_feature_mask, cp, sizeof(kdp_crashdump_feature_mask)); + kdp_crashdump_pkt_size = KDP_LARGE_CRASHDUMP_PKT_SIZE; + PE_parse_boot_argn("kdp_crashdump_pkt_size", &kdp_crashdump_pkt_size, sizeof(kdp_crashdump_pkt_size)); + cp += sizeof(kdp_crashdump_feature_mask); + *(uint32_t *)cp = htonl(kdp_crashdump_pkt_size); + } else { + coreh->th_block = htonl((unsigned int) block); + } + + pkt.off -= (unsigned int)sizeof (struct kdp_udpiphdr); + pkt.off -= (unsigned int)sizeof (struct kdp_ether_header); + + eh = (struct kdp_ether_header *)&pkt.data[pkt.off]; + enaddr_copy(&kdp_current_mac_address, eh->ether_shost); + enaddr_copy(&destination_mac, eh->ether_dhost); + eh->ether_type = htons(ETHERTYPE_IP); - pkt.len += sizeof (struct ether_header); - return coreh; + pkt.len += (unsigned int)sizeof (struct kdp_ether_header); + return coreh; } -int kdp_send_panic_packets (unsigned int request, char *corename, - unsigned int length, unsigned int txstart) +static int +kdp_send_crashdump_seek(char *corename, uint64_t seek_off) { - unsigned int txend = txstart + length; - int panic_error = 0; - - if (length <= SEGSIZE) { - if ((panic_error = kdp_send_panic_pkt (request, corename, length, (caddr_t) txstart)) < 0) { - printf ("kdp_send_panic_pkt failed with error %d\n", panic_error); - return panic_error ; - } - } - else - { - while (txstart <= (txend - SEGSIZE)) { - if ((panic_error = kdp_send_panic_pkt (KDP_DATA, NULL, SEGSIZE, (caddr_t) txstart)) < 0) { - printf ("kdp_send_panic_pkt failed with error %d\n", panic_error); - return panic_error; + int panic_error; + + if (kdp_feature_large_crashdumps) { + panic_error = kdp_send_crashdump_pkt(KDP_SEEK, corename, + sizeof(seek_off), + &seek_off); + } else { + uint32_t off = (uint32_t) seek_off; + panic_error = kdp_send_crashdump_pkt(KDP_SEEK, corename, + sizeof(off), &off); + } + + if (panic_error < 0) { + printf ("kdp_send_crashdump_pkt failed with error %d\n", + panic_error); + return panic_error; } - txstart += SEGSIZE; - if (!(panic_block % 2000)) - printf("."); - } - if (txstart < txend) { - kdp_send_panic_pkt(request, corename, (txend - txstart), (caddr_t) txstart); - } - } - return 0; + + return KERN_SUCCESS; } -int -kdp_send_panic_pkt (unsigned int request, char *corename, - unsigned int length, void *panic_data) +int +kdp_send_crashdump_data(unsigned int request, char *corename, + uint64_t length, void * txstart) { - struct corehdr *th = NULL; - int poll_count = 2500; - - char rretries = 0, tretries = 0; - /* - extern signed long gIODebuggerSemaphore; - */ - pkt.off = pkt.len = 0; - - if (request == KDP_WRQ) /* longer timeout for initial request */ - poll_count += 1000; + int panic_error = 0; + + while ((length > 0) || !txstart) { + uint64_t chunk = MIN(kdp_crashdump_pkt_size, length); + + panic_error = kdp_send_crashdump_pkt(request, corename, chunk, + txstart); + if (panic_error < 0) { + printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); + return panic_error; + } + if (!txstart) break; + txstart = (void *)(((uintptr_t) txstart) + chunk); + length -= chunk; + } + return KERN_SUCCESS; +} + +uint32_t kdp_crashdump_short_pkt; + +int +kdp_send_crashdump_pkt(unsigned int request, char *corename, + uint64_t length, void *panic_data) +{ + int poll_count; + struct corehdr *th = NULL; + char rretries, tretries; + + if (kdp_dump_start_time == 0) { + kdp_dump_start_time = mach_absolute_time(); + kdp_superblock_dump_start_time = kdp_dump_start_time; + } + + tretries = rretries = 0; + poll_count = KDP_CRASHDUMP_POLL_COUNT; + pkt.off = pkt.len = 0; + if (request == KDP_WRQ) /* longer timeout for initial request */ + poll_count += 1000; TRANSMIT_RETRY: - tretries++; + tretries++; + + if (tretries >=15) { +/* The crashdump server is unreachable for some reason. This could be a network + * issue or, if we've been especially unfortunate, we've hit Radar 2760413, + * which is a long standing problem with the IOKit polled mode network driver + * shim which can prevent transmits/receives completely. + */ + printf ("Cannot contact panic server, timing out.\n"); + return (-3); + } - if (tretries > 2) - printf("TX retry #%d ", tretries ); + if (tretries > 2) + printf("TX retry #%d ", tretries ); - if (tretries >=15) { - /* This iokit layer issue can potentially - *cause a hang, uncomment to check if it's happening. - */ - /* - if (gIODebuggerSemaphore) - printf("The gIODebuggerSemaphore is raised, preventing packet transmission (2760413)\n"); - */ - - printf ("Cannot contact panic server, timing out.\n"); - return (-3); - } + th = create_panic_header(request, corename, (unsigned)length, panic_block); + + if (request == KDP_DATA) { + /* as all packets are kdp_crashdump_pkt_size in length, the last packet + * may end up with trailing bits. make sure that those + * bits aren't confusing. */ + if (length < kdp_crashdump_pkt_size) { + kdp_crashdump_short_pkt++; + memset(th->th_data + length, 'Y', + kdp_crashdump_pkt_size - (uint32_t) length); + } - th = create_panic_header(request, corename, length, panic_block); + if (!kdp_machine_vm_read((mach_vm_address_t)(uintptr_t)panic_data, (caddr_t) th->th_data, length)) { + uintptr_t next_page = round_page((uintptr_t)panic_data); + memset((caddr_t) th->th_data, 'X', (size_t)length); + if ((next_page - ((uintptr_t) panic_data)) < length) { + uint64_t resid = length - (next_page - (intptr_t) panic_data); + if (!kdp_machine_vm_read((mach_vm_address_t)(uintptr_t)next_page, (caddr_t) th->th_data + (length - resid), resid)) { + memset((caddr_t) th->th_data + (length - resid), 'X', (size_t)resid); + } + } + } + } + else if (request == KDP_SEEK) { + if (kdp_feature_large_crashdumps) + *(uint64_t *) th->th_data = OSSwapHostToBigInt64((*(uint64_t *) panic_data)); + else + *(unsigned int *) th->th_data = htonl(*(unsigned int *) panic_data); + } - if (request == KDP_DATA || request == KDP_SEEK) { - if (!kdp_vm_read ((caddr_t) panic_data, (caddr_t) th->th_data, length)) { - memset ((caddr_t) th->th_data, 'X', length); - } - } + kdp_send_data(&pkt.data[pkt.off], pkt.len); - (*kdp_en_send_pkt)(&pkt.data[pkt.off], pkt.len); + /* Listen for the ACK */ +RECEIVE_RETRY: + while (!pkt.input && flag_panic_dump_in_progress && poll_count) { + kdp_poll(); + poll_count--; + } - /* Now we have to listen for the ACK */ - RECEIVE_RETRY: + if (pkt.input) { + pkt.input = FALSE; + + th = (struct corehdr *) &pkt.data[pkt.off]; + if (request == KDP_WRQ) { + uint16_t opcode64 = ntohs(th->th_opcode); + uint16_t features64 = (opcode64 & 0xFF00)>>8; + if ((opcode64 & 0xFF) == KDP_ACK) { + kdp_feature_large_crashdumps = features64 & KDP_FEATURE_LARGE_CRASHDUMPS; + if (features64 & KDP_FEATURE_LARGE_PKT_SIZE) { + kdp_feature_large_pkt_size = 1; + } + else { + kdp_feature_large_pkt_size = 0; + kdp_crashdump_pkt_size = 512; + } + printf("Protocol features: 0x%x\n", (uint32_t) features64); + th->th_opcode = htons(KDP_ACK); + } + } + if (ntohs(th->th_opcode) == KDP_ACK && ntohl(th->th_block) == panic_block) { + } else { + if (ntohs(th->th_opcode) == KDP_ERROR) { + printf("Panic server returned error %d, retrying\n", ntohl(th->th_code)); + poll_count = 1000; + goto TRANSMIT_RETRY; + } else if (ntohl(th->th_block) == (panic_block - 1)) { + printf("RX retry "); + if (++rretries > 1) + goto TRANSMIT_RETRY; + else + goto RECEIVE_RETRY; + } + } + } else if (!flag_panic_dump_in_progress) { /* we received a debugging packet, bail*/ + printf("Received a debugger packet,transferring control to debugger\n"); + /* Configure that if not set ..*/ + kdp_flag |= DBG_POST_CORE; + return (-2); + } else { /* We timed out */ + if (0 == poll_count) { + poll_count = 1000; + kdp_us_spin ((tretries%4) * panic_timeout); /* capped linear backoff */ + goto TRANSMIT_RETRY; + } + } - while (!pkt.input && flag_panic_dump_in_progress && poll_count) { - kdp_poll(); - poll_count--; - } + if (!(++panic_block % SBLOCKSZ)) { + uint64_t ctime; + kdb_printf_unbuffered("."); + ctime = mach_absolute_time(); + kdp_superblock_dump_time = ctime - kdp_superblock_dump_start_time; + kdp_superblock_dump_start_time = ctime; + if (kdp_superblock_dump_time > kdp_max_superblock_dump_time) + kdp_max_superblock_dump_time = kdp_superblock_dump_time; + if (kdp_superblock_dump_time < kdp_min_superblock_dump_time) + kdp_min_superblock_dump_time = kdp_superblock_dump_time; + } - if (pkt.input) { - - pkt.input = FALSE; - - th = (struct corehdr *) &pkt.data[pkt.off]; - /* These will eventually have to be ntoh[ls]'ed as appropriate */ - - if (th->th_opcode == KDP_ACK && th->th_block == panic_block) { - } - else - if (th->th_opcode == KDP_ERROR) { - printf("Panic server returned error %d, retrying\n", th->th_code); - poll_count = 1000; - goto TRANSMIT_RETRY; - } - else - if (th->th_block == (panic_block -1)) { - printf("RX retry "); - if (++rretries > 1) - goto TRANSMIT_RETRY; - else - goto RECEIVE_RETRY; + if (request == KDP_EOF) { + printf("\nTotal number of packets transmitted: %d\n", panic_block); + printf("Avg. superblock transfer abstime 0x%llx\n", ((mach_absolute_time() - kdp_dump_start_time) / panic_block) * SBLOCKSZ); + printf("Minimum superblock transfer abstime: 0x%llx\n", kdp_min_superblock_dump_time); + printf("Maximum superblock transfer abstime: 0x%llx\n", kdp_max_superblock_dump_time); } - } - else - if (!flag_panic_dump_in_progress) /* we received a debugging packet, bail*/ - { - printf("Received a debugger packet,transferring control to debugger\n"); - /* Configure that if not set ..*/ - kdp_flag |= DBG_POST_CORE; - return (-2); - } - else /* We timed out */ - if (0 == poll_count) { - poll_count = 1000; - kdp_us_spin ((tretries%4) * panic_timeout); /* capped linear backoff */ - goto TRANSMIT_RETRY; - } - - panic_block++; - - if (request == KDP_EOF) - printf ("\nTotal number of packets transmitted: %d\n", panic_block); - - return 1; + return KERN_SUCCESS; } -/* Since we don't seem to have an isdigit() .. */ static int isdigit (char c) { - return ((c > 47) && (c < 58)); -} -/* From user mode Libc - this ought to be in a library */ -static char * -strnstr(s, find, slen) - const char *s; - const char *find; - size_t slen; -{ - char c, sc; - size_t len; - - if ((c = *find++) != '\0') { - len = strlen(find); - do { - do { - if ((sc = *s++) == '\0' || slen-- < 1) - return (NULL); - } while (sc != c); - if (len > slen) - return (NULL); - } while (strncmp(s, find, len) != 0); - s--; - } - return ((char *)s); + return ((c > 47) && (c < 58)); } /* Horrid hack to extract xnu version if possible - a much cleaner approach @@ -1108,142 +1733,583 @@ strnstr(s, find, slen) * time - makes assumptions about sizeof(version), but will not fail if * it changes, but may be incorrect. */ - +/* 2006: Incorporated a change from Darwin user P. Lovell to extract + * the minor kernel version numbers from the version string. + */ static int kdp_get_xnu_version(char *versionbuf) { - extern const char version[]; - char *versionpos; - char vstr[10]; - int retval = -1; - - strcpy(vstr, "custom"); - if (version) { - if (kdp_vm_read(version, versionbuf, 90)) { - - versionbuf[89] = '\0'; - - versionpos = strnstr(versionbuf, "xnu-", 80); - - if (versionpos) { - strncpy (vstr, versionpos, (isdigit (versionpos[7]) ? 8 : 7)); - vstr[(isdigit (versionpos[7]) ? 8 : 7)] = '\0'; - retval = 0; - } - } - } - strcpy(versionbuf, vstr); - return retval; + char *versionpos; + char vstr[20]; + int retval = -1; + char *vptr; + + strlcpy(vstr, "custom", 10); + if (kdp_machine_vm_read((mach_vm_address_t)(uintptr_t)version, versionbuf, 128)) { + versionbuf[127] = '\0'; + versionpos = strnstr(versionbuf, "xnu-", 115); + if (versionpos) { + strncpy(vstr, versionpos, sizeof(vstr)); + vstr[sizeof(vstr)-1] = '\0'; + vptr = vstr + 4; /* Begin after "xnu-" */ + while (*vptr && (isdigit(*vptr) || *vptr == '.')) + vptr++; + *vptr = '\0'; + /* Remove trailing period, if any */ + if (*(--vptr) == '.') + *vptr = '\0'; + retval = 0; + } + } + strlcpy(versionbuf, vstr, KDP_MAXPACKET); + return retval; +} + +void +kdp_set_dump_info(const uint32_t flags, const char *filename, + const char *destipstr, const char *routeripstr, + const uint32_t port) +{ + uint32_t cmd; + + if (destipstr && (destipstr[0] != '\0')) { + strlcpy(panicd_ip_str, destipstr, sizeof(panicd_ip_str)); + panicd_specified = 1; + } + + if (routeripstr && (routeripstr[0] != '\0')) { + strlcpy(router_ip_str, routeripstr, sizeof(router_ip_str)); + router_specified = 1; + } + + if (filename && (filename[0] != '\0')) { + strlcpy(corename_str, filename, sizeof(corename_str)); + corename_specified = TRUE; + } else { + corename_specified = FALSE; + } + + if (port) + panicd_port = port; + + /* on a disconnect, should we stay in KDP or not? */ + noresume_on_disconnect = (flags & KDP_DUMPINFO_NORESUME) ? 1 : 0; + + if ((flags & KDP_DUMPINFO_DUMP) == 0) + return; + + /* the rest of the commands can modify kdp_flags */ + cmd = flags & KDP_DUMPINFO_MASK; + if (cmd == KDP_DUMPINFO_DISABLE) { + kdp_flag &= ~KDP_PANIC_DUMP_ENABLED; + panicd_specified = 0; + kdp_trigger_core_dump = 0; + return; + } + + kdp_flag &= ~REBOOT_POST_CORE; + if (flags & KDP_DUMPINFO_REBOOT) + kdp_flag |= REBOOT_POST_CORE; + + kdp_flag &= ~PANIC_LOG_DUMP; + if (cmd == KDP_DUMPINFO_PANICLOG) + kdp_flag |= PANIC_LOG_DUMP; + + kdp_flag &= ~SYSTEM_LOG_DUMP; + if (cmd == KDP_DUMPINFO_SYSTEMLOG) + kdp_flag |= SYSTEM_LOG_DUMP; + + /* trigger a dump */ + kdp_flag |= DBG_POST_CORE; + + flag_dont_abort_panic_dump = (flags & KDP_DUMPINFO_NOINTR) ? + TRUE : FALSE; + + reattach_wait = 1; + logPanicDataToScreen = 1; + disableConsoleOutput = 0; + disable_debug_output = 0; + kdp_trigger_core_dump = 1; +} + +void +kdp_get_dump_info(kdp_dumpinfo_reply_t *rp) +{ + if (rp->destip) { + if (panicd_specified) + strlcpy(rp->destip, panicd_ip_str, + sizeof(panicd_ip_str)); + else + rp->destip[0] = '\0'; + } + + if (rp->routerip) { + if (router_specified) + strlcpy(rp->routerip, router_ip_str, + sizeof(router_ip_str)); + else + rp->routerip[0] = '\0'; + } + + if (rp->name) { + if (corename_specified) + strlcpy(rp->name, corename_str, + sizeof(corename_str)); + else + rp->name[0] = '\0'; + + } + + rp->port = panicd_port; + + rp->type = 0; + if (!panicd_specified) + rp->type |= KDP_DUMPINFO_DISABLE; + else if (kdp_flag & PANIC_LOG_DUMP) + rp->type |= KDP_DUMPINFO_PANICLOG; + else + rp->type |= KDP_DUMPINFO_CORE; + + if (noresume_on_disconnect) + rp->type |= KDP_DUMPINFO_NORESUME; } + /* Primary dispatch routine for the system dump */ void -kdp_panic_dump() +kdp_panic_dump(void) { - char corename[50]; - char coreprefix[10]; - int panic_error; + char coreprefix[10]; + char coresuffix[4]; + int panic_error; - extern vm_map_t kernel_map; + uint64_t abstime; + uint32_t current_ip = ntohl((uint32_t)kdp_current_ip_address); - extern char *inet_aton(const char *cp, struct in_addr *pin); + if (flag_panic_dump_in_progress) { + kdb_printf("System dump aborted.\n"); + goto panic_dump_exit; + } + + printf("Entering system dump routine\n"); - uint64_t abstime; + /* try a local disk dump */ + if (kdp_has_polled_corefile()) { + flag_panic_dump_in_progress = TRUE; + kern_dump(KERN_DUMP_DISK); + abort_panic_transfer(); + } - printf ("Entering system dump routine\n"); - - if (!panicd_specified) { - printf ("A panic server was not specified in the boot-args, terminating kernel core dump.\n"); - goto panic_dump_exit; - } + if (!strcmp("local", panicd_ip_str)) return; /* disk only request */ + + if (!kdp_en_recv_pkt || !kdp_en_send_pkt) { + if (!kdp_has_polled_corefile()) { + kdb_printf("Error: No transport device registered for kernel crashdump\n"); + } + return; + } - flag_panic_dump_in_progress = 1; - not_in_kdp = 0; + if (!panicd_specified) { + if (!kdp_has_polled_corefile()) { + kdb_printf("A dump server was not specified in the boot-args, terminating kernel core dump.\n"); + } + goto panic_dump_exit; + } - if (pkt.input) - kdp_panic("kdp_panic_dump"); + flag_panic_dump_in_progress = TRUE; - kdp_get_xnu_version((char *) &pkt.data[0]); + if (pkt.input) + kdp_panic("kdp_panic_dump: unexpected pending input packet"); - /* Panic log bit takes precedence over core dump bit */ - if ((panicstr != (char *) 0) && (kdp_flag & PANIC_LOG_DUMP)) - strncpy(coreprefix, "paniclog", sizeof(coreprefix)); - else - strncpy(coreprefix, "core", sizeof(coreprefix)); - - abstime = mach_absolute_time(); - pkt.data[10] = '\0'; - snprintf (corename, sizeof(corename), "%s-%s-%d.%d.%d.%d-%x", - coreprefix, &pkt.data[0], - (kdp_current_ip_address & 0xff000000) >> 24, - (kdp_current_ip_address & 0xff0000) >> 16, - (kdp_current_ip_address & 0xff00) >> 8, - (kdp_current_ip_address & 0xff), - (unsigned int) (abstime & 0xffffffff)); - - if (0 == inet_aton(panicd_ip_str, (struct in_addr *) &panic_server_ip)) { - printf("inet_aton() failed interpreting %s as a panic server IP\n", - panicd_ip_str); - } - else - printf("Attempting connection to panic server configured at IP %s\n", - panicd_ip_str); - - if (router_specified) { - if (0 == inet_aton(router_ip_str, (struct in_addr *) &parsed_router_ip)){ - printf("inet_aton() failed interpreting %s as an IP\n", router_ip); + kdp_get_xnu_version((char *) &pkt.data[0]); + + if (!corename_specified) { + coresuffix[0] = 0; + /* Panic log bit takes precedence over core dump bit */ + if ((panicstr != (char *) 0) && (kdp_flag & PANIC_LOG_DUMP)) + strlcpy(coreprefix, "paniclog", sizeof(coreprefix)); + else if (kdp_flag & SYSTEM_LOG_DUMP) + strlcpy(coreprefix, "systemlog", sizeof(coreprefix)); + else { + strlcpy(coreprefix, "core", sizeof(coreprefix)); + if (!kdp_corezip_disabled) strlcpy(coresuffix, ".gz", sizeof(coresuffix)); + } + + abstime = mach_absolute_time(); + pkt.data[20] = '\0'; + snprintf (corename_str, + sizeof(corename_str), + "%s-%s-%d.%d.%d.%d-%x%s", + coreprefix, &pkt.data[0], + (current_ip & 0xff000000) >> 24, + (current_ip & 0xff0000) >> 16, + (current_ip & 0xff00) >> 8, + (current_ip & 0xff), + (unsigned int) (abstime & 0xffffffff), + coresuffix); } - else { - router_ip = parsed_router_ip; - printf("Routing through specified router IP %s (%d)\n", router_ip_str, router_ip); - /* We will eventually need to resolve the router's MAC ourselves, - * if one is specified,rather than being set through the BSD callback - * but the _router_ip option does not function currently - */ + + if (0 == inet_aton(panicd_ip_str, (struct kdp_in_addr *) &panic_server_ip)) { + kdb_printf("inet_aton() failed interpreting %s as a panic server IP\n", panicd_ip_str); + } else { + kdb_printf("Attempting connection to panic server configured at IP %s, port %d\n", panicd_ip_str, panicd_port); + } + + destination_mac = router_mac; + + if (kdp_arp_resolve(panic_server_ip, &temp_mac)) { + kdb_printf("Resolved %s's (or proxy's) link level address\n", panicd_ip_str); + destination_mac = temp_mac; + } else { + if (!flag_panic_dump_in_progress) goto panic_dump_exit; + if (router_specified) { + if (0 == inet_aton(router_ip_str, (struct kdp_in_addr *) &parsed_router_ip)) { + kdb_printf("inet_aton() failed interpreting %s as an IP\n", router_ip_str); + } else { + router_ip = parsed_router_ip; + if (kdp_arp_resolve(router_ip, &temp_mac)) { + destination_mac = temp_mac; + kdb_printf("Routing through specified router IP %s (%d)\n", router_ip_str, router_ip); + } + } + } + } + + if (!flag_panic_dump_in_progress) goto panic_dump_exit; + + kdb_printf("Transmitting packets to link level address: %02x:%02x:%02x:%02x:%02x:%02x\n", + destination_mac.ether_addr_octet[0] & 0xff, + destination_mac.ether_addr_octet[1] & 0xff, + destination_mac.ether_addr_octet[2] & 0xff, + destination_mac.ether_addr_octet[3] & 0xff, + destination_mac.ether_addr_octet[4] & 0xff, + destination_mac.ether_addr_octet[5] & 0xff); + + kdb_printf("Kernel map size is %llu\n", (unsigned long long) get_vmmap_size(kernel_map)); + kdb_printf("Sending write request for %s\n", corename_str); + + if ((panic_error = kdp_send_crashdump_pkt(KDP_WRQ, corename_str, 0 , NULL)) < 0) { + kdb_printf ("kdp_send_crashdump_pkt failed with error %d\n", panic_error); + goto panic_dump_exit; + } + + /* Just the panic log requested */ + if ((panicstr != (char *) 0) && (kdp_flag & PANIC_LOG_DUMP)) { + kdb_printf_unbuffered("Transmitting panic log, please wait: "); + kdp_send_crashdump_data(KDP_DATA, corename_str, + debug_buf_ptr - debug_buf_addr, + debug_buf_addr); + kdp_send_crashdump_pkt (KDP_EOF, NULL, 0, ((void *) 0)); + printf("Please file a bug report on this panic, if possible.\n"); + goto panic_dump_exit; } - } - - printf("Routing via router MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", - router_mac.ether_addr_octet[0] & 0xff, - router_mac.ether_addr_octet[1] & 0xff, - router_mac.ether_addr_octet[2] & 0xff, - router_mac.ether_addr_octet[3] & 0xff, - router_mac.ether_addr_octet[4] & 0xff, - router_mac.ether_addr_octet[5] & 0xff); - - printf("Kernel map size is %llu\n", (unsigned long long) get_vmmap_size(kernel_map)); - printf ("Sending write request for %s\n", corename); - - if ((panic_error = kdp_send_panic_pkt (KDP_WRQ, corename, 0 , NULL)) < 0) { - printf ("kdp_send_panic_pkt failed with error %d\n", panic_error); - goto panic_dump_exit; - } - - /* Just the panic log requested */ - if ((panicstr != (char *) 0) && (kdp_flag & PANIC_LOG_DUMP)) { - printf("Transmitting panic log, please wait: "); - kdp_send_panic_packets (KDP_DATA, corename, (debug_buf_ptr - debug_buf), (unsigned int) debug_buf); - kdp_send_panic_pkt (KDP_EOF, NULL, 0, ((void *) 0)); - printf("Please file a bug report on this panic, if possible.\n"); - goto panic_dump_exit; - } - /* We want a core dump if we're here */ - kern_dump(); + /* maybe we wanted the systemlog */ + if (kdp_flag & SYSTEM_LOG_DUMP) { + long start_off = msgbufp->msg_bufx; + long len; + + kdb_printf_unbuffered("Transmitting system log, please wait: "); + if (start_off >= msgbufp->msg_bufr) { + len = msgbufp->msg_size - start_off; + kdp_send_crashdump_data(KDP_DATA, corename_str, len, + msgbufp->msg_bufc + start_off); + /* seek to remove trailing bytes */ + kdp_send_crashdump_seek(corename_str, len); + start_off = 0; + } + + if (start_off != msgbufp->msg_bufr) { + len = msgbufp->msg_bufr - start_off; + kdp_send_crashdump_data(KDP_DATA, corename_str, len, + msgbufp->msg_bufc + start_off); + } + + kdp_send_crashdump_pkt (KDP_EOF, NULL, 0, ((void *) 0)); + goto panic_dump_exit; + } + + /* We want a core dump if we're here */ + kern_dump(KERN_DUMP_NET); + panic_dump_exit: - not_in_kdp = 1; - flag_panic_dump_in_progress = 0; - panic_block = 0; - pkt.input = FALSE; - pkt.len = 0; - kdp_reset(); - return; + abort_panic_transfer(); + kdp_reset(); + return; } void -abort_panic_transfer() +abort_panic_transfer(void) +{ + flag_panic_dump_in_progress = FALSE; + flag_dont_abort_panic_dump = FALSE; + panic_block = 0; +} + +#if CONFIG_SERIAL_KDP + +static boolean_t needs_serial_init = TRUE; + +static void +kdp_serial_send(void *rpkt, unsigned int rpkt_len) +{ + // printf("tx\n"); + kdp_serialize_packet((unsigned char *)rpkt, rpkt_len, pal_serial_putc_nocr); +} + +static void +kdp_serial_receive(void *rpkt, unsigned int *rpkt_len, unsigned int timeout) +{ + int readkar; + uint64_t now, deadline; + + clock_interval_to_deadline(timeout, 1000 * 1000 /* milliseconds */, &deadline); + +// printf("rx\n"); + for(clock_get_uptime(&now); now < deadline; clock_get_uptime(&now)) { + readkar = pal_serial_getc(); + if(readkar >= 0) { + unsigned char *packet; + // printf("got char %02x\n", readkar); + if((packet = kdp_unserialize_packet(readkar,rpkt_len))) { + memcpy(rpkt, packet, *rpkt_len); + return; + } + } + } + *rpkt_len = 0; +} + +static boolean_t +kdp_serial_setmode(boolean_t active) +{ + if (active == FALSE) /* leaving KDP */ + return TRUE; + + if (!needs_serial_init) + return TRUE; + + pal_serial_init(); + needs_serial_init = FALSE; + return TRUE; +} + + +static void +kdp_serial_callout(__unused void *arg, kdp_event_t event) +{ + /* + * When we stop KDP, set the bit to re-initialize the console serial + * port the next time we send/receive a KDP packet. We don't do it on + * KDP_EVENT_ENTER directly because it also gets called when we trap to + * KDP for non-external debugging, i.e., stackshot or core dumps. + * + * Set needs_serial_init on exit (and initialization, see above) and not + * enter because enter is sent multiple times and causes excess + * reinitialization. + */ + + switch (event) + { + case KDP_EVENT_PANICLOG: + case KDP_EVENT_ENTER: + break; + case KDP_EVENT_EXIT: + needs_serial_init = TRUE; + break; + } +} + +#endif /* CONFIG_SERIAL_KDP */ + +void +kdp_init(void) +{ + strlcpy(kdp_kernelversion_string, version, sizeof(kdp_kernelversion_string)); + + /* Relies on platform layer calling panic_init() before kdp_init() */ + if (kernel_uuid_string[0] != '\0') { + /* + * Update kdp_kernelversion_string with our UUID + * generated at link time. + */ + + strlcat(kdp_kernelversion_string, "; UUID=", sizeof(kdp_kernelversion_string)); + strlcat(kdp_kernelversion_string, kernel_uuid_string, sizeof(kdp_kernelversion_string)); + } + + debug_log_init(); + +#if defined(__x86_64__) || defined(__arm__) || defined(__arm64__) + if (vm_kernel_slide) { + char KASLR_stext[19]; + strlcat(kdp_kernelversion_string, "; stext=", sizeof(kdp_kernelversion_string)); + snprintf(KASLR_stext, sizeof(KASLR_stext), "%p", (void *) vm_kernel_stext); + strlcat(kdp_kernelversion_string, KASLR_stext, sizeof(kdp_kernelversion_string)); + } +#endif + + if (debug_boot_arg & DB_REBOOT_POST_CORE) + kdp_flag |= REBOOT_POST_CORE; +#if defined(__x86_64__) + kdp_machine_init(); +#endif + + kdp_timer_callout_init(); + kdp_crashdump_feature_mask = htonl(kdp_crashdump_feature_mask); + kdp_core_init(); + +#if CONFIG_SERIAL_KDP + char kdpname[80]; + struct kdp_in_addr ipaddr; + struct kdp_ether_addr macaddr; + + boolean_t kdp_match_name_found = PE_parse_boot_argn("kdp_match_name", kdpname, sizeof(kdpname)); + boolean_t kdp_not_serial = kdp_match_name_found ? (strncmp(kdpname, "serial", sizeof(kdpname))) : TRUE; + + // serial must be explicitly requested + if(!kdp_match_name_found || kdp_not_serial) + return; + +#if WITH_CONSISTENT_DBG + if (kdp_not_serial && PE_consistent_debug_enabled() && debug_boot_arg) { + current_debugger = HW_SHM_CUR_DB; + return; + } else { + printf("Serial requested, consistent debug disabled or debug boot arg not present, configuring debugging over serial\n"); + } +#endif /* WITH_CONSISTENT_DBG */ + + kprintf("Initializing serial KDP\n"); + + kdp_register_callout(kdp_serial_callout, NULL); + kdp_register_link(NULL, kdp_serial_setmode); + kdp_register_send_receive(kdp_serial_send, kdp_serial_receive); + + /* fake up an ip and mac for early serial debugging */ + macaddr.ether_addr_octet[0] = 's'; + macaddr.ether_addr_octet[1] = 'e'; + macaddr.ether_addr_octet[2] = 'r'; + macaddr.ether_addr_octet[3] = 'i'; + macaddr.ether_addr_octet[4] = 'a'; + macaddr.ether_addr_octet[5] = 'l'; + ipaddr.s_addr = KDP_SERIAL_IPADDR; + kdp_set_ip_and_mac_addresses(&ipaddr, &macaddr); + +#endif /* CONFIG_SERIAL_KDP */ +} + +#else /* CONFIG_KDP_INTERACTIVE_DEBUGGING */ +void +kdp_init(void) +{ +} +#endif /* CONFIG_KDP_INTERACTIVE_DEBUGGING */ + +#if !CONFIG_KDP_INTERACTIVE_DEBUGGING +__attribute__((noreturn)) +static void +panic_spin_forever() { - flag_panic_dump_in_progress = 0; - not_in_kdp = 1; - panic_block = 0; + kdb_printf("\nPlease go to https://panic.apple.com to report this panic\n"); + + for (;;) { } +} +#endif + +#if WITH_CONSISTENT_DBG && CONFIG_KDP_INTERACTIVE_DEBUGGING +__attribute__((noreturn)) +static void +panic_spin_shmcon() +{ + kdb_printf("\nPlease go to https://panic.apple.com to report this panic\n"); + kdb_printf("Waiting for hardware shared memory debugger, handshake structure is at virt: %p, phys %p\n", + hwsd_info, (void *)kvtophys((vm_offset_t)hwsd_info)); + + assert(hwsd_info != NULL); + hwsd_info->xhsdci_status = XHSDCI_STATUS_KERNEL_READY; + hwsd_info->xhsdci_seq_no = 0; + FlushPoC_DcacheRegion((vm_offset_t) hwsd_info, sizeof(*hwsd_info)); + + for (;;) { + FlushPoC_DcacheRegion((vm_offset_t) hwsd_info, sizeof(*hwsd_info)); + if (hwsd_info->xhsdci_status == XHSDCI_COREDUMP_BEGIN) { + kern_dump(KERN_DUMP_HW_SHMEM_DBG); + } + + if ((hwsd_info->xhsdci_status == XHSDCI_COREDUMP_REMOTE_DONE) || + (hwsd_info->xhsdci_status == XHSDCI_COREDUMP_ERROR)) { + hwsd_info->xhsdci_status = XHSDCI_STATUS_KERNEL_READY; + hwsd_info->xhsdci_seq_no = 0; + FlushPoC_DcacheRegion((vm_offset_t) hwsd_info, sizeof(*hwsd_info)); + } + } +} +#endif /* WITH_CONSISTENT_DBG && CONFIG_KDP_INTERACTIVE_DEBUGGING */ + +#if !CONFIG_KDP_INTERACTIVE_DEBUGGING +__attribute__((noreturn)) +void +kdp_raise_exception( + __unused unsigned int exception, + __unused unsigned int code, + __unused unsigned int subcode, + __unused void *saved_state + ) +#else +void +kdp_raise_exception( + unsigned int exception, + unsigned int code, + unsigned int subcode, + void *saved_state + ) +#endif +{ + +#if CONFIG_KDP_INTERACTIVE_DEBUGGING + + unsigned int initial_not_in_kdp = not_in_kdp; + not_in_kdp = 0; + + disable_preemption(); + + if (current_debugger != KDP_CUR_DB) { + /* try a local disk dump */ + if (kdp_has_polled_corefile()) { +#if WITH_CONSISTENT_DBG + if (current_debugger == HW_SHM_CUR_DB) { + hwsd_info->xhsdci_status = XHSDCI_STATUS_KERNEL_BUSY; + } +#endif /* WITH_CONSISTENT_DBG */ + flag_panic_dump_in_progress = TRUE; + kern_dump(KERN_DUMP_DISK); + abort_panic_transfer(); + } +#if WITH_CONSISTENT_DBG + if (current_debugger == HW_SHM_CUR_DB) { + panic_spin_shmcon(); + } +#endif /* WITH_CONSISTENT_DBG */ + + + if (!panicDebugging) { + kdp_machine_reboot(); + } + } + + kdp_debugger_loop(exception, code, subcode, saved_state); + not_in_kdp = initial_not_in_kdp; + enable_preemption(); +#else /* CONFIG_KDP_INTERACTIVE_DEBUGGING */ + assert(current_debugger != KDP_CUR_DB); + + /* + * If kernel debugging is enabled via boot-args, but KDP debugging + * is not compiled into the kernel, spin here waiting for debugging + * via another method. Why here? Because we want to have watchdog + * disabled (via KDP callout) while sitting waiting to be debugged. + */ + panic_spin_forever(); +#endif /* CONFIG_KDP_INTERACTIVE_DEBUGGING */ }