summaryrefslogtreecommitdiff
path: root/cmd/sbc_harness/fs_harness_flash_bin.c
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-06-12 02:32:05 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-06-12 02:32:05 -0600
commit8ce568bae1cc9a77996f16b859482992d27e0b37 (patch)
tree3d88dac69ba2c9bb75bfffcda0e9df627ba0ec32 /cmd/sbc_harness/fs_harness_flash_bin.c
parent7ddc93c7084e2893cf771dc7b5c027a6303404da (diff)
parentbaa075b9d499f13ea445d1371b5338c9043a95de (diff)
Merge branch 'lukeshu/io-pread'
Diffstat (limited to 'cmd/sbc_harness/fs_harness_flash_bin.c')
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.c189
1 files changed, 116 insertions, 73 deletions
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.c b/cmd/sbc_harness/fs_harness_flash_bin.c
index 9a5bb2f..07675d1 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) ******************/
@@ -111,7 +111,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) {
@@ -131,6 +131,103 @@ 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;
+ self->written = true;
+ }
+ }
+ return ERROR_AND(size_t, total_done, ERROR_NULL);
+}
+
+static error flashio_close(struct flashio *self) {
+ assert(self);
+
+ if (self->wbuf.ok)
+ ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat);
+
+ if (self->written)
+ ab_flash_finalize(self->wbuf.dat);
+
+ return ERROR_NULL;
+}
+
/* srv_file *******************************************************************/
void flash_file_free(struct flash_file *self) {
@@ -179,23 +276,18 @@ error flash_file_remove(struct flash_file *self, struct lib9p_srv_ctx *ctx) {
LIB9P_SRV_NOTDIR(, struct flash_file, flash_file);
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 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;
+ ab_flash_initialize_zero(self->io.wbuf.dat);
+ self->io.written = true;
} else {
- ab_flash_initialize(self->wbuf.dat);
- self->written = false;
+ ab_flash_initialize(self->io.wbuf.dat);
}
- self->wbuf.ok = false;
}
return ERROR_NEW_VAL(lib9p_srv_fio, LO_BOX(lib9p_srv_fio, self));
@@ -214,47 +306,16 @@ 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);
-
- if (self->written)
- ab_flash_finalize(self->wbuf.dat);
+ error err = flashio_close(&self->io);
+ 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"));
-
- /* 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);
-
- 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 ERROR_NEW_VAL(iovec, ((struct iovec){
- .iov_base = &self->rbuf.dat[byte_offset-sector_base],
- .iov_len = byte_count,
- }));
+ return flashio_pread_to(&self->io, dst, byte_offset, byte_count).err;
}
/* TODO: Short/corrupt writes are dangerous. This should either (1)
@@ -267,28 +328,10 @@ static uint32_t_or_error flash_file_pwrite(struct flash_file *self, struct lib9p
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 = flashio_pwritev(&self->io,
+ &((struct iovec){.iov_base = buf, .iov_len = byte_count}), 1,
+ byte_offset);
+ 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);
}