1#![doc = ""]
85#![cfg_attr(not(feature = "std"), doc = concat!("For ", esp_metadata_generated::chip!(), " the size of the reclaimed memory is ", esp_metadata_generated::memory_range!(size as str, "DRAM2_UNINIT")," bytes."))]
86#![doc = ""]
87#![doc = ""]
94#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_bootloader_esp_idf_config_table.md"))]
95#![doc = ""]
96#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
98#![doc(html_logo_url = "https://docs.espressif.com/projects/rust/esp-rs-grey-bg.svg")]
99#![no_std]
100
101mod fmt;
103
104#[cfg(not(feature = "std"))]
105mod rom;
106#[cfg(not(feature = "std"))]
107pub(crate) use rom as crypto;
108
109#[cfg(feature = "std")]
110mod non_rom;
111#[cfg(embedded_test)]
112pub use crypto::Crc32 as Crc32ForTesting;
113#[cfg(feature = "std")]
114pub(crate) use non_rom as crypto;
115
116mod flash;
117
118pub mod partitions;
119
120pub mod ota;
121
122pub mod ota_updater;
123
124#[cfg(not(target_os = "macos"))]
127#[unsafe(link_section = ".espressif.metadata")]
128#[used]
129#[unsafe(export_name = "bootloader.NAME")]
130static OTA_FEATURE: [u8; 7] = *b"ESP-IDF";
131
132#[repr(C)]
136pub struct EspAppDesc {
137 magic_word: u32,
139 secure_version: u32,
141 reserv1: [u32; 2],
143 version: [core::ffi::c_char; 32],
145 project_name: [core::ffi::c_char; 32],
147 time: [core::ffi::c_char; 16],
149 date: [core::ffi::c_char; 16],
151 idf_ver: [core::ffi::c_char; 32],
153 app_elf_sha256: [u8; 32],
155 min_efuse_blk_rev_full: u16,
158 max_efuse_blk_rev_full: u16,
161 mmu_page_size: u8,
163 reserv3: [u8; 3],
165 reserv2: [u32; 18],
167}
168
169impl EspAppDesc {
170 #[doc(hidden)]
172 #[expect(clippy::too_many_arguments, reason = "For internal use only")]
173 pub const fn new_internal(
174 version: &str,
175 project_name: &str,
176 build_time: &str,
177 build_date: &str,
178 idf_ver: &str,
179 min_efuse_blk_rev_full: u16,
180 max_efuse_blk_rev_full: u16,
181 mmu_page_size: u32,
182 secure_version: u32,
183 ) -> Self {
184 Self {
185 magic_word: ESP_APP_DESC_MAGIC_WORD,
186 secure_version,
187 reserv1: [0; 2],
188 version: str_to_cstr_array(version),
189 project_name: str_to_cstr_array(project_name),
190 time: str_to_cstr_array(build_time),
191 date: str_to_cstr_array(build_date),
192 idf_ver: str_to_cstr_array(idf_ver),
193 app_elf_sha256: [0; 32],
194 min_efuse_blk_rev_full,
195 max_efuse_blk_rev_full,
196 mmu_page_size: (mmu_page_size.ilog2()) as u8,
197 reserv3: [0; 3],
198 reserv2: [0; 18],
199 }
200 }
201
202 pub fn magic_word(&self) -> u32 {
204 self.magic_word
205 }
206
207 pub fn secure_version(&self) -> u32 {
209 self.secure_version
210 }
211
212 pub fn version(&self) -> &str {
214 array_to_str(&self.version)
215 }
216
217 pub fn project_name(&self) -> &str {
219 array_to_str(&self.project_name)
220 }
221
222 pub fn time(&self) -> &str {
224 array_to_str(&self.time)
225 }
226
227 pub fn date(&self) -> &str {
229 array_to_str(&self.date)
230 }
231
232 pub fn idf_ver(&self) -> &str {
234 array_to_str(&self.idf_ver)
235 }
236
237 pub fn app_elf_sha256(&self) -> &[u8; 32] {
241 &self.app_elf_sha256
242 }
243
244 pub fn min_efuse_blk_rev_full(&self) -> u16 {
248 self.min_efuse_blk_rev_full
249 }
250
251 pub fn max_efuse_blk_rev_full(&self) -> u16 {
255 self.max_efuse_blk_rev_full
256 }
257
258 pub fn mmu_page_size(&self) -> u32 {
260 2_u32.pow(self.mmu_page_size as u32)
261 }
262}
263
264impl core::fmt::Debug for EspAppDesc {
265 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
266 f.debug_struct("EspAppDesc")
267 .field("magic_word", &self.magic_word)
268 .field("secure_version", &self.secure_version)
269 .field("version", &self.version())
270 .field("project_name", &self.project_name())
271 .field("time", &self.time())
272 .field("date", &self.date())
273 .field("idf_ver", &self.idf_ver())
274 .field("app_elf_sha256", &self.app_elf_sha256)
275 .field("min_efuse_blk_rev_full", &self.min_efuse_blk_rev_full)
276 .field("max_efuse_blk_rev_full", &self.max_efuse_blk_rev_full)
277 .field("mmu_page_size", &self.mmu_page_size)
278 .finish()
279 }
280}
281
282#[cfg(feature = "defmt")]
283impl defmt::Format for EspAppDesc {
284 fn format(&self, fmt: defmt::Formatter) {
285 defmt::write!(
286 fmt,
287 "EspAppDesc (\
288 magic_word = {}, \
289 secure_version = {}, \
290 version = {}, \
291 project_name = {}, \
292 time = {}, \
293 date = {}, \
294 idf_ver = {}, \
295 app_elf_sha256 = {}, \
296 min_efuse_blk_rev_full = {}, \
297 max_efuse_blk_rev_full = {}, \
298 mmu_page_size = {}\
299 )",
300 self.magic_word,
301 self.secure_version,
302 self.version(),
303 self.project_name(),
304 self.time(),
305 self.date(),
306 self.idf_ver(),
307 self.app_elf_sha256,
308 self.min_efuse_blk_rev_full,
309 self.max_efuse_blk_rev_full,
310 self.mmu_page_size,
311 )
312 }
313}
314
315fn array_to_str(array: &[core::ffi::c_char]) -> &str {
316 let len = array.iter().position(|b| *b == 0).unwrap_or(array.len());
317 unsafe {
318 core::str::from_utf8_unchecked(core::slice::from_raw_parts(array.as_ptr().cast(), len))
319 }
320}
321
322const ESP_APP_DESC_MAGIC_WORD: u32 = 0xABCD5432;
323
324const fn str_to_cstr_array<const C: usize>(s: &str) -> [::core::ffi::c_char; C] {
325 let bytes = s.as_bytes();
326 let mut ret: [::core::ffi::c_char; C] = [0; C];
327 let mut i = 0;
328 loop {
329 ret[i] = bytes[i] as _;
330 i += 1;
331 if i >= bytes.len() || i >= C {
332 break;
333 }
334 }
335 ret
336}
337
338pub const BUILD_TIME: &str = env!("ESP_BOOTLOADER_BUILD_TIME");
340
341pub const BUILD_DATE: &str = env!("ESP_BOOTLOADER_BUILD_DATE");
343
344pub const MMU_PAGE_SIZE: u32 = {
346 let mmu_page_size =
347 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_MMU_PAGE_SIZE").as_bytes();
348 match mmu_page_size {
349 b"8k" => 8 * 1024,
350 b"16k" => 16 * 1024,
351 b"32k" => 32 * 1024,
352 b"64k" => 64 * 1024,
353 _ => 64 * 1024,
354 }
355};
356
357pub const SECURE_VERSION: u32 =
359 esp_config::esp_config_int!(u32, "ESP_BOOTLOADER_ESP_IDF_CONFIG_SECURE_VERSION");
360
361pub const ESP_IDF_COMPATIBLE_VERSION: &str =
363 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_ESP_IDF_VERSION");
364
365#[macro_export]
370macro_rules! esp_app_desc {
371 () => {
372 $crate::esp_app_desc!(
373 env!("CARGO_PKG_VERSION"),
374 env!("CARGO_PKG_NAME"),
375 $crate::BUILD_TIME,
376 $crate::BUILD_DATE,
377 $crate::ESP_IDF_COMPATIBLE_VERSION,
378 $crate::MMU_PAGE_SIZE,
379 0,
380 u16::MAX,
381 $crate::SECURE_VERSION
382 );
383 };
384
385 (
386 $version: expr,
387 $project_name: expr,
388 $build_time: expr,
389 $build_date: expr,
390 $idf_ver: expr,
391 $mmu_page_size: expr,
392 $min_efuse_blk_rev_full: expr,
393 $max_efuse_blk_rev_full: expr,
394 $secure_version: expr
395 ) => {
396 #[unsafe(export_name = "esp_app_desc")]
397 #[unsafe(link_section = ".flash.appdesc")]
398 #[used]
399 pub static ESP_APP_DESC: $crate::EspAppDesc = $crate::EspAppDesc::new_internal(
401 $version,
402 $project_name,
403 $build_time,
404 $build_date,
405 $idf_ver,
406 $min_efuse_blk_rev_full,
407 $max_efuse_blk_rev_full,
408 $mmu_page_size,
409 $secure_version,
410 );
411 };
412}