diff options
Diffstat (limited to 'cmd')
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_ */ |