#![doc = ""]
#![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."))]
#![doc = ""]
#![doc = ""]
#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_bootloader_esp_idf_config_table.md"))]
#![doc = ""]
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
#![no_std]
mod fmt;
#[cfg(not(feature = "std"))]
mod rom;
#[cfg(not(feature = "std"))]
pub(crate) use rom as crypto;
#[cfg(feature = "std")]
mod non_rom;
#[cfg(embedded_test)]
pub use crypto::Crc32 as Crc32ForTesting;
#[cfg(feature = "std")]
pub(crate) use non_rom as crypto;
pub mod partitions;
pub mod ota;
pub mod ota_updater;
#[cfg(not(target_os = "macos"))]
#[unsafe(link_section = ".espressif.metadata")]
#[used]
#[unsafe(export_name = "bootloader.NAME")]
static OTA_FEATURE: [u8; 7] = *b"ESP-IDF";
#[repr(C)]
pub struct EspAppDesc {
magic_word: u32,
secure_version: u32,
reserv1: [u32; 2],
version: [core::ffi::c_char; 32],
project_name: [core::ffi::c_char; 32],
time: [core::ffi::c_char; 16],
date: [core::ffi::c_char; 16],
idf_ver: [core::ffi::c_char; 32],
app_elf_sha256: [u8; 32],
min_efuse_blk_rev_full: u16,
max_efuse_blk_rev_full: u16,
mmu_page_size: u8,
reserv3: [u8; 3],
reserv2: [u32; 18],
}
impl EspAppDesc {
#[doc(hidden)]
#[expect(clippy::too_many_arguments, reason = "For internal use only")]
pub const fn new_internal(
version: &str,
project_name: &str,
build_time: &str,
build_date: &str,
idf_ver: &str,
min_efuse_blk_rev_full: u16,
max_efuse_blk_rev_full: u16,
mmu_page_size: u32,
secure_version: u32,
) -> Self {
Self {
magic_word: ESP_APP_DESC_MAGIC_WORD,
secure_version,
reserv1: [0; 2],
version: str_to_cstr_array(version),
project_name: str_to_cstr_array(project_name),
time: str_to_cstr_array(build_time),
date: str_to_cstr_array(build_date),
idf_ver: str_to_cstr_array(idf_ver),
app_elf_sha256: [0; 32],
min_efuse_blk_rev_full,
max_efuse_blk_rev_full,
mmu_page_size: (mmu_page_size.ilog2()) as u8,
reserv3: [0; 3],
reserv2: [0; 18],
}
}
pub fn magic_word(&self) -> u32 {
self.magic_word
}
pub fn secure_version(&self) -> u32 {
self.secure_version
}
pub fn version(&self) -> &str {
array_to_str(&self.version)
}
pub fn project_name(&self) -> &str {
array_to_str(&self.project_name)
}
pub fn time(&self) -> &str {
array_to_str(&self.time)
}
pub fn date(&self) -> &str {
array_to_str(&self.date)
}
pub fn idf_ver(&self) -> &str {
array_to_str(&self.idf_ver)
}
pub fn app_elf_sha256(&self) -> &[u8; 32] {
&self.app_elf_sha256
}
pub fn min_efuse_blk_rev_full(&self) -> u16 {
self.min_efuse_blk_rev_full
}
pub fn max_efuse_blk_rev_full(&self) -> u16 {
self.max_efuse_blk_rev_full
}
pub fn mmu_page_size(&self) -> u32 {
2_u32.pow(self.mmu_page_size as u32)
}
}
impl core::fmt::Debug for EspAppDesc {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EspAppDesc")
.field("magic_word", &self.magic_word)
.field("secure_version", &self.secure_version)
.field("version", &self.version())
.field("project_name", &self.project_name())
.field("time", &self.time())
.field("date", &self.date())
.field("idf_ver", &self.idf_ver())
.field("app_elf_sha256", &self.app_elf_sha256)
.field("min_efuse_blk_rev_full", &self.min_efuse_blk_rev_full)
.field("max_efuse_blk_rev_full", &self.max_efuse_blk_rev_full)
.field("mmu_page_size", &self.mmu_page_size)
.finish()
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for EspAppDesc {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"EspAppDesc (\
magic_word = {}, \
secure_version = {}, \
version = {}, \
project_name = {}, \
time = {}, \
date = {}, \
idf_ver = {}, \
app_elf_sha256 = {}, \
min_efuse_blk_rev_full = {}, \
max_efuse_blk_rev_full = {}, \
mmu_page_size = {}\
)",
self.magic_word,
self.secure_version,
self.version(),
self.project_name(),
self.time(),
self.date(),
self.idf_ver(),
self.app_elf_sha256,
self.min_efuse_blk_rev_full,
self.max_efuse_blk_rev_full,
self.mmu_page_size,
)
}
}
fn array_to_str(array: &[core::ffi::c_char]) -> &str {
let len = array.iter().position(|b| *b == 0).unwrap_or(array.len());
unsafe {
core::str::from_utf8_unchecked(core::slice::from_raw_parts(array.as_ptr().cast(), len))
}
}
const ESP_APP_DESC_MAGIC_WORD: u32 = 0xABCD5432;
const fn str_to_cstr_array<const C: usize>(s: &str) -> [::core::ffi::c_char; C] {
let bytes = s.as_bytes();
let mut ret: [::core::ffi::c_char; C] = [0; C];
let mut i = 0;
loop {
ret[i] = bytes[i] as _;
i += 1;
if i >= bytes.len() || i >= C {
break;
}
}
ret
}
pub const BUILD_TIME: &str = env!("ESP_BOOTLOADER_BUILD_TIME");
pub const BUILD_DATE: &str = env!("ESP_BOOTLOADER_BUILD_DATE");
pub const MMU_PAGE_SIZE: u32 = {
let mmu_page_size =
esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_MMU_PAGE_SIZE").as_bytes();
match mmu_page_size {
b"8k" => 8 * 1024,
b"16k" => 16 * 1024,
b"32k" => 32 * 1024,
b"64k" => 64 * 1024,
_ => 64 * 1024,
}
};
pub const SECURE_VERSION: u32 =
esp_config::esp_config_int!(u32, "ESP_BOOTLOADER_ESP_IDF_CONFIG_SECURE_VERSION");
pub const ESP_IDF_COMPATIBLE_VERSION: &str =
esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_ESP_IDF_VERSION");
#[macro_export]
macro_rules! esp_app_desc {
() => {
$crate::esp_app_desc!(
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_NAME"),
$crate::BUILD_TIME,
$crate::BUILD_DATE,
$crate::ESP_IDF_COMPATIBLE_VERSION,
$crate::MMU_PAGE_SIZE,
0,
u16::MAX,
$crate::SECURE_VERSION
);
};
(
$version: expr,
$project_name: expr,
$build_time: expr,
$build_date: expr,
$idf_ver: expr,
$mmu_page_size: expr,
$min_efuse_blk_rev_full: expr,
$max_efuse_blk_rev_full: expr,
$secure_version: expr
) => {
#[unsafe(export_name = "esp_app_desc")]
#[unsafe(link_section = ".flash.appdesc")]
#[used]
pub static ESP_APP_DESC: $crate::EspAppDesc = $crate::EspAppDesc::new_internal(
$version,
$project_name,
$build_time,
$build_date,
$idf_ver,
$min_efuse_blk_rev_full,
$max_efuse_blk_rev_full,
$mmu_page_size,
$secure_version,
);
};
}