| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826 | #include "common.h"#include "zlib.h"#include "spiflash.h"#include "esp.h"#include "matchver.h"#include "boardinfo_fpga.h"#if 1# include "console.h"# define MSG(...) con_printf(__VA_ARGS__)#else# define MSG(...) ((void)0)#endifstruct spz_stream;typedef struct spz_stream spz_stream;#define NBUF 4struct spz_stream {    z_stream zs;    const struct spiflash *flash;    uint8_t *optr;		    /* Output data pointer into obuf */    /* Note: available output data ends at zs->next_out */    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 fw_header header;	    /* Header of currently processed chunk */    struct fw_header vmatch;	    /* Matched firmware version string */    int err;			    /* Error code to return */    bool eoi;			    /* Reached end of input */    bool cleanup;		    /* Call inflateEnd() */};static void *spz_malloc(spz_stream *spz, size_t bytes){    void *p = malloc(bytes);    if (!p && !spz->err) {	spz->err = Z_MEM_ERROR;    }    return p;}static int spiflash_read_data(spz_stream *spz, void *buf, unsigned int len){    uint8_t *p = buf;    while (len) {	unsigned int avail = spz->zs.next_out - spz->optr;	if (spz->err)	    break;	if (avail) {	    if (avail > len)		avail = len;	    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;		}		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 p - (uint8_t *)buf;}/* * spz needs to be initialized to zero except the flash, zs.next_in, * and zs.avail_in fields. */static int spiflash_data_init(spz_stream *spz){    int rv = Z_OK;    uint8_t *rdbuf = NULL;    int rlen;    uint32_t header_crc;    for (int i = 0; i < NBUF; i++) {	spz->bufs[i] = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);	if (!spz->bufs[i])		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:    return spz->err;}static int spiflash_data_cleanup(spz_stream *spz){    int err = 0;    if (!spz)	return 0;    err = spz->err;    if (spz->cleanup)	inflateEnd(&spz->zs);    for (int i = 0; i < NBUF; i++) {	if (spz->bufs[i])	    free(spz->bufs[i]);    }    return err;}/* * Set up a command header with an address according to the SPI * addressing mode. Returns a pointer to the first byte past the * address. */static void *spiflash_setup_addrcmd(const struct spiflash *flash,				    uint32_t addr,				    uint8_t cmd24, uint8_t cmd32,				    void *cmdbuf){    enum spiflash_addr_mode mode = flash->param->addr;    uint8_t *cmd = cmdbuf;    if (!mode)	mode = addr < (1 << 24) ? SPIFLASH_ADDR_24BIT : SPIFLASH_ADDR_32BIT;    if (mode == SPIFLASH_ADDR_24BIT) {	*cmd++ = cmd24;    } else {	*cmd++ = cmd32;	*cmd++ = addr >> 24;    }    *cmd++ = addr >> 16;    *cmd++ = addr >> 8;    *cmd++ = addr;    return cmd;}static int spiflash_get_status(const struct spiflash *flash,			       uint8_t cmd, uint8_t *sr){    return flash->ops->spi_read(flash->cookie, &cmd, 1, sr, 1,				flash->param->tshsl);}/* This needs a timeout function */static int spiflash_wait_status(const struct spiflash *flash,				int delay, uint8_t mask, uint8_t val){    uint8_t sr1;    int rv;    do {	if (flash->ops->yield)	    flash->ops->yield(flash->cookie, delay);	rv = spiflash_get_status(flash, ROM_READ_SR1, &sr1);	if (rv)	    return rv;    } while ((sr1 & mask) != val); /* Waiting... */    return 0;}int spiflash_read(const struct spiflash *flash,		  uint32_t addr, void *buffer, size_t len){    uint8_t cmdbuf[6];    uint8_t *cmd;    cmd = spiflash_setup_addrcmd(flash, addr,				 ROM_FAST_READ, ROM_FAST_READ_32BIT,				 cmdbuf);    *cmd++ = 0;			/* Dummy cycles */    return flash->ops->spi_read(flash->cookie, cmdbuf, cmd - cmdbuf,				buffer, len, flash->param->tshsl1);}static int spiflash_simple_command(const struct spiflash *flash, uint8_t cmd){    return flash->ops->spi_write(flash->cookie, &cmd, 1, NULL, 0,				 flash->param->tshsl);}static int spiflash_write_enable(const struct spiflash *flash){    uint8_t sr1;    int rv;    rv = spiflash_wait_status(flash, 0, 1, 0);    if (rv)	return rv;    rv = spiflash_simple_command(flash, ROM_WRITE_ENABLE);    if (rv)	return rv;    return spiflash_wait_status(flash, 0, 3, 2);}static int spiflash_program(const struct spiflash *flash,			    uint32_t addr, const void *buffer, size_t len){    uint8_t cmdbuf[5];    uint8_t *cmd;    int rv;    rv = spiflash_write_enable(flash);    if (rv)	return rv;    cmd = spiflash_setup_addrcmd(flash, addr,				 ROM_PAGE_PROGRAM, ROM_PAGE_PROGRAM_32BIT,				 cmdbuf);    rv = flash->ops->spi_write(flash->cookie, cmdbuf, cmd - cmdbuf,			       buffer, len, flash->param->tshsl2);    if (rv)	return rv;    return spiflash_wait_status(flash, flash->param->tpp, 3, 0);}/* * Erase up to (long bits) sectors, using block erase if possible. */static int spiflash_erase(const struct spiflash *flash,			  uint32_t addr, unsigned long sector_mask){    uint8_t cmdbuf[5];    uint8_t *cmd;    uint8_t cmd24, cmd32;    uint32_t erasesize;    int rv;    int delay;    const uint32_t block_mask = SPIFLASH_BLOCK_SIZE - 1;    const unsigned long block_sector_mask	= block_mask >> SPIFLASH_SECTOR_SHIFT;    if (!sector_mask) {	MSG("update: nothing to erase\n");	return 0;    }    while (sector_mask) {	if (!(addr & block_mask) &&	    ((sector_mask & block_sector_mask) == block_sector_mask)) {		cmd24 = ROM_ERASE_64K;		cmd32 = ROM_ERASE_64K_32BIT;		delay = flash->param->tbe2;		erasesize = SPIFLASH_BLOCK_SIZE;	} else {	    cmd24 = ROM_ERASE_4K;	    cmd32 = ROM_ERASE_4K_32BIT;	    delay = flash->param->tse;	    erasesize = SPIFLASH_SECTOR_SIZE;	}	if (sector_mask & 1) {	    rv = spiflash_write_enable(flash);	    if (rv)		return rv;	    cmd = spiflash_setup_addrcmd(flash, addr, cmd24, cmd32, cmdbuf);	    rv = flash->ops->spi_write(flash->cookie, cmdbuf, cmd - cmdbuf,				       NULL, 0, flash->param->tshsl2);	    if (rv)		return rv;	    rv = spiflash_wait_status(flash, delay, 3, 0);	    if (rv)		return rv;	}	addr += erasesize;	sector_mask >>= (erasesize >> SPIFLASH_SECTOR_SHIFT);    }    MSG("ok\n");    return 0;}/* * from: current flash contents * to:   desired flash contents * * These are assumed to be aligned full block buffers */enum flashmem_status {    FMS_DONE,			/* All done, no programming needed */    FMS_PROGRAM,		/* Can be programmed */    FMS_ERASE,			/* Needs erase before programming */    FMS_NOTCHECKED		/* Not checked yet */};static enum flashmem_statusspiflash_memcmp(const void *from, const void *to, size_t len){    const uint32_t *pf = from;    const uint32_t *pt = to;    const uint32_t *pfend = (const uint32_t *)((const char *)from + len);    uint32_t doprog  = 0;    uint32_t doerase = 0;    while (pf < pfend) {	uint32_t f = *pf++;	uint32_t t = *pt++;	doprog  |=  f ^ t;	/* Need programming if any data mismatch */	doerase |= ~f & t;	/* Need erasing if any 0 -> 1 */    }    return doerase ? FMS_ERASE : doprog ? FMS_PROGRAM : FMS_DONE;}/* * 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 should be * already read into spz->vbuf. * */static void spiflash_check_block(spz_stream *spz, uint32_t addr,				uint32_t *erase_mask, uint32_t *prog_mask){    const uint8_t *p, *q;    unsigned int page;    *erase_mask = 0;    memset(prog_mask, 0, SPIFLASH_BLOCK_SIZE/SPIFLASH_PAGE_SIZE/8);    p = spz->vbuf;    q = spz->dbuf;    for (page = 0; page < SPIFLASH_BLOCK_SIZE/SPIFLASH_PAGE_SIZE; page++) {	enum flashmem_status status;	switch (spiflash_memcmp(p, q, SPIFLASH_PAGE_SIZE)) {	case FMS_ERASE:	    *erase_mask |= UINT32_C(1) <<		(page >> (SPIFLASH_SECTOR_SHIFT-SPIFLASH_PAGE_SHIFT));	    break;	case FMS_PROGRAM:	    prog_mask[page >> 5] |= UINT32_C(1) << (page & 31);	    break;	default:	    /* Nothing to do! */	    break;	}	p += SPIFLASH_PAGE_SIZE;	q += SPIFLASH_PAGE_SIZE;    }}static int spiflash_flash_chunk(spz_stream *spz){    unsigned int data_left = spz->header.len;    unsigned int addr      = spz->header.addr;    int rv;    while (data_left && !spz->err) {	unsigned int pre_padding  = addr & (SPIFLASH_BLOCK_SIZE-1);	unsigned int post_padding;	unsigned int bytes;	bytes = SPIFLASH_BLOCK_SIZE - pre_padding;	post_padding = 0;	if (bytes > data_left) {	    post_padding = bytes - data_left;	    bytes = data_left;	}	addr -= pre_padding;	/* Read the current content of this block into vbuf */	rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);	if (rv)	    goto err;	/* 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", bytes, rv);	    rv = Z_DATA_ERROR;	    goto err;	}	uint32_t erase_mask;	uint32_t prog_mask[SPIFLASH_BLOCK_SIZE >> (SPIFLASH_PAGE_SHIFT+5)];	spiflash_check_block(spz, addr, &erase_mask, prog_mask);	if (erase_mask) {	    MSG("flash: erasing at 0x%06x mask %04x... ", addr, 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) {		MSG("readback ");		goto err;	    }	    spiflash_check_block(spz, addr, &erase_mask, prog_mask);	    if (erase_mask) {		spz->err = FWUPDATE_ERR_ERASE_FAILED;		MSG("%04x left, ", erase_mask);		goto err;	    }	    MSG("ok\n");	}	unsigned int page;	bool programmed = false;	for (page = 0; page < (SPIFLASH_BLOCK_SIZE >> SPIFLASH_PAGE_SHIFT);	     page++) {	    uint32_t page_offs = page << SPIFLASH_PAGE_SHIFT;	    if (!(prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))))		continue;	/* No need to program */	    programmed = true;	    udelay(100);	    MSG("flash: writing at 0x%06x... ", addr + page_offs);	    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;	    }	    if (memcmp(spz->dbuf + page_offs, spz->vbuf + page_offs,		       SPIFLASH_PAGE_SIZE)) {		MSG("verify ");		spz->err = FWUPDATE_ERR_PROGRAM_FAILED;		goto err;	    }	    MSG("ok\n");	}	if (programmed)	    MSG("ok\n");	else	    MSG("unchanged\n");	addr += pre_padding + bytes;	data_left -= bytes;    }    return spz->err;err:    MSG("failed\n");    if (!spz->err)	spz->err = rv;    return spz->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 };    return flash->ops->spi_read(flash->cookie, cmd_read_sfdp,				sizeof cmd_read_sfdp, sfdp, SPIFLASH_SFDP_SIZE,				flash->param->tshsl);}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;    }    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;}/* 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;    }    return 0;}/* Read a chunk into malloc()'d storage */static int spiflash_load_chunk(spz_stream *spz, void **dptr){    void *data;    int len = spz->header.len;    *dptr = NULL;    data = spz_malloc(spz, len);    if (!data) {	spiflash_skip_chunk(spz);	return spz->err;    } else {	int rv = spiflash_read_data(spz, data, len);	if (!spz->err && rv != len)	    spz->err = Z_DATA_ERROR;	if (spz->err) {	    free(data);	    return spz->err;	}	*dptr = data;	return rv;    }}static int esp_ota_chunk(spz_stream *spz){    void *data;    int len = spiflash_load_chunk(spz, &data);    if (data) {	esp_ota(data, len);	free(data);    }    return spz->err;}/* Get a piece of the chunk header */static int fwupdate_get_header_data(spz_stream *spz, void *buf, int len){    int rv;    rv = spiflash_read_data(spz, buf, len);    if (spz->err)	return spz->err;    else if (!rv)	return Z_STREAM_END;    else if (rv != len)	return spz->err = Z_STREAM_ERROR;    else	return Z_OK;}/* Get and validate a chunk header */static int fwupdate_get_header(spz_stream *spz){    struct fw_header * const hdr = &spz->header;    uint8_t *hptr = (uint8_t *)hdr;    int rv;    unsigned int hlen;    memset(hdr, 0, sizeof *hdr);    hdr->vmax = -1;    rv = fwupdate_get_header_data(spz, hptr, FW_HDR_LEN_V1);    if (rv)	return rv;    switch (hdr->magic) {    case FW_MAGIC_V1:	hlen = FW_HDR_LEN_V1;	break;    case FW_MAGIC_V2:	hlen = FW_HDR_LEN_V2;	break;    default:	MSG("update: bad chunk header magic 0x%08x\n", hdr->magic);	hlen = 0;	rv = Z_DATA_ERROR;	break;    }    if (hlen > FW_HDR_LEN_V1) {	rv = fwupdate_get_header_data(spz, hptr + FW_HDR_LEN_V1,				      hlen - FW_HDR_LEN_V1);	if (rv == Z_STREAM_END)	/* Only valid for the first chunk */	    rv = Z_STREAM_ERROR;    }    return spz->err = rv;}/* Process a data chunk; return a nonzero value if done */static int spiflash_process_chunk(spz_stream *spz){    int rv;    char *str;    rv = fwupdate_get_header(spz);    if (rv)	return rv;    if (spz->header.type != FDT_NOTE &&	spz->header.type != FDT_TARGET &&	spz->header.type != FDT_END &&	!(spz->header.flags & FDF_PRETARGET)) {	if (!spz->vmatch.magic) {	    /* No matching firmware target support */	    return spz->err = FWUPDATE_ERR_NOT_MINE;	}	if (spz->header.vmin > spz->vmatch.vmax ||	    spz->header.vmax < spz->vmatch.vmin ||	    ((spz->header.vmatch ^ spz->vmatch.vmatch) & spz->header.vmask)) {	    /* Chunk not applicable to this target */	    goto skip;	}    }    con_printf("update: chunk type %u size %u addr 0x%08x\n",	       spz->header.type, spz->header.len, spz->header.addr);    switch (spz->header.type) {    case FDT_END:	return Z_STREAM_END;	/* End of data - not an error */    case FDT_DATA:	if (!spz->flash->ops)	    goto skip;	return spiflash_flash_chunk(spz);    case FDT_TARGET:    {	bool match;	str = spiflash_read_chunk_str(spz);	match = match_version(board_info.version_str, str);	if (match || spz->header.magic == FW_MAGIC_V1)	    spz->vmatch = spz->header;	MSG("update: firmware file supports: %s%s\n",	    str, match ? " (match)" : "");	return Z_OK;    }    case FDT_NOTE:	str = spiflash_read_chunk_str(spz);	MSG("update: %s\n", str);	break;    case FDT_ESP_OTA:	if (!spz->flash->ops)	    goto skip;	return esp_ota_chunk(spz);    case FDT_FPGA_INIT:    case FDT_ESP_PART:    case FDT_ESP_SYS:    case FDT_ESP_TOOL:    case FDT_BOARDINFO:	/* Used only when flashing from ESP32 */	goto skip;    default:	if (spz->header.flags & FDF_OPTIONAL)	    goto skip;	MSG("update: unknown chunk type: %u\n", spz->header.type);	return spz->err = Z_DATA_ERROR;    }    return spz->err;skip:    return spiflash_skip_chunk(spz);}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;    memset(spz, 0, sizeof *spz);    spz->zs.avail_in = buflen;    spz->zs.next_in  = buf;    spz->flash = flash;    err = spiflash_data_init(spz);    if (err)	return err;    while (!spiflash_process_chunk(spz)) {	/* Process data chunks until end */    }    err = spiflash_data_cleanup(spz);    if (err)	MSG("failed (err %d)\n", err);    return err;}/* * Read unique serial number from flash. Note: returns id in * bigendian ("network") byte order. */int spiflash_read_id(const struct spiflash *flash, void *id){    static const uint8_t read_unique_id[] = { ROM_READ_UNIQUE_ID, 0, 0, 0, 0 };    return flash->ops->spi_read(flash->cookie, read_unique_id,				sizeof read_unique_id,				id, SPIFLASH_ID_LEN, flash->param->tshsl);}/* * Read vendor and device ID from flash. */int spiflash_read_vdid(const struct spiflash *flash, void *vdid){    static const uint8_t read_vdid[] = { ROM_MANUFACTURER_DEVICE_ID, 0, 0, 0 };    return flash->ops->spi_read(flash->cookie, read_vdid,				sizeof read_vdid,				vdid, SPIFLASH_VDID_LEN, flash->param->tshsl);}/* * Write an absolute region to flash */int spiflash_flash_data(const struct spiflash *flash, uint32_t addr,			const void *data, size_t len){    spz_stream _spz;    spz_stream * const spz = &_spz; /* For consistency in notation */    int err = 0;    memset(spz, 0, sizeof *spz);    /* No ibuf or obuf */    for (int i = 2; i < NBUF; i++) {	spz->bufs[i] = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);	if (!spz->bufs[i])		goto err;    }    spz->flash       = flash;    spz->optr        = (uint8_t *)data; /* OK to lose const here */    spz->obuf        = (uint8_t *)data; /* OK to lose const here */    spz->zs.next_out = (uint8_t *)data + len;    spz->eoi         = true;    spz->header.len  = len;    spz->header.addr = addr;    spiflash_flash_chunk(spz);err:    for (int i = 2; i < NBUF; i++)	if (spz->bufs[i])	    free(spz->bufs[i]);    return spz->err;}
 |