esp_hal/rtc_cntl/cpu_retention/
memory.rs1use core::{
4 cell::UnsafeCell,
5 mem::MaybeUninit,
6 ptr::{self, NonNull},
7};
8
9use portable_atomic::{AtomicPtr, Ordering};
10use static_cell::ConstStaticCell;
11
12use crate::rtc_cntl::sleep::LowPower;
13
14cfg_select! {
15 cpu_retention = "software" => {
16 pub(crate) use super::software::BUFFER_SIZE;
17 }
18 _ => {
19 pub(crate) const BUFFER_SIZE: usize = property!("sleep.cpu_retention_mem_size");
20
21 const _: () = ::core::assert!(property!("sleep.cpu_retention_mem_align") <= 16);
24
25 const MEM_START: usize = property!("sleep.cpu_retention_mem_start");
26 const MEM_END: usize = property!("sleep.cpu_retention_mem_end");
27 }
28}
29
30static INSTALLED: AtomicPtr<u8> = AtomicPtr::new(ptr::null_mut());
31
32#[instability::unstable]
37pub struct CpuRetentionStorage {
38 inner: ConstStaticCell<CpuRetentionMemory>,
39}
40
41impl CpuRetentionStorage {
42 #[instability::unstable]
44 pub const fn new() -> Self {
45 Self {
46 inner: ConstStaticCell::new(CpuRetentionMemory {
47 storage: UnsafeCell::new(MaybeUninit::uninit()),
48 }),
49 }
50 }
51
52 #[instability::unstable]
56 pub fn take(&'static self) -> &'static mut CpuRetentionMemory {
57 self.inner.take()
58 }
59}
60
61#[instability::unstable]
62impl Default for CpuRetentionStorage {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68unsafe impl bytemuck::Zeroable for CpuRetentionStorage {}
71
72#[repr(align(16))]
81#[instability::unstable]
82pub struct CpuRetentionMemory {
83 storage: UnsafeCell<MaybeUninit<[u8; BUFFER_SIZE]>>,
84}
85
86impl CpuRetentionMemory {
87 pub(crate) fn as_mut_ptr(&self) -> *mut u8 {
88 self.storage.get().cast()
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
94#[cfg_attr(feature = "defmt", derive(defmt::Format))]
95#[instability::unstable]
96#[non_exhaustive]
97pub enum CpuRetentionMemoryError {
98 #[cfg(not(cpu_retention = "software"))]
100 OutOfRange,
101 AlreadyInstalled,
103}
104
105#[instability::unstable]
106impl core::error::Error for CpuRetentionMemoryError {}
107
108#[instability::unstable]
109impl core::fmt::Display for CpuRetentionMemoryError {
110 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111 match self {
112 #[cfg(not(cpu_retention = "software"))]
113 Self::OutOfRange => write!(
114 f,
115 "the buffer is outside the retention memory range (0x{MEM_START:X}..0x{MEM_END:X})"
116 ),
117 Self::AlreadyInstalled => write!(f, "a retention buffer is installed already"),
118 }
119 }
120}
121
122impl LowPower<'_> {
123 #[procmacros::doc_replace]
124 #[instability::unstable]
151 pub fn install_cpu_retention_memory(
152 &mut self,
153 memory: &'static mut CpuRetentionMemory,
154 ) -> Result<(), CpuRetentionMemoryError> {
155 let memory = memory.as_mut_ptr();
156
157 #[cfg(not(cpu_retention = "software"))]
158 {
159 let start = memory as usize;
160 if start < MEM_START || start + BUFFER_SIZE > MEM_END {
161 return Err(CpuRetentionMemoryError::OutOfRange);
162 }
163 }
164
165 INSTALLED
166 .compare_exchange(ptr::null_mut(), memory, Ordering::AcqRel, Ordering::Acquire)
167 .map(|_| ())
168 .map_err(|_| CpuRetentionMemoryError::AlreadyInstalled)
169 }
170}
171
172pub(crate) fn installed_buffer_ptr() -> Option<NonNull<u8>> {
173 let memory = INSTALLED.load(Ordering::Acquire);
174
175 NonNull::new(memory.cast::<u8>())
176}