OSAL-RS
Operating System Abstraction Layer for Rust - A cross-platform compatibility layer for embedded and real-time systems development.
Overview
OSAL-RS provides a unified API for developing multi-platform embedded applications in Rust. It abstracts operating system-specific functionality, allowing you to write portable code that can run on different platforms with minimal changes.
Workspace Components
- osal-rs: Main Operating System Abstraction Layer, with FreeRTOS and POSIX backends
- osal-rs-build: Build configuration tools and helpers (FreeRTOS type generation, POSIX porting shim compilation)
- osal-rs-porting: C FFI bridge layer for the FreeRTOS and POSIX backends
- osal-rs-tests: Comprehensive test suite for all components
- osal-rs-serde: ✨ Extensible serialization/deserialization framework with derive macros
Current Implementation Status
- ✅ FreeRTOS: Fully implemented and tested
- ✅ POSIX: Fully implemented and tested (glibc/Linux) - native host backend for running, testing and simulating OSAL-RS applications without embedded hardware
- ✅ Serialization: Complete osal-rs-serde implementation with derive macros
- 🧪 Async/Await: Experimental, backend-agnostic, works on both FreeRTOS and POSIX
- 🚧 Other RTOSes: Under consideration
Breaking Changes
EventGroupFn::wait gained a wait_for_all_bits parameter (from version 1.1.0)
EventGroup::wait and EventGroup::wait_with_to_tick now take an explicit wait_for_all_bits: bool, matching FreeRTOS's xEventGroupWaitBits:
// Before
let bits = event_group.wait;
// After
let bits = event_group.wait; // AND: block until every bit in `mask` is set
let bits = event_group.wait; // OR: block until any bit in `mask` is set
let bits = event_group.wait_with_to_tick;
Previously the two backends silently disagreed: POSIX only ever implemented AND-wait, while FreeRTOS hardcoded OR-wait (xWaitForAllBits = pdFALSE) regardless of what the trait docs said. Code that waited on multiple independent bits (e.g. "unblock when either of these two mutually exclusive events fires") could hang forever on POSIX while working by accident on FreeRTOS. Both backends now implement the same, explicit semantics.
Supported Backends
OSAL-RS selects its implementation at compile time via Cargo features. There is no default backend - exactly one of freertos / posix must be enabled explicitly, or the crate fails to build. Enabling neither trips a compile_error!; enabling both is equally unsupported, since the two backends are mutually exclusive by design.
FreeRTOS Backend (freertos)
For bare-metal embedded targets (no_std). FreeRTOS provides preemptive multitasking with priority-based scheduling, mutexes with priority inheritance, semaphores, queues and software timers.
Requirements:
- FreeRTOS kernel properly configured and linked into your project
- C porting layer from
osal-rs-porting/freeretos/compiled and linked (an FFI bridge between Rust and FreeRTOS) - CMake build system set up for your embedded project (see CMake Integration)
- Rust toolchain with the appropriate embedded target installed
Configuration - ensure your FreeRTOSConfig.h includes:
Build:
# Install Rust target (example for ARM Cortex-M33)
# Build with FreeRTOS support
POSIX Backend (posix)
Runs on any POSIX/pthreads host so OSAL-RS applications - and their tests and doc examples - can execute for real on Linux/macOS without embedded hardware or a cross toolchain. Enabling posix disables no_std and builds the crate against std.
Requirements:
- A glibc/Linux host (see note below)
- No special build steps: unlike
freertos, the small POSIX porting shim inosal-rs-porting/posix/is compiled and linked automatically byosal-rs-build- no CMake, cross toolchain or RTOS kernel sources required
Build:
# Native development / host testing
POSIX Backend: glibc Requirement
The posix backend links directly against glibc (the GNU C Library), not just any C compiler. It relies on glibc-specific internals - struct layouts (pthread_attr_t, pthread_mutex_t, pthread_cond_t, sigset_t, etc.) and the __libc_current_sigrtmin() extension used to implement thread suspend/resume via real-time signals.
This means:
- The compiler doesn't matter - gcc or clang both work fine.
- The C library does matter - targets linking against musl (e.g.
x86_64-unknown-linux-musl) or non-glibc platforms (e.g. macOS/BSD libc) are not supported by theposixbackend.
Real-Time Scheduling (real_time)
Threads spawned by the posix backend normally inherit the creating thread's scheduling policy/priority. The real_time feature switches them to the real-time SCHED_FIFO policy instead.
You don't need to request this feature yourself: osal-rs-build's build script probes the host at compile time and automatically turns real_time on whenever the OS/kernel supports SCHED_FIFO. It's a plain Cargo feature only so it can be inspected via cfg(feature = "real_time"); a plain cargo build --features posix is enough to get it on a capable host.
Caveats of the POSIX Backend
System::start()simply spins until [System::stop()] is called from another thread - there is no scheduler to hand control to, unlike FreeRTOS where it never returns.- Timers each spawn their own background thread and permanently block
SIGALRMon the thread that creates them; create a newTimerrather than reusing one that already fired as a one-shot.
Quick Start
use *;
use *;
use Arc;
let counter = new;
let counter_clone = counter.clone;
let mut thread = new;
thread.spawn_simple.unwrap;
use *;
let queue = new.unwrap;
// Send data
let data = ;
queue.post.unwrap;
// Receive data
let mut buffer = ;
queue.fetch.unwrap;
use *;
use ;
use Arc;
const READY: EventBits = 1 << 0;
const ERROR: EventBits = 1 << 1;
let events = new;
let events_clone = events.clone;
let mut thread = new;
thread.spawn_simple.unwrap;
events.set; // wakes the waiting thread
The same code compiles and runs unchanged against either backend - just switch the freertos/posix feature flags.
Core OSAL Features
- Thread Management: Create, manage, and synchronize threads with priorities
- Synchronization Primitives: Mutexes (recursive & non-recursive), binary & counting semaphores, event groups
- Message Queues: Type-safe inter-thread communication with blocking/non-blocking operations
- Software Timers: Periodic and one-shot timers with callbacks
- Memory Allocation: Custom allocator integration for heap management (
freertos) or the system allocator (posix) - Time Management: Duration handling and tick-based timing
- System Control: Scheduler control, task notifications, and system information
- No-std Support: Fully compatible with bare-metal embedded systems (
freertosbackend) - Host Testing: Native
stdexecution for tests, examples and simulation (posixbackend) - 🧪 EXPERIMENTAL Async/Await: Backend-agnostic
async/awaitsupport without Tokio (see below)
Cargo Features
OSAL-RS provides several Cargo features to customize the build configuration for different platforms and use cases:
Available Features
| Feature | Default | Description |
|---|---|---|
freertos |
❌ | Enable the FreeRTOS backend implementation for embedded RTOS development. Mutually exclusive with posix - exactly one of the two is required. |
posix |
❌ | Enable the POSIX/native backend implementation for host environments. Requires glibc (see note above). Mutually exclusive with freertos - exactly one of the two is required. |
real_time |
❌ | POSIX only: schedules spawned threads with the real-time SCHED_FIFO policy instead of inheriting the creating thread's policy/priority. Not meant to be requested by hand - osal-rs-build's build script enables it automatically when the host OS/kernel supports SCHED_FIFO. |
async |
❌ | Enable backend-agnostic async/await support (block_on, AsyncQueue, AsyncSemaphore, AsyncMutex). Works with both freertos and posix. No Tokio required. |
serde |
❌ | Enable serialization/deserialization support via osal-rs-serde. Includes derive macros for automatic implementation. |
There is no default feature set: you must explicitly pick freertos or posix or the build fails.
Feature Combinations
# FreeRTOS embedded development
# FreeRTOS with async support
# FreeRTOS with serialization support
# Native development (POSIX) - real_time is auto-detected, no need to request it
# Native development with async support
# Native development with serialization
Using Features in Cargo.toml
To use OSAL-RS in your project with specific features (exactly one of freertos/posix is required):
[]
= { = "1.0", = ["freertos"] }
# Or for host development/testing
= { = "1.0", = ["posix"] }
# Or with serialization support
= { = "1.0", = ["freertos", "serde"] }
🧪 EXPERIMENTAL Async/Await Support (feature async)
OSAL-RS includes a backend-agnostic async runtime that works on both FreeRTOS and POSIX without Tokio or any external async runtime.
Design
| Component | Description |
|---|---|
block_on(future) |
Drives a Future to completion on the calling RTOS task |
AsyncQueue |
Queue with fetch_async / post_async methods |
AsyncSemaphore |
Semaphore with wait_async |
AsyncMutex<T> |
Mutex whose lock() returns a Future |
- No Tokio, no
std: built oncore::future::Future+ OSAL semaphores as the blocking primitive. - Per-task executor:
block_onruns on the calling RTOS task; no thread pool is needed. - Lock-free waker storage:
WakerSlotusesAtomicPtr<Waker>- no RTOS overhead for waker updates. - Race-condition safe: the classic store-waker-then-retry double-check pattern is used in every
pollimplementation.
Quick Example
use ;
// Run async code inside any RTOS task — no runtime setup required
block_on;
Enable the feature
# Cargo.toml
[]
= { = "1.0", = ["freertos", "async"] }
# or for host development
= { = "1.0", = ["posix", "async"] }
# FreeRTOS embedded target
# POSIX host (for tests / simulation)
osal-rs-serde Features
A complete serialization framework designed specifically for embedded systems:
- No-std Compatible: Works in bare-metal environments without standard library
- Zero-Copy: Direct buffer operations with no intermediate allocations
- Derive Macros: Automatic
#[derive(Serialize, Deserialize)]implementation - Rich Type Support: Primitives, arrays, tuples, Option, Vec, nested structs
- Extensible Architecture: Create custom serializers for any format (JSON, MessagePack, CBOR, etc.)
- Memory Efficient: Little-endian binary format with predictable sizes
- Compile-Time Guarantees: Type-safe serialization with static checks
- Standalone: Can be used independently in any Rust project
osal-rs-serde Quick Example
use ;
let data = SensorData ;
// Serialize to stack buffer
let mut buffer = ;
let len = to_bytes.unwrap;
// Deserialize from buffer
let restored: SensorData = from_bytes.unwrap;
assert_eq!;
Integration with OSAL Queues
Perfect for inter-task communication:
use ;
use ;
For comprehensive documentation, examples, and advanced features, see:
- osal-rs-serde README - Complete feature documentation
- osal-rs-serde/derive README - Derive macro guide
osal-rs-serde/examples/- Working code examples
CMake Integration
CMake integration is only needed for the FreeRTOS backend, since it must link against your project's FreeRTOS kernel and C porting layer. The POSIX backend needs no CMake step - cargo build --features posix is enough (see Supported Backends).
Important: Always ensure that the C porting layer files from osal-rs-porting/freeretos/ are compiled and linked to your project, as they provide the necessary FFI bridge between Rust and FreeRTOS.
Basic CMake Integration
Add OSAL-RS to your existing CMake project:
cmake_minimum_required(VERSION 3.20)
project(my_embedded_project C CXX)
# Configure FreeRTOS (assuming it's already in your project)
add_subdirectory(freertos)
# Add OSAL-RS porting layer
add_library(osal_rs_porting STATIC
osal-rs-porting/freeretos/src/osal_rs.c
)
target_include_directories(osal_rs_porting PUBLIC
osal-rs-porting/freeretos/inc
${FREERTOS_INCLUDE_DIRS}
)
target_link_libraries(osal_rs_porting PUBLIC
freertos
)
# Configure Rust library
set(RUST_TARGET "thumbv8m.main-none-eabi") # Adjust for your target
set(OSAL_RS_LIB "${CMAKE_CURRENT_SOURCE_DIR}/osal-rs/target/${RUST_TARGET}/release/libosal_rs.a")
# Custom command to build Rust library
add_custom_command(
OUTPUT ${OSAL_RS_LIB}
COMMAND cargo build --release --target ${RUST_TARGET} --features freertos
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/osal-rs
COMMENT "Building OSAL-RS library"
)
add_custom_target(osal_rs_build DEPENDS ${OSAL_RS_LIB})
# Create imported library for OSAL-RS
add_library(osal_rs STATIC IMPORTED GLOBAL)
set_target_properties(osal_rs PROPERTIES
IMPORTED_LOCATION ${OSAL_RS_LIB}
)
add_dependencies(osal_rs osal_rs_build)
# Your main application
add_executable(my_app
src/main.c
)
target_link_libraries(my_app PRIVATE
osal_rs
osal_rs_porting
freertos
)
Advanced CMake Integration with Multiple Configurations
# Function to build OSAL-RS for different configurations
function(add_osal_rs_library TARGET_NAME RUST_TARGET CARGO_PROFILE)
set(PROFILE_DIR ${CARGO_PROFILE})
if(CARGO_PROFILE STREQUAL "release")
set(CARGO_FLAGS "--release")
else()
set(CARGO_FLAGS "")
endif()
set(LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/osal-rs/target/${RUST_TARGET}/${PROFILE_DIR}/libosal_rs.a")
add_custom_command(
OUTPUT ${LIB_PATH}
COMMAND cargo build ${CARGO_FLAGS} --target ${RUST_TARGET} --features freertos
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/osal-rs
COMMENT "Building OSAL-RS (${CARGO_PROFILE}) for ${RUST_TARGET}"
)
add_custom_target(${TARGET_NAME}_build DEPENDS ${LIB_PATH})
add_library(${TARGET_NAME} STATIC IMPORTED GLOBAL)
set_target_properties(${TARGET_NAME} PROPERTIES
IMPORTED_LOCATION ${LIB_PATH}
)
add_dependencies(${TARGET_NAME} ${TARGET_NAME}_build)
endfunction()
# Use it in your project
add_osal_rs_library(osal_rs "thumbv8m.main-none-eabi" "release")
Cross-Compilation Setup
Example CMake toolchain file for ARM Cortex-M:
# toolchain-arm-none-eabi.cmake
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++)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Rust target
set(RUST_TARGET "thumbv8m.main-none-eabi")
Use it with:
Custom FreeRTOS Configuration Path
By default, OSAL-RS looks for FreeRTOSConfig.h at <workspace_root>/inc/FreeRTOSConfig.h. You can override this path using the FREERTOS_CONFIG_PATH environment variable.
Setting via CMake
# Set custom path to FreeRTOSConfig.h
set(FREERTOS_CONFIG_PATH "${CMAKE_SOURCE_DIR}/inc/hhg-config/pico/FreeRTOSConfig.h")
# Pass to Cargo build via environment variable
add_custom_command(
OUTPUT ${OSAL_RS_LIB}
COMMAND ${CMAKE_COMMAND} -E env FREERTOS_CONFIG_PATH=${FREERTOS_CONFIG_PATH}
cargo build --release --target ${RUST_TARGET} --features freertos
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/osal-rs
COMMENT "Building OSAL-RS library"
)
Setting via Environment Variable
# Set environment variable before building
Note: The build system will automatically regenerate Rust type bindings from the specified FreeRTOSConfig.h during the build process.
Project Structure
osal-rs/
├── osal-rs/ # Main library crate (freertos + posix backends)
├── osal-rs-build/ # Build utilities
├── osal-rs-tests/ # Test suite
├── osal-rs-serde/ # Serialization framework
└── osal-rs-porting/ # Platform-specific C/C++ code
├── freeretos/ # FreeRTOS porting layer
│ ├── inc/ # Header files
│ └── src/ # Implementation
└── posix/ # POSIX porting layer (glibc shim, built automatically)
├── inc/ # Header files
└── src/ # Implementation
License
This project is licensed under the LGPL-2.1-or-later License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
Author
Antonio Salsi - passy.linux@zresa.it