| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 | #define MODULE "fwupdate"#define DEBUG 1#include "common.h"#include "jtag.h"#include "spiflash.h"#include "fpga.h"#include "ota.h"#include "spz.h"#include "httpd.h"#include "fw.h"#include "boardinfo_esp.h"#include "matchver.h"#include <unzipLIB.h>#include <zlib.h>/* Needed for struct inflate_state, due to unziplib hacks */#include <zutil.h>#include <inftrees.h>#include <inflate.h>#ifndef local# define local static#endif#define BUFFER_SIZE		SPIFLASH_SECTOR_SIZE#define FWUPDATE_STACK		8192#define FWUPDATE_PRIORITY	3static inline void spz_heap_info(bool always){    if (DEBUG || always)	heap_info();}static void *spz_malloc(void *opaque, unsigned int size){    spz_stream *spz = opaque;    MSG("spz_malloc(%u) = ", size);    void *p = malloc(size);    CMSG("%p\n", p);    spz_heap_info(!p);    if (!p) {	spz->err = Z_MEM_ERROR;	printf("[FWUP] Out of memory error!\n");    }    return p;}static void *spz_calloc(void *opaque, unsigned int items, unsigned int size){    size_t bytes = items*size;    void *p = spz_malloc(opaque, bytes);    if (likely(p))	memset(p, 0, bytes);    return p;}static void spz_free(void *opaque, void *ptr){    MSG("spz_free(%p)\n", ptr);    (void)opaque;    free(ptr);    spz_heap_info(false);}int spz_read_data(spz_stream *spz, void *buf, size_t 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 = BUFFER_SIZE;	    while (spz->zs.avail_out) {		if (!spz->zs.avail_in && !spz->eoi) {		    int rlen;		    spz->zs.next_in = spz->ibuf;		    rlen = spz->read_data(spz->token, spz->ibuf, BUFFER_SIZE);		    if (rlen < 0) {			if (!spz->err)			    spz->err = rlen;			rlen = 0;		    }		    spz->eoi = !rlen;		    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 read_data and cookie * fields. */static int fwupdate_data_init(spz_stream *spz){    spz->zs.zalloc = spz_calloc;    spz->zs.zfree  = spz_free;    spz->zs.opaque = spz;	/* For error reporting */    spz->err = Z_OK;    /* This is necessary due to unziplib damage */    spz->zs.state = spz_calloc(spz, 1, sizeof(struct inflate_state));    if (!spz->zs.state)	goto err;    for (int i = 0; i < SPZ_NBUF; i++) {	spz->bufs[i] = spz_malloc(spz, BUFFER_SIZE);	if (!spz->bufs[i])	    goto err;    }    /* gzip, max window size */    int rv = inflateInit2(&spz->zs, 16 + 15);    printf("[FWUP] fwupdate_data_init: inflateInit2 returned %d\n", rv);    if (rv != Z_OK && rv != Z_STREAM_END) {	spz->err = rv;	goto err;    }    spz->cleanup = true;err:    return spz->err;}static int fwupdate_data_cleanup(spz_stream *spz){    int err = 0;    if (!spz)	return 0;    err = spz->err;    if (spz->cleanup)	inflateEnd(&spz->zs);    /* Don't reload the FPGA on error; it wedges the JTAG bus */    if (spz->fpga_updated && !err)	fpga_reset();    for (int i = 0; i < SPZ_NBUF; i++) {	if (spz->bufs[i])	    free(spz->bufs[i]);    }    if (spz->zs.state)	free(spz->zs.state);    return err;}/* * Blash a full chunk of data as a JTAG SHIFT_DR transaction */int jtag_shift_spz(spz_stream *spz, enum jtag_io_flags flags){    unsigned int data_left = spz->header.len;    int err = 0;    if (!data_left)	return 0;    while (data_left) {	unsigned int bytes = data_left;	int rv;	if (bytes > BUFFER_SIZE)	    bytes = BUFFER_SIZE;	rv = spz_read_data(spz, spz->dbuf, bytes);	if (rv < 1) {	    err = Z_DATA_ERROR;	    break;	}	data_left -= rv;	jtag_io(rv << 3, data_left ? 0 : flags, spz->dbuf, NULL);    }    return err;}static void *fwupdate_read_chunk_str(spz_stream *spz){    int rv;    if (spz->header.len >= BUFFER_SIZE) {	spz->err = Z_DATA_ERROR;	return NULL;    }    rv = spz_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 fwupdate_skip_chunk(spz_stream *spz){    unsigned int skip = spz->header.len;    while (skip) {	unsigned int block = skip;	if (block > BUFFER_SIZE)	    block = BUFFER_SIZE;	int rv = spz_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;}static int fwupdate_boardinfo(spz_stream *spz){    uint8_t *board_info_data = spz_malloc(spz, BOARDINFO_SIZE);    int rv = Z_OK;    MSG("updating FPGA board_info\n");    if (!board_info_data)	return spz->err;    if (board_info.len >= 16 &&	board_info.len <= sizeof board_info &&	board_info.len <= BOARDINFO_SIZE) {	memcpy(board_info_data, &board_info, board_info.len);	memset(board_info_data + board_info.len, 0xff,	       BOARDINFO_SIZE - board_info.len);	rv = spiflash_write_spz(spz, board_info_data, BOARDINFO_SIZE);    }    free(board_info_data);    fwupdate_skip_chunk(spz);    if (rv)	spz->err = rv;    return spz->err;}/* Get a piece of the chunk header */static int fwupdate_get_header_data(spz_stream *spz, void *buf, size_t len){    int rv;    rv = spz_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("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 fwupdate_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 */	    return fwupdate_skip_chunk(spz);	}    }    switch (spz->header.type) {    case FDT_END:	return Z_STREAM_END;	/* End of data - not an error */    case FDT_DATA:	MSG("updating FPGA flash\n");	return spiflash_write_spz(spz, NULL, 0);    case FDT_BOARDINFO:	return fwupdate_boardinfo(spz);    case FDT_TARGET:    {	bool match;	str = fwupdate_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("firmware file supports: %s%s\n",	    str, match ? " (match)" : "");	return Z_OK;    }    case FDT_NOTE:	str = fwupdate_read_chunk_str(spz);	MSG("%s\n", str);	return Z_OK;    case FDT_ESP_OTA:	MSG("updating ESP32... ");	spz->esp_updated = true;	rv = esp_update((read_func_t)spz_read_data, (token_t)spz,			spz->header.len);	CMSG("done.\n");	return rv;    case FDT_FPGA_INIT:	MSG("initializing FPGA for flash programming... ");	spz->fpga_updated = true;	rv = fpga_program_spz(spz);	CMSG("done\n");	return rv;    case FDT_ESP_PART:    case FDT_ESP_SYS:    case FDT_ESP_TOOL:	/* Not applicable to this update method */	return fwupdate_skip_chunk(spz);    default:	if (spz->header.flags & FDF_OPTIONAL) {	    return fwupdate_skip_chunk(spz);	} else {	    MSG("unknown chunk type: %u\n", spz->header.type);	    return spz->err = Z_DATA_ERROR;	}    }}const char *firmware_errstr(int err){    static char unknown_err[32];    static const char * const errstr[] = {	[-Z_STREAM_ERROR]	         = "Decompression error",	[-Z_DATA_ERROR]		         = "Invalid data stream",	[-Z_MEM_ERROR]		         = "Out of memory",	[-Z_BUF_ERROR]		         = "Decompression error",	[-FWUPDATE_ERR_IN_PROGRESS]      =	    "Firmware update already in progress",	[-FWUPDATE_ERR_BAD_CHUNK]        = "Invalid firmware chunk header",	[-FWUPDATE_ERR_ERASE_FAILED]     = "FPGA flash erase failed",	[-FWUPDATE_ERR_PROGRAM_FAILED]   = "FGPA flash program failed",	[-FWUPDATE_ERR_WRITE_PROTECT]    = "FPGA flash write protected",	[-FWUPDATE_ERR_NOT_READY]        = "FPGA flash stuck at not ready",	[-FWUPDATE_ERR_FPGA_JTAG]        =	    "FPGA JTAG bus stuck, check for JTAG adapter or power cycle board",	[-FWUPDATE_ERR_FPGA_MISMATCH]    =	    "Bad FPGA IDCODE, check for JTAG adapter or power cycle board",	[-FWUPDATE_ERR_FPGA_FAILED]      = "FPGA reboot failed",	[-FWUPDATE_ERR_UNKNOWN]          = "Unidentified error",	[-FWUPDATE_ERR_ESP_NO_PARTITION] = "No available ESP partition",	[-FWUPDATE_ERR_ESP_BAD_OTA]      = "ESP OTA information corrupt",	[-FWUPDATE_ERR_ESP_FLASH_FAILED] = "ESP flash program failed",	[-FWUPDATE_ERR_ESP_BAD_DATA]     = "ESP firmware image corrupt",	[-FWUPDATE_ERR_CONFIG_READ]      = "Configuration upload failure",	[-FWUPDATE_ERR_CONFIG_SAVE]      = "Error saving configuration",	[-FWUPDATE_ERR_NOT_MINE]         = "Firmware file is not compatible"    };    switch (err) {    case Z_OK:	return errstr[-FWUPDATE_ERR_UNKNOWN];    case Z_ERRNO:	return strerror(errno);    case -ARRAY_SIZE(errstr)+1 ... Z_STREAM_ERROR:	if (errstr[-err])	    return errstr[-err];	/* fall through */    default:	snprintf(unknown_err, sizeof unknown_err, "error %d", -err);	return unknown_err;    }}static TaskHandle_t fwupdate_task;static spz_stream *fwupdate_spz;static SemaphoreHandle_t fwupdate_done;static bool do_reboot;static void firmware_update_task(void *pvt){    spz_stream *spz = pvt;    fpga_service_enable(false);    printf("[FWUP] fwupdate_data_init()\n");    spz->err = fwupdate_data_init(spz);    if (spz->err)	goto fail;    printf("[FWUP] fwupdate_process_chunk loop\n");    int err;    while (!(err = fwupdate_process_chunk(spz))) {	/* Process data chunks until end */    }    if (!spz->err && err != Z_STREAM_END)	spz->err = err;    printf("[FWUP] fwupdate_data_cleanup\n");    err = fwupdate_data_cleanup(spz);    if (err)	spz->err = err;fail:    if (spz->err)	MSG("failed (err %d)\n", spz->err);    xSemaphoreGive(fwupdate_done);    if (do_reboot) {	printf("[FWUP] rebooting in %d seconds\n", reboot_delayed());	while (1)	    vTaskSuspend(NULL);    } else {	exit_task();    }}static int firmware_update_cleanup(void){    int err = Z_OK;    fwupdate_task = NULL;    if (fwupdate_done) {	SemaphoreHandle_t done = fwupdate_done;	fwupdate_done = NULL;	vSemaphoreDelete(done);    } else {	err = Z_MEM_ERROR;    }    if (fwupdate_spz) {	struct spz_stream *spz = fwupdate_spz;	if (spz->err)	    err = spz->err;	fwupdate_spz = NULL;	free(spz);    } else {	err = Z_MEM_ERROR;    }    return err;}int firmware_update_start(read_func_t read_data, token_t token, bool autoreboot){    do_reboot = autoreboot;    if (fwupdate_spz)	return FWUPDATE_ERR_IN_PROGRESS;    fwupdate_spz = calloc(1, sizeof *fwupdate_spz);    if (!fwupdate_spz)	goto err;    fwupdate_spz->read_data = read_data;    fwupdate_spz->token     = token;    fwupdate_done = xSemaphoreCreateBinary();    if (!fwupdate_done)	goto err;    if (xTaskCreate(firmware_update_task, "fwupdate",		    FWUPDATE_STACK, fwupdate_spz,		    FWUPDATE_PRIORITY, &fwupdate_task) != pdPASS) {	xSemaphoreGive(fwupdate_done);    }    return Z_OK;err:    return firmware_update_cleanup();}int firmware_update_wait(TickType_t delay){    if (!fwupdate_done)	return Z_MEM_ERROR;    if (!xSemaphoreTake(fwupdate_done, delay))	return FWUPDATE_ERR_IN_PROGRESS;    return firmware_update_cleanup();}
 |