Memory Safety and Concurrency
Relevant source files
This page covers the memory safety model and concurrency characteristics of the ARM PL031 RTC driver. It explains how the driver maintains safety while performing low-level hardware operations, the boundaries between safe and unsafe code, and the thread safety guarantees provided by the implementation.
For information about the core driver architecture, see Driver Architecture and Design. For details about hardware register operations, see Register Operations.
Safety Model and Unsafe Boundaries
The arm_pl031 driver implements a layered safety model that encapsulates all unsafe hardware operations within well-defined boundaries while providing safe APIs to consumers.
Unsafe Code Isolation
The driver concentrates all unsafe operations within the core Rtc
implementation, creating a clear safety boundary:
flowchart TD subgraph subGraph2["Hardware Layer"] REGISTERS["PL031 Registers*mut Registers"] DEVICE_MEMORY["Device MemoryMMIO Region"] end subgraph subGraph1["Unsafe Boundary"] CONSTRUCTOR["unsafe fn new()Raw pointer validation"] MMIO_OPS["MMIO Operationsread_volatile()write_volatile()"] end subgraph subGraph0["Safe API Layer"] PUBLIC_API["Public Methodsget_unix_timestamp()set_unix_timestamp()enable_interrupt()"] end CONSTRUCTOR --> REGISTERS MMIO_OPS --> REGISTERS PUBLIC_API --> CONSTRUCTOR PUBLIC_API --> MMIO_OPS REGISTERS --> DEVICE_MEMORY
Sources: src/lib.rs(L46 - L60) src/lib.rs(L63 - L120)
Constructor Safety Requirements
The unsafe fn new()
constructor establishes the fundamental safety contract for the entire driver. The caller must guarantee several critical invariants:
Safety Requirement | Description | Validation |
---|---|---|
Valid MMIO Mapping | Base address points to mapped PL031 registers | Caller responsibility |
Exclusive Access | No other aliases to the memory region | Caller responsibility |
Device Memory Type | Memory mapped as device memory, not normal memory | Caller responsibility |
Alignment | Address aligned to 4-byte boundary | Enforced by#[repr(C, align(4))] |
Sources: src/lib.rs(L51 - L60)
Hardware Register Access Safety
The driver uses specific patterns to ensure safe access to hardware registers while maintaining proper volatile semantics.
Volatile Access Pattern
All register operations use volatile access through addr_of!
and addr_of_mut!
macros to prevent undefined behavior:
flowchart TD subgraph Hardware["Hardware"] DR_REGISTER["DR RegisterData Register"] end subgraph subGraph1["Unsafe Block"] ADDR_OF["addr_of!((*self.registers).dr)"] READ_VOLATILE["read_volatile()"] end subgraph subGraph0["Safe Method Call"] METHOD["get_unix_timestamp()"] end ADDR_OF --> READ_VOLATILE METHOD --> ADDR_OF READ_VOLATILE --> DR_REGISTER
Sources: src/lib.rs(L63 - L67) src/lib.rs(L70 - L74)
Register Layout Safety
The Registers
struct uses precise memory layout control to match hardware expectations:
flowchart TD subgraph subGraph1["Layout Attributes"] REPR_C["#[repr(C)]C-compatible layout"] ALIGN_4["#[repr(align(4))]4-byte alignment"] end subgraph subGraph0["Memory Layout"] DR["dr: u32Offset 0x00"] MR["mr: u32Offset 0x04"] LR["lr: u32Offset 0x08"] CR["cr: u8 + paddingOffset 0x0C"] IMSC["imsc: u8 + paddingOffset 0x10"] RIS["ris: u8 + paddingOffset 0x14"] MIS["mis: u8 + paddingOffset 0x18"] ICR["icr: u8 + paddingOffset 0x1C"] end CR --> IMSC DR --> MR IMSC --> RIS LR --> CR MIS --> ICR MR --> LR REPR_C --> DR RIS --> MIS
Sources: src/lib.rs(L15 - L39)
Thread Safety and Concurrency Model
The driver implements explicit thread safety through manual Send
and Sync
trait implementations with documented safety reasoning.
Send Implementation
The Send
trait implementation allows the Rtc
instance to be transferred between threads:
flowchart TD subgraph Hardware["Hardware"] DEVICE["PL031 DeviceDevice Memory"] end subgraph subGraph1["Thread B"] RTC_B["Rtc instance"] ACCESS_B["Hardware access"] end subgraph subGraph0["Thread A"] RTC_A["Rtc instance"] MOVE["move rtc"] end ACCESS_B --> DEVICE MOVE --> RTC_B RTC_A --> MOVE RTC_B --> ACCESS_B
The implementation is safe because device memory can be accessed from any thread context.
Sources: src/lib.rs(L123 - L124)
Sync Implementation
The Sync
trait implementation allows shared references across threads:
flowchart TD subgraph Hardware["Hardware"] REGISTERS["PL031 RegistersRead-only operations"] end subgraph subGraph2["Shared Instance"] RTC_SHARED["Rtc instance"] end subgraph subGraph1["Thread 2"] REF2["&Rtc"] READ2["matched()"] end subgraph subGraph0["Thread 1"] REF1["&Rtc"] READ1["get_unix_timestamp()"] end READ1 --> REGISTERS READ2 --> REGISTERS REF1 --> RTC_SHARED REF2 --> RTC_SHARED
The implementation is safe because shared references only allow read operations on device registers, which can be performed concurrently.
Sources: src/lib.rs(L126 - L128)
Mutation Safety
Methods requiring &mut self
ensure exclusive access for write operations:
Method Category | Reference Type | Thread Safety | Hardware Impact |
---|---|---|---|
Read Operations | &self | Concurrent safe | Read-only register access |
Write Operations | &mut self | Exclusive access | Register modification |
Interrupt Management | &mut self | Exclusive access | Control register writes |
Sources: src/lib.rs(L70 - L120)
Memory Safety Guarantees
The driver provides several layers of memory safety guarantees through its design:
Pointer Validity
Once constructed with a valid base address, all register accesses are guaranteed to be within the mapped MMIO region:
flowchart TD subgraph subGraph1["Runtime Guarantees"] NO_UB["No undefined behaviorAll accesses valid"] ATOMIC_OPS["Atomic register operationsSingle instruction access"] CONSISTENT_STATE["Consistent hardware stateProper ordering"] end subgraph subGraph0["Safety Invariants"] VALID_PTR["Valid base pointerEstablished in new()"] BOUNDED_ACCESS["Bounded register accessWithin Registers struct"] VOLATILE_OPS["Volatile operationsProper device memory handling"] end BOUNDED_ACCESS --> ATOMIC_OPS VALID_PTR --> NO_UB VOLATILE_OPS --> CONSISTENT_STATE
Sources: src/lib.rs(L56 - L60) src/lib.rs(L15 - L39)
Strict Safety Lints
The crate enforces strict safety requirements through compiler lints:
clippy::missing_safety_doc
: All unsafe functions must have safety documentationclippy::undocumented_unsafe_blocks
: All unsafe blocks must have safety commentsunsafe_op_in_unsafe_fn
: Unsafe operations in unsafe functions must be explicit
Sources: src/lib.rs(L4 - L8)
This comprehensive safety model ensures that despite the low-level hardware interaction, the driver maintains Rust's memory safety guarantees while providing efficient concurrent access to the RTC hardware.