diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-02-06 16:38:53 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-02-06 16:38:53 -0800 |
commit | 0f4c904b628adb906a3d534b5efa031071ec6b5e (patch) | |
tree | 9809cadf2ef986c956ad095c7abdac163bae54c2 /com32/sysdump | |
parent | 7d31294fd5966ba2a8d6e47f663a13d52eae9046 (diff) | |
download | syslinux-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.h | 44 | ||||
-rw-r--r-- | com32/sysdump/backends.c | 27 | ||||
-rw-r--r-- | com32/sysdump/be_tftp.c | 127 | ||||
-rw-r--r-- | com32/sysdump/cpio.c | 64 | ||||
-rw-r--r-- | com32/sysdump/data.h | 3 | ||||
-rw-r--r-- | com32/sysdump/main.c | 62 | ||||
-rw-r--r-- | com32/sysdump/zout.c | 62 |
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; +} |