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://avatars.githubusercontent.com/u/46717278")]
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
116pub mod partitions;
117
118pub mod ota;
119
120pub mod ota_updater;
121
122#[cfg(not(target_os = "macos"))]
125#[unsafe(link_section = ".espressif.metadata")]
126#[used]
127#[unsafe(export_name = "bootloader.NAME")]
128static OTA_FEATURE: [u8; 7] = *b"ESP-IDF";
129
130#[repr(C)]
134pub struct EspAppDesc {
135 magic_word: u32,
137 secure_version: u32,
139 reserv1: [u32; 2],
141 version: [core::ffi::c_char; 32],
143 project_name: [core::ffi::c_char; 32],
145 time: [core::ffi::c_char; 16],
147 date: [core::ffi::c_char; 16],
149 idf_ver: [core::ffi::c_char; 32],
151 app_elf_sha256: [u8; 32],
153 min_efuse_blk_rev_full: u16,
156 max_efuse_blk_rev_full: u16,
159 mmu_page_size: u8,
161 reserv3: [u8; 3],
163 reserv2: [u32; 18],
165}
166
167impl EspAppDesc {
168 #[doc(hidden)]
170 #[expect(clippy::too_many_arguments, reason = "For internal use only")]
171 pub const fn new_internal(
172 version: &str,
173 project_name: &str,
174 build_time: &str,
175 build_date: &str,
176 idf_ver: &str,
177 min_efuse_blk_rev_full: u16,
178 max_efuse_blk_rev_full: u16,
179 mmu_page_size: u32,
180 secure_version: u32,
181 ) -> Self {
182 Self {
183 magic_word: ESP_APP_DESC_MAGIC_WORD,
184 secure_version,
185 reserv1: [0; 2],
186 version: str_to_cstr_array(version),
187 project_name: str_to_cstr_array(project_name),
188 time: str_to_cstr_array(build_time),
189 date: str_to_cstr_array(build_date),
190 idf_ver: str_to_cstr_array(idf_ver),
191 app_elf_sha256: [0; 32],
192 min_efuse_blk_rev_full,
193 max_efuse_blk_rev_full,
194 mmu_page_size: (mmu_page_size.ilog2()) as u8,
195 reserv3: [0; 3],
196 reserv2: [0; 18],
197 }
198 }
199
200 pub fn magic_word(&self) -> u32 {
202 self.magic_word
203 }
204
205 pub fn secure_version(&self) -> u32 {
207 self.secure_version
208 }
209
210 pub fn version(&self) -> &str {
212 array_to_str(&self.version)
213 }
214
215 pub fn project_name(&self) -> &str {
217 array_to_str(&self.project_name)
218 }
219
220 pub fn time(&self) -> &str {
222 array_to_str(&self.time)
223 }
224
225 pub fn date(&self) -> &str {
227 array_to_str(&self.date)
228 }
229
230 pub fn idf_ver(&self) -> &str {
232 array_to_str(&self.idf_ver)
233 }
234
235 pub fn app_elf_sha256(&self) -> &[u8; 32] {
239 &self.app_elf_sha256
240 }
241
242 pub fn min_efuse_blk_rev_full(&self) -> u16 {
246 self.min_efuse_blk_rev_full
247 }
248
249 pub fn max_efuse_blk_rev_full(&self) -> u16 {
253 self.max_efuse_blk_rev_full
254 }
255
256 pub fn mmu_page_size(&self) -> u32 {
258 2_u32.pow(self.mmu_page_size as u32)
259 }
260}
261
262impl core::fmt::Debug for EspAppDesc {
263 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
264 f.debug_struct("EspAppDesc")
265 .field("magic_word", &self.magic_word)
266 .field("secure_version", &self.secure_version)
267 .field("version", &self.version())
268 .field("project_name", &self.project_name())
269 .field("time", &self.time())
270 .field("date", &self.date())
271 .field("idf_ver", &self.idf_ver())
272 .field("app_elf_sha256", &self.app_elf_sha256)
273 .field("min_efuse_blk_rev_full", &self.min_efuse_blk_rev_full)
274 .field("max_efuse_blk_rev_full", &self.max_efuse_blk_rev_full)
275 .field("mmu_page_size", &self.mmu_page_size)
276 .finish()
277 }
278}
279
280#[cfg(feature = "defmt")]
281impl defmt::Format for EspAppDesc {
282 fn format(&self, fmt: defmt::Formatter) {
283 defmt::write!(
284 fmt,
285 "EspAppDesc (\
286 magic_word = {}, \
287 secure_version = {}, \
288 version = {}, \
289 project_name = {}, \
290 time = {}, \
291 date = {}, \
292 idf_ver = {}, \
293 app_elf_sha256 = {}, \
294 min_efuse_blk_rev_full = {}, \
295 max_efuse_blk_rev_full = {}, \
296 mmu_page_size = {}\
297 )",
298 self.magic_word,
299 self.secure_version,
300 self.version(),
301 self.project_name(),
302 self.time(),
303 self.date(),
304 self.idf_ver(),
305 self.app_elf_sha256,
306 self.min_efuse_blk_rev_full,
307 self.max_efuse_blk_rev_full,
308 self.mmu_page_size,
309 )
310 }
311}
312
313fn array_to_str(array: &[core::ffi::c_char]) -> &str {
314 let len = array.iter().position(|b| *b == 0).unwrap_or(array.len());
315 unsafe {
316 core::str::from_utf8_unchecked(core::slice::from_raw_parts(array.as_ptr().cast(), len))
317 }
318}
319
320const ESP_APP_DESC_MAGIC_WORD: u32 = 0xABCD5432;
321
322const fn str_to_cstr_array<const C: usize>(s: &str) -> [::core::ffi::c_char; C] {
323 let bytes = s.as_bytes();
324 let mut ret: [::core::ffi::c_char; C] = [0; C];
325 let mut i = 0;
326 loop {
327 ret[i] = bytes[i] as _;
328 i += 1;
329 if i >= bytes.len() || i >= C {
330 break;
331 }
332 }
333 ret
334}
335
336pub const BUILD_TIME: &str = env!("ESP_BOOTLOADER_BUILD_TIME");
338
339pub const BUILD_DATE: &str = env!("ESP_BOOTLOADER_BUILD_DATE");
341
342pub const MMU_PAGE_SIZE: u32 = {
344 let mmu_page_size =
345 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_MMU_PAGE_SIZE").as_bytes();
346 match mmu_page_size {
347 b"8k" => 8 * 1024,
348 b"16k" => 16 * 1024,
349 b"32k" => 32 * 1024,
350 b"64k" => 64 * 1024,
351 _ => 64 * 1024,
352 }
353};
354
355pub const SECURE_VERSION: u32 =
357 esp_config::esp_config_int!(u32, "ESP_BOOTLOADER_ESP_IDF_CONFIG_SECURE_VERSION");
358
359pub const ESP_IDF_COMPATIBLE_VERSION: &str =
361 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_ESP_IDF_VERSION");
362
363#[macro_export]
368macro_rules! esp_app_desc {
369 () => {
370 $crate::esp_app_desc!(
371 env!("CARGO_PKG_VERSION"),
372 env!("CARGO_PKG_NAME"),
373 $crate::BUILD_TIME,
374 $crate::BUILD_DATE,
375 $crate::ESP_IDF_COMPATIBLE_VERSION,
376 $crate::MMU_PAGE_SIZE,
377 0,
378 u16::MAX,
379 $crate::SECURE_VERSION
380 );
381 };
382
383 (
384 $version: expr,
385 $project_name: expr,
386 $build_time: expr,
387 $build_date: expr,
388 $idf_ver: expr,
389 $mmu_page_size: expr,
390 $min_efuse_blk_rev_full: expr,
391 $max_efuse_blk_rev_full: expr,
392 $secure_version: expr
393 ) => {
394 #[unsafe(export_name = "esp_app_desc")]
395 #[unsafe(link_section = ".flash.appdesc")]
396 #[used]
397 pub static ESP_APP_DESC: $crate::EspAppDesc = $crate::EspAppDesc::new_internal(
399 $version,
400 $project_name,
401 $build_time,
402 $build_date,
403 $idf_ver,
404 $min_efuse_blk_rev_full,
405 $max_efuse_blk_rev_full,
406 $mmu_page_size,
407 $secure_version,
408 );
409 };
410}