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