From f26a034f193256961c253cbd93e818b8a0c1cd81 Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Sat, 27 Jun 2026 08:24:13 -0400 Subject: [PATCH 1/5] libtock: add syscall return type convert functions --- libtock/tock.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ libtock/tock.h | 35 +++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/libtock/tock.c b/libtock/tock.c index c74a71906..7292a255e 100644 --- a/libtock/tock.c +++ b/libtock/tock.c @@ -74,6 +74,62 @@ returncode_t tock_command_return_u32_u32_to_returncode(syscall_return_t command_ } } +returncode_t tock_command_return_u32_u32_u32_to_returncode(syscall_return_t command_return, + uint32_t* val1, uint32_t* val2, uint32_t* val3) { + if (command_return.type == TOCK_SYSCALL_SUCCESS_U32_U32_U32) { + *val1 = command_return.data[0]; + *val2 = command_return.data[1]; + *val3 = command_return.data[2]; + return RETURNCODE_SUCCESS; + } else if (command_return.type == TOCK_SYSCALL_FAILURE) { + return tock_status_to_returncode(command_return.data[0]); + } else { + return RETURNCODE_EBADRVAL; + } +} + +returncode_t tock_command_return_u32_u64_to_returncode(syscall_return_t command_return, + uint32_t* val1, uint64_t* val2) { + if (command_return.type == TOCK_SYSCALL_SUCCESS_U32_U64) { + *val1 = command_return.data[0]; + *val2 = (uint64_t)command_return.data[1] | ((uint64_t)command_return.data[2] << 32); + return RETURNCODE_SUCCESS; + } else if (command_return.type == TOCK_SYSCALL_FAILURE) { + return tock_status_to_returncode(command_return.data[0]); + } else { + return RETURNCODE_EBADRVAL; + } +} + +returncode_t tock_command_return_failure_u32_to_returncode(syscall_return_t command_return, uint32_t* val) { + if (command_return.type == TOCK_SYSCALL_FAILURE_U32) { + *val = command_return.data[1]; + return tock_status_to_returncode(command_return.data[0]); + } else { + return RETURNCODE_EBADRVAL; + } +} + +returncode_t tock_command_return_failure_u32_u32_to_returncode(syscall_return_t command_return, + uint32_t* val1, uint32_t* val2) { + if (command_return.type == TOCK_SYSCALL_FAILURE_U32_U32) { + *val1 = command_return.data[1]; + *val2 = command_return.data[2]; + return tock_status_to_returncode(command_return.data[0]); + } else { + return RETURNCODE_EBADRVAL; + } +} + +returncode_t tock_command_return_failure_u64_to_returncode(syscall_return_t command_return, uint64_t* val) { + if (command_return.type == TOCK_SYSCALL_FAILURE_U64) { + *val = (uint64_t)command_return.data[1] | ((uint64_t)command_return.data[2] << 32); + return tock_status_to_returncode(command_return.data[0]); + } else { + return RETURNCODE_EBADRVAL; + } +} + returncode_t tock_subscribe_return_to_returncode(subscribe_return_t subscribe_return) { // If the subscribe was successful, easily return SUCCESS. if (subscribe_return.success) { diff --git a/libtock/tock.h b/libtock/tock.h index c58db2813..3ba42a617 100644 --- a/libtock/tock.h +++ b/libtock/tock.h @@ -180,6 +180,41 @@ returncode_t tock_command_return_u32_u32_to_returncode(syscall_return_t, uint32_ // Convert a `syscall_return_t` with a `u64` value to a `returncode_t`. returncode_t tock_command_return_u64_to_returncode(syscall_return_t command_return, uint64_t* val); +// Convert a `syscall_return_t` with three `u32` values to a `returncode_t`. +// +// This expects exactly three `u32`s to be returned (i.e. the only success case +// is `TOCK_SYSCALL_SUCCESS_U32_U32_U32`). Do not use with other expected +// SyscallReturn variants. +returncode_t tock_command_return_u32_u32_u32_to_returncode(syscall_return_t, uint32_t*, uint32_t*, uint32_t*); + +// Convert a `syscall_return_t` with a `u32` and a `u64` value to a `returncode_t`. +// +// This expects a `u32` and a `u64` to be returned (i.e. the only success case +// is `TOCK_SYSCALL_SUCCESS_U32_U64`). Do not use with other expected +// SyscallReturn variants. +returncode_t tock_command_return_u32_u64_to_returncode(syscall_return_t, uint32_t*, uint64_t*); + +// Convert a `syscall_return_t` failure with one u32 to a `returncode_t`. +// +// This expects a failure with one u32 value (i.e. `TOCK_SYSCALL_FAILURE_U32`). +// Fills `val` with the failure u32 on failure. Do not use with other expected +// SyscallReturn variants. +returncode_t tock_command_return_failure_u32_to_returncode(syscall_return_t, uint32_t*); + +// Convert a `syscall_return_t` failure with two u32 values to a `returncode_t`. +// +// This expects a failure with two u32 values (i.e. `TOCK_SYSCALL_FAILURE_U32_U32`). +// Fills `val1` and `val2` with the failure u32s on failure. Do not use with +// other expected SyscallReturn variants. +returncode_t tock_command_return_failure_u32_u32_to_returncode(syscall_return_t, uint32_t*, uint32_t*); + +// Convert a `syscall_return_t` failure with a u64 value to a `returncode_t`. +// +// This expects a failure with one u64 value (i.e. `TOCK_SYSCALL_FAILURE_U64`). +// Fills `val` with the failure u64 on failure. Do not use with other expected +// SyscallReturn variants. +returncode_t tock_command_return_failure_u64_to_returncode(syscall_return_t, uint64_t*); + // Convert a `subscribe_return_t` to a `returncode_t`. returncode_t tock_subscribe_return_to_returncode(subscribe_return_t); From 24bd3367e6a0a1f3b0095abe268273716a15db03 Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Sat, 27 Jun 2026 08:24:40 -0400 Subject: [PATCH 2/5] tests: add syscall return test Verify we can get all return types in userspace correctly --- examples/tests/syscall-return/Makefile | 11 ++ examples/tests/syscall-return/README.md | 67 +++++++ examples/tests/syscall-return/main.c | 244 ++++++++++++++++++++++++ 3 files changed, 322 insertions(+) create mode 100644 examples/tests/syscall-return/Makefile create mode 100644 examples/tests/syscall-return/README.md create mode 100644 examples/tests/syscall-return/main.c diff --git a/examples/tests/syscall-return/Makefile b/examples/tests/syscall-return/Makefile new file mode 100644 index 000000000..54d6a7969 --- /dev/null +++ b/examples/tests/syscall-return/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/syscall-return/README.md b/examples/tests/syscall-return/README.md new file mode 100644 index 000000000..1344a0a41 --- /dev/null +++ b/examples/tests/syscall-return/README.md @@ -0,0 +1,67 @@ +Syscall Return Test +=================== + +Try every syscall type and return variant and ensure the return values match +what is expected. + +Expected Output +--------------- + +``` +syscall-return test +cmd 0: success() + SUCCESS +cmd 1: failure(FAIL) + SUCCESS +cmd 2: failure_u32(BUSY, 0x10000001) + SUCCESS +cmd 3: failure_u32_u32(NOMEM, 0x20000001, 0x20000002) + SUCCESS +cmd 4: failure_u64(INVAL, 0x4000000000000001) + SUCCESS +cmd 5: success() + SUCCESS +cmd 6: success_u32(0x60000001) + SUCCESS +cmd 7: success_u32_u32(0x70000001, 0x70000002) + SUCCESS +cmd 8: success_u32_u32_u32(0x80000001, 0x80000002, 0x80000003) + SUCCESS +cmd 9: success_u64(0x9000000000000001) + SUCCESS +cmd 10: success_u32_u64(0xA0000001, 0xA000000000000002) + SUCCESS +allow_ro success (first call, expect prev ptr=0 len=0) + SUCCESS +allow_ro success (second call, expect prev ptr=ro_buf len=16) + SUCCESS +allow_ro failure (invalid ptr 0x90, expect INVAL ptr=0x90 size=4) + SUCCESS +allow_ro failure (invalid ptr 0x9000000000000000, expect INVAL ptr=0x9000000000000000 size=4) + SUCCESS +allow_rw success (first call, expect prev ptr=0 len=0) + SUCCESS +allow_rw success (second call, expect prev ptr=rw_buf len=16) + SUCCESS +allow_rw failure (invalid ptr 0x90, expect INVAL ptr=0x90 size=4) + SUCCESS +allow_rw failure (invalid ptr 0xa000000000000000, expect INVAL ptr=0xa000000000000000 size=4) + SUCCESS +allow_ur success (first call, expect prev ptr=0 len=0) + SUCCESS +allow_ur success (second call, expect prev ptr=ur_buf len=16) + SUCCESS +allow_ur failure (invalid ptr 0x90, expect INVAL ptr=0x90 size=4) + SUCCESS +allow_ur failure (invalid ptr 0xb000000000000000, expect INVAL ptr=0xb000000000000000 size=4) + SUCCESS +subscribe success (first call, expect prev cb=NULL data=NULL) + SUCCESS +subscribe success (second call, expect prev cb=dummy_upcall data=NULL) + SUCCESS +subscribe failure (invalid fn ptr 0x90, expect INVAL cb=0x90 data=0xfffffffffffffffe) + SUCCESS +subscribe failure (invalid fn ptr 0xc000000000000000, expect INVAL cb=0xc000000000000000 data=0xfffffffffffffffe) + SUCCESS +done +``` \ No newline at end of file diff --git a/examples/tests/syscall-return/main.c b/examples/tests/syscall-return/main.c new file mode 100644 index 000000000..e543835f5 --- /dev/null +++ b/examples/tests/syscall-return/main.c @@ -0,0 +1,244 @@ +#include +#include +#include +#include + +#include + +#define DRIVER_NUM 0xA0000 + +static void dummy_upcall(int a __attribute__((unused)), + int b __attribute__((unused)), + int c __attribute__((unused)), + void* d __attribute__((unused))) {} + +#define CHECK(test, fmt, ...) \ + do { \ + if (test) { \ + printf(" SUCCESS\n"); \ + } else { \ + printf(" FAILURE: " fmt "\n",##__VA_ARGS__); \ + } \ + } while (0) + +int main(void) { + printf("syscall-return test\n"); + + syscall_return_t ret; + int rc; + uint32_t v1, v2, v3; + uint64_t v64; + + // 0: success() + printf("cmd 0: success()\n"); + ret = command(DRIVER_NUM, 0, 0, 0); + rc = tock_command_return_novalue_to_returncode(ret); + CHECK(rc == RETURNCODE_SUCCESS, "rc=%d", rc); + + // 1: failure(FAIL) + printf("cmd 1: failure(FAIL)\n"); + ret = command(DRIVER_NUM, 1, 0, 0); + rc = tock_command_return_novalue_to_returncode(ret); + CHECK(rc == RETURNCODE_FAIL, "rc=%d", rc); + + // 2: failure_u32(BUSY, 0x10000001) + printf("cmd 2: failure_u32(BUSY, 0x10000001)\n"); + ret = command(DRIVER_NUM, 2, 0, 0); + rc = tock_command_return_failure_u32_to_returncode(ret, &v1); + CHECK(rc == RETURNCODE_EBUSY && v1 == 0x10000001u, + "rc=%d v1=0x%08" PRIx32, rc, v1); + + // 3: failure_u32_u32(NOMEM, 0x20000001, 0x20000002) + printf("cmd 3: failure_u32_u32(NOMEM, 0x20000001, 0x20000002)\n"); + ret = command(DRIVER_NUM, 3, 0, 0); + rc = tock_command_return_failure_u32_u32_to_returncode(ret, &v1, &v2); + CHECK(rc == RETURNCODE_ENOMEM && v1 == 0x20000001u && v2 == 0x20000002u, + "rc=%d v1=0x%08" PRIx32 " v2=0x%08" PRIx32, rc, v1, v2); + + // 4: failure_u64(INVAL, 0x4000000000000001) + printf("cmd 4: failure_u64(INVAL, 0x4000000000000001)\n"); + ret = command(DRIVER_NUM, 4, 0, 0); + rc = tock_command_return_failure_u64_to_returncode(ret, &v64); + CHECK(rc == RETURNCODE_EINVAL && v64 == 0x4000000000000001ull, + "rc=%d val=0x%016" PRIx64, rc, v64); + + // 5: success() + printf("cmd 5: success()\n"); + ret = command(DRIVER_NUM, 5, 0, 0); + rc = tock_command_return_novalue_to_returncode(ret); + CHECK(rc == RETURNCODE_SUCCESS, "rc=%d", rc); + + // 6: success_u32(0x60000001) + printf("cmd 6: success_u32(0x60000001)\n"); + ret = command(DRIVER_NUM, 6, 0, 0); + rc = tock_command_return_u32_to_returncode(ret, &v1); + CHECK(rc == RETURNCODE_SUCCESS && v1 == 0x60000001u, + "rc=%d v1=0x%08" PRIx32, rc, v1); + + // 7: success_u32_u32(0x70000001, 0x70000002) + printf("cmd 7: success_u32_u32(0x70000001, 0x70000002)\n"); + ret = command(DRIVER_NUM, 7, 0, 0); + rc = tock_command_return_u32_u32_to_returncode(ret, &v1, &v2); + CHECK(rc == RETURNCODE_SUCCESS && v1 == 0x70000001u && v2 == 0x70000002u, + "rc=%d v1=0x%08" PRIx32 " v2=0x%08" PRIx32, rc, v1, v2); + + // 8: success_u32_u32_u32(0x80000001, 0x80000002, 0x80000003) + printf("cmd 8: success_u32_u32_u32(0x80000001, 0x80000002, 0x80000003)\n"); + ret = command(DRIVER_NUM, 8, 0, 0); + rc = tock_command_return_u32_u32_u32_to_returncode(ret, &v1, &v2, &v3); + CHECK(rc == RETURNCODE_SUCCESS && v1 == 0x80000001u && v2 == 0x80000002u && v3 == 0x80000003u, + "rc=%d v1=0x%08" PRIx32 " v2=0x%08" PRIx32 " v3=0x%08" PRIx32, + rc, v1, v2, v3); + + // 9: success_u64(0x9000000000000001) + printf("cmd 9: success_u64(0x9000000000000001)\n"); + ret = command(DRIVER_NUM, 9, 0, 0); + rc = tock_command_return_u64_to_returncode(ret, &v64); + CHECK(rc == RETURNCODE_SUCCESS && v64 == 0x9000000000000001ull, + "rc=%d val=0x%016" PRIx64, rc, v64); + + // 10: success_u32_u64(0xA0000001, 0xA000000000000002) + printf("cmd 10: success_u32_u64(0xA0000001, 0xA000000000000002)\n"); + ret = command(DRIVER_NUM, 10, 0, 0); + rc = tock_command_return_u32_u64_to_returncode(ret, &v1, &v64); + CHECK(rc == RETURNCODE_SUCCESS && v1 == 0xA0000001u && v64 == 0xA000000000000002ull, + "rc=%d v1=0x%08" PRIx32 " val=0x%016" PRIx64, + rc, v1, v64); + + // --- Allow read-only --- + static uint8_t ro_buf[16]; + + // allow_readonly success: first call returns previous (ptr=0, len=0) + printf("allow_ro success (first call, expect prev ptr=0 len=0)\n"); + allow_ro_return_t aro = allow_readonly(DRIVER_NUM, 0, ro_buf, sizeof(ro_buf)); + rc = tock_allow_ro_return_to_returncode(aro); + CHECK(rc == RETURNCODE_SUCCESS && aro.ptr == NULL && aro.size == 0, + "rc=%d ptr=%p size=%zu", rc, aro.ptr, aro.size); + + // allow_readonly success: second call echoes back the buffer just registered + printf("allow_ro success (second call, expect prev ptr=ro_buf len=16)\n"); + allow_ro_return_t aro2 = allow_readonly(DRIVER_NUM, 0, ro_buf, sizeof(ro_buf)); + rc = tock_allow_ro_return_to_returncode(aro2); + CHECK(rc == RETURNCODE_SUCCESS && aro2.ptr == ro_buf && aro2.size == sizeof(ro_buf), + "rc=%d ptr=%p size=%zu", rc, aro2.ptr, aro2.size); + + // allow_readonly failure: invalid pointer — kernel rejects and echoes ptr+size back + printf("allow_ro failure (invalid ptr 0x90, expect INVAL ptr=0x90 size=4)\n"); + allow_ro_return_t aro_f = allow_readonly(DRIVER_NUM, 0, (void*)0x90, 4); + rc = tock_allow_ro_return_to_returncode(aro_f); + CHECK(rc == RETURNCODE_EINVAL && aro_f.ptr == (void*)0x90 && aro_f.size == 4, + "rc=%d ptr=%p size=%zu", rc, aro_f.ptr, aro_f.size); + +#if defined(__riscv) && __riscv_xlen == 64 + // allow_readonly failure: invalid pointer — kernel rejects and echoes ptr+size back + printf("allow_ro failure (invalid ptr 0x9000000000000000, expect INVAL ptr=0x9000000000000000 size=4)\n"); + allow_ro_return_t aro_f2 = allow_readonly(DRIVER_NUM, 0, (void*)0x9000000000000000, 4); + rc = tock_allow_ro_return_to_returncode(aro_f2); + CHECK(rc == RETURNCODE_EINVAL && aro_f2.ptr == (void*)0x9000000000000000 && aro_f2.size == 4, + "rc=%d ptr=%p size=%zu", rc, aro_f2.ptr, aro_f2.size); +#endif + + // --- Allow read-write --- + static uint8_t rw_buf[16]; + + // allow_readwrite success: first call returns previous (ptr=0, len=0) + printf("allow_rw success (first call, expect prev ptr=0 len=0)\n"); + allow_rw_return_t arw = allow_readwrite(DRIVER_NUM, 0, rw_buf, sizeof(rw_buf)); + rc = tock_allow_rw_return_to_returncode(arw); + CHECK(rc == RETURNCODE_SUCCESS && arw.ptr == NULL && arw.size == 0, + "rc=%d ptr=%p size=%zu", rc, arw.ptr, arw.size); + + // allow_readwrite success: second call echoes back the buffer just registered + printf("allow_rw success (second call, expect prev ptr=rw_buf len=16)\n"); + allow_rw_return_t arw2 = allow_readwrite(DRIVER_NUM, 0, rw_buf, sizeof(rw_buf)); + rc = tock_allow_rw_return_to_returncode(arw2); + CHECK(rc == RETURNCODE_SUCCESS && arw2.ptr == rw_buf && arw2.size == sizeof(rw_buf), + "rc=%d ptr=%p size=%zu", rc, arw2.ptr, arw2.size); + + // allow_readwrite failure: invalid pointer — kernel rejects and echoes ptr+size back + printf("allow_rw failure (invalid ptr 0x90, expect INVAL ptr=0x90 size=4)\n"); + allow_rw_return_t arw_f = allow_readwrite(DRIVER_NUM, 0, (void*)0x90, 4); + rc = tock_allow_rw_return_to_returncode(arw_f); + CHECK(rc == RETURNCODE_EINVAL && arw_f.ptr == (void*)0x90 && arw_f.size == 4, + "rc=%d ptr=%p size=%zu", rc, arw_f.ptr, arw_f.size); + +#if defined(__riscv) && __riscv_xlen == 64 + // allow_readwrite failure: invalid pointer — kernel rejects and echoes ptr+size back + printf("allow_rw failure (invalid ptr 0xa000000000000000, expect INVAL ptr=0xa000000000000000 size=4)\n"); + allow_rw_return_t arw_f2 = allow_readwrite(DRIVER_NUM, 0, (void*)0xa000000000000000, 4); + rc = tock_allow_rw_return_to_returncode(arw_f2); + CHECK(rc == RETURNCODE_EINVAL && arw_f2.ptr == (void*)0xa000000000000000 && arw_f2.size == 4, + "rc=%d ptr=%p size=%zu", rc, arw_f2.ptr, arw_f2.size); +#endif + + // --- Allow userspace readable --- + static uint8_t ur_buf[16]; + + // allow_userspace_read success: first call returns previous (ptr=0, len=0) + printf("allow_ur success (first call, expect prev ptr=0 len=0)\n"); + allow_userspace_r_return_t aur = allow_userspace_read(DRIVER_NUM, 0, ur_buf, sizeof(ur_buf)); + rc = tock_allow_userspace_r_return_to_returncode(aur); + CHECK(rc == RETURNCODE_SUCCESS && aur.ptr == NULL && aur.size == 0, + "rc=%d ptr=%p size=%zu", rc, aur.ptr, aur.size); + + // allow_userspace_read success: second call echoes back the buffer just registered + printf("allow_ur success (second call, expect prev ptr=ur_buf len=16)\n"); + allow_userspace_r_return_t aur2 = allow_userspace_read(DRIVER_NUM, 0, ur_buf, sizeof(ur_buf)); + rc = tock_allow_userspace_r_return_to_returncode(aur2); + CHECK(rc == RETURNCODE_SUCCESS && aur2.ptr == ur_buf && aur2.size == sizeof(ur_buf), + "rc=%d ptr=%p size=%zu", rc, aur2.ptr, aur2.size); + + // allow_userspace_read failure: invalid pointer — kernel rejects and echoes ptr+size back + printf("allow_ur failure (invalid ptr 0x90, expect INVAL ptr=0x90 size=4)\n"); + allow_userspace_r_return_t aur_f = allow_userspace_read(DRIVER_NUM, 0, (void*)0x90, 4); + rc = tock_allow_userspace_r_return_to_returncode(aur_f); + CHECK(rc == RETURNCODE_EINVAL && aur_f.ptr == (void*)0x90 && aur_f.size == 4, + "rc=%d ptr=%p size=%zu", rc, aur_f.ptr, aur_f.size); + +#if defined(__riscv) && __riscv_xlen == 64 + // allow_userspace_read failure: invalid pointer — kernel rejects and echoes ptr+size back + printf("allow_ur failure (invalid ptr 0xb000000000000000, expect INVAL ptr=0xb000000000000000 size=4)\n"); + allow_userspace_r_return_t aur_f2 = allow_userspace_read(DRIVER_NUM, 0, (void*)0xb000000000000000, 4); + rc = tock_allow_userspace_r_return_to_returncode(aur_f2); + CHECK(rc == RETURNCODE_EINVAL && aur_f2.ptr == (void*)0xb000000000000000 && aur_f2.size == 4, + "rc=%d ptr=%p size=%zu", rc, aur_f2.ptr, aur_f2.size); +#endif + + // --- Subscribe --- + + // subscribe success: first call returns previous callback (NULL, NULL) + printf("subscribe success (first call, expect prev cb=NULL data=NULL)\n"); + subscribe_return_t sub = subscribe(DRIVER_NUM, 0, dummy_upcall, NULL); + rc = tock_subscribe_return_to_returncode(sub); + CHECK(rc == RETURNCODE_SUCCESS && sub.callback == NULL && sub.userdata == NULL, + "rc=%d cb=%p data=%p", rc, (void*)sub.callback, sub.userdata); + + // subscribe success: second call echoes back the previously registered callback + printf("subscribe success (second call, expect prev cb=dummy_upcall data=NULL)\n"); + subscribe_return_t sub2 = subscribe(DRIVER_NUM, 0, dummy_upcall, NULL); + rc = tock_subscribe_return_to_returncode(sub2); + CHECK(rc == RETURNCODE_SUCCESS && sub2.callback == dummy_upcall && sub2.userdata == NULL, + "rc=%d cb=%p data=%p", rc, (void*)sub2.callback, sub2.userdata); + + // subscribe failure: invalid function pointer — kernel rejects and echoes ptr+userdata back + void* max_minus_one = (void*)(UINTPTR_MAX - 1); + printf("subscribe failure (invalid fn ptr 0x90, expect INVAL cb=0x90 data=%p)\n", max_minus_one); + subscribe_return_t sub_f = subscribe(DRIVER_NUM, 0, (subscribe_upcall*)0x90, max_minus_one); + rc = tock_subscribe_return_to_returncode(sub_f); + CHECK(rc == RETURNCODE_EINVAL && + sub_f.callback == (subscribe_upcall*)0x90 && sub_f.userdata == max_minus_one, + "rc=%d cb=%p data=%p", rc, (void*)sub_f.callback, sub_f.userdata); + +#if defined(__riscv) && __riscv_xlen == 64 + // subscribe failure: invalid function pointer — kernel rejects and echoes ptr+userdata back + printf("subscribe failure (invalid fn ptr 0xc000000000000000, expect INVAL cb=0xc000000000000000 data=%p)\n", max_minus_one); + subscribe_return_t sub_f2 = subscribe(DRIVER_NUM, 0, (subscribe_upcall*)0xc000000000000000, max_minus_one); + rc = tock_subscribe_return_to_returncode(sub_f2); + CHECK(rc == RETURNCODE_EINVAL && + sub_f2.callback == (subscribe_upcall*)0xc000000000000000 && sub_f2.userdata == max_minus_one, + "rc=%d cb=%p data=%p", rc, (void*)sub_f2.callback, sub_f2.userdata); +#endif + + printf("done\n"); + return 0; +} From 07ea622c75dc034b1c4415dd0441bb4d2542a85c Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Fri, 14 Aug 2026 14:51:09 -0700 Subject: [PATCH 3/5] libtock: impl syscall ret u64 for rv64 --- libtock/tock.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/libtock/tock.c b/libtock/tock.c index 7292a255e..dca9a70fd 100644 --- a/libtock/tock.c +++ b/libtock/tock.c @@ -92,7 +92,17 @@ returncode_t tock_command_return_u32_u64_to_returncode(syscall_return_t command_ uint32_t* val1, uint64_t* val2) { if (command_return.type == TOCK_SYSCALL_SUCCESS_U32_U64) { *val1 = command_return.data[0]; - *val2 = (uint64_t)command_return.data[1] | ((uint64_t)command_return.data[2] << 32); +#if defined(__riscv) && __riscv_xlen == 64 + // TRD-RISCV64BIT + *val2 = command_return.data[1]; +#else + // TRD104 + uint32_t lsb; + uint32_t msb; + lsb = command_return.data[1]; + msb = command_return.data[2]; + *val2 = ((uint64_t)msb << 32) | lsb; +#endif return RETURNCODE_SUCCESS; } else if (command_return.type == TOCK_SYSCALL_FAILURE) { return tock_status_to_returncode(command_return.data[0]); @@ -123,7 +133,17 @@ returncode_t tock_command_return_failure_u32_u32_to_returncode(syscall_return_t returncode_t tock_command_return_failure_u64_to_returncode(syscall_return_t command_return, uint64_t* val) { if (command_return.type == TOCK_SYSCALL_FAILURE_U64) { - *val = (uint64_t)command_return.data[1] | ((uint64_t)command_return.data[2] << 32); +#if defined(__riscv) && __riscv_xlen == 64 + // TRD-RISCV64BIT + *val = command_return.data[1]; +#else + // TRD104 + uint32_t lsb; + uint32_t msb; + lsb = command_return.data[1]; + msb = command_return.data[2]; + *val = ((uint64_t)msb << 32) | lsb; +#endif return tock_status_to_returncode(command_return.data[0]); } else { return RETURNCODE_EBADRVAL; From 86bcd6f6daa83052d8e5ca233adcfd362384d41e Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Fri, 14 Aug 2026 14:51:27 -0700 Subject: [PATCH 4/5] libtock: fix command return args --- libtock/tock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libtock/tock.c b/libtock/tock.c index dca9a70fd..acce27d3c 100644 --- a/libtock/tock.c +++ b/libtock/tock.c @@ -591,9 +591,9 @@ syscall_return_t command(uint32_t driver, uint32_t command, register uint32_t a3 __asm__ ("a3") = arg2; register uint32_t a4 __asm__ ("a4") = 2; register int rtype __asm__ ("a0"); - register int rv1 __asm__ ("a1"); - register int rv2 __asm__ ("a2"); - register int rv3 __asm__ ("a3"); + register uintptr_t rv1 __asm__ ("a1"); + register uintptr_t rv2 __asm__ ("a2"); + register uintptr_t rv3 __asm__ ("a3"); __asm__ volatile ( "ecall\n" : "=r" (rtype), "=r" (rv1), "=r" (rv2), "=r" (rv3) From 63917aa6f22bff30e4dd5a0ac743dc8eaacd663e Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Tue, 25 Aug 2026 11:26:29 -0400 Subject: [PATCH 5/5] syscall return test: fix formatting --- examples/tests/syscall-return/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/tests/syscall-return/main.c b/examples/tests/syscall-return/main.c index e543835f5..c3e7603b4 100644 --- a/examples/tests/syscall-return/main.c +++ b/examples/tests/syscall-return/main.c @@ -231,7 +231,8 @@ int main(void) { #if defined(__riscv) && __riscv_xlen == 64 // subscribe failure: invalid function pointer — kernel rejects and echoes ptr+userdata back - printf("subscribe failure (invalid fn ptr 0xc000000000000000, expect INVAL cb=0xc000000000000000 data=%p)\n", max_minus_one); + printf("subscribe failure (invalid fn ptr 0xc000000000000000, expect INVAL cb=0xc000000000000000 data=%p)\n", + max_minus_one); subscribe_return_t sub_f2 = subscribe(DRIVER_NUM, 0, (subscribe_upcall*)0xc000000000000000, max_minus_one); rc = tock_subscribe_return_to_returncode(sub_f2); CHECK(rc == RETURNCODE_EINVAL &&