speck_core/storage/
mod.rs1use alloc::vec::Vec;
9use alloc::string::String;
10use crate::error::Result;
11use crate::flash::{Flash, PageId};
12use crate::format::Module;
13
14pub mod journal;
15pub mod layout;
16pub mod wear_leveling;
17
18pub use journal::{Journal, Transaction};
19pub use layout::StorageLayout;
20pub use wear_leveling::WearLevelingPolicy;
21
22#[derive(Clone, Debug)]
24pub struct StorageConfig {
25 pub module_slots: usize,
27 pub enable_journaling: bool,
29 pub enable_wear_leveling: bool,
31 pub max_module_size: usize,
33}
34
35impl Default for StorageConfig {
36 fn default() -> Self {
37 Self {
38 module_slots: 3, enable_journaling: true,
40 enable_wear_leveling: true,
41 max_module_size: 32768, }
43 }
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum InstallationStatus {
49 Empty,
51 Valid,
53 Pending,
55 Corrupted,
57}
58
59pub struct StorageManager<'f> {
61 flash: &'f mut Flash,
62 config: StorageConfig,
63 layout: StorageLayout,
64 journal: Option<Journal>,
65}
66
67impl<'f> StorageManager<'f> {
68 pub fn new(flash: &'f mut Flash, config: StorageConfig) -> Result<Self> {
70 let layout = StorageLayout::new(flash.capacity(), &config)?;
71 let journal = if config.enable_journaling {
72 Some(Journal::new(layout.journal_region()))
73 } else {
74 None
75 };
76
77 Ok(Self {
78 flash,
79 config,
80 layout,
81 journal,
82 })
83 }
84
85 pub fn read_module(&self, slot: usize) -> Result<Option<Module>> {
87 if slot >= self.config.module_slots {
88 return Err(crate::Error::Storage(format!(
89 "invalid slot {}", slot
90 )));
91 }
92
93 let addr = self.layout.slot_address(slot);
94 let header_data = self.flash.read(addr, crate::format::FIXED_HEADER_SIZE)?;
96
97 if &header_data[0..4] != crate::format::MAGIC {
99 return Ok(None);
100 }
101
102 let total_size = u32::from_le_bytes([
103 header_data[6], header_data[7], header_data[8], header_data[9]
104 ]) as usize;
105
106 let full_data = self.flash.read(addr, total_size)?;
107 Module::from_bytes(full_data).map(Some)
108 }
109
110 pub fn begin_install(&mut self, slot: usize, expected_size: usize) -> Result<Transaction> {
112 if slot >= self.config.module_slots {
113 return Err(crate::Error::Storage(format!(
114 "invalid slot {}", slot
115 )));
116 }
117
118 if expected_size > self.config.max_module_size {
119 return Err(crate::Error::CapacityExceeded(format!(
120 "module size {} exceeds maximum {}",
121 expected_size, self.config.max_module_size
122 )));
123 }
124
125 let addr = self.layout.slot_address(slot);
126 let pages_needed = (expected_size + self.flash.page_size() - 1) / self.flash.page_size();
127
128 for i in 0..pages_needed {
130 let page_addr = addr + i * self.flash.page_size();
131 let page_id = PageId(page_addr / self.flash.page_size());
132 self.flash.erase_page(page_id)?;
133 }
134
135 Ok(Transaction::new(slot, addr, expected_size))
136 }
137
138 pub fn commit_install(&mut self, tx: Transaction, module: &Module) -> Result<()> {
140 module.verify()?;
142
143 let data = module.to_bytes()?;
145 self.flash.write(tx.address(), &data)?;
146
147 if let Some(ref mut journal) = self.journal {
149 journal.commit(tx, self.flash)?;
150 }
151
152 Ok(())
153 }
154
155 pub fn status(&self, slot: usize) -> Result<InstallationStatus> {
157 match self.read_module(slot) {
158 Ok(Some(_)) => Ok(InstallationStatus::Valid),
159 Ok(None) => Ok(InstallationStatus::Empty),
160 Err(_) => Ok(InstallationStatus::Corrupted),
161 }
162 }
163
164 pub fn current_version(&self) -> Result<u64> {
166 let meta_addr = self.layout.metadata_address();
167 let data = self.flash.read(meta_addr, 8)?;
168 Ok(u64::from_le_bytes([
169 data[0], data[1], data[2], data[3],
170 data[4], data[5], data[6], data[7],
171 ]))
172 }
173
174 pub fn update_version(&mut self, version: u64) -> Result<()> {
176 let current = self.current_version()?;
177 if version <= current {
178 return Err(crate::Error::AntiRollback {
179 current,
180 attempted: version,
181 });
182 }
183
184 let meta_addr = self.layout.metadata_address();
185 let data = version.to_le_bytes();
186 self.flash.write(meta_addr, &data)?;
187 Ok(())
188 }
189}