eculocate

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 14045825f3601924ae7f2fef2624eed08b508d51
parent 3a0dbe5c907677ca5e8bda6c46ab4a0665fff985
Author: Daniel Barlow <dan@telent.net>
Date:   Fri, 23 Jan 2026 23:57:25 +0000

extract module for ota flashing

Diffstat:
Mecu-esp32/src/main.rs | 15++++++++++-----
Aecu-esp32/src/ota.rs | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mecu-esp32/src/wifi.rs | 123++++---------------------------------------------------------------------------
3 files changed, 172 insertions(+), 122 deletions(-)

diff --git a/ecu-esp32/src/main.rs b/ecu-esp32/src/main.rs @@ -13,6 +13,7 @@ pub mod ecu; pub mod wifi; pub mod mdns; pub mod streamer; +pub mod ota; use esp_hal::{ timer::timg::TimerGroup, @@ -26,9 +27,10 @@ use embassy_time::Timer; use log::{info}; -use crate::ecu::{ - Ecu -}; +use crate::ecu::Ecu; +use crate::ota::Flasher; + + extern crate alloc; @@ -65,11 +67,14 @@ async fn main(s : Spawner) { peripherals.UART0 ); + + let flasher = Flasher::new(peripherals.FLASH, peripherals.SHA); + join( crate::wifi::start_wifi(&s, + flasher, peripherals.WIFI, - peripherals.FLASH, - peripherals.SHA, &mut ecu), + &mut ecu), async { loop { Timer::after_millis(1000).await; diff --git a/ecu-esp32/src/ota.rs b/ecu-esp32/src/ota.rs @@ -0,0 +1,156 @@ +use nb::block; + +use embassy_time::{Timer}; + +use ed25519_dalek::{VerifyingKey, Verifier, Signature}; + +use esp_hal_ota::Ota; +use esp_storage::FlashStorage; + +use esp_hal::sha; + +use log::{warn, info, error}; + +const ERASE_BLOCK_BYTES : usize = 4096; +const ED25519_SIGNATURE_BYTES : usize = 64; +const SHA256_BYTES : usize = 32; + +async fn read_all(socket: &mut embassy_net::tcp::TcpSocket<'_>, + buf: &mut [u8], + len: usize) -> usize { + let mut n = 0; + while n < len { + match socket.read(&mut buf[n..len]).await { + Ok(0) => { + warn!("read EOF"); + break; + }, + Ok(count) => { + n = n + count; + } + Err(e) => { + warn!("read err {:?}", e); + return 0; // ew + } + } + } + n +} + +pub struct Flasher<'a> { + ota : Ota<FlashStorage<'a>>, + sha_engine : sha::Sha<'a> +} + +impl Flasher<'_> { + pub fn new(flash_peripheral: esp_hal::peripherals::FLASH<'static>, + sha_peripheral: esp_hal::peripherals::SHA<'static>) -> Self { + let flash = FlashStorage::new(flash_peripheral); + let sha_engine = sha::Sha::new(sha_peripheral); + let ota = Ota::new(flash).unwrap(); + + Self { ota, sha_engine } + } + + pub async fn write(&mut self, sock: &mut embassy_net::tcp::TcpSocket<'_>) { + write_ota(&mut self.ota, &mut self.sha_engine, sock).await + } +} + + +async fn write_ota(ota : &mut Ota<FlashStorage<'_>>, + sha_engine : &mut sha::Sha<'_>, + sock: &mut embassy_net::tcp::TcpSocket<'_>) { + let mut buf = [0u8; SHA256_BYTES + ERASE_BLOCK_BYTES]; + let mut buf_offset = 0; + let mut signed; + let mut signature = [0u8; ED25519_SIGNATURE_BYTES ]; + let mut next_block_hash = [0u8; SHA256_BYTES]; + + // ed25519 signature of first block + // first block is sha256 of second block, plus length as u32 + // second+ block is sha256 of successor block, plus data + + let mut block0 = [0u8; SHA256_BYTES + 4]; + + read_all(sock, &mut signature, ED25519_SIGNATURE_BYTES).await; + read_all(sock, &mut block0, SHA256_BYTES + 4).await; + + let verifying_key_bytes = include_bytes!("../../pubkey.bin"); + let verifying_key: VerifyingKey = VerifyingKey::from_bytes(verifying_key_bytes).expect("verify"); + signed = verifying_key.verify(&mut block0, + &Signature::try_from(&signature).expect("sig")).is_ok(); + + info!("initial sig checks out? {:?}", signed); + + let (sha, len_bytes) = block0.split_at(SHA256_BYTES); + let len = u32::from_be_bytes(len_bytes.try_into().expect("4 bytes")); + next_block_hash.clone_from_slice(sha); + + info!("ota length {}", len); + if let Err(e) = ota.ota_begin(len, 0) { + error!("ota_begin failed {:?}", e); + return; + }; + + // total payload to expect is image size plus per-block hashes + let mut remaining = len + (len/(ERASE_BLOCK_BYTES as u32)+ 1) * (SHA256_BYTES as u32); + + while remaining > 0 { + // info!("remaining {}, offset {}", remaining, buf_offset); + match sock.read(&mut buf[buf_offset..]).await { + Ok(0) => { + info!("unexpected eof on read"); + break; + } + Ok(chunksize) => { + buf_offset += chunksize; + remaining -= chunksize as u32; + } + Err(x) => { + warn!("error reading ota image: {:?}", x); + break; + } + } + + if remaining <= 0 || buf_offset == SHA256_BYTES + ERASE_BLOCK_BYTES { + let mut actual_hash = [0u8; SHA256_BYTES]; + let mut hasher = sha_engine.start::<sha::Sha256>(); + let mut p = &buf[..buf_offset]; + while !p.is_empty() { + match block!(hasher.update(p)) { + Ok(remaining) => { p = remaining; } + _ => () + } + } + block!(hasher.finish(&mut actual_hash)).expect("hasher.finish expected infallible"); + + signed = signed && next_block_hash == actual_hash; + + if signed { + if let Err(_) = ota.ota_write_chunk(&buf[ED25519_SIGNATURE_BYTES..buf_offset]) { + break; + } + } else { + // accept the rest of the update, just don't flash it. + warn!("signature invalid with {remaining} left"); + } + // + buf_offset = 0; + next_block_hash.copy_from_slice(&buf[0..SHA256_BYTES]); + } + } + if remaining == 0 { + info!("wrote all bytes"); + info!("write {:?}", sock.write(b"BYE\n").await); + let _ = sock.flush().await; + sock.close(); + if signed && !ota.ota_flush(false, false).is_ok() { + error!("ota failed"); + } + Timer::after_millis(1000).await; + esp_hal::system::software_reset(); + } else { + warn!("stream ended early or flash failed with {} bytes left", remaining); + } +} diff --git a/ecu-esp32/src/wifi.rs b/ecu-esp32/src/wifi.rs @@ -6,9 +6,7 @@ use alloc::vec::Vec; use alloc::string::String; use alloc::format; -use nb::block; -use ed25519_dalek::{VerifyingKey, Verifier, Signature}; use embassy_executor::Spawner; use embassy_net::{ @@ -36,14 +34,10 @@ use esp_radio::{ WifiStaState, }, }; -use esp_hal_ota::Ota; -use esp_hal::sha; - -use esp_storage::FlashStorage; use embassy_net::Stack; -use log::{warn, info, error}; +use log::{warn, info}; use crate::mdns; @@ -95,9 +89,8 @@ fn dns_records(my_ip_address: [u8;4], serial: Option<usize>) -> Vec<mdns::Resour } pub async fn start_wifi(spawner: &Spawner, + flasher: crate::ota::Flasher<'_>, wifi_peripheral: esp_hal::peripherals::WIFI<'static>, - flash_peripheral: esp_hal::peripherals::FLASH<'static>, - sha_peripheral: esp_hal::peripherals::SHA<'static>, ecu: &mut crate::ecu::Ecu<'_>) { @@ -149,7 +142,7 @@ pub async fn start_wifi(spawner: &Spawner, join( join( crate::streamer::loop_udp_thing(stack, ecu), - run_tcp_handler(stack, sha_peripheral, flash_peripheral), + run_tcp_handler(stack, flasher) ), async { loop { @@ -242,122 +235,18 @@ async fn read_all(socket: &mut embassy_net::tcp::TcpSocket<'_>, n } -const ERASE_BLOCK_BYTES : usize = 4096; -const ED25519_SIGNATURE_BYTES : usize = 64; -const SHA256_BYTES : usize = 32; - -async fn write_ota(ota : &mut Ota<FlashStorage<'_>>, - sha_engine : &mut sha::Sha<'_>, - sock: &mut embassy_net::tcp::TcpSocket<'_>) { - let mut buf = [0u8; SHA256_BYTES + ERASE_BLOCK_BYTES]; - let mut buf_offset = 0; - let mut signed; - let mut signature = [0u8; ED25519_SIGNATURE_BYTES ]; - let mut next_block_hash = [0u8; SHA256_BYTES]; - - // ed25519 signature of first block - // first block is sha256 of second block, plus length as u32 - // second+ block is sha256 of successor block, plus data - - let mut block0 = [0u8; SHA256_BYTES + 4]; - - read_all(sock, &mut signature, ED25519_SIGNATURE_BYTES).await; - read_all(sock, &mut block0, SHA256_BYTES + 4).await; - - let verifying_key_bytes = include_bytes!("../../pubkey.bin"); - let verifying_key: VerifyingKey = VerifyingKey::from_bytes(verifying_key_bytes).expect("verify"); - signed = verifying_key.verify(&mut block0, - &Signature::try_from(&signature).expect("sig")).is_ok(); - - info!("initial sig checks out? {:?}", signed); - - let (sha, len_bytes) = block0.split_at(SHA256_BYTES); - let len = u32::from_be_bytes(len_bytes.try_into().expect("4 bytes")); - next_block_hash.clone_from_slice(sha); - - info!("ota length {}", len); - if let Err(e) = ota.ota_begin(len, 0) { - error!("ota_begin failed {:?}", e); - return; - }; - - // total payload to expect is image size plus per-block hashes - let mut remaining = len + (len/(ERASE_BLOCK_BYTES as u32)+ 1) * (SHA256_BYTES as u32); - - while remaining > 0 { - // info!("remaining {}, offset {}", remaining, buf_offset); - match sock.read(&mut buf[buf_offset..]).await { - Ok(0) => { - info!("unexpected eof on read"); - break; - } - Ok(chunksize) => { - buf_offset += chunksize; - remaining -= chunksize as u32; - } - Err(x) => { - warn!("error reading ota image: {:?}", x); - break; - } - } - - if remaining <= 0 || buf_offset == SHA256_BYTES + ERASE_BLOCK_BYTES { - let mut actual_hash = [0u8; SHA256_BYTES]; - let mut hasher = sha_engine.start::<sha::Sha256>(); - let mut p = &buf[..buf_offset]; - while !p.is_empty() { - match block!(hasher.update(p)) { - Ok(remaining) => { p = remaining; } - _ => () - } - } - block!(hasher.finish(&mut actual_hash)); - - signed = signed && next_block_hash == actual_hash; - - if signed { - if let Err(_) = ota.ota_write_chunk(&buf[ED25519_SIGNATURE_BYTES..buf_offset]) { - break; - } - } else { - // accept the rest of the update, just don't flash it. - warn!("signature invalid with {remaining} left"); - } - // - buf_offset = 0; - next_block_hash.copy_from_slice(&buf[0..SHA256_BYTES]); - } - } - if remaining == 0 { - info!("wrote all bytes"); - info!("write {:?}", sock.write(b"BYE\n").await); - let _ = sock.flush().await; - sock.close(); - if signed && !ota.ota_flush(false, false).is_ok() { - error!("ota failed"); - } - Timer::after_millis(1000).await; - esp_hal::system::software_reset(); - } else { - warn!("stream ended early or flash failed with {} bytes left", remaining); - } -} fn register_session_key(key : &[u8]) { info!("got session key{:?}", key); } + async fn run_tcp_handler(stack : Stack<'_>, - sha_peripheral: esp_hal::peripherals::SHA<'static>, - flash_peripheral: esp_hal::peripherals::FLASH<'static>) { + mut flasher : crate::ota::Flasher<'_>) { let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; let mut buf = [0u8; 96]; - let flash = FlashStorage::new(flash_peripheral); - let mut sha_engine = sha::Sha::new(sha_peripheral); - let mut ota = Ota::new(flash).unwrap(); - loop { let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); @@ -377,7 +266,7 @@ async fn run_tcp_handler(stack : Stack<'_>, match &buf[0..4] { b"ROM0" => { info!("OTA request"); - write_ota(&mut ota, &mut sha_engine, &mut socket).await; + flasher.write(&mut socket).await; }, b"KEY0" => { info!("sessionkey");