summaryrefslogtreecommitdiff
path: root/cmd/sbc_harness/fs_harness_flash_bin.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/sbc_harness/fs_harness_flash_bin.c')
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.c258
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);
}