[syslinux:master] sysdump: first working version (can dump lowmem over TFTP)

syslinux-bot for H. Peter Anvin hpa at zytor.com
Sat Feb 6 17:48:13 PST 2010


Commit-ID:  0f4c904b628adb906a3d534b5efa031071ec6b5e
Gitweb:     http://syslinux.zytor.com/commit/0f4c904b628adb906a3d534b5efa031071ec6b5e
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Sat, 6 Feb 2010 16:38:53 -0800
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Sat, 6 Feb 2010 16:38:53 -0800

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 at zytor.com>


---
 Makefile                 |    3 +-
 com32/Makefile           |    3 +-
 com32/sysdump/backend.h  |   44 ++++++++++++++++
 com32/sysdump/backends.c |   27 ++++++++++
 com32/sysdump/be_tftp.c  |  127 ++++++++++++++++++++++++++++++++++++++++++++++
 com32/sysdump/cpio.c     |   64 +++++++++++++++++++++++
 com32/sysdump/data.h     |    3 +
 com32/sysdump/main.c     |   62 +++++++++++++++++++++--
 com32/sysdump/zout.c     |   62 ++++++++++++++++++++++
 9 files changed, 389 insertions(+), 6 deletions(-)

diff --git a/Makefile b/Makefile
index d0f5e71..d9f43fc 100644
--- 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
diff --git a/com32/Makefile b/com32/Makefile
index 69a125e..3821e58 100644
--- a/com32/Makefile
+++ b/com32/Makefile
@@ -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
index 0000000..eb6530e
--- /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 0000000..75f1d9d
--- /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 0000000..17275b1
--- /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 0000000..984ed3c
--- /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 0000000..697b1a3
--- /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 5aba85e..1390d5c 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 0000000..27e6669
--- /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;
+}



More information about the Syslinux-commits mailing list