diff options
Diffstat (limited to 'cmd/sbc_harness/fs_harness_flash_bin.c')
-rw-r--r-- | cmd/sbc_harness/fs_harness_flash_bin.c | 258 |
1 files changed, 147 insertions, 111 deletions
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.c b/cmd/sbc_harness/fs_harness_flash_bin.c index 8eebe9e..510a247 100644 --- a/cmd/sbc_harness/fs_harness_flash_bin.c +++ b/cmd/sbc_harness/fs_harness_flash_bin.c @@ -26,8 +26,8 @@ LO_IMPLEMENTATION_STATIC(lib9p_srv_fio, struct flash_file, flash_file); static_assert(DATA_SIZE % FLASH_SECTOR_SIZE == 0); static_assert(DATA_HSIZE % FLASH_SECTOR_SIZE == 0); -/* There are some memcpy()s (and memcmp()s?) in here that can (and - * arguably should) be replaced with SSI DMA. */ +/* There are some memcpy()s (and memcmp()s?) in here that maybe should + * be replaced with SSI DMA. */ /* ab_flash_* (mid-level utilities for our A/B write scheme) ******************/ @@ -60,50 +60,24 @@ static_assert(DATA_HSIZE % FLASH_SECTOR_SIZE == 0); } /** - * Set the upper half of flash to all zero bytes. + * Set the upper half of flash to all "1" bits. * * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE */ -static void ab_flash_initialize_zero(uint8_t *buf) { +static void ab_flash_initialize(void *buf) { assert(buf); - memset(buf, 0, FLASH_SECTOR_SIZE); + memset(buf, 0xFF, FLASH_SECTOR_SIZE); - log_infoln("zeroing upper flash..."); + log_infoln("erasing upper flash..."); for (size_t off = DATA_HSIZE; off < DATA_SIZE; off += FLASH_SECTOR_SIZE) { if (memcmp(buf, DATA_START+off, FLASH_SECTOR_SIZE) == 0) continue; bool saved = cr_save_and_disable_interrupts(); - /* No need to `flash_range_erase()`; the way the flash - * works is that _erase() sets all bits to 1, and - * _program() sets some bits to 0. If we don't need - * any bits to be 1, then we can skip the - * _erase(). */ - flash_range_program(off, buf, FLASH_SECTOR_SIZE); - cr_restore_interrupts(saved); - } - log_debugln("... zeroed"); -} - -/** - * Copy the lower half of flash to the upper half of flash. - * - * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE - */ -static void ab_flash_initialize(uint8_t *buf) { - assert(buf); - - log_infoln("initializing upper flash..."); - for (size_t off = 0; off < DATA_HSIZE; off += FLASH_SECTOR_SIZE) { - memcpy(buf, DATA_START+off, FLASH_SECTOR_SIZE); - if (memcmp(buf, DATA_START+DATA_HSIZE+off, FLASH_SECTOR_SIZE) == 0) - continue; - bool saved = cr_save_and_disable_interrupts(); - flash_range_erase(DATA_HSIZE+off, FLASH_SECTOR_SIZE); - flash_range_program(DATA_HSIZE+off, buf, FLASH_SECTOR_SIZE); + flash_range_erase(off, FLASH_SECTOR_SIZE); cr_restore_interrupts(saved); } - log_debugln("... initialized"); + log_debugln("... erased"); } /** @@ -111,7 +85,7 @@ static void ab_flash_initialize(uint8_t *buf) { * sector in the lower half, but this function writes to the upper * half). * - * @param pos : start-position of the sector to write to, must be in the upper half of the flash + * @param pos : start-position of the sector to write to * @param dat : the FLASH_SECTOR_SIZE bytes to write */ static void ab_flash_write_sector(size_t pos, uint8_t *dat) { @@ -121,7 +95,7 @@ static void ab_flash_write_sector(size_t pos, uint8_t *dat) { pos += DATA_HSIZE; - log_infoln("write flash sector @ %zu...", pos); + log_infoln("write flash sector @ ", (base16_u32_, pos), "..."); if (memcmp(dat, DATA_START+pos, FLASH_SECTOR_SIZE) != 0) { bool saved = cr_save_and_disable_interrupts(); flash_range_erase(pos, FLASH_SECTOR_SIZE); @@ -131,6 +105,102 @@ static void ab_flash_write_sector(size_t pos, uint8_t *dat) { log_debugln("... written"); } +/* io_preader_to, io_pwriter, io_closer ***************************************/ + +LO_IMPLEMENTATION_STATIC(io_preader_to, struct flashio, flashio); +LO_IMPLEMENTATION_STATIC(io_pwriter, struct flashio, flashio); +LO_IMPLEMENTATION_STATIC(io_closer, struct flashio, flashio); + +/** read from anywhere on the chip */ +static size_t_and_error flashio_pread_to(struct flashio *self, lo_interface io_writer dst, uoff_t _src_offset, size_t count) { + assert(self); + + if (_src_offset > DATA_SIZE) + return ERROR_AND(size_t, 0, error_new(E_POSIX_EINVAL, "offset is past the chip size")); + size_t src_offset = (size_t) _src_offset; + + if (src_offset == DATA_SIZE) + return ERROR_AND(size_t, 0, error_new(E_EOF)); + + /* Assume that somewhere down the line the pointer we pass to + * io_write() will be passed to DMA. We don't want the DMA + * engine to hit (slow) XIP (for instance, this can cause + * reads/writes to the SSP to get out of sync with eachother). + * So, copy the data to a buffer in (fast) RAM first. It's + * lame that the DMA engine can only have a DREQ on one side + * of the channel. + */ + size_t sector_base = LM_ROUND_DOWN(src_offset, FLASH_SECTOR_SIZE); + if (src_offset + count > sector_base + FLASH_SECTOR_SIZE) + count = (sector_base + FLASH_SECTOR_SIZE) - src_offset; + assert(src_offset + count <= DATA_SIZE); + + if (!self->rbuf.ok || self->rbuf.pos != sector_base) { + self->rbuf.ok = true; + self->rbuf.pos = sector_base; + memcpy(self->rbuf.dat, DATA_START+sector_base, FLASH_SECTOR_SIZE); + } + + return io_write(dst, &self->rbuf.dat[src_offset-sector_base], count); +} + +/** takes offsets in the lower half, writes to the upper half */ +static size_t_and_error flashio_pwritev(struct flashio *self, const struct iovec *iov, int iovcnt, uoff_t _offset) { + assert(self); + assert(iov); + assert(iovcnt > 0); + + size_t total_count = 0; + for (int i = 0; i < iovcnt; i++) + total_count += iov[i].iov_len; + assert(total_count > 0); + + if (_offset >= DATA_HSIZE) + return ERROR_AND(size_t, 0, error_new(E_POSIX_ENOSPC, "cannot write past half the chip size")); + size_t offset = (size_t) _offset; + + size_t total_done = 0; + for (int i = 0; i < iovcnt; i++) { + size_t iov_done = 0; + while (iov_done < iov[i].iov_len) { + if (offset >= DATA_HSIZE) + return ERROR_AND(size_t, total_done, error_new(E_POSIX_ENOSPC, "cannot write past half the chip size")); + size_t sector_base = LM_ROUND_DOWN(offset, FLASH_SECTOR_SIZE); + size_t len = iov[i].iov_len - iov_done; + if (offset + len > sector_base + FLASH_SECTOR_SIZE) + len = (sector_base + FLASH_SECTOR_SIZE) - offset; + assert(offset + len <= DATA_HSIZE); + + if (self->wbuf.ok && self->wbuf.pos != sector_base) + ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat); + if (!self->wbuf.ok || self->wbuf.pos != sector_base) { + self->wbuf.ok = true; + self->wbuf.pos = sector_base; + if (len != FLASH_SECTOR_SIZE) + /* Don't bother with a read if we're just going to overwrite it. */ + memcpy(self->wbuf.dat, DATA_START+DATA_HSIZE+sector_base, FLASH_SECTOR_SIZE); + } + memcpy(&self->wbuf.dat[offset-sector_base], iov[i].iov_base+iov_done, len); + total_done += len; + iov_done += len; + offset += len; + } + } + return ERROR_AND(size_t, total_done, ERROR_NULL); +} + +static error flashio_close(struct flashio *self) { + assert(self); + + if (self->finalize) { + if (self->wbuf.ok) + ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat); + ab_flash_finalize(self->wbuf.dat); + } + + return ERROR_NULL; +} + /* srv_file *******************************************************************/ void flash_file_free(struct flash_file *self) { @@ -140,7 +210,7 @@ struct lib9p_qid flash_file_qid(struct flash_file *self) { assert(self); return (struct lib9p_qid){ - .type = LIB9P_QT_FILE|LIB9P_QT_EXCL, + .type = LIB9P_QT_FILE|LIB9P_QT_EXCL|LIB9P_QT_APPEND, .vers = 1, .path = self->pathnum, }; @@ -152,7 +222,7 @@ lib9p_srv_stat_or_error flash_file_stat(struct flash_file *self, struct lib9p_sr return ERROR_NEW_VAL(lib9p_srv_stat, ((struct lib9p_srv_stat){ .qid = flash_file_qid(self), - .mode = LIB9P_DM_EXCL|0666, + .mode = LIB9P_DM_EXCL|LIB9P_DM_APPEND|0666, .atime_sec = UTIL9P_ATIME, .mtime_sec = UTIL9P_MTIME, .size = DATA_SIZE, @@ -178,26 +248,24 @@ error flash_file_remove(struct flash_file *self, struct lib9p_srv_ctx *ctx) { LIB9P_SRV_NOTDIR(, struct flash_file, flash_file); +static error flash_handle_ihex_data(void *, uint32_t off, uint8_t count, uint8_t *dat); +static error flash_handle_ihex_eof(void *); + lib9p_srv_fio_or_error flash_file_fopen(struct flash_file *self, struct lib9p_srv_ctx *ctx, - bool rd, bool wr, bool trunc) { + bool LM_UNUSED(rd), bool wr, bool LM_UNUSED(trunc)) { assert(self); assert(ctx); - if (rd) { - self->rbuf.ok = false; - } - + memset(&self->io, 0, sizeof(self->io)); if (wr) { - if (trunc) { - ab_flash_initialize_zero(self->wbuf.dat); - self->written = true; - } else { - ab_flash_initialize(self->wbuf.dat); - self->written = false; - } - self->wbuf.ok = false; + ab_flash_initialize(self->io.wbuf.dat); } + memset(&self->ihex, 0, sizeof(self->ihex)); + self->ihex.handle_arg = self; + self->ihex.handle_data = flash_handle_ihex_data; + self->ihex.handle_eof = flash_handle_ihex_eof; + return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, self)); } @@ -214,81 +282,49 @@ static uint32_t flash_file_iounit(struct flash_file *self) { static void flash_file_iofree(struct flash_file *self) { assert(self); - if (self->wbuf.ok) - ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat); + error err = flashio_close(&self->io); + assert(ERROR_IS_NULL(err)); - if (self->written) - ab_flash_finalize(self->wbuf.dat); + err = ihex_decoder_close(&self->ihex); + assert(ERROR_IS_NULL(err)); } -static iovec_or_error flash_file_pread(struct flash_file *self, struct lib9p_srv_ctx *ctx, - uint32_t byte_count, uint64_t byte_offset) { +static error flash_file_pread(struct flash_file *self, struct lib9p_srv_ctx *ctx, + lo_interface io_writer dst, uint64_t byte_offset, uint32_t byte_count) { assert(self); assert(ctx); - if (byte_offset > DATA_SIZE) - return ERROR_NEW_ERR(iovec, error_new(E_POSIX_EINVAL, "offset is past the chip size")); + return flashio_pread_to(&self->io, dst, byte_offset, byte_count).err; +} - /* Assume that somewhere down the line the iovec we return - * will be passed to DMA. We don't want the DMA engine to hit - * (slow) XIP (for instance, this can cause reads/writes to - * the SSP to get out of sync with eachother), so copy the - * data to a buffer in (fast) RAM first. It's lame that the - * DMA engine can only have a DREQ on one side of the channel. - */ - if (byte_offset == DATA_SIZE) - return ERROR_NEW_VAL(iovec, ((struct iovec){ - .iov_len = 0, - })); - size_t sector_base = LM_ROUND_DOWN(byte_offset, FLASH_SECTOR_SIZE); - if (byte_offset + byte_count > sector_base + FLASH_SECTOR_SIZE) - byte_count = (sector_base + FLASH_SECTOR_SIZE) - byte_offset; - assert(byte_offset + byte_count <= DATA_SIZE); +static error flash_handle_ihex_data(void *_self, uint32_t off, uint8_t count, uint8_t *dat) { + struct flash_file *self = _self; - if (!self->rbuf.ok || self->rbuf.pos != sector_base) { - self->rbuf.ok = true; - self->rbuf.pos = sector_base; - memcpy(self->rbuf.dat, DATA_START+sector_base, FLASH_SECTOR_SIZE); - } + if (off < XIP_BASE || off >= XIP_BASE + DATA_HSIZE) + return error_new(E_POSIX_ENOSPC, "cannot write outside of the first half of the chip: ", + (base16_u32_, off), " is outside of [", (base16_u32_, XIP_BASE), ",", (base16_u32_, XIP_BASE + DATA_HSIZE), ")"); - return ERROR_NEW_VAL(iovec, ((struct iovec){ - .iov_base = &self->rbuf.dat[byte_offset-sector_base], - .iov_len = byte_count, - })); + return flashio_pwritev(&self->io, + &((struct iovec){.iov_base = dat, .iov_len = count}), 1, + off - XIP_BASE).err; +} +static error flash_handle_ihex_eof(void *_self) { + struct flash_file *self = _self; + self->io.finalize = true; + return ERROR_NULL; } -/* TODO: Short/corrupt writes are dangerous. This should either (1) - * check a checksum, (2) use uf2 instead of verbatim data, or (3) use - * ihex instead of verbatim data. */ +/* TODO: Also support uf2, not just ihex. */ static uint32_t_or_error flash_file_pwrite(struct flash_file *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, - uint64_t byte_offset) { + uint64_t LM_UNUSED(byte_offset)) { assert(self); assert(ctx); - if (byte_offset > DATA_HSIZE) - return ERROR_NEW_ERR(uint32_t, error_new(E_POSIX_EINVAL, "offset is past half the chip size")); - if (byte_count == 0) - return ERROR_NEW_VAL(uint32_t, 0); - if (byte_offset == DATA_HSIZE) - return ERROR_NEW_ERR(uint32_t, error_new(E_POSIX_EINVAL, "offset is at half the chip size")); - - size_t sector_base = LM_ROUND_DOWN(byte_offset, FLASH_SECTOR_SIZE); - if (byte_offset + byte_count > sector_base + FLASH_SECTOR_SIZE) - byte_count = (sector_base + FLASH_SECTOR_SIZE) - byte_offset; - assert(byte_offset + byte_count < DATA_HSIZE); - - if (self->wbuf.ok && self->wbuf.pos != sector_base) - ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat); - if (!self->wbuf.ok || self->wbuf.pos != sector_base) { - self->wbuf.ok = true; - self->wbuf.pos = sector_base; - if (byte_count != FLASH_SECTOR_SIZE) - memcpy(self->wbuf.dat, DATA_START+DATA_HSIZE+sector_base, FLASH_SECTOR_SIZE); - } - memcpy(&self->wbuf.dat[byte_offset-sector_base], buf, byte_count); - - self->written = true; - return ERROR_NEW_VAL(uint32_t, byte_count); + size_t_and_error r = ihex_decoder_writev(&self->ihex, + &((struct iovec){.iov_base = buf, .iov_len = byte_count}), 1); + if (r.size_t == 0 && !ERROR_IS_NULL(r.err)) + return ERROR_NEW_ERR(uint32_t, r.err); + return ERROR_NEW_VAL(uint32_t, (uint32_t) r.size_t); } |