Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,20 @@ jobs:
continue-on-error: true
run: bash scripts/check-s1-lwip.sh

# Runtime gate: cross-compile libsrt for bare-metal arm-none-eabi
# (CMAKE_SYSTEM_NAME=Generic -> ::select path, pthread backend, logging +
# encryption OFF), link it with the FreeRTOS+lwIP substrate, and run a
# srt_startup+create_socket+close+cleanup boot smoke under QEMU mps2-an386.
# Same phase-in (continue-on-error) rationale as S0/S1. No new apt packages
# -- cmake is already on the runner (the host libsrt builds use it); reuses
# gcc-arm-none-eabi + libstdc++-arm-none-eabi-newlib + qemu-system-arm.
# build.sh applies our bare-metal portability patches to the pinned
# vendor/srt submodule at build time (pointer stays pristine). Promote to
# hard-gate after a few green apt runs.
- name: QEMU runtime gate (S2 libsrt boot smoke)
continue-on-error: true
run: bash scripts/check-s2-libsrt.sh

fuzz-smoke:
name: fuzz smoke (nightly, compile only)
runs-on: ubuntu-latest
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ embedded/s1-lwip/firmware.elf
embedded/s1-lwip/*.elf
embedded/s1-lwip/*.o

# Embedded S2 libsrt cross-compile + boot smoke build artifacts
embedded/s2-libsrt/firmware.elf
embedded/s2-libsrt/*.elf
embedded/s2-libsrt/*.o
embedded/s2-libsrt/srt-build/
embedded/s2-libsrt/srt-install/

# Rust analyzer
.rust-analyzer/

Expand Down
89 changes: 89 additions & 0 deletions embedded/s2-libsrt/FreeRTOSConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* Minimal FreeRTOS V11.1.0 config for the Cortex-M4F (ARM_CM4F) port on
QEMU mps2-an386. C++ exceptions want generous task stacks, so stacks are
sized at task-create time; the heap is large to leave room for libstdc++. */

#define configUSE_PREEMPTION 1
#define configUSE_TIME_SLICING 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configCPU_CLOCK_HZ (25000000) /* QEMU mps2 ~25 MHz */
#define configTICK_RATE_HZ (1000)
#define configMAX_PRIORITIES (5)
#define configMINIMAL_STACK_SIZE (256) /* words */
#define configSTACK_DEPTH_TYPE uint32_t /* lwIP sys_thread_new byte→word conversion */
#define configTOTAL_HEAP_SIZE (192 * 1024)
#define configMAX_TASK_NAME_LEN (16)
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 0
#define configUSE_TASK_NOTIFICATIONS 1
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1

/* Hooks */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configUSE_MALLOC_FAILED_HOOK 1
#define configCHECK_FOR_STACK_OVERFLOW 2
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0

/* Software timers */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY (3)
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH (256)

/* Thread-local storage — S0-critical: Task 4 stores per-task __cxa_eh_globals
in a TLS pointer slot so concurrent exceptions don't clobber each other. */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1

/* Allocation. FreeRTOS-Plus-POSIX creates pthread join mutex/barrier via the
xSemaphoreCreate*Static APIs, so static allocation must be on. Let the kernel
supply the idle/timer task static memory (configKERNEL_PROVIDED_STATIC_MEMORY)
so we don't hand-roll vApplicationGet{Idle,Timer}TaskMemory. */
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configKERNEL_PROVIDED_STATIC_MEMORY 1

/* FreeRTOS-Plus-POSIX needs these: it stashes the pthread object in the task's
application tag (vTaskSetApplicationTaskTag), and uses the POSIX errno field
in the TCB for its return-error reporting. */
#define configUSE_APPLICATION_TASK_TAG 1
#define configUSE_POSIX_ERRNO 1
#define configUSE_COUNTING_SEMAPHORES 1

/* Cortex-M interrupt priority configuration */
#define configPRIO_BITS 3
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY (7)
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (5)
#define configKERNEL_INTERRUPT_PRIORITY \
(configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
#define configMAX_SYSCALL_INTERRUPT_PRIORITY \
(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))

/* Optional API */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
/* FreeRTOS-Plus-POSIX uses these in pthread_detach/join/cond/cancel paths. */
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTaskAbortDelay 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_xSemaphoreGetMutexHolder 1

#define configASSERT(x) if ((x) == 0) { taskDISABLE_INTERRUPTS(); for(;;); }

/* Map the FreeRTOS port handlers onto the names our vector table uses. The
ARM_CM4F port already exports vPortSVCHandler/xPortPendSVHandler/
xPortSysTickHandler, and startup.c references those directly, so no remap
is needed — but if a CMSIS-style build expected SVC_Handler etc., this is
where the #define would go. */

#endif /* FREERTOS_CONFIG_H */
14 changes: 14 additions & 0 deletions embedded/s2-libsrt/FreeRTOS_POSIX_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* FreeRTOS+POSIX knob overrides for the S0 C++ exceptions gate.
*
* The wrapper pulls all posixconfig* defaults from
* FreeRTOS_POSIX_portable_default.h; the only port-specific deviations we need
* (the newlib typedef-collision suppressions) live in FreeRTOS_POSIX_portable.h.
* This file exists as the documented override point — currently the defaults
* suffice, so it is intentionally empty of overrides. */

#ifndef _FREERTOS_POSIX_CONFIG_H_
#define _FREERTOS_POSIX_CONFIG_H_

/* (no overrides — defaults from FreeRTOS_POSIX_portable_default.h are fine) */

#endif /* _FREERTOS_POSIX_CONFIG_H_ */
36 changes: 36 additions & 0 deletions embedded/s2-libsrt/FreeRTOS_POSIX_portable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* Port-specific FreeRTOS+POSIX configuration for the S0 C++ exceptions gate
* (arm-none-eabi-gcc / newlib, Cortex-M4F, QEMU mps2-an386).
*
* FreeRTOS_POSIX.h includes this header first. Its job is to suppress the
* FreeRTOS+POSIX typedefs that the arm-none-eabi newlib headers ALSO define,
* so the two don't collide. Modeled on the vendored STM32+GNU port header
* (portable/st/stm32h745zi_nucleo/FreeRTOS_POSIX_portable.h) — same newlib
* lineage, same collisions. */

#ifndef _FREERTOS_POSIX_PORTABLE_H_
#define _FREERTOS_POSIX_PORTABLE_H_

/* newlib already defines these via <sys/_timespec.h> / <sys/timespec.h>. */
#define posixconfigENABLE_TIMESPEC 0
#include <sys/_timespec.h>

#define posixconfigENABLE_ITIMERSPEC 0
#include <sys/timespec.h>

/* newlib already provides mode_t and clockid_t. */
#define posixconfigENABLE_MODE_T 0
#define posixconfigENABLE_CLOCKID_T 0

/* Suppress newlib timer_t so it doesn't fight the FreeRTOS+POSIX one. */
#define __timer_t_defined 1

/* Block newlib's <sys/_pthreadtypes.h> definitions (which differ from the
* FreeRTOS+POSIX pthread_* typedefs) before pulling <sys/types.h>. */
#define _SYS__PTHREADTYPES_H_
#include <sys/types.h>

/* Use newlib's sched_param/sched_yield/etc. rather than the POSIX-layer copy. */
#define posixconfigENABLE_SCHED_PARAM 0
#include <sys/sched.h>

#endif /* _FREERTOS_POSIX_PORTABLE_H_ */
17 changes: 17 additions & 0 deletions embedded/s2-libsrt/arch/cc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* lwIP arch shim for arm-none-eabi + newlib. lwIP pulls fixed-width types and
* printf formatters from the C library (LWIP_NO_STDINT_H / LWIP_NO_INTTYPES_H
* default 0 → <stdint.h>/<inttypes.h>); we only need the diagnostic/abort
* hooks and a randomness source. */
#ifndef LWIP_ARCH_CC_H
#define LWIP_ARCH_CC_H

#include <stdio.h>
#include <stdlib.h>
/* LWIP_TIMEVAL_PRIVATE=0 (lwipopts.h) → every lwIP TU must see the system
* struct timeval; lwIP's docs require including <sys/time.h> from cc.h. */
#include <sys/time.h>

#define LWIP_PLATFORM_DIAG(x) do { printf x; } while (0)
#define LWIP_PLATFORM_ASSERT(x) do { printf("LWIP ASSERT: %s\n", x); abort(); } while (0)

#endif /* LWIP_ARCH_CC_H */
20 changes: 20 additions & 0 deletions embedded/s2-libsrt/arch/sys_arch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef LWIP_ARCH_SYS_ARCH_H
#define LWIP_ARCH_SYS_ARCH_H

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

#define SYS_MBOX_NULL NULL
#define SYS_SEM_NULL NULL
#define SYS_ARCH_TIMEOUT 0xffffffffUL
#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT

typedef UBaseType_t sys_prot_t;
typedef struct { SemaphoreHandle_t sem; } sys_sem_t;
typedef struct { SemaphoreHandle_t mtx; } sys_mutex_t;
typedef struct { QueueHandle_t mbox; } sys_mbox_t;
typedef TaskHandle_t sys_thread_t;

#endif /* LWIP_ARCH_SYS_ARCH_H */
42 changes: 42 additions & 0 deletions embedded/s2-libsrt/arm-none-eabi.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Bare-metal arm-none-eabi toolchain for cross-building libsrt against the S2
# FreeRTOS+lwIP substrate. CMAKE_SYSTEM_NAME=Generic (NOT Linux) so libsrt's
# POSIX var is false and it compiles the portable ::select() path, not epoll.
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)

# CMake's compiler sanity probe must build a static lib, not a semihosted exe
# (no _exit/OS at configure time).
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

get_filename_component(_S2 "${CMAKE_CURRENT_LIST_DIR}" ABSOLUTE)
set(_ROOT "${_S2}/../..")
set(_K "${_ROOT}/vendor/freertos-kernel")
set(_P "${_ROOT}/vendor/freertos-plus-posix")
set(_L "${_ROOT}/vendor/lwip")

set(_ARCH "-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16")
# -D__GNU__: selects libsrt utilities.h's endian branch
# (__linux__||__CYGWIN__||__GNU__||__GLIBC__) — the only non-Linux option that
# doesn't also pull epoll — so it #includes our posix-shims/endian.h instead of
# hitting its unconditional `#error Endian: platform not supported`.
# -DSRT_NO_PTHREAD_CANCEL: FreeRTOS-Plus-POSIX has no pthread_cancel; a one-line
# vendor/srt patch guards its single use (an IPE error path) on this define.
set(_DEFS "-D__GNU__=1 -DSRT_NO_PTHREAD_CANCEL -include ${CMAKE_CURRENT_LIST_DIR}/posix-shims/s2_prefix.h")
# Include order matters: posix-shims FIRST (so <pthread.h>/<netinet/in.h> route
# to FreeRTOS-Plus-POSIX/lwIP), then the substrate dir (lwipopts.h, arch/cc.h),
# then FreeRTOS + lwIP. lwIP's compat/posix supplies <sys/socket.h>/<arpa/inet.h>.
set(_INC "-I${_S2}/posix-shims -I${_S2} \
-I${_K}/include -I${_K}/portable/GCC/ARM_CM4F \
-I${_P}/include -I${_P}/include/private \
-I${_P}/FreeRTOS-Plus-POSIX/include -I${_P}/FreeRTOS-Plus-POSIX/include/portable \
-I${_L}/src/include -I${_L}/src/include/compat/posix")

set(CMAKE_C_FLAGS "${_ARCH} ${_DEFS} ${_INC}" CACHE STRING "")
set(CMAKE_CXX_FLAGS "${_ARCH} ${_DEFS} ${_INC}" CACHE STRING "")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
84 changes: 84 additions & 0 deletions embedded/s2-libsrt/atomic64_stub.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* 64-bit atomic libcalls for single-core Cortex-M4.
*
* libsrt uses std::atomic<int64_t>/<uint64_t> (bandwidth/seqno counters). The
* Cortex-M4 has no native 64-bit atomic instructions, so GCC lowers these to
* __atomic_*_8 libcalls — normally satisfied by libatomic, which the bare-metal
* arm-none-eabi multilib for cortex-m4f does NOT ship. On a single core, a
* 64-bit atomic is just a critical section: mask interrupts (PRIMASK), do the
* op, restore. No SMP, so this is correct here. */
#include <stdint.h>
#include <stddef.h>

static inline uint32_t irq_save(void)
{
uint32_t primask;
__asm volatile("mrs %0, primask\n\tcpsid i" : "=r"(primask) :: "memory");
return primask;
}
static inline void irq_restore(uint32_t primask)
{
__asm volatile("msr primask, %0" :: "r"(primask) : "memory");
}

uint64_t __atomic_load_8(const volatile void* ptr, int memorder)
{
(void)memorder;
uint32_t s = irq_save();
uint64_t v = *(const volatile uint64_t*)ptr;
irq_restore(s);
return v;
}

void __atomic_store_8(volatile void* ptr, uint64_t val, int memorder)
{
(void)memorder;
uint32_t s = irq_save();
*(volatile uint64_t*)ptr = val;
irq_restore(s);
}

uint64_t __atomic_exchange_8(volatile void* ptr, uint64_t val, int memorder)
{
(void)memorder;
uint32_t s = irq_save();
uint64_t old = *(volatile uint64_t*)ptr;
*(volatile uint64_t*)ptr = val;
irq_restore(s);
return old;
}

uint64_t __atomic_fetch_add_8(volatile void* ptr, uint64_t val, int memorder)
{
(void)memorder;
uint32_t s = irq_save();
uint64_t old = *(volatile uint64_t*)ptr;
*(volatile uint64_t*)ptr = old + val;
irq_restore(s);
return old;
}

uint64_t __atomic_fetch_sub_8(volatile void* ptr, uint64_t val, int memorder)
{
(void)memorder;
uint32_t s = irq_save();
uint64_t old = *(volatile uint64_t*)ptr;
*(volatile uint64_t*)ptr = old - val;
irq_restore(s);
return old;
}

int __atomic_compare_exchange_8(volatile void* ptr, void* expected,
uint64_t desired, int weak,
int success, int failure)
{
(void)weak; (void)success; (void)failure;
uint32_t s = irq_save();
uint64_t cur = *(volatile uint64_t*)ptr;
int ok = (cur == *(uint64_t*)expected);
if (ok)
*(volatile uint64_t*)ptr = desired;
else
*(uint64_t*)expected = cur;
irq_restore(s);
return ok;
}
Loading
Loading