summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/sbc_harness/CMakeLists.txt64
-rw-r--r--cmd/sbc_harness/config/config.h87
-rw-r--r--cmd/sbc_harness/config/tusb_config.h43
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.c312
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.h30
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.c156
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.h19
-rw-r--r--cmd/sbc_harness/main.c213
-rw-r--r--cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md29
l---------cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt1
l---------cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt1
l---------cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt1
l---------cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt1
l---------cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt1
-rw-r--r--cmd/sbc_harness/static/Documentation/harness_flash_bin.txt33
-rw-r--r--cmd/sbc_harness/static/Documentation/harness_rom_bin.txt41
-rw-r--r--cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt17
-rw-r--r--cmd/sbc_harness/tusb_log.c15
-rw-r--r--cmd/sbc_harness/usb_keyboard.c14
-rw-r--r--cmd/sbc_harness/usb_keyboard.h4
-rw-r--r--cmd/srv9p/CMakeLists.txt25
-rw-r--r--cmd/srv9p/config/config.h53
-rw-r--r--cmd/srv9p/main.c116
-rw-r--r--cmd/srv9p/static/Documentation/x1
-rw-r--r--cmd/srv9p/static/README.md1
-rw-r--r--cmd/srv9p/static9p.c242
-rw-r--r--cmd/srv9p/static9p.h47
27 files changed, 1006 insertions, 561 deletions
diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt
index 2018cf7..678af07 100644
--- a/cmd/sbc_harness/CMakeLists.txt
+++ b/cmd/sbc_harness/CMakeLists.txt
@@ -1,31 +1,53 @@
# cmd/sbc_harness/CMakeLists.txt - Build script for main sbc_harness.uf2 firmware file
#
-# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
if (PICO_PLATFORM STREQUAL "rp2040")
# Compile ######################################################################
-add_library(sbc_harness_objs
+add_library(sbc_harness_objs OBJECT
main.c
usb_keyboard.c
+ tusb_log.c
+
+ fs_harness_flash_bin.c
+ fs_harness_uptime_txt.c
)
target_include_directories(sbc_harness_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
target_include_directories(sbc_harness_objs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(sbc_harness_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(sbc_harness_objs
- pico_stdlib
+ pico_runtime
+ pico_stdio_uart
+
hardware_flash
+ hardware_watchdog
libmisc
+ libfmt
libusb
- #libdhcp
- libhw
+ libdhcp
+ libhw_cr
+ lib9p
+ lib9p_util
+)
+pico_minimize_runtime(sbc_harness_objs
+ INCLUDE PRINTF PRINTF_MINIMAL PRINTF_LONG_LONG PRINTF_PTRDIFF_T
)
-pico_enable_stdio_usb(sbc_harness_objs 0)
-pico_enable_stdio_uart(sbc_harness_objs 1)
-pico_enable_stdio_semihosting(sbc_harness_objs 0)
-pico_enable_stdio_rtt(sbc_harness_objs 0)
+target_compile_definitions(sbc_harness_objs PRIVATE
+ #PICO_USE_FASTEST_SUPPORTED_CLOCK=1
+
+ # Calculated by `./3rd-party/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --cmake-only 170`
+ PLL_SYS_REFDIV=2
+ PLL_SYS_VCO_FREQ_HZ=1530000000
+ PLL_SYS_POSTDIV1=3
+ PLL_SYS_POSTDIV2=3
+ SYS_CLK_HZ=170000000
+)
+
+suppress_tinyusb_warnings()
# Analyze the stack ############################################################
@@ -34,9 +56,29 @@ add_stack_analysis(sbc_harness_stack.c sbc_harness_objs)
# Link #########################################################################
add_executable(sbc_harness)
-target_link_libraries(sbc_harness sbc_harness_objs)
-target_sources(sbc_harness PRIVATE sbc_harness_stack.c)
+target_sources(sbc_harness PRIVATE
+ sbc_harness_stack.c
+ "$<TARGET_OBJECTS:sbc_harness_objs>"
+)
+target_link_libraries(sbc_harness
+ pico_standard_link
+)
+target_link_options(sbc_harness PRIVATE "$<TARGET_PROPERTY:sbc_harness_objs,LINK_OPTIONS>")
pico_add_extra_outputs(sbc_harness) # create .map/.bin/.hex/.uf2 files in addition to .elf
pico_set_program_url(sbc_harness "https://git.lukeshu.com/sbc-harness")
+# Embed ########################################################################
+
+target_embed_sources(sbc_harness_objs sbc_harness static.h
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
+ static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
+ static/Documentation/harness_rom_bin.txt
+ static/Documentation/harness_flash_bin.txt
+ static/Documentation/harness_uptime_txt.txt
+)
+
endif()
diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h
index 5ee8634..14c2f03 100644
--- a/cmd/sbc_harness/config/config.h
+++ b/cmd/sbc_harness/config/config.h
@@ -1,22 +1,41 @@
/* config.h - Compile-time configuration for sbc_harness
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
+#include <stddef.h> /* for size_t */
+
+#define CONFIG_FLASH_DEBUG 1
+
+/* RP2040 *********************************************************************/
+
+#define CONFIG_RP2040_SPI_DEBUG 1 /* bool */
+
/* W5500 **********************************************************************/
/**
- * How many W5500 chips we have.
+ * When allocating an arbitrary local port, what range should it be
+ * allocated from?
+ *
+ * These are the default values of the Linux kernel's
+ * net.ipv4.ip_local_port_range, so I figure they're probably good
+ * values to use.
*/
-#define CONFIG_W5500_NUM 1
+#define CONFIG_W5500_LOCAL_PORT_MIN 32768
+#define CONFIG_W5500_LOCAL_PORT_MAX 60999
+
+#define CONFIG_W5500_VALIDATE_SPI 1 /* bool */
+#define CONFIG_W5500_DEBUG 0 /* bool */
+#define CONFIG_W5500_LL_DEBUG 0 /* bool */
/* 9P *************************************************************************/
-#define CONFIG_9P_PORT 564
+#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
+
/**
* This max-msg-size is sized so that a Twrite message can return
* 8KiB of data.
@@ -34,31 +53,63 @@
* negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to
* (8*1024)+160 in 2e and 3e.
*/
-#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24)
+#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24)
/**
* Maximum host-data-structure size. A message may be larger in
* unmarshaled-host-structures than marshaled-net-bytes due to (1)
- * struct padding, (2) nul-terminator byes for strings.
+ * struct padding, (2) array pointers.
+ */
+#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16
+#define CONFIG_9P_SRV_MAX_FIDS 16
+#define CONFIG_9P_SRV_MAX_REQS 2
+#define CONFIG_9P_SRV_MAX_DEPTH 3
+
+#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
+
+/* DHCP ***********************************************************************/
+
+#define CONFIG_DHCP_CAN_RECV_UNICAST_IP_WITHOUT_IP 0 /* bool */
+#define CONFIG_DHCP_DEBUG 1 /* bool */
+#define CONFIG_DHCP_OPT_SIZE 312 /* minimum of 312 */
+#define CONFIG_DHCP_SELECTING_NS (5*NS_PER_S)
+
+/* USB KEYBOARD ***************************************************************/
+
+/**
+ * Which USB port to use for the Root Hub.
+ *
+ * The RP2040 only has 1 port, so it's gotta be port #0.
*/
-#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16
-#define CONFIG_9P_MAX_FIDS 16
-#define CONFIG_9P_MAX_REQS 2
-#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
+#define CONFIG_USB_COMMON_RHPORT 0
+
+#define CONFIG_USB_COMMON_DEBUG 1 /* bool */
/* COROUTINE ******************************************************************/
-#define CONFIG_COROUTINE_DEFAULT_STACK_SIZE 512
+extern const size_t CONFIG_COROUTINE_STACK_SIZE_dhcp_cr;
+extern const size_t CONFIG_COROUTINE_STACK_SIZE_init_cr;
+extern const size_t CONFIG_COROUTINE_STACK_SIZE_read9p_cr;
+extern const size_t CONFIG_COROUTINE_STACK_SIZE_write9p_cr;
+extern const size_t CONFIG_COROUTINE_STACK_SIZE_usb_common_cr;
+extern const size_t CONFIG_COROUTINE_STACK_SIZE_usb_keyboard_cr;
+extern const size_t CONFIG_COROUTINE_STACK_SIZE_w5500_irq_cr;
+#define CONFIG_COROUTINE_NAME_LEN 16
#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */
#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */
#define CONFIG_COROUTINE_DEBUG 0 /* bool */
#define CONFIG_COROUTINE_VALGRIND 0 /* bool */
+#define CONFIG_COROUTINE_GDB 1 /* bool */
-#define _CONFIG_9P_NUM_SOCKS 7
-#define CONFIG_COROUTINE_NUM ( \
- 1 /* usb_common */ + \
- 1 /* usb_keyboard */ + \
- CONFIG_W5500_NUM /* irq handler */ + \
- _CONFIG_9P_NUM_SOCKS /* 9P accept()+read() */ + \
- (CONFIG_9P_MAX_REQS*_CONFIG_9P_NUM_SOCKS) /* 9P work+write() */ )
+#define _CONFIG_9P_NUM_SOCKS 3 /* FIXME: bump this back up to 8 */
+#define CONFIG_COROUTINE_NUM ( \
+ 1 /* usb_common */ + \
+ 1 /* usb_keyboard */ + \
+ 1 /* W5500 irq handler */ + \
+ _CONFIG_9P_NUM_SOCKS /* 9P accept()+read() */ + \
+ (CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS) /* 9P work+write() */ )
#endif /* _CONFIG_H_ */
diff --git a/cmd/sbc_harness/config/tusb_config.h b/cmd/sbc_harness/config/tusb_config.h
index aeff31c..0a6d3e4 100644
--- a/cmd/sbc_harness/config/tusb_config.h
+++ b/cmd/sbc_harness/config/tusb_config.h
@@ -36,16 +36,35 @@ extern "C" {
#endif
//--------------------------------------------------------------------
-// TinyUSB Device (TUD) initialization for rp2040-based boards
+// Override the default definition of TU_ASSERT() to use our logging
//--------------------------------------------------------------------
-// Which USB port to use for the RootHub.
-// The rp2040 only has 1 port, so it's gotta be port #0.
-#define BOARD_TUD_RHPORT 0
-
-// RHPort max operational speed.
-// Use OPT_MODE_DEFAULT_SPEED for max speed supported by MCU.
-#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+// "magically" select between the 1-arg and 2-args variants, inject a
+// stringified version of `_cond`.
+//
+// Note: Use GNU-C `, ##__VA_ARGS__`, not standard C `__VA_OPT__(,)
+// __VA_ARGS__`; because __VA_OPT__ doesn't handle present-but-empty
+// arguments the way that we need.
+#define TU_ASSERT(_cond, ...) \
+ _GET_3RD_ARG(_cond, ##__VA_ARGS__, \
+ _LIBMISC_TU_ASSERT_2ARGS, _LIBMISC_TU_ASSERT_1ARGS, _dummy) \
+ (_cond, #_cond, ##__VA_ARGS__)
+
+#define _LIBMISC_TU_ASSERT_1ARGS(_cond, _cond_str) \
+ _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, false)
+
+#define _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, _ret) \
+ do { \
+ if ( !(_cond) ) { \
+ _libmisc_tu_mess_failed(_cond_str, \
+ __FILE__, __LINE__, __func__); \
+ TU_BREAKPOINT(); \
+ return _ret; \
+ } \
+ } while(0)
+
+void _libmisc_tu_mess_failed(const char *expr,
+ const char *file, unsigned int line, const char *func);
//--------------------------------------------------------------------
// Configuration: common
@@ -66,13 +85,13 @@ extern "C" {
// Tinyusb use follows macros to declare transferring memory so that they can be put
// into those specific section.
// e.g
-// - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
-// - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+// - CFG_TUSB_MEM SECTION : [[gnu::section(".usb_ram")]]
+// - CFG_TUSB_MEM_ALIGN : [[gnu::aligned(4)]]
#define CFG_TUSB_MEM_SECTION /* blank */
-#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#define CFG_TUSB_MEM_ALIGN [[gnu::aligned(4)]]
#define CFG_TUD_ENABLED 1
-#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+#define CFG_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
//--------------------------------------------------------------------
// Configuration: TinyUSB Device (TUD)
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.c b/cmd/sbc_harness/fs_harness_flash_bin.c
new file mode 100644
index 0000000..bcdf296
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_flash_bin.c
@@ -0,0 +1,312 @@
+/* sbc_harness/fs_harness_flash_bin.c - 9P access to flash storage
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h>
+
+#include <hardware/flash.h>
+#include <hardware/watchdog.h>
+
+#define LOG_NAME FLASH
+#include <libmisc/log.h>
+
+#include <util9p/static.h>
+
+#define IMPLEMENTATION_FOR_FS_HARNESS_FLASH_BIN YES
+#include "fs_harness_flash_bin.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct flash_file, flash_file, static);
+
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct flash_file, flash_file);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct flash_file, flash_file, static);
+
+#define DATA_START ((const char *)(XIP_NOALLOC_BASE))
+#define DATA_SIZE PICO_FLASH_SIZE_BYTES
+#define DATA_HSIZE (DATA_SIZE/2)
+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. */
+
+/* ab_flash_* (mid-level utilities for our A/B write scheme) ******************/
+
+/**
+ * Copy the upper half of flash to the lower half of flash, then reboot.
+ *
+ * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE
+ */
+[[noreturn]] static void __no_inline_not_in_flash_func(ab_flash_finalize)(uint8_t *buf) {
+ assert(buf);
+
+ infof("copying upper flash to lower flash...");
+
+ cr_save_and_disable_interrupts();
+
+ for (size_t off = 0; off < DATA_HSIZE; off += FLASH_SECTOR_SIZE) {
+ memcpy(buf, DATA_START+DATA_HSIZE+off, FLASH_SECTOR_SIZE);
+ if (memcmp(DATA_START+off, buf, FLASH_SECTOR_SIZE) == 0)
+ continue;
+ flash_range_erase(off, FLASH_SECTOR_SIZE);
+ flash_range_program(off, buf, FLASH_SECTOR_SIZE);
+ }
+
+ infof("rebooting...");
+
+ watchdog_reboot(0, 0, 300);
+
+ for (;;)
+ asm volatile ("nop");
+}
+
+/**
+ * Set the upper half of flash to all zero bytes.
+ *
+ * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE
+ */
+static void ab_flash_initialize_zero(uint8_t *buf) {
+ assert(buf);
+
+ memset(buf, 0, FLASH_SECTOR_SIZE);
+
+ infof("zeroing 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);
+ }
+ debugf("... 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);
+
+ infof("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);
+ cr_restore_interrupts(saved);
+ }
+ debugf("... initialized");
+}
+
+/**
+ * Write `dat` to flash sector `pos`+(DATA_SIZE/2) (i.e. `pos` is a
+ * 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 dat : the FLASH_SECTOR_SIZE bytes to write
+ */
+static void ab_flash_write_sector(size_t pos, uint8_t *dat) {
+ assert(pos < DATA_HSIZE);
+ assert(pos % FLASH_SECTOR_SIZE == 0);
+ assert(dat);
+
+ pos += DATA_HSIZE;
+
+ infof("write flash sector @ %zu...", 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);
+ flash_range_program(pos, dat, FLASH_SECTOR_SIZE);
+ cr_restore_interrupts(saved);
+ }
+ debugf("... written");
+}
+
+/* srv_file *******************************************************************/
+
+static void flash_file_free(struct flash_file *self) {
+ assert(self);
+}
+static struct lib9p_qid flash_file_qid(struct flash_file *self) {
+ assert(self);
+
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE|LIB9P_QT_EXCL,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+static struct lib9p_stat flash_file_stat(struct flash_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ return (struct lib9p_stat){
+ .kern_type = 0,
+ .kern_dev = 0,
+ .file_qid = flash_file_qid(self),
+ .file_mode = LIB9P_DM_EXCL|0666,
+ .file_atime = UTIL9P_ATIME,
+ .file_mtime = UTIL9P_MTIME,
+ .file_size = DATA_SIZE,
+ .file_name = lib9p_str(self->name),
+ .file_owner_uid = lib9p_str("root"),
+ .file_owner_gid = lib9p_str("root"),
+ .file_last_modified_uid = lib9p_str("root"),
+ .file_extension = lib9p_str(NULL),
+ .file_owner_n_uid = 0,
+ .file_owner_n_gid = 0,
+ .file_last_modified_n_uid = 0,
+ };
+}
+static void flash_file_wstat(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_stat) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+static void flash_file_remove(struct flash_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+
+LIB9P_SRV_NOTDIR(struct flash_file, flash_file);
+
+static lo_interface lib9p_srv_fio flash_file_fopen(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ bool rd, bool wr, bool trunc) {
+ assert(self);
+ assert(ctx);
+
+ if (rd) {
+ self->rbuf.ok = false;
+ }
+
+ 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;
+ }
+
+ return lo_box_flash_file_as_lib9p_srv_fio(self);
+}
+
+/* srv_fio ********************************************************************/
+
+static uint32_t flash_file_iounit(struct flash_file *self) {
+ assert(self);
+ return FLASH_SECTOR_SIZE;
+}
+
+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);
+}
+
+static void flash_file_pread(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ uint32_t byte_count, uint64_t byte_offset,
+ struct iovec *ret) {
+ assert(self);
+ assert(ctx);
+ assert(ret);
+
+ if (byte_offset > DATA_SIZE) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is past the chip size");
+ return;
+ }
+
+ /* 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) {
+ *ret = (struct iovec){
+ .iov_len = 0,
+ };
+ return;
+ }
+ 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);
+ }
+
+ *ret = (struct iovec){
+ .iov_base = &self->rbuf.dat[byte_offset-sector_base],
+ .iov_len = byte_count,
+ };
+}
+
+/* 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. */
+static uint32_t flash_file_pwrite(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ void *buf,
+ uint32_t byte_count,
+ uint64_t byte_offset) {
+ assert(self);
+ assert(ctx);
+
+ if (byte_offset > DATA_HSIZE) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is past half the chip size");
+ return 0;
+ }
+ if (byte_count == 0)
+ return 0;
+ if (byte_offset == DATA_HSIZE) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is at half the chip size");
+ return 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_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 byte_count;
+}
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.h b/cmd/sbc_harness/fs_harness_flash_bin.h
new file mode 100644
index 0000000..36382be
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_flash_bin.h
@@ -0,0 +1,30 @@
+/* sbc_harness/fs_harness_flash_bin.h - 9P access to flash storage
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_
+#define _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_
+
+#include <hardware/flash.h> /* for FLASH_SECTOR_SIZE */
+
+#include <lib9p/srv.h>
+
+struct flash_file {
+ char *name;
+ uint64_t pathnum;
+
+ BEGIN_PRIVATE(FS_HARNESS_FLASH_BIN);
+ bool written;
+ struct {
+ bool ok;
+ size_t pos;
+ uint8_t dat[FLASH_SECTOR_SIZE];
+ } wbuf, rbuf;
+ END_PRIVATE(FS_HARNESS_FLASH_BIN);
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct flash_file, flash_file);
+#define lo_box_flash_file_as_lib9p_srv_file(obj) util9p_box(flash_file, obj)
+
+#endif /* _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_ */
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.c b/cmd/sbc_harness/fs_harness_uptime_txt.c
new file mode 100644
index 0000000..9216986
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.c
@@ -0,0 +1,156 @@
+/* sbc_harness/fs_harness_uptime_txt.c - 9P access to harness uptime
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdio.h> /* for snprintf() */
+#include <stdlib.h> /* for malloc(), free() */
+
+#include <libhw/generic/alarmclock.h>
+#include <util9p/static.h>
+
+#include "fs_harness_uptime_txt.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct uptime_file, uptime_file, static);
+
+struct uptime_fio {
+ struct uptime_file *parent;
+ size_t buf_len;
+ char buf[24]; /* len(str(UINT64_MAX)+"ns\n\0") */
+};
+
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct uptime_fio, uptime_fio);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct uptime_fio, uptime_fio, static);
+
+/* srv_file *******************************************************************/
+
+static void uptime_file_free(struct uptime_file *self) {
+ assert(self);
+}
+static struct lib9p_qid uptime_file_qid(struct uptime_file *self) {
+ assert(self);
+
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+static struct lib9p_stat uptime_file_stat(struct uptime_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ uint64_t now = LO_CALL(bootclock, get_time_ns);
+ uint64_t size = 0;
+ while (now) {
+ size++;
+ now /= 10;
+ }
+ if (!size)
+ size++;
+ size += 3;
+
+ return (struct lib9p_stat){
+ .kern_type = 0,
+ .kern_dev = 0,
+ .file_qid = uptime_file_qid(self),
+ .file_mode = 0444,
+ .file_atime = UTIL9P_ATIME,
+ .file_mtime = UTIL9P_MTIME,
+ .file_size = size,
+ .file_name = lib9p_str(self->name),
+ .file_owner_uid = lib9p_str("root"),
+ .file_owner_gid = lib9p_str("root"),
+ .file_last_modified_uid = lib9p_str("root"),
+ .file_extension = lib9p_str(NULL),
+ .file_owner_n_uid = 0,
+ .file_owner_n_gid = 0,
+ .file_last_modified_n_uid = 0,
+ };
+}
+static void uptime_file_wstat(struct uptime_file *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_stat) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+static void uptime_file_remove(struct uptime_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+
+LIB9P_SRV_NOTDIR(struct uptime_file, uptime_file);
+
+static lo_interface lib9p_srv_fio uptime_file_fopen(struct uptime_file *self, struct lib9p_srv_ctx *ctx,
+ bool LM_UNUSED(rd), bool LM_UNUSED(wr), bool LM_UNUSED(trunc)) {
+ assert(self);
+ assert(ctx);
+
+ struct uptime_fio *ret = malloc(sizeof(struct uptime_fio));
+ ret->parent = self;
+ ret->buf_len = 0;
+
+ return lo_box_uptime_fio_as_lib9p_srv_fio(ret);
+}
+
+/* srv_fio ********************************************************************/
+
+static uint32_t uptime_fio_iounit(struct uptime_fio *self) {
+ assert(self);
+ return sizeof(self->buf)-1;
+}
+
+static void uptime_fio_iofree(struct uptime_fio *self) {
+ assert(self);
+ free(self);
+}
+
+static struct lib9p_qid uptime_fio_qid(struct uptime_fio *self) {
+ assert(self);
+ assert(self->parent);
+ return uptime_file_qid(self->parent);
+}
+
+static void uptime_fio_pread(struct uptime_fio *self, struct lib9p_srv_ctx *ctx,
+ uint32_t byte_count, uint64_t byte_offset,
+ struct iovec *ret) {
+ assert(self);
+ assert(ctx);
+ assert(ret);
+
+ if (byte_offset == 0 || self->buf_len == 0) {
+ uint64_t now = LO_CALL(bootclock, get_time_ns);
+ self->buf_len = snprintf(self->buf, sizeof(self->buf), "%"PRIu64"ns\n", now);
+ }
+
+ if (byte_offset > (uint64_t)self->buf_len) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is past end-of-file length");
+ return;
+ }
+
+ size_t beg_off = (size_t)byte_offset;
+ size_t end_off = beg_off + (size_t)byte_count;
+ if (end_off > self->buf_len)
+ end_off = self->buf_len;
+ *ret = (struct iovec){
+ .iov_base = &self->buf[beg_off],
+ .iov_len = end_off-beg_off,
+ };
+}
+
+static uint32_t uptime_fio_pwrite(struct uptime_fio *self, struct lib9p_srv_ctx *ctx,
+ void *LM_UNUSED(buf),
+ uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(byte_offset)) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+ return 0;
+}
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.h b/cmd/sbc_harness/fs_harness_uptime_txt.h
new file mode 100644
index 0000000..7bf2945
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.h
@@ -0,0 +1,19 @@
+/* sbc_harness/fs_harness_uptime_txt.h - 9P access to harness uptime
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_
+#define _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_
+
+#include <lib9p/srv.h>
+
+struct uptime_file {
+ char *name;
+ uint64_t pathnum;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct uptime_file, uptime_file);
+#define lo_box_uptime_file_as_lib9p_srv_file(obj) util9p_box(uptime_file, obj)
+
+#endif /* _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_ */
diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c
index b9c5330..20cf5b1 100644
--- a/cmd/sbc_harness/main.c
+++ b/cmd/sbc_harness/main.c
@@ -1,24 +1,120 @@
/* sbc_harness/main.c - Main entry point and event loop for sbc-harness
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <string.h> /* libc: for strlen() */
-#include <stdio.h> /* libc: for printf() */
+/* libc */
+#include <string.h> /* libc: for strlen() */
-#include <pico/stdlib.h> /* pico-sdk:pico_stdlib: for stdio_uart_init() */
-#include <hardware/flash.h> /* pico-sdk:hardware_flash: for flash_get_unique_id() */
+/* pico-sdk */
+#include <pico/stdio_uart.h> /* pico-sdk:pico_stdio_uart: for stdio_uart_init() */
+#include <hardware/flash.h> /* pico-sdk:hardware_flash: for flash_get_unique_id() */
+/* our OS */
#include <libcr/coroutine.h>
+#include <libhw/generic/alarmclock.h> /* so we can set `bootclock` */
#include <libhw/rp2040_hwspi.h>
+#include <libhw/rp2040_hwtimer.h>
#include <libhw/w5500.h>
-#include <libmisc/hash.h>
+
+/* our application libraries */
+#include <libdhcp/client.h>
#include <libusb/usb_common.h>
+#include <lib9p/srv.h>
+#include <util9p/static.h>
+
+/* our utility libraries */
+#include <libmisc/hash.h>
+#define LOG_NAME MAIN
+#include <libmisc/log.h>
+/* local headers */
#include "usb_keyboard.h"
+#include "static.h"
+#include "fs_harness_flash_bin.h"
+#include "fs_harness_uptime_txt.h"
+
+/* configuration **************************************************************/
+
+#include "config.h"
-COROUTINE hello_world_cr(void *_chan) {
+/* file tree ******************************************************************/
+
+enum { PATH_BASE = __COUNTER__ };
+#define PATH_COUNTER __COUNTER__ - PATH_BASE
+
+#define STATIC_FILE(STRNAME, ...) UTIL9P_STATIC_FILE(PATH_COUNTER, STRNAME, __VA_ARGS__)
+#define STATIC_DIR(STRNAME, ...) UTIL9P_STATIC_DIR(PATH_COUNTER, STRNAME, __VA_ARGS__)
+
+#define API_FILE(STRNAME, SYMNAME, ...) \
+ lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \
+ .name = STRNAME, \
+ .pathnum = PATH_COUNTER \
+ __VA_OPT__(,) __VA_ARGS__ \
+ }))
+
+struct lib9p_srv_file root =
+ STATIC_DIR("",
+ STATIC_DIR("Documentation",
+ STATIC_FILE("YOUR_RIGHTS_AND_OBLIGATIONS.md",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_md_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_md_end),
+ STATIC_DIR("YOUR_RIGHTS_AND_OBLIGATIONS",
+ STATIC_FILE("agpl-3.0.txt",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_end),
+ STATIC_FILE("pico-sdk.bsd3.txt",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_end),
+ STATIC_FILE("printf.mit.txt",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_end),
+ STATIC_FILE("tinyusb.mit.txt",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_end),
+ STATIC_FILE("newlib.txt",
+ .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_start,
+ .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_end),
+ ),
+ STATIC_FILE("harness_rom_bin.txt",
+ .data_start = _binary_static_Documentation_harness_rom_bin_txt_start,
+ .data_end = _binary_static_Documentation_harness_rom_bin_txt_end),
+ STATIC_FILE("harness_flash_bin.txt",
+ .data_start = _binary_static_Documentation_harness_flash_bin_txt_start,
+ .data_end = _binary_static_Documentation_harness_flash_bin_txt_end),
+ STATIC_FILE("harness_uptime_txt.txt",
+ .data_start = _binary_static_Documentation_harness_uptime_txt_txt_start,
+ .data_end = _binary_static_Documentation_harness_uptime_txt_txt_end),
+ ),
+ STATIC_DIR("harness",
+ STATIC_FILE("rom.bin",
+ .data_start = (void*)0x00000000,
+ .data_size = 16*1024),
+ API_FILE("flash.bin",
+ flash),
+ API_FILE("uptime.txt",
+ uptime),
+ // TODO: system.log
+ // TODO: proc.txt
+ // TODO: cpuinfo.txt
+ // TODO: ctl
+ ),
+ STATIC_DIR("dut",
+ // TODO: hdmi.nut
+ // TODO: uart.txt
+ // TODO: usb-keyboard.txt
+ ),
+ );
+
+static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) {
+ return root;
+}
+
+/* Code ***********************************************************************/
+
+/*
+static COROUTINE hello_world_cr(void *_chan) {
const char *msg = "Hello world!\n";
usb_keyboard_rpc_t *chan = _chan;
cr_begin();
@@ -26,17 +122,55 @@ COROUTINE hello_world_cr(void *_chan) {
for (size_t i = 0;; i = (i+1) % strlen(msg)) {
int result = usb_keyboard_rpc_send_req(chan, (uint32_t)msg[i]);
if (result < 1) {
- printf("error!\n");
+ errorf("error sending rune U+%d", (uint32_t)msg[i]);
break;
}
}
cr_end();
}
+*/
-int main() {
- /* initialization *****************************************************/
- stdio_uart_init();
+struct {
+ struct rp2040_hwspi dev_spi;
+ struct w5500 dev_w5500;
+ usb_keyboard_rpc_t keyboard_chan;
+ uint16_t usb_serial[sizeof(uint64_t)*2]; /* UTF-16 */
+ struct lib9p_srv srv;
+} globals;
+
+static COROUTINE dhcp_cr(void *) {
+ cr_begin();
+
+ dhcp_client_main(lo_box_w5500_if_as_net_iface(&globals.dev_w5500), "harness");
+
+ cr_end();
+}
+
+static COROUTINE read9p_cr(void *) {
+ cr_begin();
+
+ lo_interface net_iface iface = lo_box_w5500_if_as_net_iface(&globals.dev_w5500);
+ lo_interface net_stream_listener listener = LO_CALL(iface, tcp_listen, LIB9P_DEFAULT_PORT_9FS);
+
+ lib9p_srv_accept_and_read_loop(&globals.srv, listener);
+
+ cr_end();
+}
+
+static COROUTINE write9p_cr(void *) {
+ cr_begin();
+
+ lib9p_srv_worker_loop(&globals.srv);
+
+ cr_end();
+}
+
+const char *const hexdig = "0123456789ABCDEF";
+static_assert(CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS <= 16);
+
+COROUTINE init_cr(void *) {
+ cr_begin();
/* NOR flash chips have a (bog-?)standard "RUID" "Read Unique
* ID" instruction; use our flash chip's unique ID as the
@@ -52,16 +186,18 @@ int main() {
(uint8_t)((flash_id32 >> 0) & 0xFF),
};
- struct rp2040_hwspi dev_spi;
- struct w5500 dev_w5500;
- rp2040_hwspi_init(&dev_spi, "W5500", RP2040_HWSPI_0,
+ rp2040_hwspi_init(&globals.dev_spi, "W5500", RP2040_HWSPI_0,
SPI_MODE_0, /* the W5500 supports mode 0 or mode 3 */
- 80*1000*1000, /* run at the W5500's max rate of 80MHz */
- 16, /* PIN_MISO */
- 19, /* PIN_MOSI */
- 18, /* PIN_CLK */
- 17); /* PIN_CS */
- w5500_init(&dev_w5500, "W5500", &dev_spi,
+ 42500000, /* min(w5500, hwspi); w5500=80MHz; hwspi=42.5MHz, see rp2040_hwspi.h for a comment about why this is so low */
+ 30, /* W5500 datasheet says min(T_CS = SCSn High Time) = 30ns */
+ 0, /* bogus write write data when doing a read */
+ 16, /* PIN_MISO */
+ 19, /* PIN_MOSI */
+ 18, /* PIN_CLK */
+ 17, /* PIN_CS */
+ 0, 1, 2, 3); /* DMA channels */
+ w5500_init(&globals.dev_w5500, "W5500",
+ lo_box_rp2040_hwspi_as_spi(&globals.dev_spi),
21, /* PIN_INTR */
20, /* PIN_RESET */
((struct net_eth_addr){{
@@ -71,17 +207,40 @@ int main() {
flash_id24[0], flash_id24[1], flash_id24[2],
}}));
- usb_common_earlyinit();
+ static_assert(sizeof(flash_id64)*2 == LM_ARRAY_LEN(globals.usb_serial));
+ for (size_t i = 0; i < LM_ARRAY_LEN(globals.usb_serial); i++)
+ globals.usb_serial[i] = hexdig[(flash_id64 >> ((sizeof(flash_id64)*8)-((i+1)*4))) & 0xF];
+ usb_common_earlyinit(globals.usb_serial, sizeof(globals.usb_serial));
usb_keyboard_init();
usb_common_lateinit();
+ globals.keyboard_chan = (usb_keyboard_rpc_t){0};
+
+ globals.srv.rootdir = get_root;
+
/* set up coroutines **************************************************/
- coroutine_add(usb_common_cr, NULL);
- usb_keyboard_rpc_t keyboard_chan = {0};
- coroutine_add(usb_keyboard_cr, &keyboard_chan);
- //coroutine_add(hello_world_cr, &keyboard_chan);
- //coroutine_add(dhcp_client_cr, NULL);
+ coroutine_add("usb_common", usb_common_cr, NULL);
+ coroutine_add("usb_keyboard", usb_keyboard_cr, &globals.keyboard_chan);
+ //coroutine_add("hello_world", hello_world_cr, &globals.keyboard_chan);
+ coroutine_add("dhcp", dhcp_cr, NULL);
+ for (int i = 0; i < _CONFIG_9P_NUM_SOCKS; i++) {
+ char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'};
+ coroutine_add(name, read9p_cr, NULL);
+ }
+ for (int i = 0; i < CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS; i++) {
+ char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'};
+ coroutine_add(name, write9p_cr, NULL);
+ }
+
+ cr_exit();
+}
- /* event loop *********************************************************/
+int main() {
+ bootclock = rp2040_hwtimer(0);
+ stdio_uart_init();
+ /* char *hdr = "=" * (80-strlen("info : MAIN: ")); */
+ infof("===================================================================");
+ coroutine_add("init", init_cr, NULL);
coroutine_main();
+ assert_notreached("all coroutines exited");
}
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
new file mode 100644
index 0000000..b3fc12e
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
@@ -0,0 +1,29 @@
+<!--
+ Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md - Overview of your
+ rights and obligations with regard to the SBC-Harness firmware
+
+ Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+The firmware running on the SBC-Harness is Free Software -- if you
+have access to this file, then you have the freedom to use, study,
+share, and improve the firmware; as long as you follow a few
+conditions that basically amount to "paying it forward".
+
+The precise terms of your rights and obligations are given in the
+files in the `YOUR_RIGHTS_AND_OBLIGATIONS/` directory. You must obey
+each of the files in that directory when distributing
+verbatim-or-modified copies of the firmware:
+
+ - `agpl-3.0.txt`: The firmware is as-a-whole licensed to you under
+ the terms of the GNU Affero General Public License (GNU AGPL) as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version. This is the main
+ document that protects your rights and defines your obligations.
+
+ - other files: The firmware makes use of other code that is under
+ various other licenses. When taken with the AGPL, they amount to
+ requiring that you pass along the copyright and license text in
+ those files when you distribute verbatim-or-modified copies of the
+ firmware.
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
new file mode 120000
index 0000000..8a9b6f3
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt
@@ -0,0 +1 @@
+../../../../../COPYING.txt \ No newline at end of file
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
new file mode 120000
index 0000000..5c5939c
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
@@ -0,0 +1 @@
+../../../../../3rd-party/COPYING.newlib.txt \ No newline at end of file
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
new file mode 120000
index 0000000..52c4374
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
@@ -0,0 +1 @@
+../../../../../3rd-party/pico-sdk/LICENSE.TXT \ No newline at end of file
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
new file mode 120000
index 0000000..5a6e342
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
@@ -0,0 +1 @@
+../../../../../3rd-party/pico-sdk/src/rp2_common/pico_printf/LICENSE \ No newline at end of file
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
new file mode 120000
index 0000000..22a67cf
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
@@ -0,0 +1 @@
+../../../../../3rd-party/pico-sdk/lib/tinyusb/LICENSE \ No newline at end of file
diff --git a/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt b/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt
new file mode 100644
index 0000000..115f2ee
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt
@@ -0,0 +1,33 @@
+NAME
+ /harness/flash.bin
+
+DESCRIPTION
+ Access to the flash storage chip (where the harness firmware
+ is stored).
+
+ Any number of readers may read the flash contents.
+
+ Only one writer can have the file open at a time; once the
+ file is closed, the harness reboots into the new firmware.
+ Writes to the top half of the chip will fail.
+
+BUGS
+ - The size of the chip is configured at compile-time. If the
+ firmware is loaded onto hardware with a larger flash chip
+ than it was compiled for, then the upper part of the chip
+ will not be accessible with this file. If the firmware is
+ loaded onto hardware with a smaller flash chip than it was
+ compiled for, then accessing the missing upper part of the
+ chip will crash.
+
+ - When writing to the flash using this file, only half of the
+ chip capacity is usable; the top half and bottom half are
+ mirrors of each-other. This is to avoid the firmware
+ crashing as its program text is overwritten; the firmware is
+ executing out of the bottom half, and writing to the top
+ half; once the file is closed, a minimal in-RAM function
+ copies the top half to the bottom half and reboots.
+
+AUTHOR
+ Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt b/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt
new file mode 100644
index 0000000..63fd0a3
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt
@@ -0,0 +1,41 @@
+NAME
+ /harness/rom.bin
+
+DESCRIPTION
+ Read access to the RP2040 CPU's ROM. This contains code that
+ initializes the chip to load the main firmware from the
+ external flash chip, provides a failsafe USB-programmable
+ mode, and provides a few functions that the main firmware can
+ call to.
+
+BUGS
+ This ROM is programmed into the chip at the factory; revising
+ it means issuing a new revison of the RP2040 CPU. So while
+ the source code to the ROM is freely available to be used,
+ studied, and shared; one cannot install modified versions onto
+ the CPU.
+
+HISTORY
+ - RP2040 B0 : chips manufactured before September 2020 or so
+ - RP2040 B1 : chips manufactured after September 2020 or so
+ - Released to the public January 2021; chance whether you get
+ a B0 or a B1 chip.
+ - RP2040 B2 : released September 2021
+
+ Printed on the physical CPU is a label that indicates which
+ revision it is. For example:
+
+ RP2-B2 21/24
+
+ indicates that it is the "B2" revision (and was manufactured
+ the 21st week (late May) of 2024).
+
+SEE ALSO
+ - /harness/cpuinfo.txt can report which CPU version you have.
+
+ - The source code to each ROM revision is published at
+ https://github.com/raspberrypi/pico-bootrom-rp2040
+
+AUTHOR
+ Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt b/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt
new file mode 100644
index 0000000..1ab86f7
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt
@@ -0,0 +1,17 @@
+NAME
+ /harness/uptime.txt
+
+DESCRIPTION
+ Reading this file gives a text string of the format
+ `sprintf("%uns\n", uptime_ns)` indicating the harness's uptime
+ in an integer number of nanoseconds.
+
+BUGS
+ Using nanoseconds gives the illusion of more precision than
+ there actually is; the harness' clock only has microsecond
+ resolution; the last 3 digits of the returned nanosecond count
+ will always be 0.
+
+AUTHOR
+ Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/cmd/sbc_harness/tusb_log.c b/cmd/sbc_harness/tusb_log.c
new file mode 100644
index 0000000..4c6b7df
--- /dev/null
+++ b/cmd/sbc_harness/tusb_log.c
@@ -0,0 +1,15 @@
+/* sbc_harness/tusb_log.c - Logger for tusb_config.h
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#define LOG_NAME TINY_USB
+#include <libmisc/log.h>
+
+void _libmisc_tu_mess_failed(const char *expr,
+ const char *file, unsigned int line, const char *func) {
+ errorf("%s:%u:%s(): assertion \"%s\" failed",
+ file, line, func,
+ expr);
+}
diff --git a/cmd/sbc_harness/usb_keyboard.c b/cmd/sbc_harness/usb_keyboard.c
index 3500e31..f3cb42d 100644
--- a/cmd/sbc_harness/usb_keyboard.c
+++ b/cmd/sbc_harness/usb_keyboard.c
@@ -1,6 +1,6 @@
/* sbc_harness/usb_keyboard.c - Implementation of a USB keyboard device
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -8,11 +8,10 @@
#include <libusb/tusb_helpers.h> /* for TUD_ENDPOINT_IN */
#include <libusb/usb_common.h>
+#include <libmisc/macro.h>
#include "usb_keyboard.h"
-#define UNUSED(name) /* name __attribute__ ((unused)) */
-
/**
* A USB-HID "Report Descriptor" (see USB-HID 1.11 ยง6.2.2 "Report
* Descriptor") describing a keyboard.
@@ -24,7 +23,6 @@ static uint8_t kbd_ifc = 0;
void usb_keyboard_init() {
if (kbd_ifc)
return;
- usb_common_earlyinit();
kbd_ifc = usb_add_interface(cfgnum_std, TUD_HID_DESC_LEN, (uint8_t[]){
/* USB-HID input-only descriptor for inclusion in the config descriptor; consisting of 3 parts:
@@ -97,7 +95,7 @@ uint8_t const *tud_hid_descriptor_report_cb(uint8_t index) {
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
- // TODO not Implemented
+ // TODO not implemented
(void) instance;
(void) report_id;
(void) report_type;
@@ -109,7 +107,11 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
-void tud_hid_set_report_cb(uint8_t UNUSED(instance), uint8_t UNUSED(report_id), hid_report_type_t UNUSED(report_type), uint8_t const *UNUSED(buffer), uint16_t UNUSED(bufsize))
+void tud_hid_set_report_cb(uint8_t LM_UNUSED(instance),
+ uint8_t LM_UNUSED(report_id),
+ hid_report_type_t LM_UNUSED(report_type),
+ uint8_t const *LM_UNUSED(buffer),
+ uint16_t LM_UNUSED(bufsize))
{
// TODO not implemented
}
diff --git a/cmd/sbc_harness/usb_keyboard.h b/cmd/sbc_harness/usb_keyboard.h
index 210014d..cf8483b 100644
--- a/cmd/sbc_harness/usb_keyboard.h
+++ b/cmd/sbc_harness/usb_keyboard.h
@@ -1,6 +1,6 @@
/* sbc_harness/usb_keyboard.h - Implementation of a USB keyboard device
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -12,7 +12,7 @@
#include <libcr/coroutine.h> /* for COROUTINE */
#include <libcr_ipc/rpc.h> /* for CR_RPC_DECLARE */
-CR_RPC_DECLARE(usb_keyboard_rpc, uint32_t, int)
+CR_RPC_DECLARE(usb_keyboard_rpc, uint32_t, int);
void usb_keyboard_init(void);
COROUTINE usb_keyboard_cr(void *arg);
diff --git a/cmd/srv9p/CMakeLists.txt b/cmd/srv9p/CMakeLists.txt
deleted file mode 100644
index b747882..0000000
--- a/cmd/srv9p/CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-# cmd/srv9p/CMakeLists.txt - Build script for srv9p test/dev executable
-#
-# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-if (PICO_PLATFORM STREQUAL "host")
-
-add_executable(srv9p
- main.c
- static9p.c
-)
-target_embed_sources(srv9p static.h
- static/README.md
- static/Documentation/x
-)
-target_include_directories(srv9p PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-target_include_directories(srv9p PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
-target_link_libraries(srv9p
- libcr
- libcr_ipc
- libmisc
- lib9p
-)
-
-endif()
diff --git a/cmd/srv9p/config/config.h b/cmd/srv9p/config/config.h
deleted file mode 100644
index a184e6f..0000000
--- a/cmd/srv9p/config/config.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* config.h - Compile-time configuration for srv9p
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _CONFIG_H_
-#define _CONFIG_H_
-
-#define CONFIG_NETIO_NUM_CONNS 8
-
-#define CONFIG_9P_PORT 564
-/**
- * This max-msg-size is sized so that a Twrite message can return
- * 8KiB of data.
- *
- * This is the same as the default in Plan 9 4e's lib9p; it has the
- * comment that "24" is "ample room for Twrite/Rread header
- * (iounit)". In fact, the Twrite header is only 23 bytes
- * ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and the
- * Rread header is even shorter at 11 bytes ("size[4] Rread[1]
- * tag[2] count[4]"), so "24" appears to be the size of the Twrite
- * header rounded up to a nice round number.
- *
- * In older versions of 9P ("9P1"), the max message size was
- * defined as part of the protocol specification rather than
- * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to
- * (8*1024)+160 in 2e and 3e.
- */
-#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24)
-/**
- * Maximum host-data-structure size. A message may be larger in
- * unmarshaled-host-structures than marshaled-net-bytes due to (1)
- * struct padding, (2) nul-terminator byes for strings.
- */
-#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16
-#define CONFIG_9P_MAX_FIDS 16
-#define CONFIG_9P_MAX_REQS 2
-#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
-#define CONFIG_9P_ENABLE_9P2000_u
-/*#define CONFIG_9P_ENABLE_9P2000_e*/
-
-#define CONFIG_COROUTINE_DEFAULT_STACK_SIZE (32*1024)
-#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */
-#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */
-#define CONFIG_COROUTINE_DEBUG 0 /* bool */
-#define CONFIG_COROUTINE_VALGRIND 1 /* bool */
-#define CONFIG_COROUTINE_NUM (1 /* usb_common */ +\
- 1 /* usb_keyboard */ +\
- CONFIG_NETIO_NUM_CONNS /* accept+read */ +\
- (CONFIG_9P_MAX_REQS*CONFIG_NETIO_NUM_CONNS) /* work+write */ )
-
-#endif /* _CONFIG_H_ */
diff --git a/cmd/srv9p/main.c b/cmd/srv9p/main.c
deleted file mode 100644
index d30c4f9..0000000
--- a/cmd/srv9p/main.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* srv9p/main.c - Main entry point for test 9P server
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <error.h>
-#include <stdio.h>
-
-#include <lib9p/srv.h>
-#include <libcr/coroutine.h>
-#include <libhw/generic/net.h>
-#include <libhw/host_net.h>
-
-#include "static9p.h"
-#include "static.h"
-
-/* configuration **************************************************************/
-
-#include "config.h"
-
-#ifndef CONFIG_NETIO_NUM_CONNS
- #error config.h must define CONFIG_NETIO_NUM_CONNS
-#endif
-
-/* implementation *************************************************************/
-
-#define UNUSED(name) /* name __attribute__((unused)) */
-
-/* file tree ******************************************************************/
-
-#define FILE_COMMON(NAME) { \
- .vtable = &static_file_vtable, \
- \
- .u_name = "root", .u_num = 0, /* owner user */ \
- .g_name = "root", .g_num = 0, /* owner group */ \
- .m_name = "root", .m_num = 0, /* last-modified-by user */ \
- \
- .pathnum = __COUNTER__, \
- .name = NAME, \
- .perm = 0444, \
- .atime = 1728337905, \
- .mtime = 1728337904, \
- }
-
-#define DIR_COMMON(NAME) { \
- .vtable = &static_dir_vtable, \
- \
- .u_name = "root", .u_num = 0, /* owner user */ \
- .g_name = "root", .g_num = 0, /* owner group */ \
- .m_name = "root", .m_num = 0, /* last-modified-by user */ \
- \
- .pathnum = __COUNTER__, \
- .name = NAME, \
- .perm = 0555, \
- .atime = 1728337905, \
- .mtime = 1728337904, \
- }
-
-#define STATIC_FILE(STRNAME, SYMNAME) \
- &((static struct static_file){ \
- ._static_common = FILE_COMMON(STRNAME), \
- .data_start = _binary_static_##SYMNAME##_start, \
- .data_end = _binary_static_##SYMNAME##_end, \
- })
-
-static struct static_dir root = {
- ._static_common = DIR_COMMON(""),
- .members = {
- &((static struct static_dir){
- ._static_common = DIR_COMMON("Documentation"),
- .members = {
- STATIC_FILE("x", Documentation_x),
- NULL
- },
- }),
- STATIC_FILE("README.md", README_md),
- NULL,
- },
-};
-
-static implements_lib9p_srv_file *get_root(struct lib9p_srv_ctx *UNUSED(ctx), char *UNUSED(treename)) {
- return &root;
-}
-
-/* main ***********************************************************************/
-
-static COROUTINE read_cr(void *_srv) {
- struct lib9p_srv *srv = _srv;
- assert(srv);
-
- cr_begin();
-
- struct hostnet_tcp_listener listener;
- hostnet_tcp_listener_init(&listener, 9000);
-
- lib9p_srv_read_cr(srv, &listener);
-
- cr_end();
-}
-
-int main() {
- struct lib9p_srv srv = {
- .rootdir = get_root,
- };
-
- for (int i = 0; i < CONFIG_NETIO_NUM_CONNS; i++)
- if (!coroutine_add(read_cr, &srv))
- error(1, 0, "coroutine_add(read_cr, &srv)");
- for (int i = 0; i < 2*CONFIG_NETIO_NUM_CONNS; i++)
- if (!coroutine_add(lib9p_srv_write_cr, &srv))
- error(1, 0, "coroutine_add(lib9p_srv_write_cr, &srv)");
-
- coroutine_main();
- return 1;
-}
diff --git a/cmd/srv9p/static/Documentation/x b/cmd/srv9p/static/Documentation/x
deleted file mode 100644
index 257cc56..0000000
--- a/cmd/srv9p/static/Documentation/x
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/cmd/srv9p/static/README.md b/cmd/srv9p/static/README.md
deleted file mode 100644
index af5626b..0000000
--- a/cmd/srv9p/static/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Hello, world!
diff --git a/cmd/srv9p/static9p.c b/cmd/srv9p/static9p.c
deleted file mode 100644
index 3632c26..0000000
--- a/cmd/srv9p/static9p.c
+++ /dev/null
@@ -1,242 +0,0 @@
-/* srv9p/static9p.c - Serve static files over 9P
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <assert.h>
-
-#include <libmisc/vcall.h>
-
-#include "static9p.h"
-
-#define UNUSED(name) /* name __attribute__((unused)) */
-#define p9_str(cstr) ((struct lib9p_s){ .len = strlen(cstr), .utf8 = cstr })
-#define p9_nulstr ((struct lib9p_s){ .len = 0, .utf8 = NULL })
-
-/* common *********************************************************************/
-
-static implements_lib9p_srv_file *static_common_clone(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) {
- _static_common *self = VCALL_SELF(_static_common, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- return self;
-}
-
-static void static_common_free(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) {
- _static_common *self = VCALL_SELF(_static_common, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- /* do nothing */
-}
-
-static uint32_t static_common_io(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, lib9p_o_t UNUSED(flags)) {
- _static_common *self = VCALL_SELF(_static_common, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- return 0;
-}
-
-static void static_common_wstat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx,
- struct lib9p_stat UNUSED(new)) {
- _static_common *self = VCALL_SELF(_static_common, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
-}
-
-static void static_common_remove(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) {
- _static_common *self = VCALL_SELF(_static_common, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
-}
-
-/* dir ************************************************************************/
-
-static struct lib9p_stat static_dir_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) {
- struct static_dir *self = VCALL_SELF(struct static_dir, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- return (struct lib9p_stat){
- .kern_type = 0,
- .kern_dev = 0,
- .file_qid = {
- .type = LIB9P_QT_DIR,
- .vers = 1,
- .path = self->pathnum,
- },
- .file_mode = LIB9P_DM_DIR | (self->perm & 0555),
- .file_atime = self->atime,
- .file_mtime = self->mtime,
- .file_size = 0,
- .file_name = p9_str(self->name),
- .file_owner_uid = p9_str(self->u_name),
- .file_owner_gid = p9_str(self->g_name),
- .file_last_modified_uid = p9_str(self->m_name),
- .file_extension = p9_nulstr,
- .file_owner_n_uid = self->u_num,
- .file_owner_n_gid = self->g_num,
- .file_last_modified_n_uid = self->m_num,
- };
-}
-
-static implements_lib9p_srv_file *static_dir_dopen(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx,
- char *childname) {
- struct static_dir *self = VCALL_SELF(struct static_dir, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- for (size_t i = 0; self->members[i]; i++) {
- implements_lib9p_srv_file *filep = self->members[i];
- struct lib9p_stat stat = VCALL(filep, stat, ctx);
- if (lib9p_ctx_has_error(&ctx->basectx))
- break;
- lib9p_assert_stat(stat);
- if (strcmp(stat.file_name.utf8, childname) == 0)
- return filep;
- }
- lib9p_error(&ctx->basectx,
- LINUX_ENOENT, "no such file or directory");
- return NULL;
-}
-
-static implements_lib9p_srv_file *static_dir_dcreate(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx,
- char *UNUSED(childname),
- lib9p_dm_t UNUSED(perm), lib9p_o_t UNUSED(flags)) {
- struct static_dir *self = VCALL_SELF(struct static_dir, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
- return NULL;
-}
-
-static size_t static_dir_dread(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx,
- uint8_t *buf,
- uint32_t byte_count,
- size_t _obj_offset) {
- struct static_dir *self = VCALL_SELF(struct static_dir, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- uint32_t byte_offset = 0;
- size_t obj_offset = _obj_offset;
- while (self->members[obj_offset]) {
- implements_lib9p_srv_file *filep = self->members[obj_offset];
- struct lib9p_stat stat = VCALL(filep, stat, ctx);
- if (lib9p_ctx_has_error(&ctx->basectx))
- break;
- lib9p_assert_stat(stat);
- uint32_t nbytes = lib9p_marshal_stat(&ctx->basectx, byte_count-byte_offset, &stat,
- &buf[byte_offset]);
- if (!nbytes) {
- if (obj_offset == _obj_offset)
- lib9p_error(&ctx->basectx,
- LINUX_ERANGE, "stat object does not fit into negotiated max message size");
- break;
- }
- byte_offset += nbytes;
- obj_offset++;
- }
- return obj_offset - _obj_offset;
-}
-
-struct lib9p_srv_file_vtable static_dir_vtable = {
- .clone = static_common_clone,
- .free = static_common_free,
-
- .io = static_common_io,
- .stat = static_dir_stat,
- .wstat = static_common_wstat,
- .remove = static_common_remove,
-
- .dopen = static_dir_dopen,
- .dcreate = static_dir_dcreate,
-
- .dread = static_dir_dread,
-};
-
-/* file ***********************************************************************/
-
-static inline size_t static_file_size(struct static_file *file) {
- assert(file);
-
-#if __unix__
- assert(file->data_start);
-#endif
- if (!file->data_end)
- return file->data_size;
- return (size_t)((uintptr_t)file->data_end - (uintptr_t)file->data_start);
-}
-
-static struct lib9p_stat static_file_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) {
- struct static_file *self = VCALL_SELF(struct static_file, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- return (struct lib9p_stat){
- .kern_type = 0,
- .kern_dev = 0,
- .file_qid = {
- .type = LIB9P_QT_FILE,
- .vers = 1,
- .path = self->pathnum,
- },
- .file_mode = self->perm & 0444,
- .file_atime = self->atime,
- .file_mtime = self->mtime,
- .file_size = (uint64_t)static_file_size(self),
- .file_name = p9_str(self->name),
- .file_owner_uid = p9_str(self->u_name),
- .file_owner_gid = p9_str(self->g_name),
- .file_last_modified_uid = p9_str(self->m_name),
- .file_extension = p9_nulstr,
- .file_owner_n_uid = self->u_num,
- .file_owner_n_gid = self->g_num,
- .file_last_modified_n_uid = self->m_num,
- };
-}
-
-static uint32_t static_file_pread(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx,
- void *buf,
- uint32_t byte_count,
- uint64_t byte_offset) {
- struct static_file *self = VCALL_SELF(struct static_file, implements_lib9p_srv_file, _self);
- assert(self);
- assert(ctx);
-
- size_t data_size = static_file_size(self);
-
- if (byte_offset > (uint64_t)data_size) {
- lib9p_error(&ctx->basectx,
- LINUX_EINVAL, "offset is past end-of-file length");
- return 0;
- }
-
- size_t beg_off = (size_t)byte_offset;
- size_t end_off = beg_off + (size_t)byte_count;
- if (end_off > data_size)
- end_off = data_size;
- memcpy(buf, &self->data_start[beg_off], end_off-beg_off);
- return (uint32_t)(end_off-beg_off);
-}
-
-struct lib9p_srv_file_vtable static_file_vtable = {
- .clone = static_common_clone,
- .free = static_common_free,
-
- .io = static_common_io,
- .stat = static_file_stat,
- .wstat = static_common_wstat,
- .remove = static_common_remove,
-
- .pread = static_file_pread,
- .pwrite = NULL,
-};
diff --git a/cmd/srv9p/static9p.h b/cmd/srv9p/static9p.h
deleted file mode 100644
index 7a3d476..0000000
--- a/cmd/srv9p/static9p.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* srv9p/static9p.h - Serve static files over 9P
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _SRV9P_STATIC9P_H_
-#define _SRV9P_STATIC9P_H_
-
-#include <lib9p/srv.h>
-
-typedef struct {
- implements_lib9p_srv_file;
-
- char *u_name;
- uint32_t u_num;
- char *g_name;
- uint32_t g_num;
- char *m_name;
- uint32_t m_num;
-
- uint64_t pathnum;
- char *name;
- lib9p_dm_t perm;
- uint32_t atime, mtime;
-} _static_common;
-
-struct static_dir {
- _static_common;
-
- /* NULL-terminated */
- implements_lib9p_srv_file *members[];
-};
-
-
-struct static_file {
- _static_common;
-
- char *data_start; /* must not be NULL */
- char *data_end; /* may be NULL, in which case data_size is used */
- size_t data_size; /* only used if .data_end==NULL */
-};
-
-extern struct lib9p_srv_file_vtable static_dir_vtable;
-extern struct lib9p_srv_file_vtable static_file_vtable;
-
-#endif /* _SRV9P_STATIC9P_H_ */