|
| 1 | +use bytemuck::bytes_of; |
| 2 | +use sha2::{Digest, Sha256}; |
| 3 | + |
| 4 | +use crate::{ |
| 5 | + chip::{ |
| 6 | + encode_flash_size, get_segment_padding, save_flash_segment, save_segment, Chip, ChipType, |
| 7 | + EspCommonHeader, ExtendedHeader, SegmentHeader, SpiRegisters, ESP_MAGIC, SEG_HEADER_LEN, |
| 8 | + WP_PIN_DISABLED, |
| 9 | + }, |
| 10 | + elf::{FirmwareImage, RomSegment, ESP_CHECKSUM_MAGIC}, |
| 11 | + Error, PartitionTable, |
| 12 | +}; |
| 13 | + |
| 14 | +use std::{borrow::Cow, io::Write, iter::once}; |
| 15 | + |
| 16 | +pub struct Esp32s2; |
| 17 | + |
| 18 | +const IROM_MAP_START: u32 = 0x40080000; |
| 19 | +const IROM_MAP_END: u32 = 0x40b80000; |
| 20 | + |
| 21 | +const DROM_MAP_START: u32 = 0x3F000000; |
| 22 | +const DROM_MAP_END: u32 = 0x3F3F0000; |
| 23 | + |
| 24 | +const BOOT_ADDR: u32 = 0x1000; |
| 25 | +const PARTITION_ADDR: u32 = 0x8000; |
| 26 | +const NVS_ADDR: u32 = 0x9000; |
| 27 | +const PHY_INIT_DATA_ADDR: u32 = 0xf000; |
| 28 | +const APP_ADDR: u32 = 0x10000; |
| 29 | + |
| 30 | +const NVS_SIZE: u32 = 0x6000; |
| 31 | +const PHY_INIT_DATA_SIZE: u32 = 0x1000; |
| 32 | +const APP_SIZE: u32 = 0x100000; |
| 33 | + |
| 34 | +impl ChipType for Esp32s2 { |
| 35 | + const CHIP_DETECT_MAGIC_VALUE: u32 = 0x000007c6; |
| 36 | + |
| 37 | + const SPI_REGISTERS: SpiRegisters = SpiRegisters { |
| 38 | + base: 0x3f402000, |
| 39 | + usr_offset: 0x18, |
| 40 | + usr1_offset: 0x1C, |
| 41 | + usr2_offset: 0x20, |
| 42 | + w0_offset: 0x58, |
| 43 | + mosi_length_offset: Some(0x24), |
| 44 | + miso_length_offset: Some(0x28), |
| 45 | + }; |
| 46 | + |
| 47 | + fn addr_is_flash(addr: u32) -> bool { |
| 48 | + (IROM_MAP_START..IROM_MAP_END).contains(&addr) |
| 49 | + || (DROM_MAP_START..DROM_MAP_END).contains(&addr) |
| 50 | + } |
| 51 | + |
| 52 | + fn get_flash_segments<'a>( |
| 53 | + image: &'a FirmwareImage, |
| 54 | + bootloader: Option<Vec<u8>>, |
| 55 | + partition_table: Option<PartitionTable>, |
| 56 | + ) -> Box<dyn Iterator<Item = Result<RomSegment<'a>, Error>> + 'a> { |
| 57 | + let bootloader = if let Some(bytes) = bootloader { |
| 58 | + bytes |
| 59 | + } else { |
| 60 | + let bytes = include_bytes!("../../bootloader/esp32s2-bootloader.bin"); |
| 61 | + bytes.to_vec() |
| 62 | + }; |
| 63 | + |
| 64 | + let partition_table = if let Some(table) = partition_table { |
| 65 | + table |
| 66 | + } else { |
| 67 | + PartitionTable::basic( |
| 68 | + NVS_ADDR, |
| 69 | + NVS_SIZE, |
| 70 | + PHY_INIT_DATA_ADDR, |
| 71 | + PHY_INIT_DATA_SIZE, |
| 72 | + APP_ADDR, |
| 73 | + APP_SIZE, |
| 74 | + ) |
| 75 | + }; |
| 76 | + let partition_table = partition_table.to_bytes(); |
| 77 | + |
| 78 | + fn get_data<'a>(image: &'a FirmwareImage) -> Result<RomSegment<'a>, Error> { |
| 79 | + let mut data = Vec::new(); |
| 80 | + |
| 81 | + let header = EspCommonHeader { |
| 82 | + magic: ESP_MAGIC, |
| 83 | + segment_count: 0, |
| 84 | + flash_mode: image.flash_mode as u8, |
| 85 | + flash_config: encode_flash_size(image.flash_size)? + image.flash_frequency as u8, |
| 86 | + entry: image.entry, |
| 87 | + }; |
| 88 | + data.write_all(bytes_of(&header))?; |
| 89 | + |
| 90 | + let extended_header = ExtendedHeader { |
| 91 | + wp_pin: WP_PIN_DISABLED, |
| 92 | + clk_q_drv: 0, |
| 93 | + d_cs_drv: 0, |
| 94 | + gd_wp_drv: 0, |
| 95 | + chip_id: 2, |
| 96 | + min_rev: 0, |
| 97 | + padding: [0; 8], |
| 98 | + append_digest: 1, |
| 99 | + }; |
| 100 | + data.write_all(bytes_of(&extended_header))?; |
| 101 | + |
| 102 | + let mut checksum = ESP_CHECKSUM_MAGIC; |
| 103 | + |
| 104 | + let _ = image.segments().collect::<Vec<_>>(); |
| 105 | + |
| 106 | + let mut flash_segments: Vec<_> = image.rom_segments(Chip::Esp32s2).collect(); |
| 107 | + flash_segments.sort(); |
| 108 | + let mut ram_segments: Vec<_> = image.ram_segments(Chip::Esp32s2).collect(); |
| 109 | + ram_segments.sort(); |
| 110 | + let mut ram_segments = ram_segments.into_iter(); |
| 111 | + |
| 112 | + let mut segment_count = 0; |
| 113 | + |
| 114 | + for segment in flash_segments { |
| 115 | + loop { |
| 116 | + let pad_len = get_segment_padding(data.len(), &segment); |
| 117 | + if pad_len > 0 { |
| 118 | + if pad_len > SEG_HEADER_LEN { |
| 119 | + if let Some(ram_segment) = ram_segments.next() { |
| 120 | + checksum = save_segment(&mut data, &ram_segment, checksum)?; |
| 121 | + segment_count += 1; |
| 122 | + continue; |
| 123 | + } |
| 124 | + } |
| 125 | + let pad_header = SegmentHeader { |
| 126 | + addr: 0, |
| 127 | + length: pad_len as u32, |
| 128 | + }; |
| 129 | + data.write_all(bytes_of(&pad_header))?; |
| 130 | + for _ in 0..pad_len { |
| 131 | + data.write_all(&[0])?; |
| 132 | + } |
| 133 | + segment_count += 1; |
| 134 | + } else { |
| 135 | + break; |
| 136 | + } |
| 137 | + } |
| 138 | + checksum = save_flash_segment(&mut data, &segment, checksum)?; |
| 139 | + segment_count += 1; |
| 140 | + } |
| 141 | + |
| 142 | + for segment in ram_segments { |
| 143 | + checksum = save_segment(&mut data, &segment, checksum)?; |
| 144 | + segment_count += 1; |
| 145 | + } |
| 146 | + |
| 147 | + let padding = 15 - (data.len() % 16); |
| 148 | + let padding = &[0u8; 16][0..padding as usize]; |
| 149 | + data.write_all(padding)?; |
| 150 | + |
| 151 | + data.write_all(&[checksum])?; |
| 152 | + |
| 153 | + // since we added some dummy segments, we need to patch the segment count |
| 154 | + data[1] = segment_count as u8; |
| 155 | + |
| 156 | + let mut hasher = Sha256::new(); |
| 157 | + hasher.update(&data); |
| 158 | + let hash = hasher.finalize(); |
| 159 | + data.write_all(&hash)?; |
| 160 | + |
| 161 | + Ok(RomSegment { |
| 162 | + addr: APP_ADDR, |
| 163 | + data: Cow::Owned(data), |
| 164 | + }) |
| 165 | + } |
| 166 | + |
| 167 | + Box::new( |
| 168 | + once(Ok(RomSegment { |
| 169 | + addr: BOOT_ADDR, |
| 170 | + data: Cow::Owned(bootloader), |
| 171 | + })) |
| 172 | + .chain(once(Ok(RomSegment { |
| 173 | + addr: PARTITION_ADDR, |
| 174 | + data: Cow::Owned(partition_table), |
| 175 | + }))) |
| 176 | + .chain(once(get_data(image))), |
| 177 | + ) |
| 178 | + } |
| 179 | +} |
0 commit comments