1#![doc = ""]
19#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_bootloader_esp_idf_config_table.md"))]
20#![doc = ""]
21#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
23#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
24#![no_std]
25
26mod fmt;
28
29#[cfg(not(feature = "std"))]
30mod rom;
31#[cfg(not(feature = "std"))]
32pub(crate) use rom as crypto;
33
34#[cfg(feature = "std")]
35mod non_rom;
36#[cfg(feature = "std")]
37pub(crate) use non_rom as crypto;
38
39pub mod partitions;
40
41pub mod ota;
42
43#[cfg(not(target_os = "macos"))]
46#[unsafe(link_section = ".espressif.metadata")]
47#[used]
48#[unsafe(export_name = "bootloader.NAME")]
49static OTA_FEATURE: [u8; 7] = *b"ESP-IDF";
50
51#[repr(C)]
55pub struct EspAppDesc {
56 magic_word: u32,
58 secure_version: u32,
60 reserv1: [u32; 2],
62 version: [core::ffi::c_char; 32],
64 project_name: [core::ffi::c_char; 32],
66 time: [core::ffi::c_char; 16],
68 date: [core::ffi::c_char; 16],
70 idf_ver: [core::ffi::c_char; 32],
72 app_elf_sha256: [u8; 32],
74 min_efuse_blk_rev_full: u16,
77 max_efuse_blk_rev_full: u16,
80 mmu_page_size: u8,
82 reserv3: [u8; 3],
84 reserv2: [u32; 18],
86}
87
88impl EspAppDesc {
89 #[doc(hidden)]
91 #[allow(clippy::too_many_arguments, reason = "For internal use only")]
92 pub const fn new_internal(
93 version: &str,
94 project_name: &str,
95 build_time: &str,
96 build_date: &str,
97 idf_ver: &str,
98 min_efuse_blk_rev_full: u16,
99 max_efuse_blk_rev_full: u16,
100 mmu_page_size: u32,
101 ) -> Self {
102 Self {
103 magic_word: ESP_APP_DESC_MAGIC_WORD,
104 secure_version: 0,
105 reserv1: [0; 2],
106 version: str_to_cstr_array(version),
107 project_name: str_to_cstr_array(project_name),
108 time: str_to_cstr_array(build_time),
109 date: str_to_cstr_array(build_date),
110 idf_ver: str_to_cstr_array(idf_ver),
111 app_elf_sha256: [0; 32],
112 min_efuse_blk_rev_full,
113 max_efuse_blk_rev_full,
114 mmu_page_size: (mmu_page_size.ilog2()) as u8,
115 reserv3: [0; 3],
116 reserv2: [0; 18],
117 }
118 }
119
120 pub fn magic_word(&self) -> u32 {
122 self.magic_word
123 }
124
125 pub fn secure_version(&self) -> u32 {
127 self.secure_version
128 }
129
130 pub fn version(&self) -> &str {
132 array_to_str(&self.version)
133 }
134
135 pub fn project_name(&self) -> &str {
137 array_to_str(&self.project_name)
138 }
139
140 pub fn time(&self) -> &str {
142 array_to_str(&self.time)
143 }
144
145 pub fn date(&self) -> &str {
147 array_to_str(&self.date)
148 }
149
150 pub fn idf_ver(&self) -> &str {
152 array_to_str(&self.idf_ver)
153 }
154
155 pub fn app_elf_sha256(&self) -> &[u8; 32] {
159 &self.app_elf_sha256
160 }
161
162 pub fn min_efuse_blk_rev_full(&self) -> u16 {
166 self.min_efuse_blk_rev_full
167 }
168
169 pub fn max_efuse_blk_rev_full(&self) -> u16 {
173 self.max_efuse_blk_rev_full
174 }
175
176 pub fn mmu_page_size(&self) -> u32 {
178 2_u32.pow(self.mmu_page_size as u32)
179 }
180}
181
182impl core::fmt::Debug for EspAppDesc {
183 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184 f.debug_struct("EspAppDesc")
185 .field("magic_word", &self.magic_word)
186 .field("secure_version", &self.secure_version)
187 .field("version", &self.version())
188 .field("project_name", &self.project_name())
189 .field("time", &self.time())
190 .field("date", &self.date())
191 .field("idf_ver", &self.idf_ver())
192 .field("app_elf_sha256", &self.app_elf_sha256)
193 .field("min_efuse_blk_rev_full", &self.min_efuse_blk_rev_full)
194 .field("max_efuse_blk_rev_full", &self.max_efuse_blk_rev_full)
195 .field("mmu_page_size", &self.mmu_page_size)
196 .finish()
197 }
198}
199
200#[cfg(feature = "defmt")]
201impl defmt::Format for EspAppDesc {
202 fn format(&self, fmt: defmt::Formatter) {
203 defmt::write!(
204 fmt,
205 "EspAppDesc (\
206 magic_word = {}, \
207 secure_version = {}, \
208 version = {}, \
209 project_name = {}, \
210 time = {}, \
211 date = {}, \
212 idf_ver = {}, \
213 app_elf_sha256 = {}, \
214 min_efuse_blk_rev_full = {}, \
215 max_efuse_blk_rev_full = {}, \
216 mmu_page_size = {}\
217 )",
218 self.magic_word,
219 self.secure_version,
220 self.version(),
221 self.project_name(),
222 self.time(),
223 self.date(),
224 self.idf_ver(),
225 self.app_elf_sha256,
226 self.min_efuse_blk_rev_full,
227 self.max_efuse_blk_rev_full,
228 self.mmu_page_size,
229 )
230 }
231}
232
233fn array_to_str(array: &[core::ffi::c_char]) -> &str {
234 let len = array.iter().position(|b| *b == 0).unwrap_or(array.len());
235 unsafe {
236 core::str::from_utf8_unchecked(core::slice::from_raw_parts(array.as_ptr().cast(), len))
237 }
238}
239
240const ESP_APP_DESC_MAGIC_WORD: u32 = 0xABCD5432;
241
242const fn str_to_cstr_array<const C: usize>(s: &str) -> [::core::ffi::c_char; C] {
243 let bytes = s.as_bytes();
244 let mut ret: [::core::ffi::c_char; C] = [0; C];
245 let mut i = 0;
246 loop {
247 ret[i] = bytes[i] as _;
248 i += 1;
249 if i >= bytes.len() {
250 break;
251 }
252 }
253 ret
254}
255
256pub const BUILD_TIME: &str = env!("ESP_BOOTLOADER_BUILD_TIME");
258
259pub const BUILD_DATE: &str = env!("ESP_BOOTLOADER_BUILD_DATE");
261
262pub const MMU_PAGE_SIZE: u32 = {
264 let mmu_page_size =
265 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_MMU_PAGE_SIZE").as_bytes();
266 match mmu_page_size {
267 b"8k" => 8 * 1024,
268 b"16k" => 16 * 1024,
269 b"32k" => 32 * 1024,
270 b"64k" => 64 * 1024,
271 _ => 64 * 1024,
272 }
273};
274
275pub const ESP_IDF_COMPATIBLE_VERSION: &str =
277 esp_config::esp_config_str!("ESP_BOOTLOADER_ESP_IDF_CONFIG_MMU_PAGE_SIZE");
278
279#[macro_export]
284macro_rules! esp_app_desc {
285 () => {
286 $crate::esp_app_desc!(
287 env!("CARGO_PKG_VERSION"),
288 env!("CARGO_PKG_NAME"),
289 $crate::BUILD_TIME,
290 $crate::BUILD_DATE,
291 $crate::ESP_IDF_COMPATIBLE_VERSION,
292 $crate::MMU_PAGE_SIZE,
293 0,
294 u16::MAX
295 );
296 };
297
298 (
299 $version: expr,
300 $project_name: expr,
301 $build_time: expr,
302 $build_date: expr,
303 $idf_ver: expr,
304 $mmu_page_size: expr,
305 $min_efuse_blk_rev_full: expr,
306 $max_efuse_blk_rev_full: expr
307 ) => {
308 #[unsafe(export_name = "esp_app_desc")]
309 #[unsafe(link_section = ".rodata_desc.appdesc")]
310 pub static ESP_APP_DESC: $crate::EspAppDesc = $crate::EspAppDesc::new_internal(
311 $version,
312 $project_name,
313 $build_time,
314 $build_date,
315 $idf_ver,
316 $min_efuse_blk_rev_full,
317 $max_efuse_blk_rev_full,
318 $mmu_page_size,
319 );
320 };
321}