summaryrefslogtreecommitdiff
path: root/src/appdesc.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-25 12:40:53 -0300
committerGabriel Schneider <[email protected]>2026-08-25 12:46:51 -0300
commitf5f8068fac59b4f16046c2022c2fc7c7e447ef4c (patch)
tree2731a3ed4e51cae09e184e25778eded5fc37d1f5 /src/appdesc.zig
downloadesp32p4-f5f8068fac59b4f16046c2022c2fc7c7e447ef4c.tar.gz
esp32p4-f5f8068fac59b4f16046c2022c2fc7c7e447ef4c.zip
zig-p4: pure-Zig ESP32-P4 toolchain
build.zig generates the linker script and drives Zig's own LLD; tools/image.zig turns the ELF into a flashable image and tools/{rom,serial}.zig speak the mask ROM loader over the UART. No CMake, ninja, idf.py, esptool, or external linker. src/soc.zig is a comptime register model over ESP-IDF's own *_reg.h headers; src/hal/ adds peripheral sequences; src/io/ implements std.Io for the chip; src/oracle/ diffs this HAL against ESP-IDF's on the die.
Diffstat (limited to 'src/appdesc.zig')
-rw-r--r--src/appdesc.zig63
1 files changed, 63 insertions, 0 deletions
diff --git a/src/appdesc.zig b/src/appdesc.zig
new file mode 100644
index 0000000..bec2915
--- /dev/null
+++ b/src/appdesc.zig
@@ -0,0 +1,63 @@
+//! esp_app_desc_t, the 256-byte block the second-stage bootloader reads from image offset 0x20.
+//!
+//! Two facts about it were established by flashing deliberately broken images at this board:
+//! * the magic word is never validated in a default build - the descriptor is trusted purely by
+//! position, so an image with ordinary rodata there can boot;
+//! * what the loader actually reads is min/max_efuse_blk_rev_full at offsets 0xB0/0xB2, and
+//! `IS_FIELD_SET()` treats zero as "unset" (bootloader_common_loader.c:102-112).
+//!
+//! Everything past 0xB5 is reserved padding, so `minimal` stops there and saves 72 bytes of flash.
+//! `full` keeps all 256 so that `esptool image-info` can parse it.
+
+const config = @import("config");
+
+pub const magic: u32 = 0xABCD5432;
+
+pub const Minimal = extern struct {
+ magic_word: u32 = magic,
+ secure_version: u32 = 0,
+ reserv1: [2]u32 = .{ 0, 0 },
+ version: [32]u8,
+ project_name: [32]u8,
+ time: [16]u8 = @splat(0),
+ date: [16]u8 = @splat(0),
+ idf_ver: [32]u8 = @splat(0),
+ app_elf_sha256: [32]u8 = @splat(0),
+ min_efuse_blk_rev_full: u16,
+ max_efuse_blk_rev_full: u16,
+ mmu_page_size: u8 = 16, // log2(64 KiB); fixed on the P4, which has no configurable MMU page
+ reserv3: [3]u8 = @splat(0),
+};
+
+pub const Full = extern struct {
+ head: Minimal,
+ reserv2: [18]u32 = @splat(0),
+};
+
+fn str(comptime n: usize, comptime s: []const u8) [n]u8 {
+ var out: [n]u8 = @splat(0);
+ @memcpy(out[0..s.len], s);
+ return out;
+}
+
+const head: Minimal = .{
+ .version = str(32, "0.1"),
+ .project_name = str(32, "zig-p4"),
+ // The eFuse *block* revision, which has nothing to do with the silicon revision window that
+ // -Dmin-rev sets in the image header. Deriving one from the other made `-Dmin-rev=150` emit an
+ // image the bootloader refuses with "Image requires efuse blk rev >= v0.50". Zero means unset,
+ // which is what `IS_FIELD_SET()` checks for (bootloader_common_loader.c:102-112).
+ .min_efuse_blk_rev_full = 0,
+ .max_efuse_blk_rev_full = 0,
+};
+
+/// Placed first in .flash.rodata by the linker script, so it lands at image offset 0x20.
+///
+/// `export` is load-bearing: a `comptime _ = descriptor;` reference is enough in Debug but not
+/// under ReleaseSmall, where the constant is folded away, the section disappears, `KEEP` has
+/// nothing to keep, and the image ends up with one mapped segment starting at the wrong offset.
+/// An exported symbol is always emitted.
+pub export const esp_app_desc linksection(".rodata.appdesc") = if (config.full_descriptor)
+ Full{ .head = head }
+else
+ head;