Kaynağa Gözat

update: .fw file is a single compressed container; simplify I/O code

H. Peter Anvin 3 yıl önce
ebeveyn
işleme
835310c76e

+ 4 - 3
fpga/Makefile

@@ -160,9 +160,10 @@ $(outdir)/%.rpf: $(outdir)/%.rpd $(outdir)/%.z.rbf
 $(outdir)/%.gz: $(outdir)/%
 	$(GZIP) -9 < $< > $@
 
-$(outdir)/%.fw: $(outdir)/%.rpf.gz ../rv32/dram.bin.gz ../tools/wrapflash.pl
-	$(PERL) ../tools/wrapflash.pl 'MAX80$*' $@ \
-		$< 0 ../rv32/dram.bin.gz 0x100000
+$(outdir)/%.fw: $(outdir)/%.rpf ../rv32/dram.bin ../tools/wrapflash.pl
+	$(PERL) ../tools/wrapflash.pl 'MAX80 $*' \
+		$< 0 ../rv32/dram.bin 0x100000 | \
+	$(GZIP) -9 > $@
 
 $(outdir)/%.update.svf: ./scripts/flashsvf.pl \
 	$(outdir)/jtagupd/%.svf $(outdir)/%.map.rpt $(outdir)/%.fw

+ 11 - 13
fpga/iodevs.vh

@@ -6,7 +6,7 @@
 	localparam SRAM_BITS               = 'h0000000f; // 15
 	localparam SYS_MAGIC_MAX80         = 'h3858414d; // 945307981
 	localparam TIMER_SHIFT             = 'h00000005; // 5
-	localparam TTY_CHANNELS            = 'h00000004; // 4
+	localparam TTY_CHANNELS            = 'h00000002; // 2
 	localparam XDEV_ADDR_BITS          = 'h00000002; // 2
 	localparam XDEV_ADDR_SHIFT         = 'h0000001c; // 28
 	localparam _PC_IRQ                 = 'h00000020; // 32
@@ -36,7 +36,7 @@
 	tri1 [ 0:0] iodev_wait_n_sysclock;
 
 	wire [31:0] iodev_rdata_tty;
-	wire [ 3:0] iodev_irq_tty;
+	wire [ 1:0] iodev_irq_tty;
 	wire [ 0:0] iodev_valid_tty = iodev_valid[3:3];
 	tri1 [ 0:0] iodev_wait_n_tty;
 
@@ -101,17 +101,15 @@
 	assign sys_irq[ 4] = iodev_irq_sysclock[0];
 	assign sys_irq[ 5] = iodev_irq_tty[0];
 	assign sys_irq[ 6] = iodev_irq_tty[1];
-	assign sys_irq[ 7] = iodev_irq_tty[2];
-	assign sys_irq[ 8] = iodev_irq_tty[3];
-	assign sys_irq[ 9] = iodev_irq_romcopy[0];
-	assign sys_irq[10] = iodev_irq_sdcard[0];
-	assign sys_irq[11] = iodev_irq_i2c[0];
-	assign sys_irq[12] = iodev_irq_esp[0];
-	assign sys_irq[13] = iodev_irq_random[0];
-	assign sys_irq[14] = iodev_irq_vjtag[0];
-
-	localparam [31:0] irq_edge_mask =  32'h00004010;
-	localparam [31:0] irq_masked    = ~32'h00007fff;
+	assign sys_irq[ 7] = iodev_irq_romcopy[0];
+	assign sys_irq[ 8] = iodev_irq_sdcard[0];
+	assign sys_irq[ 9] = iodev_irq_i2c[0];
+	assign sys_irq[10] = iodev_irq_esp[0];
+	assign sys_irq[11] = iodev_irq_random[0];
+	assign sys_irq[12] = iodev_irq_vjtag[0];
+
+	localparam [31:0] irq_edge_mask =  32'h00001010;
+	localparam [31:0] irq_masked    = ~32'h00001fff;
 
 	wire iodev_wait_n = (&iodev_wait_n_sys) & 
 		(&iodev_wait_n_abc) & 

+ 3 - 3
fpga/max80.qpf

@@ -19,14 +19,14 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 13:05:48  February 18, 2022
+# Date created = 00:38:40  February 28, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "13:05:48  February 18, 2022"
+DATE = "00:38:40  February 28, 2022"
 
 # Revisions
 
-PROJECT_REVISION = "v2"
 PROJECT_REVISION = "v1"
+PROJECT_REVISION = "v2"

BIN
fpga/output/jtagupd/v1.rbf.gz


BIN
fpga/output/jtagupd/v1.sof


BIN
fpga/output/jtagupd/v1.svf.gz


BIN
fpga/output/jtagupd/v2.rbf.gz


BIN
fpga/output/jtagupd/v2.sof


BIN
fpga/output/jtagupd/v2.svf.gz


BIN
fpga/output/v1.fw


BIN
fpga/output/v1.jic


BIN
fpga/output/v1.rbf.gz


BIN
fpga/output/v1.rpd.gz


BIN
fpga/output/v1.sof


BIN
fpga/output/v1.svf.gz


BIN
fpga/output/v1.update.svf.gz


BIN
fpga/output/v1.update.xsvf.gz


BIN
fpga/output/v1.xsvf.gz


BIN
fpga/output/v2.fw


BIN
fpga/output/v2.jic


BIN
fpga/output/v2.rbf.gz


BIN
fpga/output/v2.rpd.gz


BIN
fpga/output/v2.sof


BIN
fpga/output/v2.svf.gz


BIN
fpga/output/v2.update.svf.gz


BIN
fpga/output/v2.update.xsvf.gz


BIN
fpga/output/v2.xsvf.gz


+ 3 - 0
fpga/scripts/flashsvf.pl

@@ -104,7 +104,10 @@ printf $svf "SDR 32 TDI (%08X);\n", $ramaddr;
 
 # Write data to SDRAM
 virt_sir($svf, 0x16);
+# Prepend ABC80FED synchronization word
 $bindata = pack("V", $flashpfx) . $bindata;
+# Pad to a full 32-bit word (the JTAG command writes full 32-bit words only)
+$bindata .= "\0" x ((4 - (length($bindata) & 3)) & 3);
 my $binbits = length($bindata) << 3;
 printf $svf "SDR %d\n   TDI (", $binbits;
 print_bitstring($svf, $binbits, $bindata);

+ 1 - 1
iodevs.conf

@@ -13,7 +13,7 @@ our %consts = (
     'SDRAM_BITS' => 25,
     'SYS_MAGIC_MAX80' => unpack('V', 'MAX8'),
     'TIMER_SHIFT' => 5,		# 32 Hz
-    'TTY_CHANNELS' => 4
+    'TTY_CHANNELS' => 2
     );
 
 # I/O address definitions

+ 3 - 0
rv32/abcdisk.c

@@ -664,6 +664,9 @@ static int mount_disk(void)
     con_printf("sdcard: %u/%u clusters free, clusters = %u bytes\n",
 	       freeclust, fs->n_fatent - 2, fs->csize << 9);
 
+    /* Check for firmware update image and install if it exists */
+    rom_flash_from_sdcard();
+
     return 0;
 }
 

+ 1 - 1
rv32/checksum.h

@@ -1,4 +1,4 @@
 #ifndef CHECKSUM_H
 #define CHECKSUM_H
-#define SDRAM_SUM 0xa9babb0a
+#define SDRAM_SUM 0xd2caeaf6
 #endif

+ 1 - 0
rv32/fw.h

@@ -71,4 +71,5 @@ extern qword_t rom_serial;
 extern void run_test_image(void);
 
 extern void rom_flash_from_memory(void *, size_t);
+extern void rom_flash_from_sdcard(void);
 #endif /* FW_H */

+ 105 - 19
rv32/romcopy.c

@@ -1,7 +1,10 @@
+#include "compiler.h"
 #include "fw.h"
 #include "console.h"
 #include "io.h"
 #include "spiflash.h"
+#include "ff.h"
+#include <stdio.h>
 
 #define SPIROM_DUAL_MODE 1
 
@@ -12,7 +15,7 @@ void __hot romcopy_download(void *dst, size_t offset, size_t len)
 
     if (!len)
 	return;
-    
+
     if (SPIROM_DUAL_MODE) {
 	cmd = ROM_FAST_READ_DUAL;
 	flags |= ROMCOPY_SPI_DUAL;
@@ -29,7 +32,7 @@ void __hot romcopy_bzero(void *dst, size_t len)
 {
     if (!len)
 	return;
-    
+
     ROMCOPY_RAMADDR = (size_t)dst;
     ROMCOPY_ROMCMD  = 0;
     ROMCOPY_DATALEN = len | ROMCOPY_ZERO_BUFFER | ROMCOPY_WRITE_RAM;
@@ -260,8 +263,6 @@ static int max80_spi_read(void *cookie,
 }
 
 static const struct spiflash_ops max80_spiflash_ops = {
-    .read_data  = NULL,		/* From memory buffer */
-    .close_data = NULL,		/* Nothing to do */
     .spi_write  = max80_spi_write,
     .spi_read   = max80_spi_read,
     .yield      = NULL		/* Nothing to yield to... */
@@ -292,12 +293,15 @@ static const struct spiflash_param max80_spiflash_param = {
     .tce    = MS(40000),	/* typ, max = 200000 */
 };
 
-static struct spiflash max80_flash = {
-    .ops    = &max80_spiflash_ops,
-    .cookie = NULL,
-    .param  = &max80_spiflash_param,
-    .target = "MAX80v?"
-};
+static void rom_spiflash_init(struct spiflash *flash)
+{
+    static char target[] = "MAX80 v?";
+
+    memset(flash, 0, sizeof *flash);
+    target[sizeof target - 2] = SYS_BOARDFPGA + '0';
+    flash->target = target;
+    flash->param  = &max80_spiflash_param;
+}
 
 /*
  * Flash an image into SPI flash, and reload the FPGA if successful,
@@ -305,25 +309,107 @@ static struct spiflash max80_flash = {
  */
 void rom_flash_from_memory(void *buf, size_t buflen)
 {
-    const struct spiflash_header *hdr = buf;
-    uint32_t romid[2];
+    struct spiflash max80_flash;
+
+    rom_spiflash_init(&max80_flash);
+
+    if (spiflash_flash_file(&max80_flash, buf, buflen)) {
+	con_puts("update: flash update data invalid\n");
+	return;
+    }
+
+    /* Now do it for real */
+    max80_flash.ops = &max80_spiflash_ops;
+    
+    if (spiflash_flash_file(&max80_flash, buf, buflen)) {
+	con_puts("update: flash update failed\n");
+	return;
+    }
+
+    con_puts("update: flash complete, restarting in 500 ms...\n");
+    udelay(500000);
+
+    reset(SYS_RESET_RECONFIG);
+}
+
+static int rom_sdcard_read_data(void *cookie, void *buf, unsigned int bufsize)
+{
+    unsigned int bytesread;
+    FRESULT fr = f_read(cookie, buf, bufsize, &bytesread);
+
+    return fr == FR_OK ? bytesread : 0;
+}
+
+/*
+ * Flash an image from an SD card, and reload the FPGA if successful
+ */
+void rom_flash_from_sdcard(void)
+{
+    struct spiflash max80_flash;
+    char fw_orig_file_name[32];
+    char fw_file_name[32];
+    int l;
+    FRESULT fr;
+    FILINFO fno;
+    FIL f;
+
+    rom_spiflash_init(&max80_flash);
+    max80_flash.read_data = rom_sdcard_read_data;
+    max80_flash.cookie    = &f;
+
+    l = snprintf(fw_orig_file_name, sizeof fw_orig_file_name,
+		 "/max80/v%u.fw", SYS_BOARDFPGA);
+
+    fr = f_stat(fw_orig_file_name, &fno);
+    if (fr != FR_OK || (fno.fattrib & AM_DIR))
+	return;			/* No firmware file found */
+
+    con_printf("update: firmware update file %s found\n", fw_orig_file_name);
 
-    for (int i = 0; i < 8; i++)
-	con_printf("%08x ", ((const uint32_t *)buf)[i]);
-    con_putc('\n');
+    /* Rename the firmware file to avoid repeated updates */
+    memcpy(fw_file_name, fw_orig_file_name, l);
 
-    if (hdr->magic != SPIFLASH_MAGIC) {
-	con_printf("update: no flash update data found in memory at %p\n", hdr);
+    for (unsigned int i = 1; i < 100000; i++) {
+	snprintf(fw_file_name+l, sizeof fw_file_name-l, ".%03u", i);
+
+	fr = f_rename(fw_orig_file_name, fw_file_name);
+	if (fr != FR_EXIST)
+	    break;
+    }
+
+    if (fr != FR_OK) {
+	con_puts("update: unable to rename update file, skipping\n");
 	return;
     }
 
-    max80_flash.target[6] = SYS_BOARDFPGA + '0';
+    con_printf("update: renamed %s -> %s\n", fw_orig_file_name, fw_file_name);
+
+    fr = f_open(&f, fw_file_name, FA_READ);
+    if (fr != FR_OK) {
+	con_puts("update: cannot open file, terminating\n");
+	return;
+    }
 
-    if (spiflash_flash_files(&max80_flash, buf, buflen)) {
+    if (spiflash_flash_file(&max80_flash, NULL, 0)) {
 	con_puts("update: flash update data invalid\n");
+	f_close(&f);
 	return;
     }
 
+    /* Now do it for real */
+    f_rewind(&f);
+    max80_flash.ops = &max80_spiflash_ops;
+
+    if (spiflash_flash_file(&max80_flash, NULL, 0)) {
+	con_puts("update: flash update failed\n");
+	f_close(&f);
+	return;
+    }
+
+    /* Close file and then umount the filesystem to force sync */
+    f_close(&f);
+    f_unmount("");
+
     con_puts("update: flash complete, restarting in 500 ms...\n");
     udelay(500000);
 

+ 328 - 442
rv32/spiflash.c

@@ -11,23 +11,26 @@
 
 struct spz_stream;
 typedef struct spz_stream spz_stream;
+
+#define NBUF 4
 struct spz_stream {
     z_stream zs;
     const struct spiflash *flash;
-    int (*read_data)(spz_stream *); /* Routine to get more data */
-    int (*end_data)(z_stream *);    /* Termination routine for zlib */
     uint8_t *optr;		    /* Output data pointer into obuf */
     /* Note: available output data ends at zs->next_out */
-    uint8_t *ibuf;		    /* Input buffer if compressed */
-    uint8_t *obuf;		    /* Output buffer */
-    uint8_t *dbuf;		    /* Block data buffer */
-    uint8_t *vbuf;		    /* Readback/verify buffer */
-    const struct spiflash_header *header;
-    uint32_t crc32;		    /* Input data CRC32 */
-    unsigned int input_left;	    /* Input data unread */
+    union {
+	uint8_t *bufs[NBUF];
+	struct {
+	    uint8_t *ibuf;	    /* Input buffer if compressed */
+	    uint8_t *obuf;	    /* Output buffer */
+	    uint8_t *dbuf;	    /* Block data buffer */
+	    uint8_t *vbuf;	    /* Readback/verify buffer */
+	};
+    };
+    struct spiflash_header header;  /* Header of currently processed chunk */
     int err;			    /* Error code to return */
     bool eoi;			    /* Reached end of input */
-    bool free_header;		    /* header is malloc()'d */
+    bool cleanup;		    /* Call inflateEnd() */
 };
 
 static void *spz_malloc(spz_stream *spz, size_t bytes)
@@ -39,246 +42,88 @@ static void *spz_malloc(spz_stream *spz, size_t bytes)
     return p;
 }
 
-static int spiflash_read_data(spz_stream *spz)
+static int spiflash_read_data(spz_stream *spz, void *buf, unsigned int len)
 {
-    int rv;
-    int (*read_data)(void *, void *, unsigned int);
-    unsigned int read_block_size;
+    uint8_t *p = buf;
 
-    if (spz->eoi || spz->err)
-	return 0;
+    while (len) {
+	unsigned int avail = spz->zs.next_out - spz->optr;
 
-    read_data = spz->flash->ops->read_data;
-    if (!spz->input_left || !read_data) {
-	spz->eoi = true;
-	return 0;
-    }
+	if (spz->err)
+	    break;
 
-    read_block_size = min(spz->input_left, SPIFLASH_BLOCK_SIZE);
+	if (avail) {
+	    if (avail > len)
+		avail = len;
 
-    if (!spz->ibuf) {
-	spz->ibuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
-	if (!spz->ibuf) {
-	    spz->eoi = true;
-	    return 0;
-	}
-    }
-
-    spz->zs.next_in  = spz->ibuf;
-    spz->zs.avail_in = 0;
+	    memcpy(p, spz->optr, avail);
+	    p += avail;
+	    spz->optr += avail;
+	    len -= avail;
+	} else {
+	    spz->optr = spz->zs.next_out = spz->obuf;
+	    spz->zs.avail_out = SPIFLASH_BLOCK_SIZE;
+
+	    while (spz->zs.avail_out) {
+		if (!spz->zs.avail_in && !spz->eoi) {
+		    int (*read_data)(void *, void *, unsigned int);
+		    read_data = spz->flash->read_data;
+		    spz->zs.next_in = spz->ibuf;
+		    int rlen = read_data(spz->flash->cookie,
+					 spz->ibuf, SPIFLASH_BLOCK_SIZE);
+
+		    spz->eoi = rlen < SPIFLASH_BLOCK_SIZE;
+		    if (rlen < 0) {
+			if (!spz->err)
+			    spz->err = rlen;
+			rlen = 0;
+		    }
+		    spz->zs.avail_in = rlen;
+		}
 
-    rv = read_data(spz->flash->cookie, spz->ibuf, read_block_size);
-    if (spz->err) {
-	rv = 0;
-    } else if (rv < 0) {
-	spz->err = rv;
-	rv = 0;
-    }
-    if (rv != (int)read_block_size)
-	spz->eoi = true;
-
-    if (rv) {
-	spz->crc32 = crc32(spz->crc32, spz->ibuf, rv);
-	spz->input_left -= rv;
-	if (!spz->input_left) {
-	    if (spz->crc32 != spz->header->crc32) {
-		spz->err = Z_STREAM_ERROR;
-		rv = 0;
+		int rv = inflate(&spz->zs, Z_SYNC_FLUSH);
+		if (rv == Z_OK || (rv == Z_BUF_ERROR && !spz->eoi))
+		    continue;
+		
+		spz->eoi = true;
+		if (rv != Z_STREAM_END && !spz->err)
+		    spz->err = rv;
+		break;
 	    }
 	}
     }
-
-    return spz->zs.avail_in = rv;
-}
-
-static int read_data_raw(spz_stream *spz)
-{
-    int rlen;
-
-    if (spz->eoi)
-	return 0;
-
-    rlen = spiflash_read_data(spz);
-    if (rlen) {
-	spz->optr         = spz->ibuf;
-	spz->zs.next_out  = spz->ibuf + rlen;
-	spz->zs.avail_out = SPIFLASH_BLOCK_SIZE - rlen;
-    }
-
-    return rlen;
-}
-
-static int read_data_inflate(spz_stream *spz)
-{
-    int rv = Z_STREAM_END;
-
-    spz->optr = spz->zs.next_out = spz->obuf;
-    spz->zs.avail_out = SPIFLASH_BLOCK_SIZE;
-
-    while (spz->zs.avail_out) {
-	if (!spz->zs.avail_in && !spz->eoi) {
-	    int rlen = spiflash_read_data(spz);
-	    spz->zs.next_in  = spz->ibuf;
-	    spz->zs.avail_in = rlen;
-	}
-
-	rv = inflate(&spz->zs, Z_SYNC_FLUSH);
-
-	if (rv == Z_OK || (rv == Z_BUF_ERROR && !spz->eoi))
-	    continue;
-
-	spz->eoi = true;
-	if (rv != Z_STREAM_END && !spz->err)
-	    spz->err = rv;
-	break;
-    }
-
-    return spz->zs.next_out - spz->optr;
+    return p - (uint8_t *)buf;
 }
 
 /*
  * spz needs to be initialized to zero except the flash, zs.next_in,
  * and zs.avail_in fields.
- *
- * Returns Z_STREAM_END on end of data.
  */
 static int spiflash_data_init(spz_stream *spz)
 {
     int rv = Z_OK;
     uint8_t *rdbuf = NULL;
     int rlen;
-    int (*read_data)(void *cookie, void *buf, unsigned int bufsize);
     uint32_t header_crc;
 
-    MSG("update: ");
-
-    spz->dbuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
-    spz->vbuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
-    if (!spz->dbuf || !spz->vbuf)
-	goto err;
-
-    rlen = spz->zs.avail_in;
-    spz->header = (void *)spz->zs.next_in;
-
-    read_data = spz->flash->ops->read_data;
-    spz->eoi = !read_data;
-    if (!rlen && read_data) {
-	struct spiflash_header *header;
-	spz->header = header = spz_malloc(spz, sizeof *spz->header);
-	spz->free_header = true;
-	spz->input_left = UINT_MAX; /* Unknown at this point */
-	rlen = read_data(spz->flash->cookie, header, sizeof *header);
-    }
-    if (spz->err)
-	goto err;
-    if (!rlen) {
-	MSG("done");
-	spz->err = Z_STREAM_END;
-	goto not_err;
-    }
-    if (rlen < (int)sizeof *spz->header) {
-	MSG("input underrun");
-	spz->err = Z_STREAM_ERROR;
-	goto err;
-    }
-
-    if (memcmp(spz->header->target, spz->flash->target, 8)) {
-	MSG("expected firmware for \"%.8s\", but this image is for \"%.8s\"\n",
-	    spz->flash->target, spz->header->target);
-	spz->err = Z_DATA_ERROR;
-	goto err;
-    }
-
-    rlen -= sizeof *spz->header;
-    spz->zs.next_in += sizeof *spz->header;
-
-    /*
-     * Check header magic and CRC
-     */
-    if (spz->header->magic != SPIFLASH_MAGIC) {
-	MSG("bad header magic");
-	spz->err = Z_STREAM_ERROR;
-	goto err;
-    }
-    header_crc = crc32(0, NULL, 0);
-    header_crc = crc32(header_crc, (const void *)spz->header,
-		       sizeof *spz->header - 4);
-    if (header_crc != spz->header->header_crc32) {
-	MSG("header CRC error (0x%08x vs 0x%08x)...",
-	    header_crc, spz->header->header_crc32);
-	spz->err = Z_STREAM_ERROR;
-	goto err;
-    }
-    if (!spz->header->dlen) {
-	/* End of data */
-	spz->err = Z_STREAM_END;
-	goto not_err;
-    }
-    if (spz->header->zlen > spz->header->dlen ||
-	(spz->eoi && (int)spz->header->zlen > rlen)) {
-	spz->err = Z_STREAM_ERROR;
-	goto err;
-    }
-
-    MSG("data 0x%06x..0x%06x (%u bytes)\n",
-	spz->header->address, spz->header->address + spz->header->dlen - 1,
-	spz->header->dlen);
-
-    if (rlen > (int)spz->header->zlen)
-	rlen = spz->header->zlen;
-    spz->zs.avail_in = rlen;
-
-    spz->crc32 = crc32(0, NULL, 0);
-    if (rlen) {
-	/* Received data in input buffer already */
-	spz->crc32 = crc32(spz->crc32, spz->zs.next_in, spz->zs.avail_in);
-    }
-
-    spz->input_left = spz->header->zlen - rlen;
-    if (!spz->input_left) {
-	if (spz->crc32 != spz->header->crc32) {
-	    spz->err = Z_STREAM_ERROR;
+    for (int i = 0; i < NBUF; i++) {
+	spz->bufs[i] = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
+	if (!spz->bufs[i])
 	    goto err;
-	}
     }
 
-    if (spz->header->zlen == spz->header->dlen) {
-	/* Assume it is a raw binary; input buffer is output buffer */
-	spz->read_data    = read_data_raw;
-	spz->optr         = spz->zs.next_in;
-	spz->zs.next_out  = spz->zs.next_in + spz->zs.avail_in;
-    } else {
-	/* Compressed data? */
-	spz->obuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
-	if (!spz->obuf)
-	    goto err;
-
-	if (rlen >= 14 && !memcmp(spz->zs.next_in, "\37\213\10", 3)) {
-	    /* It is a gzip file */
-	    spz->read_data = read_data_inflate;
-	    /* gzip, max window size */
-	    rv = inflateInit2(&spz->zs, 16 + 15);
-	    if (rv != Z_OK && rv != Z_STREAM_END) {
-		spz->err = rv;
-		goto err;
-	    }
-	    spz->eoi = rv == Z_STREAM_END;
-	    spz->end_data = inflateEnd;
-	} else {
-	    /* Unknown compression format */
-	    spz->err = Z_STREAM_ERROR;
-	    goto err;
-	}
+    spz->eoi = !spz->flash->read_data;
+    /* gzip, max window size */
+    rv = inflateInit2(&spz->zs, 16 + 15);
+    if (rv != Z_OK && rv != Z_STREAM_END) {
+	spz->err = rv;
+	goto err;
     }
+    spz->cleanup = true;
 
 err:
-    if (spz->err)
-	MSG(" failed (err %d)\n", spz->err);
-
-not_err:
-    MSG("\n");
     return spz->err;
- }
+}
 
 static int spiflash_data_cleanup(spz_stream *spz)
 {
@@ -289,25 +134,13 @@ static int spiflash_data_cleanup(spz_stream *spz)
 
     err = spz->err;
 
-    if (spz->flash->ops->close_data) {
-	int rv = spz->flash->ops->close_data(spz->flash->cookie);
-	if (!err)
-	    err = rv;
-    }
-
-    if (spz->end_data)
-	spz->end_data(&spz->zs);
-    if (spz->free_header)
-	free((void *)spz->header);
-    if (spz->vbuf)
-	free(spz->vbuf);
-    if (spz->dbuf)
-	free(spz->dbuf);
-    if (spz->obuf)
-	free(spz->obuf);
-    if (spz->ibuf)
-	free(spz->ibuf);
+    if (spz->cleanup)
+	inflateEnd(&spz->zs);
 
+    for (int i = 0; i < NBUF; i++) {
+	if (spz->bufs[i])
+	    free(spz->bufs[i]);
+    }
     return err;
 }
 
@@ -522,24 +355,16 @@ spiflash_memcmp(const void *from, const void *to, size_t len)
  * Check a block for sectors which need erasing and pages which need
  * programming; the prog_mask is 256 bits long and so span multiple words.
  *
- * The desired input is spz->dbuf and the existing flash content is
- * written to spz->vbuf.
+ * The desired input is spz->dbuf and the existing flash content should be
+ * already read into spz->vbuf.
  *
  */
-static int spiflash_check_block(spz_stream *spz, uint32_t addr,
+static void spiflash_check_block(spz_stream *spz, uint32_t addr,
 				uint32_t *erase_mask, uint32_t *prog_mask)
 {
-    int rv;
     const uint8_t *p, *q;
     unsigned int page;
 
-    rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);
-    if (rv) {
-	if (!spz->err)
-	    spz->err = rv;
-	return rv;
-    }
-
     *erase_mask = 0;
     memset(prog_mask, 0, SPIFLASH_BLOCK_SIZE/SPIFLASH_PAGE_SIZE/8);
     
@@ -565,238 +390,303 @@ static int spiflash_check_block(spz_stream *spz, uint32_t addr,
 	p += SPIFLASH_PAGE_SIZE;
 	q += SPIFLASH_PAGE_SIZE;
     }
-
-    return 0;
 }
 
-/* Serial Flash Discoverable Parameter Table, see JESD216 */
-static int spiflash_get_sfdp(const struct spiflash *flash, void *sfdp)
+static int spiflash_flash_chunk(spz_stream *spz)
 {
-    static const uint8_t cmd_read_sfdp[] = { ROM_READ_SFDP, 0, 0, 0, 0 };
+    unsigned int data_left = spz->header.len;
+    unsigned int addr      = spz->header.addr;
+    int rv;
 
-    return flash->ops->spi_read(flash->cookie, cmd_read_sfdp,
-				sizeof cmd_read_sfdp, sfdp, SPIFLASH_SFDP_SIZE,
-				flash->param->tshsl);
-}
-    
+    while (data_left && !spz->err) {
+	unsigned int pre_padding  = addr & (SPIFLASH_BLOCK_SIZE-1);
+	unsigned int post_padding;
+	unsigned int bytes;
 
-int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
-{
-    spz_stream _spz;
-    spz_stream * const spz = &_spz; /* For consistency in notation */
-    int  err = 0;
-    enum flashmem_status fs;
+	bytes = SPIFLASH_BLOCK_SIZE - pre_padding;
+	post_padding = 0;
+	if (bytes > data_left) {
+	    post_padding = bytes - data_left;
+	    bytes = data_left;
+	}
 
-#if 0
-    static const uint8_t read_sr_cmd[2] = { ROM_READ_SR1, ROM_READ_SR2 };
-    uint8_t sr1;
-    uint32_t *sfdp;
+	/* Read the current content of this block into vbuf */
+	rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);
+	if (rv)
+	    goto err;
 
-    sfdp = malloc(SPIFLASH_SFDP_SIZE);
-    memset(sfdp, 0, SPIFLASH_SFDP_SIZE);
+	/* Copy any invariant chunk */
+	if (pre_padding)
+	    memcpy(spz->dbuf, spz->vbuf, pre_padding);
+	if (post_padding)
+	    memcpy(spz->dbuf+SPIFLASH_BLOCK_SIZE-post_padding,
+		   spz->vbuf+SPIFLASH_BLOCK_SIZE-post_padding,
+		   post_padding);
+
+	rv = spiflash_read_data(spz, spz->dbuf+pre_padding, bytes);
+	if (rv != (int)bytes) {
+	    MSG("needed %u bytes got %d\n", rv);
+	    rv = Z_DATA_ERROR;
+	    goto err;
+	}
 
-    /* Note: SFDP data is littleendian! */
-    err = spiflash_get_sfdp(flash, sfdp);
-    if (err)
-	return err;
+	MSG("update: flash block at 0x%06x (%5u bytes):\n", addr, bytes);
 
-    for (int i = 0; i < SPIFLASH_SFDP_SIZE; i += 16) {
-	MSG("%04x :", i);
-	for (int j = 0; j < 16; j += 4) {
-	    MSG(" %08x", sfdp[(i+j) >> 2]);
-	}
-	MSG("\n");
-    }
-    
-    if (sfdp[0] != 0x50444653) {
-	MSG("update: invalid SFDP information read\n");
-	return SPIFLASH_ERR_DETECT;
-    }
+	addr -= pre_padding;
 
-    /*
-     * If the flash is busy, try to reset it
-     */
-    err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
-    if (err)
-	return err;
-    if (sr1 & 0x01) {
-	udelay(60);
-	err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
-	if (err)
-	    return err;
-	if (sr1 & 0x01) {
-	    MSG("update: flash busy, trying reset... ");
-	    
-	    err = spiflash_simple_command(flash, ROM_ENABLE_RESET);
-	    if (err)
-		return err;
-	    err = spiflash_simple_command(flash, ROM_RESET);
-	    if (err)
-		return err;
+	uint32_t erase_mask;
+	uint32_t prog_mask[SPIFLASH_BLOCK_SIZE >> (SPIFLASH_PAGE_SHIFT+5)];
 
-	    udelay(60);
-	    err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
-	    if (err || (sr1 & 0x01)) {
-		MSG("failed\n");
-		return SPIFLASH_ERR_NOT_READY;
+	spiflash_check_block(spz, addr, &erase_mask, prog_mask);
+	if (erase_mask) {
+	    rv = spiflash_erase(spz->flash, addr, erase_mask);
+	    if (rv)
+		goto err;
+
+	    /* Verify that the sector did erase */
+	    rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);
+	    if (rv)
+		goto err;
+	    
+	    spiflash_check_block(spz, addr, &erase_mask, prog_mask);
+	    if (erase_mask) {
+		MSG("[erase mask = %04x] ", erase_mask);
+		spz->err = SPIFLASH_ERR_ERASE_FAILED;
+		goto err;
 	    }
-	    MSG("ok\n");
 	}
-    }
 
-#endif
-    
-    while (!err) {
-	int rv;
-	uint32_t addr;
-	uint32_t data_left;
+	unsigned int page;
+	bool programmed = false;
 
-	memset(spz, 0, sizeof *spz);
-	spz->zs.avail_in = buflen;
-	spz->zs.next_in  = buf;
-	spz->flash = flash;
+	for (page = 0; page < (SPIFLASH_BLOCK_SIZE >> SPIFLASH_PAGE_SHIFT);
+	     page++) {
+	    uint32_t page_offs = page << SPIFLASH_PAGE_SHIFT;
 
-	if (spiflash_data_init(spz))
-	    goto err;
+	    if (!(prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))))
+		continue;	/* No need to program */
+	    
+	    programmed = true;
+	    
+	    udelay(100);
+	    MSG("\rupdate: writing at 0x%06x... ", addr + page_offs);
 
-	if (!spz->ibuf) {
-	    /* No ibuf allocated, feeding from raw data buffer */
-	    unsigned int bufskip = (spz->header->zlen + sizeof *spz->header + 3) & ~3;
-	    if (bufskip >= buflen) {
-		buflen = 0;
-		buf = NULL;
-	    } else {
-		buflen -= bufskip;
-		buf += bufskip;
+	    rv = spiflash_program(spz->flash, addr + page_offs,
+				  spz->dbuf + page_offs,
+				  SPIFLASH_PAGE_SIZE);
+	    if (rv)
+		goto err;
+
+	    /* Verify that the page did write */
+	    rv = spiflash_read(spz->flash, addr + page_offs,
+			       spz->vbuf + page_offs,
+			       SPIFLASH_PAGE_SIZE);
+	    if (rv) {
+		MSG("readback ");
+		goto err;
 	    }
-	} else {
-	    /* Buffer exhausted, additional data read */
-	    buflen = 0;
-	    buf = NULL;
-	}
 
-	data_left = spz->header->dlen;
-	addr      = spz->header->address;
+	    if (memcmp(spz->dbuf + page_offs, spz->vbuf + page_offs,
+		       SPIFLASH_PAGE_SIZE)) {
+		MSG("verify @ 0x%06x ", addr + page_offs);
+		spz->err = SPIFLASH_ERR_PROGRAM_FAILED;
+		goto err;
+	    }
+	}
+	if (programmed)
+	    MSG("ok\n");
+	else
+	    MSG("update: nothing to write\n");
+	
+	addr += pre_padding + bytes;
+	data_left -= bytes;
+    }
+    return spz->err;
 
-	while (data_left && !spz->err) {
-	    unsigned int bytes = 0;
-	    unsigned int padding;
-	    
-	    while (data_left && bytes < SPIFLASH_BLOCK_SIZE) {
-		unsigned int avail = spz->zs.next_out - spz->optr;
-		unsigned int need = SPIFLASH_BLOCK_SIZE - bytes;
-		int rv;
+err:
+    if (!spz->err)
+	spz->err = rv;
 
-		if (need > data_left)
-		    need = data_left;
-		
-		if (avail) {
-		    if (avail > need)
-			avail = need;
-
-		    memcpy(spz->dbuf + bytes, spz->optr, avail);
-		    spz->optr += avail;
-		    bytes     += avail;
-		    data_left -= avail;
-		    continue;
-		}
-		
-		rv = spz->read_data(spz);
+    return spz->err;
+}
 
-		if (spz->err)
-		    goto err;
+/* Serial Flash Discoverable Parameter Table, see JESD216 */
+static int spiflash_get_sfdp(const struct spiflash *flash, void *sfdp)
+{
+    static const uint8_t cmd_read_sfdp[] = { ROM_READ_SFDP, 0, 0, 0, 0 };
 
-		if (!rv) {
-		    spz->err = Z_STREAM_ERROR; /* Input underrun */
-		    goto err;
-		}
-	    }
+    return flash->ops->spi_read(flash->cookie, cmd_read_sfdp,
+				sizeof cmd_read_sfdp, sfdp, SPIFLASH_SFDP_SIZE,
+				flash->param->tshsl);
+}
 
-	    if (bytes < SPIFLASH_BLOCK_SIZE) {
-		memset(spz->dbuf + bytes, 0xff, SPIFLASH_BLOCK_SIZE - bytes);
-	    }
+static void *spiflash_read_chunk_str(spz_stream *spz)
+{
+    int rv;
+    
+    if (spz->header.len >= SPIFLASH_BLOCK_SIZE) {
+	spz->err = Z_DATA_ERROR;
+	return NULL;
+    }
 
-	    MSG("update: flash block at 0x%06x (%5u bytes):\n", addr, bytes);
+    rv = spiflash_read_data(spz, spz->dbuf, spz->header.len);
+    if (spz->err) {
+	return NULL;
+    }
+    if (rv != (int)spz->header.len) {
+	spz->err = Z_DATA_ERROR;
+	return NULL;
+    }
+    spz->dbuf[spz->header.len] = '\0';
+    return spz->dbuf;
+}
 
-	    uint32_t erase_mask;
-	    uint32_t prog_mask[SPIFLASH_BLOCK_SIZE >> (SPIFLASH_PAGE_SHIFT+5)];
+/* Skip a data chunk */
+static int spiflash_skip_chunk(spz_stream *spz)
+{
+    unsigned int skip = spz->header.len;
+
+    while (skip) {
+	unsigned int block = min(skip, SPIFLASH_BLOCK_SIZE);
+	int rv = spiflash_read_data(spz, spz->dbuf, block);
+	if (spz->err)
+	    return spz->err;
+	if (rv != (int)block) {
+	    return spz->err = Z_DATA_ERROR;
+	}
+	skip -= block;
+    }
 
-	    rv = spiflash_check_block(spz, addr, &erase_mask, prog_mask);
-	    if (rv)
-		goto err;
+    return 0;
+}
 
-	    if (erase_mask) {
-		rv = spiflash_erase(spz->flash, addr, erase_mask);
-		if (rv) {
-		    spz->err = rv;
-		    goto err;
-		}
+/* Process a data chunk; return a nonzero value if done */
+static int spiflash_process_chunk(spz_stream *spz)
+{
+    int rv;
+    char *str;
 
-		rv = spiflash_check_block(spz, addr, &erase_mask, prog_mask);
-		if (spz->err)
-		    goto err;
-		if (erase_mask) {
-		    MSG("[erase mask = %04x] ", erase_mask);
-		    spz->err = SPIFLASH_ERR_ERASE_FAILED;
-		    goto err;
-		}
-	    }
+    rv = spiflash_read_data(spz, &spz->header, sizeof spz->header);
+    if (spz->err)
+	return spz->err;
+    else if (!rv)
+	return Z_STREAM_END;
+    else if (rv != sizeof spz->header)
+	return spz->err = Z_STREAM_ERROR;
+
+    if (spz->header.magic != SPIFLASH_MAGIC) {
+	MSG("update: bad chunk header magic 0x%08x\n", spz->header.magic);
+	return spz->err = Z_DATA_ERROR;
+    }
 
-	    unsigned int page;
-	    bool programmed = false;
+    switch (spz->header.type) {
+    case FDT_END:
+	return Z_STREAM_END;	/* End of data - not an error */
+    case FDT_DATA:
+	if (!spz->flash->ops)
+	    return spiflash_skip_chunk(spz);
+	else
+	    return spiflash_flash_chunk(spz);
+    case FDT_TARGET:
+	str = spiflash_read_chunk_str(spz);
+	if (!str || strcmp(str, spz->flash->target)) {
+	    MSG("update: this firmware file targets \"%s\", need \"%s\"\n",
+		str, spz->flash->target);
+	    return spz->err = Z_DATA_ERROR;
+	}
+	return Z_OK;
+    case FDT_NOTE:
+	str = spiflash_read_chunk_str(spz);
+	MSG("update: %s\n", str);
+	return Z_OK;
+    default:
+	if (spz->header.flags & FDF_OPTIONAL) {
+	    return spiflash_skip_chunk(spz);
+	} else {
+	    MSG("update: unknown chunk type: %u\n", spz->header.type);
+	    return spz->err = Z_DATA_ERROR;
+	}
+    }
+}
 
-	    for (page = 0; page < (SPIFLASH_BLOCK_SIZE >> SPIFLASH_PAGE_SHIFT);
-		 page++) {
-		uint32_t page_offs = page << SPIFLASH_PAGE_SHIFT;
+int spiflash_flash_file(const struct spiflash *flash, void *buf, size_t buflen)
+{
+    spz_stream _spz;
+    spz_stream * const spz = &_spz; /* For consistency in notation */
+    int err = 0;
 
-		if (!(prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))))
-		    continue;	/* No need to program */
+    memset(spz, 0, sizeof *spz);
+    spz->zs.avail_in = buflen;
+    spz->zs.next_in  = buf;
+    spz->flash = flash;
 
-		programmed = true;
+    err = spiflash_data_init(spz);
+    if (err)
+	return err;
 
-		udelay(10000);
-		
-		MSG("\rupdate: writing at 0x%06x... ", addr + page_offs);
+    if (0 && flash->ops) {
+	static const uint8_t read_sr_cmd[2] = { ROM_READ_SR1, ROM_READ_SR2 };
+	uint8_t sr1;
+	uint32_t *sfdp;
 
-		rv = spiflash_program(spz->flash, addr + page_offs,
-				      spz->dbuf + page_offs,
-				      SPIFLASH_PAGE_SIZE);
-		if (rv) {
-		    spz->err = rv;
-		    goto err;
-		}
+	sfdp = malloc(SPIFLASH_SFDP_SIZE);
+	memset(sfdp, 0, SPIFLASH_SFDP_SIZE);
 
-		rv = spiflash_read(spz->flash, addr + page_offs,
-				   spz->vbuf + page_offs,
-				   SPIFLASH_PAGE_SIZE);
-		if (rv) {
-		    MSG("readback ");
-		    goto err;
-		}
+	/* Note: SFDP data is littleendian! */
+	err = spiflash_get_sfdp(flash, sfdp);
+	if (err)
+	    return err;
 
+	for (int i = 0; i < SPIFLASH_SFDP_SIZE; i += 16) {
+	    MSG("%04x :", i);
+	    for (int j = 0; j < 16; j += 4) {
+		MSG(" %08x", sfdp[(i+j) >> 2]);
+	    }
+	    MSG("\n");
+	}
+    
+	if (sfdp[0] != 0x50444653) {
+	    MSG("update: invalid SFDP information read\n");
+	    return SPIFLASH_ERR_DETECT;
+	}
 
-		if (memcmp(spz->dbuf + page_offs, spz->vbuf + page_offs,
-			   SPIFLASH_PAGE_SIZE)) {
-		    MSG("verify @ 0x%06x ", addr + page_offs);
-		    spz->err = SPIFLASH_ERR_PROGRAM_FAILED;
-		    goto err;
+	/*
+	 * If the flash is busy, try to reset it
+	 */
+	err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
+	if (err)
+	    return err;
+	if (sr1 & 0x01) {
+	    udelay(60);
+	    err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
+	    if (err)
+		return err;
+	    if (sr1 & 0x01) {
+		MSG("update: flash busy, trying reset... ");
+		
+		err = spiflash_simple_command(flash, ROM_ENABLE_RESET);
+		if (err)
+		    return err;
+		err = spiflash_simple_command(flash, ROM_RESET);
+		if (err)
+		    return err;
+		
+		udelay(60);
+		err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
+		if (err || (sr1 & 0x01)) {
+		    MSG("failed\n");
+		    return SPIFLASH_ERR_NOT_READY;
 		}
-	    }
-	    if (programmed)
 		MSG("ok\n");
-	    else
-		MSG("update: nothing to write\n");
-	    
-	    addr += SPIFLASH_BLOCK_SIZE;
+	    }
 	}
-
-    err:
-	err = spiflash_data_cleanup(spz);
     }
 
-    if (err == Z_STREAM_END)
-	err = 0;		/* End of data is not an error */
+    while (!spiflash_process_chunk(spz)) {
+	/* Process data chunks until end */
+    }
 
+    err = spiflash_data_cleanup(spz);
     if (err)
 	MSG("failed (err %d)\n", err);
 
@@ -827,7 +717,3 @@ int spiflash_read_vdid(const struct spiflash *flash, void *vdid)
 				sizeof read_vdid,
 				vdid, SPIFLASH_VDID_LEN, flash->param->tshsl);
 }
-
-/*
- * Flash data from memory buffer(s)
- */

+ 49 - 48
rv32/spiflash.h

@@ -61,20 +61,26 @@ enum romcmd {
 #define SPIFLASH_SFDP_SIZE	256
 
 /*
- * Firmware blob data header. A dlen of 0 means no futher blobs.
- * dlen == zlen means raw binary data, not compressed.
+ * Firmware chunk header.
  */
-#define SPIFLASH_MAGIC		0xe301e7eb
-#define SPIFLASH_HEADER_CRC32	0x99d8ef20
+#define SPIFLASH_MAGIC		0x7a07fbd6
 
 struct spiflash_header {
     uint32_t magic;		/* Magic number */
-    uint32_t zlen;		/* Compressed data length */
-    uint32_t dlen;		/* Uncompressed data length */
-    uint32_t crc32;		/* CRC32 of (raw) data block */
-    uint32_t address;		/* Target address in flash (block aligned) */
-    char target[8];		/* Flash target */
-    uint32_t header_crc32;	/* CRC32 of above fields */
+    uint16_t type;		/* Content type */
+    uint16_t flags;		/* Content flags */
+    uint32_t len;		/* Content length (excluding header) */
+    uint32_t addr;		/* Address or similar */
+};
+
+enum flash_data_type {
+    FDT_END,			/* End of stream */
+    FDT_DATA,			/* Data to be flashed */
+    FDT_TARGET,			/* Subsystem string (must match) */
+    FDT_NOTE			/* Version: XXXXX or similar */
+};
+enum flash_data_flags {
+    FDF_OPTIONAL     = 0x0001	/* Ignore if chunk data type unknown */
 };
 
 /*
@@ -112,40 +118,6 @@ struct spiflash_header {
  * be.
  */
 struct spiflash_ops {
-    /*
-     * Read input data for flash write. Return the number of bytes
-     * read.  A short read or a 0 byte return value represents end of
-     * file/end of data; a negative value is treated as 0, and will be
-     * returned from the top-level operation as a status code.
-     *
-     * It is not required to detect end of input if and only if the
-     * input is a gzip file, as in that case the gzip data will contain
-     * an end of stream indicator.
-     *
-     * The buffer initially passed to this function will always be
-     * aligned to a malloc() alignment boundary; it will preserve
-     * alignment boundaries if and only if short read returns only byte
-     * counts in multiple of those alignment boundaries.
-     *
-     * If there is a memory buffer available containing full or
-     * partial input data on entry, pass it to spiflash_flash_file();
-     * If this memory buffer contains all available input, this
-     * function can be NULL.
-     *
-     * A partial memory buffer must contain the full stream header.
-     *
-     * A full memory buffer must either be expanded to a multiple
-     * of SPIFLASH_BLOCK_SIZE or safely allow the flash code to do so.
-     */
-    int (*read_data)(void *cookie, void *buf, unsigned int bufsize);
-
-    /*
-     * Indicates that no more data will be read (end of stream detected
-     * or an error happened.) May be NULL. A nonzero value will be passed
-     * to the top-level routine as an error.
-     */
-    int (*close_data)(void *cookie);
-
     /*
      * Perform a SPI write operation. The SPI write operation consists of:
      * 1. Assert CS# (and deassert HOLD# if applicable)
@@ -261,10 +233,39 @@ struct spiflash_param {
 
 /* Common structure for the above */
 struct spiflash {
-    const struct spiflash_ops *ops;
+    /*
+     * Read input data for flash write. Return the number of bytes
+     * read.  A short read or a 0 byte return value represents end of
+     * file/end of data; a negative value is treated as 0, and will be
+     * returned from the top-level operation as a status code.
+     *
+     * It is not required to detect end of input if and only if the
+     * input is a gzip file, as in that case the gzip data will contain
+     * an end of stream indicator.
+     *
+     * The buffer initially passed to this function will always be
+     * aligned to a malloc() alignment boundary; it will preserve
+     * alignment boundaries if and only if short read returns only byte
+     * counts in multiple of those alignment boundaries.
+     *
+     * If there is a memory buffer available containing full or
+     * partial input data on entry, pass it to spiflash_flash_file();
+     * If this memory buffer contains all available input, this
+     * function can be NULL.
+     *
+     * A partial memory buffer must contain a full stream header block.
+     */
+    int (*read_data)(void *cookie, void *buf, unsigned int bufsize);
     void *cookie;		/* Pointer passed to spiflash_ops functions */
+
+    /*
+     * Operations on the SPI flash itself; if ops == NULL then this is a
+     * dry run operation.
+     */
+    const struct spiflash_ops *ops;
     const struct spiflash_param *param;
-    char target[8];		/* What are we programming for? */
+
+    const char *target;		/* What are we programming? */
 };
 
 /*
@@ -281,8 +282,8 @@ struct spiflash {
  * functions, any of the negative error values defined in zlib.h,
  * or one of the above error codes.
  */
-int spiflash_flash_files(const struct spiflash *flash,
-			 void *data, size_t datalen);
+int spiflash_flash_file(const struct spiflash *flash,
+			void *data, size_t datalen);
 
 /*
  * Read identifying data from SPI flash.

+ 22 - 35
tools/wrapflash.pl

@@ -2,10 +2,15 @@
 
 use strict;
 use integer;
-use Digest::CRC;
 
-my $MAX_DATA_LEN = 16 << 20;
-my $SPIFLASH_MAGIC = 0xe301e7eb;
+my $SPIFLASH_MAGIC = 0x7a07fbd6;
+
+my $FDT_END      = 0;
+my $FDT_DATA     = 1;
+my $FDT_TARGET   = 2;
+my $FDT_NOTE     = 3;
+
+my $FDF_OPTIONAL = 0x0001;
 
 sub getint($) {
     my($s) = @_;
@@ -17,25 +22,21 @@ sub filelen($) {
 
     return $s[7];
 }
-sub makeheader($$$$$) {
-    my($target,$zlen,$dlen,$dcrc,$address) = @_;
+sub output_chunk($$$$) {
+    my($type,$flags,$addr,$data) = @_;
 
-    my $hdr = pack("V*", $SPIFLASH_MAGIC, $zlen, $dlen, $dcrc, $address);
-    $hdr .= substr($target.("\0" x 8), 0, 8);
-    $hdr .= pack("V", Digest::CRC::crc32($hdr));
-    return $hdr;
+    print pack("VvvVV", $SPIFLASH_MAGIC, $type, $flags, length($data), $addr);
+    print $data;
 }
 
 my $target  = shift @ARGV;
-my $outfile = shift @ARGV;
-
-if (!defined($outfile)) {
-    die "Usage: $0 target_name outfile [infile addr]...\n";
+if (!defined($target)) {
+    die "Usage: $0 target_name [infile addr]...\n";
 }
 
-open(my $out, '>', $outfile)
-    or die "$0: $outfile: $!\n";
-binmode($outfile);
+binmode(STDOUT);
+
+output_chunk($FDT_TARGET, 0, 0, $target);
 
 my $err;
 while (1) {
@@ -50,30 +51,16 @@ while (1) {
 	last;
     }
     binmode($in);
-
+    my @is = stat($in);
     my $data;
-    my $zlen = read($in, $data, $MAX_DATA_LEN);
+    my $dlen = read($in, $data, $is[7]);
     close($in);
 
-    my $dlen = $zlen;
-    if (substr($in, 0, 3) == "\37\213\10") {
-	# gzip file
-	$dlen = unpack("V", substr($data, -4));
-    }
-    my $dcrc = Digest::CRC::crc32($data);
-    print $out makeheader($target, $zlen, $dlen, $dcrc, $inaddr);
-    print $out $data;
-    if ($zlen & 3) {
-	# pad to dword boundary
-	print $out "\0" x ((4-$zlen) & 3);
-    }
+    output_chunk($FDT_DATA, 0, $inaddr, $data);
 }
 
+output_chunk($FDT_END, 0, 0, '');
+
 if (defined($err)) {
-    close($out);
-    unlink($outfile);
     die "$0: $err\n";
 }
-
-print $out makeheader($target, 0, 0, Digest::CRC::crc32(''), 0);
-close($out);