From 88f5f9b480f036ef9da9cf9deb41935c9ad9743c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 7 Feb 2010 15:14:21 -0800 Subject: [PATCH] sysdump: change to a two-phase generate/output model, buffer in memory Instead of outputting data as it is generated, buffer it all in memory and then output it all at once. This not only gives us exact size and so on before we start, but turns out to be faster at least for TFTP. Signed-off-by: H. Peter Anvin --- com32/sysdump/backend.h | 30 +++--------- com32/sysdump/be_null.c | 30 ------------ com32/sysdump/be_tftp.c | 82 ++++++++++++++++++-------------- com32/sysdump/be_ymodem.c | 116 ++++++++++++++++++++++++---------------------- com32/sysdump/cpio.c | 17 +++---- com32/sysdump/dmi.c | 8 ++-- com32/sysdump/main.c | 17 ++++--- com32/sysdump/memmap.c | 2 +- com32/sysdump/serial.c | 2 - com32/sysdump/zout.c | 72 +++++++++++++++++++--------- 10 files changed, 183 insertions(+), 193 deletions(-) delete mode 100644 com32/sysdump/be_null.c diff --git a/com32/sysdump/backend.h b/com32/sysdump/backend.h index 0be80c6..21a9ff8 100644 --- a/com32/sysdump/backend.h +++ b/com32/sysdump/backend.h @@ -15,41 +15,26 @@ struct backend { const char *helpmsg; int minargs; - unsigned int blocksize; - unsigned int flags; - size_t dbytes; size_t zbytes; + const char **argv; - int (*open)(struct backend *, const char *argv[], size_t len); - int (*write)(struct backend *, const char *buf, size_t len); + int (*write)(struct backend *); 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; - struct { - struct serial_if serial; - uint16_t seq; - } ymodem; - }; + size_t alloc; }; /* zout.c */ -int init_data(struct backend *be, const char *argv[], size_t len); -int write_data(struct backend *be, const void *buf, size_t len, bool flush); +int init_data(struct backend *be, const char *argv[]); +int write_data(struct backend *be, const void *buf, size_t len); +int flush_data(struct backend *be); /* cpio.c */ int cpio_hdr(struct backend *be, uint32_t mode, size_t datalen, const char *filename); -int cpio_init(struct backend *be, const char *argv[], size_t len); +int cpio_init(struct backend *be, const char *argv[]); int cpio_mkdir(struct backend *be, const char *filename); int cpio_writefile(struct backend *be, const char *filename, const void *data, size_t len); @@ -63,6 +48,5 @@ struct backend *get_backend(const char *name); /* backends */ extern struct backend be_tftp; extern struct backend be_ymodem; -extern struct backend be_null; #endif /* BACKEND_H */ diff --git a/com32/sysdump/be_null.c b/com32/sysdump/be_null.c deleted file mode 100644 index 4aab99c..0000000 --- a/com32/sysdump/be_null.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Null data output backend - */ - -#include "backend.h" - -static int be_null_open(struct backend *be, const char *argv[], size_t len) -{ - (void)be; - (void)argv; - (void)len; - return 0; -} - -static int be_null_write(struct backend *be, const char *buf, size_t len) -{ - (void)be; - (void)buf; - (void)len; - return 0; -} - -struct backend be_null = { - .name = "null", - .helpmsg = "", - .minargs = 0, - .blocksize = 32768, /* arbitrary */ - .open = be_null_open, - .write = be_null_write, -}; diff --git a/com32/sysdump/be_tftp.c b/com32/sysdump/be_tftp.c index e9a9887..7964238 100644 --- a/com32/sysdump/be_tftp.c +++ b/com32/sysdump/be_tftp.c @@ -18,9 +18,16 @@ enum tftp_opcode { TFTP_ERROR = 5, }; -static uint16_t local_port = 0x4000; +struct tftp_state { + uint32_t my_ip; + uint32_t srv_ip; + uint16_t my_port; + uint16_t srv_port; + uint16_t seq; +}; -static int send_ack_packet(struct backend *be, const void *pkt, size_t len) +static int send_ack_packet(struct tftp_state *tftp, + const void *pkt, size_t len) { com32sys_t ireg, oreg; t_PXENV_UDP_WRITE *uw = __com32.cs_bounce; @@ -38,9 +45,9 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len) 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->ip = tftp->srv_ip; + uw->src_port = tftp->my_port; + uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69); uw->buffer_size = len; uw->buffer = FAR_PTR(uw+1); @@ -54,10 +61,10 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len) 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->src_ip = tftp->srv_ip; + ur->dest_ip = tftp->my_ip; + ur->s_port = tftp->srv_port; + ur->d_port = tftp->my_port; ur->buffer_size = __com32.cs_bounce_size - sizeof *ur; ur->buffer = FAR_PTR(ur+1); @@ -68,13 +75,13 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len) 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)) { + tftp->srv_ip == ur->src_ip && + (tftp->srv_port == 0 || + 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; + ntohs(xb[1]) == tftp->seq) { + tftp->srv_port = ur->s_port; return 0; /* All good! */ } else if (ntohs(xb[1]) == TFTP_ERROR) { return -1; /* All bad! */ @@ -86,47 +93,50 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len) return -1; /* No success... */ } -static int be_tftp_open(struct backend *be, const char *argv[], size_t len) +static int be_tftp_write(struct backend *be) { + static uint16_t local_port = 0x4000; + struct tftp_state tftp; char buffer[512+4+6]; int nlen; const union syslinux_derivative_info *sdi = syslinux_derivative_info(); + const char *data = be->outbuf; + size_t len = be->zbytes; + size_t chunk; - (void)len; - - 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; + tftp.my_ip = sdi->pxe.myip; + tftp.my_port = htons(local_port++); + tftp.srv_ip = pxe_dns(be->argv[1]); + tftp.srv_port = 0; + tftp.seq = 0; buffer[0] = 0; buffer[1] = TFTP_WRQ; - nlen = strlcpy(buffer+2, argv[0], 512); + nlen = strlcpy(buffer+2, be->argv[0], 512); memcpy(buffer+3+nlen, "octet", 6); - return send_ack_packet(be, buffer, 2+nlen+1+6); -} + if (send_ack_packet(&tftp, buffer, 2+nlen+1+6)) + return -1; -static int be_tftp_write(struct backend *be, const char *buf, size_t len) -{ - char buffer[512+4]; + do { + chunk = len >= 512 ? 512 : len; - buffer[0] = 0; - buffer[1] = TFTP_DATA; - *((uint16_t *)(buffer+2)) = htons(++be->tftp.seq); - memcpy(buffer+4, buf, len); + buffer[1] = TFTP_DATA; + *((uint16_t *)(buffer+2)) = htons(++tftp.seq); + memcpy(buffer+4, data, chunk); + data += chunk; + + if (send_ack_packet(&tftp, buffer, chunk+4)) + return -1; + } while (chunk == 512); - return send_ack_packet(be, buffer, len+4); + return 0; } struct backend be_tftp = { .name = "tftp", .helpmsg = "filename tftp_server", .minargs = 2, - .blocksize = 512, - .flags = 0, - .open = be_tftp_open, .write = be_tftp_write, }; diff --git a/com32/sysdump/be_ymodem.c b/com32/sysdump/be_ymodem.c index 632758d..b36bd03 100644 --- a/com32/sysdump/be_ymodem.c +++ b/com32/sysdump/be_ymodem.c @@ -17,6 +17,11 @@ enum { CAN = 0x18, }; +struct ymodem_state { + struct serial_if serial; + uint16_t seq; +}; + /* * Append a CRC16 to a block */ @@ -65,101 +70,102 @@ static void add_crc16(uint8_t * blk, int len) *blk = crc; } -static void send_ack(struct backend *be, const uint8_t *blk, size_t bytes); +static void send_ack(struct ymodem_state *ym, const uint8_t *blk, + size_t bytes); -static void send_ack_blk(struct backend *be, uint8_t *blk) +static void send_ack_blk(struct ymodem_state *ym, uint8_t *blk) { - printf("Sending block %u...\r", be->ymodem.seq); + printf("Sending block %u...\r", ym->seq); blk[0] = STX; - blk[1] = be->ymodem.seq++; + blk[1] = ym->seq++; blk[2] = ~blk[1]; add_crc16(blk+3, 1024); - send_ack(be, blk, 1024+5); + send_ack(ym, blk, 1024+5); } -static void send_ack(struct backend *be, const uint8_t *blk, size_t bytes) +static void send_ack(struct ymodem_state *ym, const uint8_t *blk, size_t bytes) { uint8_t ack_buf; - serial_write(&be->ymodem.serial, blk, bytes); + serial_write(&ym->serial, blk, bytes); do { do { - serial_read(&be->ymodem.serial, &ack_buf, 1); + serial_read(&ym->serial, &ack_buf, 1); } while (ack_buf != ACK && ack_buf != NAK); } while (ack_buf == NAK); } -static int be_ymodem_open(struct backend *be, const char *argv[], size_t len) +static int be_ymodem_write(struct backend *be) { - uint8_t ack_buf, blk_buf[1024 + 5]; + static const uint8_t eot_buf = EOT; + uint8_t ack_buf; + uint8_t blk_buf[1024 + 5]; + struct ymodem_state ym; + const char *buf; + size_t len, chunk; - be->ymodem.seq = 0; + ym.seq = 0; /* Initialize serial port */ - if (serial_init(&be->ymodem.serial, argv+1)) + if (serial_init(&ym.serial, &be->argv[1])) return -1; - + /* Wait for initial handshake */ printf("Waiting for handshake...\n"); do { - serial_read(&be->ymodem.serial, &ack_buf, 1); + serial_read(&ym.serial, &ack_buf, 1); } while (ack_buf != 'C'); - snprintf((char *)blk_buf+3, 1024, "%s%c%zu", argv[0], 0, len); - send_ack_blk(be, blk_buf); + /* Send filename block */ + snprintf((char *)blk_buf+3, 1024, "%s%c%zu", be->argv[0], 0, be->zbytes); + send_ack_blk(&ym, blk_buf); - return 0; -} - -static int be_ymodem_write(struct backend *be, const char *buf, size_t len) -{ - static const uint8_t eot_buf = EOT; - uint8_t ack_buf; - uint8_t blk_buf[1024 + 5]; - - memcpy(blk_buf+3, buf, len); - if (len < 1024) - memset(blk_buf+3+1024-len, 0x1a, 1024-len); + buf = be->outbuf; + len = be->zbytes; - send_ack_blk(be, blk_buf); + while (len) { + chunk = len < 1024 ? len : 1024; - if (len < 1024) { - printf("\nSending EOT...\n"); - send_ack(be, &eot_buf, 1); + memcpy(blk_buf+3, buf, chunk); + if (chunk < 1024) + memset(blk_buf+3+1024-chunk, 0x1a, 1024-chunk); - printf("Waiting for handshake...\n"); - do { - serial_read(&be->ymodem.serial, &ack_buf, 1); - } while (ack_buf != 'C'); - be->ymodem.seq = 0; - - printf("Sending batch termination block...\n"); - memset(blk_buf+3, 0, 128); - blk_buf[0] = SOH; - blk_buf[1] = 0; - blk_buf[2] = 0xff; - add_crc16(blk_buf + 3, 128); - serial_write(&be->ymodem.serial, blk_buf, 128 + 5); - /* - * rb doesn't seem to ack the EOT for an end batch transfer, - * contrary to spec. - */ - printf("Done.\n"); - serial_cleanup(&be->ymodem.serial); + send_ack_blk(&ym, blk_buf); + len -= chunk; } + printf("\nSending EOT...\n"); + send_ack(&ym, &eot_buf, 1); + + printf("Waiting for handshake...\n"); + do { + serial_read(&ym.serial, &ack_buf, 1); + } while (ack_buf != 'C'); + ym.seq = 0; + + printf("Sending batch termination block...\n"); + memset(blk_buf+3, 0, 128); + blk_buf[0] = SOH; + blk_buf[1] = 0; + blk_buf[2] = 0xff; + add_crc16(blk_buf + 3, 128); + serial_write(&ym.serial, blk_buf, 128 + 5); + /* + * rb doesn't seem to ack the EOT for an end batch transfer, + * contrary to spec. + */ + printf("Done.\n"); + serial_cleanup(&ym.serial); + return 0; -} +} struct backend be_ymodem = { .name = "ymodem", .helpmsg = "filename port [speed]", .minargs = 2, - .blocksize = 1024, - .flags = BE_NEEDLEN, - .open = be_ymodem_open, .write = be_ymodem_write, }; diff --git a/com32/sysdump/cpio.c b/com32/sysdump/cpio.c index 30e3d49..dfc7c94 100644 --- a/com32/sysdump/cpio.c +++ b/com32/sysdump/cpio.c @@ -18,7 +18,7 @@ int cpio_pad(struct backend *be) { static char pad[4]; /* Up to 4 zero bytes */ if (be->dbytes & 3) - return write_data(be, pad, -be->dbytes & 3, false); + return write_data(be, pad, -be->dbytes & 3); else return 0; } @@ -48,16 +48,16 @@ int cpio_hdr(struct backend *be, uint32_t mode, size_t datalen, 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, hdr, 6+13*8); + rv |= write_data(be, filename, nlen); rv |= cpio_pad(be); return rv; } -int cpio_init(struct backend *be, const char *argv[], size_t len) +int cpio_init(struct backend *be, const char *argv[]) { now = posix_time(); - return init_data(be, argv, len); + return init_data(be, argv); } int cpio_mkdir(struct backend *be, const char *filename) @@ -71,7 +71,7 @@ int cpio_writefile(struct backend *be, const char *filename, int rv; rv = cpio_hdr(be, MODE_FILE, len, filename); - rv |= write_data(be, data, len, false); + rv |= write_data(be, data, len); rv |= cpio_pad(be); return rv; @@ -79,8 +79,5 @@ int cpio_writefile(struct backend *be, const char *filename, int cpio_close(struct backend *be) { - int rv; - rv = cpio_hdr(be, 0, 0, "TRAILER!!!"); - rv |= write_data(be, NULL, 0, true); - return rv; + return cpio_hdr(be, 0, 0, "TRAILER!!!"); } diff --git a/com32/sysdump/dmi.c b/com32/sysdump/dmi.c index 64f95a9..be4cce4 100644 --- a/com32/sysdump/dmi.c +++ b/com32/sysdump/dmi.c @@ -78,8 +78,8 @@ static void dump_smbios(struct backend *be, size_t dptr) smx.dmi.tbladdr = sizeof smx; smx.dmi.csum -= checksum(&smx.dmi, 0x0f); - write_data(be, &smx, sizeof smx, false); - write_data(be, (const void *)smb->dmi.tbladdr, smb->dmi.tbllen, false); + write_data(be, &smx, sizeof smx); + write_data(be, (const void *)smb->dmi.tbladdr, smb->dmi.tbllen); } static void dump_old_dmi(struct backend *be, size_t dptr) @@ -104,8 +104,8 @@ static void dump_old_dmi(struct backend *be, size_t dptr) fake.dmi.tbladdr = sizeof fake; fake.dmi.csum -= checksum(&fake.dmi, 0x0f); - write_data(be, &fake, sizeof fake, false); - write_data(be, (const void *)dmi->tbladdr, dmi->tbllen, false); + write_data(be, &fake, sizeof fake); + write_data(be, (const void *)dmi->tbladdr, dmi->tbllen); } void dump_dmi(struct backend *be) diff --git a/com32/sysdump/main.c b/com32/sysdump/main.c index 4c58f5f..1924506 100644 --- a/com32/sysdump/main.c +++ b/com32/sysdump/main.c @@ -31,11 +31,11 @@ __noreturn die(const char *msg) exit(1); } -static void dump_all(struct backend *be, const char *argv[], size_t len) +static void dump_all(struct backend *be, const char *argv[]) { static const char version[] = "SYSDUMP " VERSION_STR " " DATE "\n"; - cpio_init(be, argv, len); + cpio_init(be, argv); cpio_writefile(be, "sysdump", version, sizeof version); @@ -47,13 +47,15 @@ static void dump_all(struct backend *be, const char *argv[], size_t len) dump_vesa_tables(be); cpio_close(be); + printf("Uploading data... "); + flush_data(be); + printf("done.\n"); } static struct backend *backends[] = { &be_tftp, &be_ymodem, - &be_null, NULL }; @@ -88,11 +90,8 @@ int main(int argc, char *argv[]) /* Do this as early as possible */ snapshot_lowmem(); - if (be->flags & BE_NEEDLEN) { - dump_all(&be_null, NULL, 0); - dump_all(be, (const char **)argv + 2, be_null.zbytes); - } else { - dump_all(be, (const char **)argv + 2, 0); - } + /* Do the actual data dump */ + dump_all(be, (const char **)argv + 2); + return 0; } diff --git a/com32/sysdump/memmap.c b/com32/sysdump/memmap.c index c8cfc03..a85f092 100644 --- a/com32/sysdump/memmap.c +++ b/com32/sysdump/memmap.c @@ -33,7 +33,7 @@ static void dump_e820(struct backend *be) ireg.ecx.l = sizeof curr->data; ireg.es = SEG(curr->data); ireg.edi.w[0] = OFFS(curr->data); - + do { __intcall(0x15, &ireg, &oreg); if ((oreg.eflags.l & EFLAGS_CF) || diff --git a/com32/sysdump/serial.c b/com32/sysdump/serial.c index f96ae3f..71b3067 100644 --- a/com32/sysdump/serial.c +++ b/com32/sysdump/serial.c @@ -122,5 +122,3 @@ void serial_cleanup(struct serial_if *sif) if ((sif->old.iir & 0xc0) != 0xc0) outb(0x00, port + FCR); /* Disable FIFOs */ } - - diff --git a/com32/sysdump/zout.c b/com32/sysdump/zout.c index d3be893..61dd514 100644 --- a/com32/sysdump/zout.c +++ b/com32/sysdump/zout.c @@ -10,19 +10,21 @@ #include #include "backend.h" -int init_data(struct backend *be, const char *argv[], size_t len) +#define ALLOC_CHUNK 65536 + +int init_data(struct backend *be, const char *argv[]) { - be->outbuf = malloc(be->blocksize); - if (!be->outbuf) - return -1; + be->argv = argv; - if (be->open(be, argv, len)) + be->alloc = ALLOC_CHUNK; + be->outbuf = malloc(ALLOC_CHUNK); + if (!be->outbuf) return -1; memset(&be->zstream, 0, sizeof be->zstream); be->zstream.next_out = (void *)be->outbuf; - be->zstream.avail_out = be->blocksize; + be->zstream.avail_out = ALLOC_CHUNK; be->dbytes = be->zbytes = 0; @@ -34,7 +36,27 @@ int init_data(struct backend *be, const char *argv[], size_t len) return 0; } -int write_data(struct backend *be, const void *buf, size_t len, bool flush) +static int do_deflate(struct backend *be, int flush) +{ + int rv; + + rv = deflate(&be->zstream, flush); + be->zbytes = be->alloc - be->zstream.avail_out; + if (be->zstream.avail_out == 0) { + char *buf; + buf = realloc(be->outbuf, be->alloc + ALLOC_CHUNK); + if (!buf) + return Z_MEM_ERROR; + be->outbuf = buf; + be->zstream.next_out = (void *)(buf + be->zbytes); + be->zstream.avail_out = be->alloc - be->zbytes; + } + + return rv; +} + + +int write_data(struct backend *be, const void *buf, size_t len) { int rv = Z_OK; @@ -42,27 +64,31 @@ int write_data(struct backend *be, const void *buf, size_t len, bool flush) be->zstream.avail_in = len; be->dbytes += 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->zbytes += be->blocksize; - be->zstream.next_out = (void *)be->outbuf; - be->zstream.avail_out = be->blocksize; - } - if (rv == Z_STREAM_ERROR) + + while (be->zstream.avail_in) { + rv = do_deflate(be, Z_NO_FLUSH); + if (rv < 0) return -1; } + return 0; +} - if (flush) { - /* Output the last (fractional) packet... may be zero */ - if (be->write(be, be->outbuf, be->blocksize - be->zstream.avail_out)) +/* Output the data and shut down the stream */ +int flush_data(struct backend *be) +{ + int rv = Z_OK; + + while (rv == Z_OK) { + rv = do_deflate(be, Z_FINISH); + if (rv < 0) return -1; - be->zbytes += be->blocksize - be->zstream.avail_out; - free(be->outbuf); } + if (be->write(be)) + return -1; + + free(be->outbuf); + be->outbuf = NULL; + be->dbytes = be->zbytes = be->alloc = 0; return 0; } -- 2.7.4