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