Date: Thu, 9 Feb 2006 19:13:25 GMT From: Rob Deker <deker@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 91447 for review Message-ID: <200602091913.k19JDPwA028166@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=91447 Change 91447 by deker@deker_build1.columbia.sparta.com on 2006/02/09 19:12:59 per millert: "Make kernel-dump-to-server work properly; from plovell" Submitted by: millert Affected files ... .. //depot/projects/trustedbsd/sedarwin7/src/darwin/xnu/osfmk/kdp/kdp_udp.c#3 edit Differences ... ==== //depot/projects/trustedbsd/sedarwin7/src/darwin/xnu/osfmk/kdp/kdp_udp.c#3 (text+ko) ==== @@ -23,6 +23,19 @@ * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + */ +/* + * changes 2005/6 plovell + * ARP for MAC address of panicd server (boot-arg _panicd_ip ) + * ARP for MAC address of router (boot-arg _router_ip ) + * dump name includes minor version (rdar://3735061) + */ + /* * Kernel Debugging Protocol UDP implementation. @@ -46,11 +59,40 @@ #include <string.h> +/* external-visible prototypes (move to external header sometime) */ +void kdp_set_interface(void *ifp); +void *kdp_get_interface(void); +void kdp_set_ip_and_mac_addresses(struct in_addr *ipaddr, struct ether_addr *macaddr); +void kdp_set_gateway_mac(void *gatewaymac); +int kdp_send_panic_packets(unsigned int request, char *corename, unsigned int length, unsigned int txstart); + +/* local prototypes - keep compiler happy :) */ +static void enaddr_copy(void *src, void *dst); +static unsigned short ip_sum(unsigned char*c, unsigned inthlen); +static void kdp_reply(unsigned short reply_port); +static void kdp_send(unsigned short remote_port); +static int create_arp_request(struct in_addr* ipaddr); +static void kdp_handle_arp(void); +static void kdp_arp_respond(void); +static void kdp_handle_arp_reply(void); +static void kdp_poll(void); +static void kdp_handler(void *saved_state); +static void kdp_connection_wait(void); +static void kdp_send_exception(unsigned intexception, unsigned int code, unsigned int subcode); +static int kdp_arp_request (struct in_addr *ipaddr, struct ether_addr *macaddr); +static int isdigit(char c); +static int kdp_get_xnu_version(char *versionbuf); + + #define DO_ALIGN 1 /* align all packet data accesses */ extern int kdp_getc(void); extern int reattach_wait; +extern void kdp_call(void); +extern unsigned int disableConsoleOutput; +extern boolean_t kdp_call_kdb(void); + static u_short ip_id; /* ip packet ctr, for ids */ /* @(#)udp_usrreq.c 2.2 88/05/23 4.0NFSSRC SMI; from UCB 7.1 6/5/86 */ @@ -101,39 +143,48 @@ static struct ether_addr kdp_current_mac_address = {{0, 0, 0, 0, 0, 0}}; static void *kdp_current_ifp = 0; -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 unsigned int arp_ip = 0; +static struct ether_addr arp_mac = {{0, 0, 0 , 0, 0, 0}}; + static struct ether_addr router_mac = {{0, 0, 0 , 0, 0, 0}}; +static struct ether_addr server_mac = {{0, 0, 0 , 0, 0, 0}}; +static struct ether_addr target_mac = {{0, 0, 0 , 0, 0, 0}}; static u_char flag_panic_dump_in_progress = 0; static u_char flag_router_mac_initialized = 0; +static u_char flag_have_arp = 0; static unsigned int panic_timeout = 100000; static unsigned int last_panic_port = CORE_REMOTE_PORT; unsigned int SEGSIZE = 512; -static unsigned int PANIC_PKTSIZE = 518; +/* static unsigned int PANIC_PKTSIZE = 518; */ static char panicd_ip_str[20]; static char router_ip_str[20]; static unsigned int panic_block = 0; static volatile unsigned int kdp_trigger_core_dump = 0; +static 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); +static u_char etherbroadcastaddr[ETHER_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + void kdp_register_send_receive( kdp_send_t send, kdp_receive_t receive) { - unsigned int debug; + unsigned int debug = 0; kdp_en_send_pkt = send; kdp_en_recv_pkt = receive; @@ -162,7 +213,7 @@ if (PE_parse_boot_arg ("_panicd_ip", panicd_ip_str)) panicd_specified = 1; - /* For the future, currently non-functional */ + /* functional with changes made 20060102 */ if (PE_parse_boot_arg ("_router_ip", router_ip_str)) router_specified = 1; @@ -366,8 +417,6 @@ struct in_addr *ipaddr, struct ether_addr *macaddr) { - unsigned int debug = 0; - kdp_current_ip_address = ipaddr->s_addr; kdp_current_mac_address = *macaddr; } @@ -376,6 +425,8 @@ kdp_set_gateway_mac(void *gatewaymac) { router_mac = *(struct ether_addr *)gatewaymac; + target_mac = *(struct ether_addr *)gatewaymac; + flag_router_mac_initialized = 1; } struct ether_addr @@ -390,13 +441,94 @@ return kdp_current_ip_address; } +/* ARP request builder. Note that this saves the ip address + * we want for the use of kdp_handle_arp_reply +*/ +static int +create_arp_request(struct in_addr* ipaddr) +{ + struct ether_header header; + struct ether_header *eh = &header; + struct ether_arp aligned_ea, *ea = &aligned_ea; + + struct in_addr myaddr; + struct ether_addr my_enaddr; + + unsigned long wk; + wk = ntohl(ipaddr->s_addr); +/* printf("request arp for IP %d.%d.%d.%d\n", + (wk & 0xff000000) >> 24, + (wk & 0x00ff0000) >> 16, + (wk & 0x0000ff00) >> 8, + (wk & 0x000000ff) ); */ + + myaddr.s_addr = kdp_get_ip_address(); + my_enaddr = kdp_get_mac_addr(); + + if (!(myaddr.s_addr) || !(my_enaddr.ether_addr_octet[1])) + return 1; + + /* lay out ether header fields */ + memcpy( eh->ether_dhost, etherbroadcastaddr, sizeof(struct ether_addr)); + memcpy( eh->ether_shost, (void*)&my_enaddr, sizeof(struct ether_addr) ); + eh->ether_type = htons(ETHERTYPE_ARP); + + /* now the arp request header */ + ea->arp_hrd = htons(ARPHRD_ETHER); + ea->arp_pro = htons(ETHERTYPE_IP); + ea->arp_hln = sizeof(ea->arp_sha); + ea->arp_pln = sizeof(ea->arp_spa); + ea->arp_op = htons(ARPOP_REQUEST); + + /* sender addresses (us) */ + memcpy(ea->arp_sha, (void *)&my_enaddr, sizeof(ea->arp_sha)); + memcpy(ea->arp_spa, (void *)&myaddr, sizeof(ea->arp_spa)); + + /* target addresses */ + memcpy(ea->arp_tha, etherbroadcastaddr, sizeof(struct ether_addr)); + memcpy(ea->arp_tpa, (void *)ipaddr, sizeof(ea->arp_tpa)); + + pkt.len = 0; + pkt.off = 0; + memcpy( &pkt.data[pkt.off], eh, sizeof(struct ether_header) ); + pkt.len += sizeof(struct ether_header); + pkt.off += pkt.len; + memcpy( &pkt.data[pkt.off], ea, sizeof(struct ether_arp) ); + pkt.len += sizeof(struct ether_arp); + pkt.off = 0; + + return 0; +} + /* 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!) */ static void -kdp_arp_reply(void) +kdp_handle_arp(void) +{ + struct ether_arp aligned_ea, *ea = &aligned_ea; + int offset = sizeof(struct ether_header); + + memcpy((void *)ea, (void *)&pkt.data[offset],sizeof(*ea)); + + if( (kdp_flag & KDP_ARP) && (ntohs(ea->arp_op) == ARPOP_REQUEST) ) + kdp_arp_respond(); + else if(ntohs(ea->arp_op) == ARPOP_REPLY) + kdp_handle_arp_reply(); + + 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!) +*/ +static void +kdp_arp_respond(void) { struct ether_header *eh; struct ether_arp aligned_ea, *ea = &aligned_ea; @@ -440,6 +572,30 @@ } } +/* We have an ARP reply. Maybe we are waiting for one?? + * Handle it if we are, else ignore it. + */ +static void +kdp_handle_arp_reply(void) +{ + struct ether_arp aligned_ea, *ea = &aligned_ea; + + pkt.off = sizeof(struct ether_header); + memcpy((void *)ea, (void *)&pkt.data[pkt.off],sizeof(*ea)); + + if(ntohs(ea->arp_op) != ARPOP_REPLY) + return; + + if( ((struct in_addr *)(ea->arp_spa))->s_addr != arp_ip) /* not the one we want */ + return; + + arp_mac = *(struct ether_addr*)(ea->arp_sha); + flag_have_arp = 1; + /* printf("kdp_handle_arp_reply set flag_have_arp\n"); */ + + return; +} + static void kdp_poll(void) { @@ -470,15 +626,13 @@ { eh = (struct ether_header *)&pkt.data[pkt.off]; - if (kdp_flag & KDP_ARP) - { + /* testing for KDP_ARP now done in kdp_handle_arp() */ if (ntohs(eh->ether_type) == ETHERTYPE_ARP) { - kdp_arp_reply(); + kdp_handle_arp(); return; } } - } if (pkt.len < (sizeof (struct ether_header) + sizeof (struct udpiphdr))) return; @@ -595,10 +749,11 @@ kdp_connection_wait(void) { unsigned short reply_port; - boolean_t kdp_call_kdb(); - struct ether_addr kdp_mac_addr = kdp_get_mac_addr(); - unsigned int ip_addr = ntohl(kdp_get_ip_address()); + struct ether_addr kdp_mac_addr; + unsigned int ip_addr; + kdp_mac_addr = kdp_get_mac_addr(); + 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, @@ -751,7 +906,6 @@ int index; extern unsigned int disableDebugOuput; - extern unsigned int disableConsoleOutput; disable_preemption(); @@ -826,6 +980,16 @@ 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) @@ -918,7 +1082,7 @@ eh = (struct ether_header *)&pkt.data[pkt.off]; enaddr_copy(&kdp_current_mac_address, eh->ether_shost); - enaddr_copy(&router_mac, eh->ether_dhost); + enaddr_copy(&target_mac, eh->ether_dhost); eh->ether_type = htons(ETHERTYPE_IP); pkt.len += sizeof (struct ether_header); @@ -952,6 +1116,7 @@ kdp_send_panic_pkt(request, corename, (txend - txstart), (caddr_t) txstart); } } + return 0; } int @@ -1054,6 +1219,108 @@ return 1; } +static int +kdp_arp_request ( + struct in_addr *ipaddr, + struct ether_addr *macaddr) +{ + struct corehdr *th = NULL; + int poll_count = 2500; + int err = 0; + + char rretries = 0, tretries = 0; + /* + extern signed long gIODebuggerSemaphore; + */ + pkt.off = pkt.len = 0; + +TRANSMIT_RETRY: + tretries++; + + if (tretries > 2) + printf("TX arp 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 arp panic server, timing out.\n"); + return (-3); + } + + err = create_arp_request(ipaddr); + if ( err != 0 ) { + printf("create arp request failed\n"); + return (-4); + } + + arp_ip = ipaddr->s_addr; + flag_have_arp = 0; + + + + (*kdp_en_send_pkt)(&pkt.data[pkt.off], pkt.len); + + /* Now we have to listen for the ACK */ + RECEIVE_RETRY: + + while (!pkt.input && flag_panic_dump_in_progress && poll_count) { + kdp_poll(); + poll_count--; + if ( flag_have_arp ) { + *macaddr = arp_mac; + return 0; + } + } + + 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; + } + } + 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; + } + + + return 1; +} + /* Since we don't seem to have an isdigit() .. */ static int isdigit (char c) @@ -1090,6 +1357,19 @@ * xnu version into a string or an int somewhere at project submission * time - makes assumptions about sizeof(version), but will not fail if * it changes, but may be incorrect. + * + * Changed 20060102 to allow a more complete version number, including + * minor version. It will now look something like xnu-792.6.22 + * (hopefully fixes radar 3735061) + * + * The only use of this routine provides the packet buffer as the + * data buffer (versionbuf) which is just as well, as there's + * no size checking done. That buffer is >1500 + * The fetched size used to be 90 but that's quite close to the end + * of the name so now it's up to 100. The version string is a system-wide + * global, defined in version.c in each darwin build. The content is + * similar to ... + * "Darwin Kernel Version 8.3.0: Thu Dec 29 06:13:35 PST 2005; root:xnu-792.6.22.obj/RELEASE_PPC" */ static int @@ -1097,20 +1377,30 @@ { extern const char version[]; char *versionpos; - char vstr[10]; + char vstr[20]; int retval = -1; + char* ptr; strcpy(vstr, "custom"); if (version) { - if (kdp_vm_read(version, versionbuf, 90)) { + if (kdp_vm_read(version, versionbuf, 100)) { - versionbuf[89] = '\0'; + versionbuf[99] = '\0'; - versionpos = strnstr(versionbuf, "xnu-", 80); + versionpos = strnstr(versionbuf, "xnu-", 90); if (versionpos) { - strncpy (vstr, versionpos, (isdigit (versionpos[7]) ? 8 : 7)); - vstr[(isdigit (versionpos[7]) ? 8 : 7)] = '\0'; + strncpy(vstr, versionpos, sizeof(vstr)); + vstr[sizeof(vstr)-1] = '\0'; /* terminate */ + + ptr = vstr + 4; /* start after "xnu-" */ + while ( isdigit(*ptr) || (*ptr == '.') ) + ptr++; + + *ptr = '\0'; /* terminate */ + if ( *(--ptr) == '.' ) /* if period is last */ + *ptr = '\0'; /* then remove trailing period */ + retval = 0; } } @@ -1118,20 +1408,19 @@ strcpy(versionbuf, vstr); return retval; } + /* Primary dispatch routine for the system dump */ void kdp_panic_dump() { - char corename[50]; + char corename[64]; char coreprefix[10]; int panic_error; - extern char *debug_buf; + int err = 0; extern vm_map_t kernel_map; extern char *inet_aton(const char *cp, struct in_addr *pin); - extern char *debug_buf; - extern char *debug_buf_ptr; uint64_t abstime; printf ("Entering system dump routine\n"); @@ -1156,7 +1445,7 @@ strncpy(coreprefix, "core", sizeof(coreprefix)); abstime = mach_absolute_time(); - pkt.data[10] = '\0'; + pkt.data[20] = '\0'; /* limit file name length to something reasonable */ snprintf (corename, sizeof(corename), "%s-%s-%d.%d.%d.%d-%x", coreprefix, &pkt.data[0], (kdp_current_ip_address & 0xff000000) >> 24, @@ -1173,32 +1462,57 @@ printf("Attempting connection to panic server configured at IP %s\n", panicd_ip_str); + /* first try to ARP the dump server */ + + err = kdp_arp_request((struct in_addr *) &panic_server_ip, &server_mac); + if ( err >= 0 ) { + target_mac = server_mac; + printf("kdump server MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + target_mac.ether_addr_octet[0] & 0xff, + target_mac.ether_addr_octet[1] & 0xff, + target_mac.ether_addr_octet[2] & 0xff, + target_mac.ether_addr_octet[3] & 0xff, + target_mac.ether_addr_octet[4] & 0xff, + target_mac.ether_addr_octet[5] & 0xff); + + } + else + { + if ( err == -3 ) + printf ("No arp response for panic server.\n"); + 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); } 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 + printf("Trying specified router IP %s\n", router_ip_str); + /* Resolve the router's MAC, as specified in the _router_ip option. + * If it doesn't resolve, use the one set through the BSD callback + * in kdp_set_gateway_mac */ + err = kdp_arp_request((struct in_addr *) &router_ip, &server_mac); + if ( err >= 0 ) + target_mac = server_mac; + else if ( err == -3 ) + printf ("No arp response for panic server.\n"); } } - /* These & 0xffs aren't necessary,but cut&paste is ever so convenient */ + 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); + target_mac.ether_addr_octet[0] & 0xff, + target_mac.ether_addr_octet[1] & 0xff, + target_mac.ether_addr_octet[2] & 0xff, + target_mac.ether_addr_octet[3] & 0xff, + target_mac.ether_addr_octet[4] & 0xff, + target_mac.ether_addr_octet[5] & 0xff); + } - printf("Kernel map size is %d\n", get_vmmap_size(kernel_map)); + printf("Kernel map size is %ld\n", 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)) { + 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; }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200602091913.k19JDPwA028166>