1#![doc = ""]
82#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_bootloader_esp_idf_config_table.md"))]
83#![doc = ""]
84#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
86#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
87#![no_std]
88
89mod fmt;
91
92#[cfg(not(feature = "std"))]
93mod rom;
94#[cfg(not(feature = "std"))]
95pub(crate) use rom as crypto;
96
97#[cfg(feature = "std")]
98mod non_rom;
99#[cfg(embedded_test)]
100pub use crypto::Crc32 as Crc32ForTesting;
101#[cfg(feature = "std")]
102pub(crate) use non_rom as crypto;
103
104pub mod partitions;
105
106pub mod ota;
107
108pub mod ota_updater;
109
110#[cfg(not(target_os = "macos"))]
113#[unsafe(link_section = ".espressif.metadata")]
114#[used]
115#[unsafe(export_name = "bootloader.NAME")]
116static OTA_FEATURE: [u8; 7] = *b"ESP-IDF";
117
118#[repr(C)]
122pub struct EspAppDesc {
123 magic_word: u32,
125 secure_version: u32,
127 reserv1: [u32; 2],
129 version: [core::ffi::c_char; 32],
131 project_name: [core::ffi::c_char; 32],
133 time: [core::ffi::c_char; 16],
135 date: [core::ffi::c_char; 16],
137 idf_ver: [core::ffi::c_char; 32],
139 app_elf_sha256: [u8; 32],
141 min_efuse_blk_rev_full: u16,
144 max_efuse_blk_rev_full: u16,
147 mmu_page_size: u8,
149 reserv3: [u8; 3],
151 reserv2: [u32; 18],
153}
154
155impl EspAppDesc {
156 #[doc(hidden)]
158 #[expect(clippy::too_many_arguments, reason = "For internal use only")]
159 pub const fn new_internal(
160 version: &str,
161 project_name: &str,
162 build_time: &str,
163 build_date: &str,
164 idf_ver: &str,
165 min_efuse_blk_rev_full: u16,
166 max_efuse_blk_rev_full: u16,
167 mmu_page_size: u32,
168 ) -> Self {
169 Self {
170 magic_word: ESP_APP_DESC_MAGIC_WORD,
171 secure_version: 0,
172 reserv1: [0; 2],
173 version: str_to_cstr_array(version),
174 project_name: str_to_cstr_array(project_name),
175 time: str_to_cstr_array(build_time),
176 date: str_to_cstr_array(build_date),
177 idf_ver: str_to_cstr_array(idf_ver),
178 app_elf_sha256: [0; 32],
179 min_efuse_blk_rev_full,
180 max_efuse_blk_rev_full,
181 mmu_page_size: (mmu_page_size.ilog2()) as u8,
182 reserv3: [0; 3],
183 reserv2: [0; 18],
184 }
185 }
186
187 pub fn magic_word(&self) -> u32 {
189 self.magic_word
190 }
191
192 pub fn secure_version(&self) -> u32 {
194 self.secure_version
195 }
196
197 pub fn version(&self) -> &str {
199 array_to_str(&self.version)
200 }
201
202 pub fn project_name(&self) -> &str {
204 array_to_str(&self.project_name)
205 }
206
207 pub fn time(&self) -> &str {
209 array_to_str(&self.time)
210 }
211
212 pub fn date(&self) -> &str {
214 array_to_str(&self.date)
215 }
216
217 pub fn idf_ver(&self) -> &str {
219 array_to_str(&self.idf_ver)
220 }
221
222 pub fn app_elf_sha256(&self) -> &[u8; 32] {
226 &self.app_elf_sha256
227 }
228
229 pub fn min_efuse_blk_rev_full(&self) -> u16 {
233 self.min_efuse_blk_rev_full
234 }
235
236 pub fn max_efuse_blk_rev_full(&self) -> u16 {
240 self.max_efuse_blk_rev_full
241 }
242
243 pub fn mmu_page_size(&self) -> u32 {
245 2_u32.pow(self.mmu_page_size as u32)
246 }
247}
248
249impl core::fmt::Debug for EspAppDesc {
250 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251 f.debug_struct("EspAppDesc")
252 .field("magic_word", &self.magic_word)
253 .field("secure_version", &self.secure_version)
254 .field("version", &self.version())
255 .field("project_name", &self.project_name())
256 .field("time", &self.time())
257 .field("date", &self.date())
258 .field("idf_ver", &self.idf_ver())
259 .field("app_elf_sha256", &self.app_elf_sha256)
260 .field("min_efuse_blk_rev_full", &self.min_efuse_blk_rev_full)
261 .field("max_efuse_blk_rev_full", &self.max_efuse_blk_rev_full)
262 .field("mmu_page_size", &self.mmu_page_size)
263 .finish()
264 }
265}
266
267#[cfg(feature = "defmt")]
268impl defmt::Format for EspAppDesc {
269 fn format(&self, fmt: defmt::Formatter) {
270 defmt::write!(
271 fmt,
272 "EspAppDesc (\
273 magic_word = {}, \
274 secure_version = {}, \
275 version = {}, \
276 project_name = {}, \
277 time = {}, \
278 date = {}, \
279 idf_ver = {}, \
280 app_elf_sha256 = {}, \
281 min_efuse_blk_rev_full = {}, \
282 max_efuse_blk_rev_full = {}, \
283 mmu_page_size = {}\
284 )",
285 self.magic_word,
286 self.secure_version,
287 self.version(),
288 self.project_name(),
289 self.time(),
290 self.date(),
291 self.idf_ver(),
292 self.app_elf_sha256,
293 self.min_efuse_blk_rev_full,
294 self.max_efuse_blk_rev_full,
295 self.mmu_page_size,
296 )
297 }
298}
299
300fn array_to_str(array: &[core::ffi::c_char]) -> &str {
301 let len = array.iter().position(|b| *b == 0).unwrap_or(array.len());
302 unsafe {
303 core::str::from_utf8_unchecked(core::slice::from_raw_parts(array.as_ptr().cast(), len))
304 }
305}
306
307const ESP_APP_DESC_MAGIC_WORD: u32 = 0xABCD5432;
308
309const fn str_to_cstr_array<const C: usize>(s: &str) -> [::core::ffi::c_char; C] {
310 let bytes = s.as_bytes();
311 let mut ret: [::core::ffi::c_char; C] = [0; C];
312 let mut i = 0;
313 loop {
314 ret[i] = bytes[i] as _;
315 i += 1;
316 if i >= bytes.len() || i >= C {
317 break;
318 }
319 }
320 ret
321}
322
323pub const BUILD_TIME: &str = env!("ESP_BOOTLOADER_BUILD_TIME");
325
326pub const BUILD_DATE: &str = env!("ESP_BOOTLOADER_BUILD_DATE");
328
329pub const MMU_PAGE_SIZE: u32 = {
331 let mmu_page_size =
332 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_MMU_PAGE_SIZE").as_bytes();
333 match mmu_page_size {
334 b"8k" => 8 * 1024,
335 b"16k" => 16 * 1024,
336 b"32k" => 32 * 1024,
337 b"64k" => 64 * 1024,
338 _ => 64 * 1024,
339 }
340};
341
342pub const ESP_IDF_COMPATIBLE_VERSION: &str =
344 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_ESP_IDF_VERSION");
345
346#[macro_export]
351macro_rules! esp_app_desc {
352 () => {
353 $crate::esp_app_desc!(
354 env!("CARGO_PKG_VERSION"),
355 env!("CARGO_PKG_NAME"),
356 $crate::BUILD_TIME,
357 $crate::BUILD_DATE,
358 $crate::ESP_IDF_COMPATIBLE_VERSION,
359 $crate::MMU_PAGE_SIZE,
360 0,
361 u16::MAX
362 );
363 };
364
365 (
366 $version: expr,
367 $project_name: expr,
368 $build_time: expr,
369 $build_date: expr,
370 $idf_ver: expr,
371 $mmu_page_size: expr,
372 $min_efuse_blk_rev_full: expr,
373 $max_efuse_blk_rev_full: expr
374 ) => {
375 #[unsafe(export_name = "esp_app_desc")]
376 #[unsafe(link_section = ".rodata_desc.appdesc")]
377 pub static ESP_APP_DESC: $crate::EspAppDesc = $crate::EspAppDesc::new_internal(
379 $version,
380 $project_name,
381 $build_time,
382 $build_date,
383 $idf_ver,
384 $min_efuse_blk_rev_full,
385 $max_efuse_blk_rev_full,
386 $mmu_page_size,
387 );
388 };
389}