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.