summaryrefslogtreecommitdiff
path: root/com32/sysdump
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-02-06 16:38:53 -0800
committerH. Peter Anvin <hpa@zytor.com>2010-02-06 16:38:53 -0800
commit0f4c904b628adb906a3d534b5efa031071ec6b5e (patch)
tree9809cadf2ef986c956ad095c7abdac163bae54c2 /com32/sysdump
parent7d31294fd5966ba2a8d6e47f663a13d52eae9046 (diff)
downloadsyslinux-0f4c904b628adb906a3d534b5efa031071ec6b5e.tar.gz
sysdump: first working version (can dump lowmem over TFTP)
Most of the infrastructure for sysdump, and enough to make it useful. Sysdump will produce a gzipped cpio archive containing individual data members; for now only a memory dump of lowmem. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'com32/sysdump')
-rw-r--r--com32/sysdump/backend.h44
-rw-r--r--com32/sysdump/backends.c27
-rw-r--r--com32/sysdump/be_tftp.c127
-rw-r--r--com32/sysdump/cpio.c64
-rw-r--r--com32/sysdump/data.h3
-rw-r--r--com32/sysdump/main.c62
-rw-r--r--com32/sysdump/zout.c62
7 files changed, 385 insertions, 4 deletions
diff --git a/com32/sysdump/backend.h b/com32/sysdump/backend.h
new file mode 100644
index 00000000..eb6530e1
--- /dev/null
+++ b/com32/sysdump/backend.h
@@ -0,0 +1,44 @@
+#ifndef BACKEND_H
+#define BACKEND_H
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <zlib.h>
+
+struct backend {
+ const char *name;
+ int blocksize;
+
+ int (*open)(struct backend *, const char *argv[]);
+ int (*write)(struct backend *, const char *buf, size_t len);
+
+ z_stream zstream;
+ char *outbuf;
+
+ union {
+ struct {
+ uint32_t my_ip;
+ uint32_t srv_ip;
+ uint16_t my_port;
+ uint16_t srv_port;
+ uint16_t seq;
+ } tftp;
+ };
+};
+
+/* zout.c */
+int init_data(struct backend *be, const char *argv[]);
+int write_data(struct backend *be, const void *buf, size_t len, bool flush);
+
+/* cpio.c */
+#define cpio_init init_data
+int cpio_mkdir(struct backend *be, const char *filename);
+int cpio_writefile(struct backend *be, const char *filename,
+ const void *data, size_t len);
+int cpio_close(struct backend *be);
+
+/* backends.c */
+struct backend *get_backend(const char *name);
+
+#endif /* BACKEND_H */
diff --git a/com32/sysdump/backends.c b/com32/sysdump/backends.c
new file mode 100644
index 00000000..75f1d9d9
--- /dev/null
+++ b/com32/sysdump/backends.c
@@ -0,0 +1,27 @@
+/*
+ * backends.c
+ */
+
+#include <string.h>
+#include "backend.h"
+
+extern struct backend be_tftp;
+
+static struct backend *backends[] =
+{
+ &be_tftp,
+ NULL
+};
+
+struct backend *get_backend(const char *name)
+{
+ struct backend *be, **bep;
+
+ for (bep = backends ; (be = *bep) ; bep++) {
+ if (!strcmp(name, be->name))
+ return be;
+ }
+
+ return NULL;
+}
+
diff --git a/com32/sysdump/be_tftp.c b/com32/sysdump/be_tftp.c
new file mode 100644
index 00000000..17275b16
--- /dev/null
+++ b/com32/sysdump/be_tftp.c
@@ -0,0 +1,127 @@
+/*
+ * TFTP data output backend
+ */
+
+#include <string.h>
+#include <syslinux/pxe.h>
+#include <syslinux/config.h>
+#include <netinet/in.h>
+#include <sys/times.h>
+
+#include "backend.h"
+
+enum tftp_opcode {
+ TFTP_RRQ = 1,
+ TFTP_WRQ = 2,
+ TFTP_DATA = 3,
+ TFTP_ACK = 4,
+ TFTP_ERROR = 5,
+};
+
+static uint16_t local_port = 0x4000;
+
+static int send_ack_packet(struct backend *be, const void *pkt, size_t len)
+{
+ com32sys_t ireg, oreg;
+ t_PXENV_UDP_WRITE *uw = __com32.cs_bounce;
+ t_PXENV_UDP_READ *ur = __com32.cs_bounce;
+ clock_t start;
+ static const clock_t timeouts[] = {
+ 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
+ 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
+ };
+ const clock_t *timeout;
+
+ memset(&ireg, 0, sizeof ireg);
+ ireg.eax.w[0] = 0x0009;
+
+ for (timeout = timeouts ; *timeout ; timeout++) {
+ memset(uw, 0, sizeof uw);
+ memcpy(uw+1, pkt, len);
+ uw->ip = be->tftp.srv_ip;
+ uw->src_port = be->tftp.my_port;
+ uw->dst_port = be->tftp.srv_port ? be->tftp.srv_port : htons(69);
+ uw->buffer_size = len;
+ uw->buffer = FAR_PTR(uw+1);
+
+ ireg.ebx.w[0] = PXENV_UDP_WRITE;
+ ireg.es = SEG(uw);
+ ireg.edi.w[0] = OFFS(uw);
+
+ __intcall(0x22, &ireg, &oreg);
+
+ start = times(NULL);
+
+ do {
+ memset(ur, 0, sizeof ur);
+ ur->src_ip = be->tftp.srv_ip;
+ ur->dest_ip = be->tftp.my_ip;
+ ur->s_port = be->tftp.srv_port;
+ ur->d_port = be->tftp.my_port;
+ ur->buffer_size = __com32.cs_bounce_size - sizeof *ur;
+ ur->buffer = FAR_PTR(ur+1);
+
+ ireg.ebx.w[0] = PXENV_UDP_READ;
+ ireg.es = SEG(ur);
+ ireg.edi.w[0] = OFFS(ur);
+ __intcall(0x22, &ireg, &oreg);
+
+ if (!(oreg.eflags.l & EFLAGS_CF) &&
+ ur->status == PXENV_STATUS_SUCCESS &&
+ be->tftp.srv_ip == ur->src_ip &&
+ (be->tftp.srv_port == 0 ||
+ be->tftp.srv_port == ur->s_port)) {
+ uint16_t *xb = (uint16_t *)(ur+1);
+ if (ntohs(xb[0]) == TFTP_ACK &&
+ ntohs(xb[1]) == be->tftp.seq) {
+ be->tftp.srv_port = ur->s_port;
+ return 0; /* All good! */
+ } else if (ntohs(xb[1]) == TFTP_ERROR) {
+ return -1; /* All bad! */
+ }
+ }
+ } while ((clock_t)(times(NULL) - start) < *timeout);
+ }
+
+ return -1; /* No success... */
+}
+
+static int be_tftp_open(struct backend *be, const char *argv[])
+{
+ char buffer[512+4+6];
+ int nlen;
+ const union syslinux_derivative_info *sdi =
+ syslinux_derivative_info();
+
+ be->tftp.my_ip = sdi->pxe.myip;
+ be->tftp.my_port = htons(local_port++);
+ be->tftp.srv_ip = pxe_dns(argv[1]);
+ be->tftp.srv_port = 0;
+ be->tftp.seq = 0;
+
+ buffer[0] = 0;
+ buffer[1] = TFTP_WRQ;
+ nlen = strlcpy(buffer+2, argv[0], 512);
+ memcpy(buffer+3+nlen, "octet", 6);
+
+ return send_ack_packet(be, buffer, 2+nlen+1+6);
+}
+
+static int be_tftp_write(struct backend *be, const char *buf, size_t len)
+{
+ char buffer[512+4];
+
+ buffer[0] = 0;
+ buffer[1] = TFTP_DATA;
+ *((uint16_t *)(buffer+2)) = htons(++be->tftp.seq);
+ memcpy(buffer+4, buf, len);
+
+ return send_ack_packet(be, buffer, len+4);
+}
+
+struct backend be_tftp = {
+ .name = "tftp",
+ .blocksize = 512,
+ .open = be_tftp_open,
+ .write = be_tftp_write,
+};
diff --git a/com32/sysdump/cpio.c b/com32/sysdump/cpio.c
new file mode 100644
index 00000000..984ed3cf
--- /dev/null
+++ b/com32/sysdump/cpio.c
@@ -0,0 +1,64 @@
+/*
+ * cpio.c
+ *
+ * Write a compressed CPIO file
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <zlib.h>
+#include "backend.h"
+
+static char pad[4]; /* Up to 4 zero bytes */
+
+static int cpio_hdr(struct backend *be, uint32_t mode, size_t datalen,
+ const char *filename, bool flush)
+{
+ static uint32_t inode = 2;
+ char hdr[6+13*8+1];
+ int nlen = strlen(filename)+1;
+ int rv = 0;
+
+ sprintf(hdr, "%06o%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
+ 070701, /* c_magic */
+ inode++, /* c_ino */
+ mode, /* c_mode */
+ 0, /* c_uid */
+ 0, /* c_gid */
+ 1, /* c_nlink */
+ 0, /* c_mtime */
+ datalen, /* c_filesize */
+ 0, /* c_maj */
+ 0, /* c_min */
+ 0, /* c_rmaj */
+ 0, /* c_rmin */
+ nlen, /* c_namesize */
+ 0); /* c_chksum */
+ rv |= write_data(be, hdr, 6+13*8, false);
+ rv |= write_data(be, filename, nlen, false);
+ rv |= write_data(be, pad, -nlen & 3, flush);
+ return rv;
+}
+
+int cpio_mkdir(struct backend *be, const char *filename)
+{
+ return cpio_hdr(be, 0040755, 0, filename, false);
+}
+
+int cpio_writefile(struct backend *be, const char *filename,
+ const void *data, size_t len)
+{
+ int rv;
+
+ rv = cpio_hdr(be, 0100755, len, filename, false);
+ rv |= write_data(be, data, len, false);
+ rv |= write_data(be, pad, -len & 3, false);
+}
+
+int cpio_close(struct backend *be)
+{
+ return cpio_hdr(be, 0, 0, "TRAILER!!!", true);
+}
+
diff --git a/com32/sysdump/data.h b/com32/sysdump/data.h
new file mode 100644
index 00000000..697b1a34
--- /dev/null
+++ b/com32/sysdump/data.h
@@ -0,0 +1,3 @@
+#ifndef DATA_H
+#define DATA_H
+
diff --git a/com32/sysdump/main.c b/com32/sysdump/main.c
index 5aba85ea..1390d5c2 100644
--- a/com32/sysdump/main.c
+++ b/com32/sysdump/main.c
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,17 +17,19 @@
#include <stdbool.h>
#include <inttypes.h>
#include <dprintf.h>
-#include "ymsend.h"
-#include "srecsend.h"
+#include <console.h>
+#include <sys/cpu.h>
+#include "backend.h"
-const char *program = "memdump";
+const char *program = "sysdump";
-void __attribute__ ((noreturn)) die(const char *msg)
+__noreturn die(const char *msg)
{
printf("%s: %s\n", program, msg);
exit(1);
}
+#if 0
static void get_bytes(void *buf, size_t len, struct file_info *finfo,
size_t pos)
{
@@ -107,3 +110,54 @@ int main(int argc, char *argv[])
return 0;
}
+#endif
+
+static char *lowmem;
+static size_t lowmem_len;
+
+static void snapshot_lowmem(void)
+{
+ extern void _start(void);
+
+ lowmem_len = (size_t)_start;
+ lowmem = malloc(lowmem_len);
+ if (lowmem) {
+ printf("Snapshotting lowmem... ");
+ cli();
+ memcpy(lowmem, (void *)0, lowmem_len);
+ sti();
+ printf("ok\n");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct backend *be;
+
+ openconsole(&dev_null_r, &dev_stdcon_w);
+
+ if (argc < 4) {
+ printf("Usage:\n"
+ " sysdump tftp filename server_hostname\n");
+ exit(1);
+ }
+
+ /* Do this as early as possible */
+ snapshot_lowmem();
+
+ be = get_backend(argv[1]);
+ if (!be)
+ die("unknown backend");
+
+ if (cpio_init(be, argv+2))
+ die("backend initialization error");
+
+ if (lowmem) {
+ cpio_writefile(be, "lowmem.bin", lowmem, lowmem_len);
+ free(lowmem);
+ }
+
+ cpio_close(be);
+
+ return 0;
+}
diff --git a/com32/sysdump/zout.c b/com32/sysdump/zout.c
new file mode 100644
index 00000000..27e6669b
--- /dev/null
+++ b/com32/sysdump/zout.c
@@ -0,0 +1,62 @@
+/*
+ * Compress input and feed it to a block-oriented back end.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <zlib.h>
+#include "backend.h"
+
+int init_data(struct backend *be, const char *argv[])
+{
+ be->outbuf = malloc(be->blocksize);
+ if (!be->outbuf)
+ return -1;
+
+ if (be->open(be, argv))
+ return -1;
+
+ memset(&be->zstream, 0, sizeof be->zstream);
+
+ be->zstream.next_out = (void *)be->outbuf;
+ be->zstream.avail_out = be->blocksize;
+
+ /* Initialize a gzip data stream */
+ if (deflateInit2(&be->zstream, 9, Z_DEFLATED,
+ 16+15, 9, Z_DEFAULT_STRATEGY) < 0)
+ return -1;
+
+ return 0;
+}
+
+int write_data(struct backend *be, const void *buf, size_t len, bool flush)
+{
+ int rv = Z_OK;
+
+ be->zstream.next_in = buf;
+ be->zstream.avail_in = len;
+
+ while (be->zstream.avail_in || (flush && rv == Z_OK)) {
+ rv = deflate(&be->zstream, flush ? Z_FINISH : Z_NO_FLUSH);
+ if (be->zstream.avail_out == 0) {
+ if (be->write(be, be->outbuf, be->blocksize))
+ return -1;
+ be->zstream.next_out = (void *)be->outbuf;
+ be->zstream.avail_out = be->blocksize;
+ }
+ if (rv == Z_STREAM_ERROR)
+ return -1;
+ }
+
+ if (flush) {
+ /* Output the last (fractional) packet... may be zero */
+ if (be->write(be, be->outbuf, be->blocksize - be->zstream.avail_out))
+ return -1;
+ free(be->outbuf);
+ }
+
+ return 0;
+}