Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial checkin

  • Loading branch information...
commit 3933120721373afe88edfbf3d062b74c26d433f9 0 parents
@badeip badeip authored
25 LICENSE.txt
@@ -0,0 +1,25 @@
+Copyright (c) 2009, David Elliott
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of David Elliott nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY David Elliott ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL David Elliott BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
24 Makefile
@@ -0,0 +1,24 @@
+# Variables:
+PROGRAM = sym/SerialKDPProxy
+OBJECTS = obj/SerialKDPProxy.o obj/kdp_serial.o
+
+CFLAGS = -Wall
+
+# Phony rules:
+all: $(PROGRAM)
+
+clean:
+ rm -f $(PROGRAM) $(OBJECTS)
+
+# Programs to build:
+$(PROGRAM): $(OBJECTS)
+ $(CC) -g -D_BSD_SOURCE -o $(PROGRAM) $(OBJECTS)
+
+# Objects to build:
+obj/SerialKDPProxy.o: src/SerialKDPProxy.c
+ $(COMPILE.c) -g -D_BSD_SOURCE $(OUTPUT_OPTION) $<
+
+obj/kdp_serial.o: src/kdp_serial.c
+ $(COMPILE.c) -g -D_BSD_SOURCE $(OUTPUT_OPTION) $<
+
+
108 README.html
@@ -0,0 +1,108 @@
+<html>
+<head>
+</head>
+<body>
+<h1>README for SerialKDPProxy</h1>
+
+<p>Copyright 2009 David Elliott. All Rights Reserved.</p>
+
+<h2>About SerialKDPProxy</h2>
+
+<p>The SerialKDPProxy is used for KDP debugging over RS-232. This is a debugger of <em>last resort</em>. If
+at all possible, use KDP over FireWire or KDP over Ethernet. As far as I can tell, KDP over FireWire can
+be made to work with both builtin (the default) and add-on (there is a kernel command-line option) FireWire
+ports. Be aware that if not on a Mac your FireWire port might physically be located on the motherboard but
+the manufacturer of your board did not list it as being a built-in device in the ACPI tables. Exhaust
+all possibilities of getting the FireWire KDP kext to work on your target before attempting to use this.</p>
+
+<p>Now, that said, the huge advantage to serial KDP is that you can attach to the kernel before IOKit has
+started. If you have a machine that fails before the ACPI PE has enumerated the PCI devices then this
+is going to be your only option for examining the kernel state aside from predetermined debug print statements
+present in the code.</p>
+
+<h2>Using SerialKDPProxy</h2>
+
+<h3>Configuring the kernel on the target</h3>
+
+<p>See osfmk/kern/debug.h for the DB_* series of flags. See osfmk/kdp/kdp_udp.h for how kdp_match_name is used.</p>
+
+<p>First of all, to use the serial debugger you need kdp_match_name=serial in the boot args. This causes the code
+in kdp_init() to initialize the serial debugger.</p>
+<p>Second, for effective use you probably want to set DB_HALT (0x1)
+so the kernel will halt very early in startup, before even IOKit is attached. Without this you won't be able
+to attach to the kernel until after something has called the debug break function. By setting DB_HALT the kernel
+will call debug break and thus allow you to attach early.
+
+<p>Third, you may want some other flags like DB_KPRT (0x8)
+and DB_ARP (0x40). As of now there is no way to send an ARP although if you format it correctly and push it
+down the serial port (e.g. cat a file into the same /dev/tty* from another terminal) then the reply should be
+logged to stderr as a non-UDP packet. So for instance, debug=0x49 gives you DB_HALT, DB_KPRT and DB_ARP.</p>
+
+<p>Lastly, you may or may not want serial=3 depending on whether or not you want to enable the serial console.
+Obviously there are many more options like -v and io=XXX (see iokit/IOKit/IOKitDebug.h) that you may want depending
+on what you are trying to do. All of these are documented in various places by Apple and you can google most
+of the flags to find out what they do (usually you'll find a page on Apple's site).</p>
+
+<h3>Linking the machines together</h3>
+<p>You will need a null modem cable. Often these can be bought DB-9 female to DB-9 female with the null modem
+circuitry integrated into the cable. Other times you have a DB-9 female to DB-9 male cable with a null modem
+adapter and a DB-9 female to female gender changer. It is very rare these days to see DB-25 serial ports on
+any machines these days although technically, DB-25 is the official RS-232 standard port.</p>
+
+<p>On the target (machine whose kernel is being debugged) you must attach one end of your null-modem cable to
+COM1. The kernel has legacy COM1 0x3f8 IO base address hardcoded in it. This is how the kernel is able to provide
+kprintf and KDP over serial long before the IOKit has started. Very recent motherboards are excluding a DB-9 port
+on the ATX backplane but they almost always provide a 10-pin header (actually 9-pin because 1 is missing for key)
+corresponding to COM1 (0x3f8). The cables for these are not hard to come by and if you have been around long enough
+and were smart enough to strip them off an older machine (e.g. 486 era) before sending it to the trash heap then you
+probably have one. All Xserve (as far as I'm aware) also provide serial on COM1 at the legacy IO base address and
+the standard DB-9 male port is present on the exterior of the machine.</p>
+
+<p>The other end of the null modem cable will ideally go to your host machine, that is the one you are going to be
+running GDB on. However, this does not have to be the case. Theoretically you can perfectly well use a USB to
+Serial adapter on the host side but in practice many of the OS X drivers for these seem to be absolute crap or
+power cycling the target machine on the other end of the cable sends enough electrical noise down the serial line
+that it just locks up.</p>
+
+<p>As a workaround for these problems, SerialKDPProxy should build and run on Linux and hopefully other UNIX-like
+operating systems. Patches are encouraged if this is not the case. If you do run SerialKDPProxy on a Linux machine
+then instead of attaching GDB to localhost you'll attach GDB to the name or IP of your linux box. In all cases GDB
+itself runs on a Mac OS X machine you use as the debugger host</p>
+
+<h3>Running SerialKDPProxy</h3>
+<p>The SerialKDPProxy must be run on the machine with the serial port. If on an OS X machine with a hardware serial
+port then this will be /dev/tty.serial1. If using a USB adapter then the name will be something else but it should
+always be of the form /dev/tty.* like /dev/tty.usbserial1. If you decide to run it on a Linux machine with a hardware
+serial port then /dev/ttyS0 will usually be COM1 and /dev/ttyS1 will usually be COM2.</p>
+
+<h3>Using GDB</h3>
+<p>Your host machine (the machine you run GDB on) must be an OS X machine unless you have somehow managed to build
+a cross-GDB (not for the faint of heart). If the proxy is also running on the same machine then you will use
+the command kdp-reattach localhost to attach to it. If the proxy is running on a linux machine named "linuxbox"
+then you'll want to kdp-reattach linuxbox.</p>
+
+<p>For optimum debugging you want to download the OS X kernel debug kit (you can get this from ADC even with a free
+ADC account). You can basically follow the included instructions. That is, cd /Volumes/KernelDebugKit. Then
+gdb ./mach_kernel. Then from gdb source kgmacros. Then kdp-reattach &lt;hostname or IP of proxy&gt; as described
+above.</p>
+
+<h3>Observations</h3>
+<p>One thing to be aware of is that GDB has a tendency to basically lock up when it doesn't get the return packets
+it wants from the kernel. So you may run into situations where Ctrl+C does not work and you must kill your GDB
+process. This typically tends to happen when something has caused the kernel to fully lock up to the point where
+it won't even properly panic itself.</p>
+
+<p>One other thing to be aware of is that RS-232 isn't exactly an ideal communication medium. Using the kgmacros
+functions that dump various structures (e.g. things in the IORegistry) may very well take several minutes to
+complete and there will be plenty of cases where GDB will send a packet and not receive a reply. In general it
+will resend the packet and the second time it will receive a reply. I don't believe this is a problem with
+SerialKDPProxy but I could be wrong.</p>
+
+<p>Part of the reason for this is that the target kernel will only communicate with the host when it is stopped.
+There is no facility for stopping a running kernel and this is a general limitation of KDP, not specific to
+running it over RS-232. In general kernel debugging is only useful if you know what things you'd like to break
+on. Once the kernel has stopped on your breakpoint and has sent the machine state over to GDB you will be
+able to examine kernel memory from this point.</p>
+
+</body>
+</html>
371 src/SerialKDPProxy.c
@@ -0,0 +1,371 @@
+#define _BSD_SOURCE 1
+//#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "kdp_serial.h"
+#include "ip_sum.h"
+
+#if !defined __linux__ || defined(HAVE_SIN_LEN)
+ #ifdef INET_ADDRSTRLEN
+ #define HAVE_SIN_LEN 1
+ #else
+ #define HAVE_SIN_LEN 0
+ #endif
+#endif
+
+#define REVERSE_VIDEO "\x1b[7m"
+#define NORMAL_VIDEO "\x1b[0m"
+
+int opt_verbose = 0;
+int g_linecount = 0;
+static int g_ser;
+static void serial_putc(char c)
+{
+
+ write(g_ser, &c, 1);
+
+ if (g_linecount && !(g_linecount % 16)) {
+ //fprintf(stderr, "\n ");
+ g_linecount = 0;
+ }
+
+ if (opt_verbose) {
+ if ((uint8_t)c == 0xfa) // start:
+ ;//fprintf(stderr, " ");
+ else if ((uint8_t)c == 0xfb) { // stop:
+ fprintf(stderr, "\n\n");
+ g_linecount = 0;
+ } else {
+ fprintf(stderr, "%02x ", (uint8_t)c);
+ g_linecount++;
+ }
+ }
+}
+
+// OS X's native poll function cannot handle TTY devices.
+int working_poll(struct pollfd fds[], nfds_t nfds, int timeout)
+{
+ fd_set readfds;
+ fd_set writefds;
+ fd_set errorfds;
+ int maxfd = 0;
+ int i, r;
+
+ struct timeval tv = { timeout / 1000, (timeout % 1000) * 10 };
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&errorfds);
+
+ for (i = 0; i < nfds; ++i) {
+ if (fds[i].fd + 1 > maxfd)
+ maxfd = fds[i].fd + 1;
+
+ if (fds[i].events & POLLIN) {
+ FD_SET(fds[i].fd, &readfds);
+ }
+
+ if (fds[i].events & POLLOUT) {
+ FD_SET(fds[i].fd, &writefds);
+ }
+ }
+
+ r = select(maxfd, &readfds, &writefds, &errorfds, timeout != -1 ? &tv : NULL);
+ if (r <= 0)
+ return r;
+ r = 0;
+
+ for (i = 0; i < nfds; ++i) {
+ fds[i].revents = 0;
+
+ if (FD_ISSET(fds[i].fd, &readfds))
+ fds[i].revents |= POLLIN;
+
+ if (FD_ISSET(fds[i].fd, &writefds))
+ fds[i].revents |= POLLOUT;
+
+ if (FD_ISSET(fds[i].fd, &writefds))
+ fds[i].revents |= POLLERR;
+
+ if (fds[i].revents != 0)
+ ++r;
+ }
+
+ return r;
+}
+
+struct udp_ip_ether_frame_hdr {
+ struct ether_header eh;
+ struct ip ih;
+ struct udphdr uh;
+} __attribute__((packed));
+
+union frame_t {
+ uint8_t buf[1500];
+ struct udp_ip_ether_frame_hdr h;
+};
+union frame_t frame;
+
+/*!
+ @abstract The (fake) MAC address of the kernel's KDP
+ @discussion
+ This must be "serial" because the kernel won't allow anything else.
+ */
+u_char const client_macaddr[ETHER_ADDR_LEN] = { 's', 'e', 'r', 'i', 'a', 'l' };
+
+/*!
+ @abstract The (fake) MAC address of our side of the KDP
+ @discussion
+ This can be anything really. But it's more efficient to use characters that
+ don't need to be escaped by kdp_serialize_packet.
+ */
+u_char const our_macaddr[ETHER_ADDR_LEN] = { 'f', 'o', 'o', 'b', 'a', 'r' };
+
+/*!
+ @abstract The last IP sequence number.
+ */
+static uint16_t out_ip_id = 0;
+
+/*!
+ @abstract A helper function to initialize the new UDP ethernet frame
+ @argument pFrame Pointer to ethernet frame to initialize
+ @argument sAddr Source IP address to use for packet, in network byte order
+ @argument sPort Source UDP port to use for packet, in network byte order
+ @argument dataLen Size of UDP data
+ */
+void setup_udp_frame(union frame_t *pFrame, struct in_addr sAddr, in_port_t sPort, ssize_t dataLen)
+{
+ memcpy(pFrame->h.eh.ether_dhost, client_macaddr, ETHER_ADDR_LEN);
+ memcpy(pFrame->h.eh.ether_shost, our_macaddr, ETHER_ADDR_LEN);
+ pFrame->h.eh.ether_type = htons(ETHERTYPE_IP);
+ pFrame->h.ih.ip_v = 4;
+ pFrame->h.ih.ip_hl = sizeof(struct ip) >> 2;
+ pFrame->h.ih.ip_tos = 0;
+ pFrame->h.ih.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + dataLen);
+ pFrame->h.ih.ip_id = htons(out_ip_id++);
+ pFrame->h.ih.ip_off = 0;
+ pFrame->h.ih.ip_ttl = 60; // UDP_TTL from kdp_udp.c
+ pFrame->h.ih.ip_p = IPPROTO_UDP;
+ pFrame->h.ih.ip_sum = 0;
+ pFrame->h.ih.ip_src = sAddr; // Already in NBO
+ pFrame->h.ih.ip_dst.s_addr = 0xABADBABE; // FIXME: Endian.. little to little will be fine here.
+ //pFrame->h.ih.ip_dst.s_addr = 0xBEBAADAB; // FIXME: Endian.. host big, target little (or vice versa)
+ // Ultimately.. the address doesnt seem to actually matter to it.
+
+ pFrame->h.ih.ip_sum = htons(~ip_sum((unsigned char *)&pFrame->h.ih, pFrame->h.ih.ip_hl));
+
+ pFrame->h.uh.uh_sport = sPort; // Already in NBO
+ pFrame->h.uh.uh_dport = htons(41139);
+ pFrame->h.uh.uh_ulen = htons(sizeof(struct udphdr) + dataLen);
+ pFrame->h.uh.uh_sum = 0; // does it check this shit?
+}
+
+int set_termopts(int fd)
+{
+ struct termios options;
+ int rc;
+
+ rc = 1;
+ do {
+ memset(&options, 0, sizeof(options));
+ tcgetattr(fd, &options);
+
+ if (-1 == cfsetispeed(&options, B115200)) {
+ fprintf(stderr, "error, could not set baud rate\n");
+ break;
+ }
+
+ if (-1 == cfsetospeed(&options, B115200)) {
+ fprintf(stderr, "error, could not set baud rate\n");
+ break;
+ }
+
+ options.c_iflag = 0;
+ options.c_oflag = 0;
+ options.c_cflag = CS8 | CREAD | CLOCAL;
+ options.c_lflag = 0;
+ options.c_cc[VMIN] = 1;
+ options.c_cc[VTIME] = 5;
+
+ tcflush(fd, TCIFLUSH);
+ if (-1 == tcsetattr(fd, TCSANOW, &options)) {
+ fprintf(stderr, "error, could not tcsetattr\n");
+ break;
+ }
+ rc = 0;
+ } while(0);
+
+ return rc;
+}
+
+
+int main(int argc, char **argv)
+{
+ char *device_name;
+ int s;
+
+ device_name = NULL;
+ while (1) {
+ int c;
+
+ c = getopt(argc, argv, "vd:");
+ if (-1 == c)
+ break;
+
+ switch (c) {
+ case 'v':
+ opt_verbose = 1;
+ break;
+ }
+ }
+
+ if (argc > optind)
+ device_name = argv[optind++];
+ else
+ device_name = "/dev/tty.usbserial-A40084Fi";
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ fprintf(stderr, "Failed to open socket\n");
+ return 1;
+ }
+
+ struct sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+#if HAVE_SIN_LEN
+ saddr.sin_len = INET_ADDRSTRLEN;
+#endif
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(41139);
+ saddr.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(s, (struct sockaddr*)&saddr, sizeof(saddr)) != 0) {
+ fprintf(stderr, "Failed to bind\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Opening %s\n", device_name);
+
+ g_ser = open(device_name, O_RDWR | O_NONBLOCK | O_NOCTTY);
+ if (-1 == g_ser) {
+ fprintf(stderr, "Failed to open serial\n");
+ return 1;
+ }
+
+ set_termopts(g_ser);
+ fprintf(stderr, "Waiting for packets, pid=%lu\n", (long unsigned)getpid());
+
+ struct pollfd pollfds[3] = {
+ { s, POLLIN, 0},
+ { g_ser, POLLIN, 0},
+ { STDIN_FILENO, POLLIN, 0}
+ };
+
+ while (working_poll(pollfds, 3, -1)) {
+ ssize_t bytesReceived = 0;
+
+ if ((pollfds[0].revents & POLLIN) != 0 ) {
+ struct sockaddr_in clientAddr;
+ socklen_t cal = sizeof(clientAddr);
+ bytesReceived = recvfrom(s, frame.buf + sizeof(frame.h), sizeof(frame) - sizeof(frame.h), 0, (struct sockaddr *) &clientAddr, &cal);
+ in_port_t clntPort = ((struct sockaddr_in*)&clientAddr)->sin_port;
+
+ if (opt_verbose)
+ fprintf(stderr, "%ld bytes received from: %s\n", bytesReceived, inet_ntoa(clientAddr.sin_addr));
+
+ setup_udp_frame(&frame, ((struct sockaddr_in*)&clientAddr)->sin_addr, clntPort, bytesReceived);
+ kdp_serialize_packet(frame.buf, bytesReceived + sizeof(frame.h), &serial_putc);
+ fflush(stderr);
+ } else if ((pollfds[0].revents) != 0) {
+ fprintf(stderr, "WTF?\n");
+ }
+
+ if ((pollfds[1].revents & POLLIN) != 0) {
+ unsigned char c;
+
+ if (read(g_ser, &c, 1) == 1) {
+ unsigned int len = SERIALIZE_READING;
+ union frame_t *pInputFrame = (void*)kdp_unserialize_packet(c, &len);
+ if (pInputFrame != NULL) {
+ if (pInputFrame->h.ih.ip_p == 17) {
+ int nr;
+
+ size_t frameDataLen = len - sizeof(pInputFrame->h);
+
+ struct sockaddr_in clientAddr;
+ bzero(&clientAddr, sizeof(clientAddr));
+#if HAVE_SIN_LEN
+ clientAddr.sin_len = INET_ADDRSTRLEN;
+#endif
+ clientAddr.sin_family = AF_INET;
+ clientAddr.sin_port = pInputFrame->h.uh.uh_dport;
+ clientAddr.sin_addr = pInputFrame->h.ih.ip_dst;
+
+ nr = sendto(s, pInputFrame->buf + sizeof(pInputFrame->h), frameDataLen, 0, (struct sockaddr*)&clientAddr, sizeof(clientAddr));
+#if 0
+ size_t udpDataLen = ntohs(pInputFrame->h.uh.uh_ulen) - sizeof(struct udphdr);
+ size_t ipDataLen = ntohs(pInputFrame->h.ih.ip_len) - sizeof(struct ip) - sizeof(struct udphdr);
+ fprintf(stderr, "Sent reply packet %d, %d, %d to UDP %d\n", (int)udpDataLen, (int)ipDataLen, (int)frameDataLen, (int)ntohs(pInputFrame->h.uh.uh_dport));
+#endif
+ } else {
+ fprintf(stderr, "Discarding non-UDP packet proto %d of length %u\n", pInputFrame->h.ih.ip_p, len);
+ }
+ } else {
+ if (len == SERIALIZE_WAIT_START) {
+ uint8_t b = c;
+
+ if ((b >= 0x80) || (b > 26 && b < ' ') ) {
+ printf(REVERSE_VIDEO "\\x%02x" NORMAL_VIDEO, b);
+ fflush(stdout);
+ } else if ((b <= 26) && (b != '\r') && (b != '\n') ) {
+ printf(REVERSE_VIDEO "^%c" NORMAL_VIDEO, b + '@'); // 0 = @, 1 = A, ..., 26 = Z
+ fflush(stdout);
+ } else
+ putchar(c);
+ }
+ }
+ }
+ } else if ((pollfds[1].revents) != 0) {
+ fprintf(stderr, "Shutting down serial input due to 0x%x\n", pollfds[1].revents);
+ pollfds[1].events = 0;
+ }
+
+ if ((pollfds[2].revents & POLLIN) != 0) {
+ int nw;
+ uint8_t consoleBuf[1024];
+
+ bytesReceived = read(pollfds[2].fd, consoleBuf, sizeof(consoleBuf));
+
+ consoleBuf[bytesReceived-1] = '\0';
+ fprintf(stderr, "%zd bytes received on console\n \"%s\"\n\n", bytesReceived, consoleBuf);
+ consoleBuf[bytesReceived-1] = '\n';
+
+ nw = write(g_ser, consoleBuf, bytesReceived);
+ if (-1 == nw)
+ fprintf(stderr, "error, unable to write to the serial port\n");
+ } else if ((pollfds[2].revents) != 0) {
+ fprintf(stderr, "Shutting down console input due to 0x%x\n", pollfds[2].revents);
+ pollfds[2].events = 0;
+ }
+ }
+
+ return 0;
+}
+
52 src/ip_sum.h
@@ -0,0 +1,52 @@
+// Swiped from osfmk/kdp/kdp_udp.c
+
+/*
+ * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * 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.
+ *
+ * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+static unsigned short
+ip_sum(
+ unsigned char *c,
+ unsigned int hlen
+ )
+{
+ unsigned int high, low, sum;
+
+ high = low = 0;
+ while (hlen-- > 0) {
+ low += c[1] + c[3];
+ high += c[0] + c[2];
+
+ c += sizeof (int);
+ }
+
+ sum = (high << 8) + low;
+ sum = (sum >> 16) + (sum & 65535);
+
+ return (sum > 65535 ? sum - 65535 : sum);
+}
+
108 src/kdp_serial.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * 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.
+ *
+ * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+#include "kdp_serial.h"
+
+#define SKDP_START_CHAR 0xFA
+#define SKDP_END_CHAR 0xFB
+#define SKDP_ESC_CHAR 0xFE
+
+static enum {DS_WAITSTART, DS_READING, DS_ESCAPED} dsState;
+static unsigned char dsBuffer[1518];
+static int dsPos;
+
+void kdp_serialize_packet(unsigned char *packet, unsigned int len, void (*outFunc)(char))
+{
+ unsigned int index;
+ outFunc(SKDP_START_CHAR);
+ for (index = 0; index < len; index++) {
+ unsigned char byte = *packet++;
+ //need to escape '\n' because the kernel serial output turns it into a cr/lf
+ if(byte == SKDP_START_CHAR || byte == SKDP_END_CHAR || byte == SKDP_ESC_CHAR || byte == '\n')
+ {
+ outFunc(SKDP_ESC_CHAR);
+ byte = ~byte;
+ }
+ outFunc(byte);
+ }
+ outFunc(SKDP_END_CHAR);
+}
+
+unsigned char *kdp_unserialize_packet(unsigned char byte, unsigned int *len)
+{
+ switch(dsState)
+ {
+ case DS_WAITSTART:
+ if(byte == SKDP_START_CHAR)
+ {
+// printf("got start char\n");
+ dsState = DS_READING;
+ dsPos = 0;
+ *len = SERIALIZE_READING;
+ return 0;
+ }
+ *len = SERIALIZE_WAIT_START;
+ break;
+ case DS_READING:
+ if(byte == SKDP_ESC_CHAR)
+ {
+ dsState = DS_ESCAPED;
+ *len = SERIALIZE_READING;
+ return 0;
+ }
+ if(byte == SKDP_START_CHAR)
+ {
+// printf("unexpected start char, resetting\n");
+ dsPos = 0;
+ *len = SERIALIZE_READING;
+ return 0;
+ }
+ if(byte == SKDP_END_CHAR)
+ {
+ dsState = DS_WAITSTART;
+ *len = dsPos;
+ dsPos = 0;
+ return dsBuffer;
+ }
+ dsBuffer[dsPos++] = byte;
+ break;
+ case DS_ESCAPED:
+// printf("unescaping %02x to %02x\n", byte, ~byte);
+ dsBuffer[dsPos++] = ~byte;
+ dsState = DS_READING;
+ *len = SERIALIZE_READING;
+ break;
+ }
+ if(dsPos == sizeof(dsBuffer)) //too much data...forget this packet
+ {
+ dsState = DS_WAITSTART;
+ dsPos = 0;
+ *len = SERIALIZE_WAIT_START;
+ }
+
+ return 0;
+}
51 src/kdp_serial.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * 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.
+ *
+ * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+#ifndef _KDP_SERIAL_H_
+#define _KDP_SERIAL_H_
+
+/*
+ * APIs for escaping a KDP UDP packet into a byte stream suitable
+ * for a standard serial console
+ */
+
+enum {SERIALIZE_WAIT_START, SERIALIZE_READING};
+
+/*
+ * Take a buffer of specified length and output it with the given
+ * function. Escapes special characters as needed
+ */
+void kdp_serialize_packet(unsigned char *, unsigned int, void (*func)(char));
+
+/*
+ * Add a new character to an internal buffer, and return that
+ * buffer when a fully constructed packet has been identified.
+ * Will track intermediate state using magic enums above
+ */
+unsigned char *kdp_unserialize_packet(unsigned char, unsigned int *);
+
+#endif
Please sign in to comment.
Something went wrong with that request. Please try again.