sysdump: first working version (can dump lowmem over TFTP)
authorH. Peter Anvin <hpa@zytor.com>
Sun, 7 Feb 2010 00:38:53 +0000 (16:38 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Sun, 7 Feb 2010 00:38:53 +0000 (16:38 -0800)
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>
Makefile
com32/Makefile
com32/sysdump/backend.h [new file with mode: 0644]
com32/sysdump/backends.c [new file with mode: 0644]
com32/sysdump/be_tftp.c [new file with mode: 0644]
com32/sysdump/cpio.c [new file with mode: 0644]
com32/sysdump/data.h [new file with mode: 0644]
com32/sysdump/main.c
com32/sysdump/zout.c [new file with mode: 0644]

index d0f5e71..d9f43fc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -32,7 +32,8 @@ include $(topdir)/MCONFIG
 # List of module objects that should be installed for all derivatives
 MODULES = memdisk/memdisk memdump/memdump.com modules/*.com \
        com32/menu/*.c32 com32/modules/*.c32 com32/mboot/*.c32 \
-       com32/hdt/*.c32 com32/rosh/*.c32 com32/gfxboot/*.c32
+       com32/hdt/*.c32 com32/rosh/*.c32 com32/gfxboot/*.c32 \
+       com32/sysdump/*.c32
 
 # syslinux.exe is BTARGET so as to not require everyone to have the
 # mingw suite installed
index 69a125e..3821e58 100644 (file)
@@ -1,3 +1,4 @@
-SUBDIRS = lib gpllib libutil modules mboot menu samples rosh cmenu hdt gfxboot
+SUBDIRS = lib gpllib libutil modules mboot menu samples rosh cmenu \
+         hdt gfxboot sysdump
 all tidy dist clean spotless install:
        set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
diff --git a/com32/sysdump/backend.h b/com32/sysdump/backend.h
new file mode 100644 (file)
index 0000000..eb6530e
--- /dev/null
@@ -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 (file)
index 0000000..75f1d9d
--- /dev/null
@@ -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 (file)
index 0000000..17275b1
--- /dev/null
@@ -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 (file)
index 0000000..984ed3c
--- /dev/null
@@ -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 (file)
index 0000000..697b1a3
--- /dev/null
@@ -0,0 +1,3 @@
+#ifndef DATA_H
+#define DATA_H
+
index 5aba85e..1390d5c 100644 (file)
@@ -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
 #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 (file)
index 0000000..27e6669
--- /dev/null
@@ -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;
+}