diff --git a/src/main.rs b/src/main.rs index adcc39c..dfe8c67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -260,62 +260,447 @@ fn is_inited() -> bool { pub unsafe extern "C" fn Init_impl(_adr: u32, _clk: u32, _fnc: u32) -> i32 { dprintln!("INIT"); - #[cfg(feature = "esp32c61")] - { - // ROM data table addresses - // TODO: use for other chips, too (if they use the same format) - let _data_table_start = 0x4003700c; - let _bss_table_start = 0x400371e0; - let _etext = 0x40037310; - - unpack(_data_table_start, _bss_table_start); - zero(_bss_table_start, _etext); - - fn unpack(first: u32, last: u32) { - // Data is stored in three-byte sections: - // - Data section start address in RAM - // - Data section end address in RAM - // - Source address in ROM - let mut current = first; - while current < last { - let dst_start = unsafe { *((current) as *const u32) }; // RAM - let dst_end = unsafe { *((current + 4) as *const u32) }; // RAM - let src = unsafe { *((current + 8) as *const u32) }; // ROM - copy(dst_start, dst_end, src); - current += 12; + init_rom_data(); + + set_max_cpu_freq(&mut state().saved_cpu_state); + + if flash::attach() == 0 { + state().decompressor = Decompressor::new(); + state().inited = INITED_MAGIC; + 0 + } else { + 1 + } +} + +fn init_rom_data() { + struct DataTableDataEntry { + dst_start: u32, // RAM + dst_end: u32, // RAM + src: u32, // ROM + } + + const CONTAINS_CORE_SPEC_FIELD: bool = cfg!(any( + feature = "esp32", + feature = "esp32s2", + feature = "esp32s3", + feature = "esp32c2", + feature = "esp32c3", + )); + + impl DataTableDataEntry { + // On chips where the table entry is 4 words, if the 4th word is 0, data + // is only unpacked by the Pro core. If the 4th word is 1, data is unpacked + // by both cores. This distinction is unnecessary for the flash loader. + // We just need to jump over the 4th word. + const ENTRY_SIZE: u32 = 12 + CONTAINS_CORE_SPEC_FIELD as u32 * 4; + + fn read(from: u32) -> Self { + let dst_start = unsafe { *(from as *const u32) }; // RAM + let dst_end = unsafe { *((from + 4) as *const u32) }; // RAM + let src = unsafe { *((from + 8) as *const u32) }; // ROM + + DataTableDataEntry { + dst_start, + dst_end, + src, } } - fn copy(dst_start: u32, dst_end: u32, src: u32) { - let mut addr = src; - let mut dst = dst_start; - while dst < dst_end { + fn init(&self) { + let mut addr = self.src; + let mut dst = self.dst_start; + while dst < self.dst_end { unsafe { *(dst as *mut u32) = *(addr as *const u32) }; addr += 4; dst += 4; } } + } + + struct BssTableDataEntry { + start: u32, // RAM + end: u32, // RAM + } + + impl BssTableDataEntry { + // On chips where the table entry is 3 words, if the 3rd word is 0, data + // is only zeroed by the Pro core. If the 3rd word is 1, data is zeroed + // by both cores. This distinction is unnecessary for the flash loader. + // We just need to jump over the 3rd word. + const ENTRY_SIZE: u32 = 8 + CONTAINS_CORE_SPEC_FIELD as u32 * 4; + + fn read(from: u32) -> Self { + let start = unsafe { *(from as *const u32) }; // RAM + let end = unsafe { *((from + 4) as *const u32) }; // RAM + + BssTableDataEntry { start, end } + } - fn zero(start: u32, end: u32) { - let mut addr = start; - while addr < end { + fn init(&self) { + let mut addr = self.start; + while addr < self.end { unsafe { *(addr as *mut u32) = 0 }; addr += 4; } } } - set_max_cpu_freq(&mut state().saved_cpu_state); + type RomDataTables = &'static [(u32, RomDataTable)]; - if flash::attach() == 0 { - state().decompressor = Decompressor::new(); - state().inited = INITED_MAGIC; - 0 + struct RomDataTable { + data_start: u32, + data_end: u32, + bss_start: u32, + bss_end: u32, + } + + impl RomDataTable { + fn init(&self) { + unpack(self.data_start, self.data_end); + zero(self.bss_start, self.bss_end); + } + } + + fn unpack(first: u32, end: u32) { + // Data is stored in three/four-word sections: + // - Data section start address in RAM + // - Data section end address in RAM + // - Source address in ROM + // - Whether to unpack code on the second core + let mut current = first; + while current < end { + DataTableDataEntry::read(current).init(); + current += DataTableDataEntry::ENTRY_SIZE; + } + } + + fn zero(first: u32, end: u32) { + // Data is stored in three/four-word sections: + // - Section start address in RAM + // - Section end address in RAM + // - Whether to zero code on the second core + let mut current = first; + while current < end { + BssTableDataEntry::read(current).init(); + current += BssTableDataEntry::ENTRY_SIZE; + } + } + + let rom_data_tables: RomDataTables = if cfg!(feature = "esp32") { + &[( + 0, + RomDataTable { + data_start: 0x4000D4F8, + data_end: 0x4000D5C8, + bss_start: 0x4000D5D0, + bss_end: 0x4000D66C, + }, + )] + } else if cfg!(feature = "esp32c2") { + &[ + ( + 0, + RomDataTable { + data_start: 0x40082174, + data_end: 0x400823F4, + bss_start: 0x40082404, + bss_end: 0x400825E4, + }, + ), + ( + 400, + RomDataTable { + data_start: 0x40081EEC, + data_end: 0x4008219C, + bss_start: 0x400821AC, + bss_end: 0x400823B0, + }, + ), + ] + } else if cfg!(feature = "esp32c3") { + &[ + ( + 0, + RomDataTable { + data_start: 0x40058898, + data_end: 0x40058A88, + bss_start: 0x40058A98, + bss_end: 0x40058C0C, + }, + ), + ( + 3, + RomDataTable { + data_start: 0x40059200, + data_end: 0x40059400, + bss_start: 0x40059410, + bss_end: 0x40059590, + }, + ), + ( + 101, + RomDataTable { + data_start: 0x40059620, + data_end: 0x40059830, + bss_start: 0x40059840, + bss_end: 0x400599CC, + }, + ), + ] + } else if cfg!(feature = "esp32c5") { + &[ + ( + 0, + RomDataTable { + data_start: 0x400478A8, + data_end: 0x40047A7C, + bss_start: 0x40047A7C, + bss_end: 0x40047BAC, + }, + ), + ( + 100, + RomDataTable { + data_start: 0x4003B154, + data_end: 0x4003B340, + bss_start: 0x4003B340, + bss_end: 0x4003B480, + }, + ), + ] + } else if cfg!(feature = "esp32c6") { + &[( + 0, + RomDataTable { + data_start: 0x40041EA8, + data_end: 0x40042064, + bss_start: 0x40042064, + bss_end: 0x40042184, + }, + )] + } else if cfg!(feature = "esp32c61") { + &[ + ( + 0, + RomDataTable { + data_start: 0x4003700C, + data_end: 0x400371E0, + bss_start: 0x400371E0, + bss_end: 0x40037310, + }, + ), + ( + 100, + RomDataTable { + data_start: 0x40038A4C, + data_end: 0x40038C38, + bss_start: 0x40038C38, + bss_end: 0x40038D78, + }, + ), + ] + } else if cfg!(feature = "esp32h2") { + &[( + 0, + RomDataTable { + data_start: 0x4001A18C, + data_end: 0x4001A318, + bss_start: 0x4001A318, + bss_end: 0x4001A418, + }, + )] + } else if cfg!(feature = "esp32p4") { + &[ + ( + 100, + RomDataTable { + data_start: 0x4FC1BEC8, + data_end: 0x4FC1C054, + bss_start: 0x4FC1C054, + bss_end: 0x4FC1C154, + }, + ), + ( + 300, + RomDataTable { + data_start: 0x4FC1C860, + data_end: 0x4FC1C9EC, + bss_start: 0x4FC1C9EC, + bss_end: 0x4FC1CAEC, + }, + ), + ] + } else if cfg!(feature = "esp32s2") { + &[( + 0, + RomDataTable { + data_start: 0x4001BD64, + data_end: 0x4001BE34, + bss_start: 0x4001BE34, + bss_end: 0x4001BED0, + }, + )] + } else if cfg!(feature = "esp32s3") { + &[( + 0, + RomDataTable { + data_start: 0x40056700, + data_end: 0x40056800, + bss_start: 0x40056810, + bss_end: 0x400568D0, + }, + )] } else { - 1 + // Will not initialize memory + &[] + }; + + let rev = read_chip_revision(); + if let Some((_min_rev, table)) = rom_data_tables + .iter() + .filter(|&(min_rev, _table)| *min_rev <= rev) + .last() + { + table.init(); } } +fn read_chip_revision() -> u32 { + fn read_field() -> u8 { + struct EfuseInfo { + block0: u32, + /// In words + block_sizes: &'static [u32], + } + + let info = if cfg!(feature = "esp32") { + EfuseInfo { + block0: 0x3FF5_A000, + block_sizes: &[7, 8, 8, 8], + } + } else if cfg!(feature = "esp32s2") { + EfuseInfo { + block0: 0x3F41_A000 + 0x2C, + block_sizes: &[6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8], + } + } else if cfg!(feature = "esp32s3") { + EfuseInfo { + block0: 0x6000_7000 + 0x2C, + block_sizes: &[6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8], + } + } else if cfg!(feature = "esp32c2") { + EfuseInfo { + block0: 0x6000_8800 + 0x2C, + block_sizes: &[8, 12, 32, 32], + } + } else if cfg!(feature = "esp32c3") { + EfuseInfo { + block0: 0x6000_8800 + 0x2C, + block_sizes: &[6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8], + } + } else if cfg!(any(feature = "esp32c6", feature = "esp32h2")) { + EfuseInfo { + block0: 0x600B_0800 + 0x2C, + block_sizes: &[6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8], + } + } else if cfg!(any(feature = "esp32c5", feature = "esp32c61")) { + EfuseInfo { + block0: 0x600B_4800 + 0x2C, + block_sizes: &[6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8], + } + } else if cfg!(any(feature = "esp32p4")) { + EfuseInfo { + block0: 0x5012_D000 + 0x2C, + block_sizes: &[6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8], + } + } else { + todo!() + }; + + let word_offset = BIT_START / 32; + let bit_offset = BIT_START % 32; + + let mask = (1 << BIT_COUNT) - 1; + let bit_mask = mask << bit_offset; + + let block_offset = info.block_sizes.iter().take(BLOCK).sum::(); + let address = info.block0 + block_offset * 4 + word_offset * 4; + let value = + (unsafe { core::ptr::read_volatile(address as *const u32) } & bit_mask) >> bit_offset; + + value as u8 + } + + fn major_chip_version() -> u8 { + if cfg!(feature = "esp32") { + let eco_bit0 = read_field::<0, 111, 1>() as u32; + let eco_bit1 = read_field::<0, 180, 1>() as u32; + let apb_ctrl_date = (0x3ff6_6000 + 0x7c) as *const u32; + let eco_bit2 = (unsafe { *apb_ctrl_date } & 0x80000000) >> 31; + + match (eco_bit2 << 2) | (eco_bit1 << 1) | eco_bit0 { + 1 => 1, + 3 => 2, + 7 => 3, + _ => 0, + } + } else if cfg!(feature = "esp32s2") { + read_field::<1, 114, 2>() + } else if cfg!(feature = "esp32c2") { + read_field::<2, 52, 2>() + } else if cfg!(any(feature = "esp32c3", feature = "esp32s3")) { + read_field::<1, 184, 2>() + } else if cfg!(feature = "esp32c5") { + read_field::<1, 68, 2>() + } else if cfg!(feature = "esp32c6") { + read_field::<1, 118, 2>() + } else if cfg!(feature = "esp32c61") { + read_field::<1, 68, 2>() + } else if cfg!(feature = "esp32h2") { + read_field::<1, 117, 2>() + } else if cfg!(feature = "esp32p4") { + let lo = read_field::<1, 68, 2>(); + let hi = read_field::<1, 87, 1>(); + hi << 2 | lo + } else { + todo!() + } + } + + fn minor_chip_version() -> u8 { + if cfg!(feature = "esp32") { + read_field::<0, 184, 2>() + } else if cfg!(feature = "esp32s2") { + let lo = read_field::<1, 132, 3>(); + let hi = read_field::<1, 116, 1>(); + + hi << 3 | lo + } else if cfg!(feature = "esp32c2") { + read_field::<2, 48, 4>() + } else if cfg!(any(feature = "esp32c3", feature = "esp32s3")) { + let lo = read_field::<1, 114, 3>(); + let hi = read_field::<1, 183, 1>(); + + hi << 3 | lo + } else if cfg!(feature = "esp32c5") { + read_field::<1, 64, 4>() + } else if cfg!(feature = "esp32c6") { + read_field::<1, 114, 4>() + } else if cfg!(feature = "esp32c61") { + read_field::<1, 64, 4>() + } else if cfg!(feature = "esp32h2") { + read_field::<1, 114, 3>() + } else if cfg!(feature = "esp32p4") { + read_field::<1, 64, 4>() + } else { + todo!() + } + } + + major_chip_version() as u32 * 100 + minor_chip_version() as u32 +} + /// Erase the sector at the given address in flash /// /// Returns 0 on success, 1 on failure.