fix some ota stuff
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| use crate::{bail}; | ||||
| use crate::bail; | ||||
| use crate::config::{NetworkConfig, PlantControllerConfig}; | ||||
| use crate::hal::{get_next_slot, PLANT_COUNT, TIME_ACCESS}; | ||||
| use crate::log::{LogMessage, LOG_ACCESS}; | ||||
| @@ -9,7 +9,7 @@ use crate::fat_error::{ContextExt, FatError, FatResult}; | ||||
| use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem; | ||||
| use alloc::string::ToString; | ||||
| use alloc::sync::Arc; | ||||
| use alloc::{format, string::String, vec::Vec}; | ||||
| use alloc::{format, string::String, vec, vec::Vec}; | ||||
| use core::net::{IpAddr, Ipv4Addr, SocketAddr}; | ||||
| use core::str::FromStr; | ||||
| use core::sync::atomic::Ordering; | ||||
| @@ -21,8 +21,8 @@ use embassy_sync::mutex::{Mutex, MutexGuard}; | ||||
| use embassy_sync::once_lock::OnceLock; | ||||
| use embassy_time::{Duration, Timer}; | ||||
| use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash}; | ||||
| use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; | ||||
| use esp_bootloader_esp_idf::ota::OtaImageState::Valid; | ||||
| use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; | ||||
| use esp_bootloader_esp_idf::partitions::FlashRegion; | ||||
| use esp_hal::gpio::{Input, RtcPinWithResistors}; | ||||
| use esp_hal::rng::Rng; | ||||
| @@ -128,7 +128,7 @@ pub struct Esp<'a> { | ||||
|     pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>, | ||||
|  | ||||
|     pub ota: Ota<'static, FlashStorage>, | ||||
|     pub ota_next: &'static mut FlashRegion<'static, FlashStorage> | ||||
|     pub ota_next: &'static mut FlashRegion<'static, FlashStorage>, | ||||
| } | ||||
|  | ||||
| // SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core | ||||
| @@ -235,53 +235,32 @@ impl Esp<'_> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn write_ota( | ||||
|         &mut self, | ||||
|         offset: u32, | ||||
|         buf: &[u8], | ||||
|     ) -> Result<(), FatError> { | ||||
|     pub(crate) async fn write_ota(&mut self, offset: u32, buf: &[u8]) -> Result<(), FatError> { | ||||
|         if self.ota.current_ota_state() == Ok(OtaImageState::Invalid) { | ||||
|             bail!("Invalid OTA state, refusing ota write") | ||||
|         } | ||||
|         if self.ota.current_ota_state() == Ok(OtaImageState::Undefined) { | ||||
|             bail!("Invalid OTA state, refusing ota write") | ||||
|         } | ||||
|         let _ = check_erase(self.ota_next, offset, offset + 4096); | ||||
|         self.ota_next.erase(offset, offset + 4096)?; | ||||
|  | ||||
|         let mut read_back = [0_u8; 1024]; | ||||
|         let useful = &mut read_back[..buf.len()]; | ||||
|         let mut temp = vec![0; buf.len()]; | ||||
|         let read_back = temp.as_mut_slice(); | ||||
|         //change to nor flash, align writes! | ||||
|         self.ota_next.write(offset, buf)?; | ||||
|         self.ota_next.read(offset, useful)?; | ||||
|         if buf != useful { | ||||
|             info!("Expected {:?} but got {:?}", buf, useful); | ||||
|             bail!("Flash error, read back does not match write buffer at offset {:x}", offset) | ||||
|         self.ota_next.read(offset, read_back)?; | ||||
|         if buf != read_back { | ||||
|             info!("Expected {:?} but got {:?}", buf, read_back); | ||||
|             bail!( | ||||
|                 "Flash error, read back does not match write buffer at offset {:x}", | ||||
|                 offset | ||||
|             ) | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|         pub(crate) async fn ota_erase(&mut self, block_start: u32) -> FatResult<()> { | ||||
|             // Ensure 4K block size and alignment | ||||
|             if self.ota_next.capacity() % 4096 != 0 { | ||||
|                 bail!("Partition size is not a multiple of 4096") | ||||
|             } | ||||
|             if block_start % 4096 != 0 { | ||||
|                 bail!("ota_erase called with unaligned block_start: {:x}", block_start) | ||||
|             } | ||||
|             let capacity = self.ota_next.capacity() as u32; | ||||
|             if block_start >= capacity { | ||||
|                 bail!("ota_erase block_start out of range: {:x}", block_start) | ||||
|             } | ||||
|             let end = core::cmp::min(block_start + 4096, capacity); | ||||
|             // Check current erase state (will error if not erased); we ignore the result and erase anyway | ||||
|             let _ = check_erase(self.ota_next, block_start, end); | ||||
|             info!("erasing block {:x}-{:x}", block_start, end); | ||||
|             self.ota_next.erase(block_start, end)?; | ||||
|             Ok(()) | ||||
|         } | ||||
|  | ||||
|         pub(crate) async fn finalize_ota( | ||||
|         &mut self, | ||||
|     ) -> Result<(), FatError> { | ||||
|     pub(crate) async fn finalize_ota(&mut self) -> Result<(), FatError> { | ||||
|         if self.ota.current_ota_state() == Ok(OtaImageState::Invalid) { | ||||
|             bail!("Invalid OTA state, refusing ota write") | ||||
|         } | ||||
| @@ -289,20 +268,19 @@ impl Esp<'_> { | ||||
|             bail!("Invalid OTA state, refusing ota write") | ||||
|         } | ||||
|  | ||||
|  | ||||
|         let current_state = self.ota.current_ota_state()?; | ||||
|             info!("current state {:?}", current_state); | ||||
|         info!("current state {:?}", current_state); | ||||
|         let next_slot = get_next_slot(&mut self.ota)?; | ||||
|             info!("current slot {:?}", next_slot.next()); | ||||
|         info!("current slot {:?}", next_slot.next()); | ||||
|         if current_state == OtaImageState::PendingVerify { | ||||
|             info!("verifying ota image from pending"); | ||||
|             self.ota.set_current_ota_state(Valid)?; | ||||
|         } | ||||
|  | ||||
|         self.ota.set_current_slot(next_slot)?; | ||||
|             info!("switched slot"); | ||||
|         info!("switched slot"); | ||||
|         self.ota.set_current_ota_state(OtaImageState::New)?; | ||||
|             info!("switched state for new partition"); | ||||
|         info!("switched state for new partition"); | ||||
|         let state_new = self.ota.current_ota_state()?; | ||||
|         info!("state on new partition now {:?}", state_new); | ||||
|         //determine nextslot crc | ||||
| @@ -344,6 +322,7 @@ impl Esp<'_> { | ||||
|         if ntp_addrs.is_empty() { | ||||
|             bail!("Failed to resolve DNS"); | ||||
|         } | ||||
|         info!("NTP server: {:?}", ntp_addrs); | ||||
|  | ||||
|         let mut counter = 0; | ||||
|         loop { | ||||
| @@ -368,7 +347,6 @@ impl Esp<'_> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     pub(crate) async fn wifi_scan(&mut self) -> FatResult<Vec<AccessPointInfo>> { | ||||
|         info!("start wifi scan"); | ||||
|         let mut lock = self.controller.try_lock()?; | ||||
|   | ||||
| @@ -69,7 +69,9 @@ use eeprom24x::{Eeprom24x, SlaveAddr, Storage}; | ||||
| use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::blocking_mutex::CriticalSectionMutex; | ||||
| use esp_bootloader_esp_idf::partitions::{AppPartitionSubType, DataPartitionSubType, FlashRegion, PartitionEntry}; | ||||
| use esp_bootloader_esp_idf::partitions::{ | ||||
|     AppPartitionSubType, DataPartitionSubType, FlashRegion, PartitionEntry, | ||||
| }; | ||||
| use esp_hal::clock::CpuClock; | ||||
| use esp_hal::gpio::{Input, InputConfig, Pull}; | ||||
| use measurements::{Current, Voltage}; | ||||
| @@ -318,13 +320,12 @@ impl PlantHal { | ||||
|             ota_data.as_embedded_storage(storage_ota) | ||||
|         ); | ||||
|  | ||||
|         let mut ota = esp_bootloader_esp_idf::ota::Ota::new(ota_data)?; | ||||
|         let mut ota = Ota::new(ota_data)?; | ||||
|  | ||||
|         let state = ota.current_ota_state().unwrap_or_default(); | ||||
|         info!("Current OTA state: {:?}", state); | ||||
|  | ||||
|  | ||||
|         let next_slot = get_next_slot(&mut ota)?; | ||||
|         info!("Next OTA slot: {:?}", next_slot); | ||||
|         let ota_next = match next_slot { | ||||
|             Slot::None => { | ||||
|                 panic!("No OTA slot active?"); | ||||
| @@ -332,12 +333,13 @@ impl PlantHal { | ||||
|             Slot::Slot0 => pt | ||||
|                 .find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App( | ||||
|                     AppPartitionSubType::Ota0, | ||||
|                 ))?.context("Partition table invalid no ota0")?, | ||||
|                 ))? | ||||
|                 .context("Partition table invalid no ota0")?, | ||||
|             Slot::Slot1 => pt | ||||
|                 .find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App( | ||||
|                     AppPartitionSubType::Ota1, | ||||
|                 ))?.context("Partition table invalid no ota1")? | ||||
|                 , | ||||
|                 ))? | ||||
|                 .context("Partition table invalid no ota1")?, | ||||
|         }; | ||||
|  | ||||
|         let ota_next = mk_static!(PartitionEntry, ota_next); | ||||
| @@ -389,7 +391,7 @@ impl PlantHal { | ||||
|             boot_button, | ||||
|             wake_gpio1, | ||||
|             ota, | ||||
|             ota_next | ||||
|             ota_next, | ||||
|         }; | ||||
|  | ||||
|         //init,reset rtc memory depending on cause | ||||
| @@ -569,24 +571,16 @@ impl PlantHal { | ||||
|  | ||||
|         Ok(Mutex::new(hal)) | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| fn get_next_slot(ota: &mut Ota<FlashStorage>) -> Result<Slot, FatError> { | ||||
|     let next_slot = { | ||||
|         let state = ota.current_ota_state().unwrap_or_default(); | ||||
|         let current = ota.current_slot()?; | ||||
|         match state { | ||||
|             OtaImageState::New => { | ||||
|                 current.next() | ||||
|             } | ||||
|             OtaImageState::PendingVerify => { | ||||
|                 current.next() | ||||
|             } | ||||
|             OtaImageState::Valid => { | ||||
|                 current.next() | ||||
|             } | ||||
|             OtaImageState::New => current.next(), | ||||
|             OtaImageState::PendingVerify => current.next(), | ||||
|             OtaImageState::Valid => current.next(), | ||||
|             OtaImageState::Invalid => { | ||||
|                 //we actually booted other slot, than partition table assumes | ||||
|                 current | ||||
| @@ -623,4 +617,4 @@ pub async fn esp_set_time(time: DateTime<FixedOffset>) -> FatResult<()> { | ||||
|         .get_rtc_module() | ||||
|         .set_rtc_time(&time.to_utc()) | ||||
|         .await | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1096,6 +1096,7 @@ async fn get_version( | ||||
|     let hash = &env!("VERGEN_GIT_SHA")[0..8]; | ||||
|  | ||||
|     let board = board.board_hal.get_esp(); | ||||
|  | ||||
|     let ota_slot = board.get_current_ota_slot(); | ||||
|     let ota_state = board.get_ota_state(); | ||||
|     VersionInfo { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use crate::webserver::read_up_to_bytes_from_request; | ||||
| use crate::BOARD_ACCESS; | ||||
| use alloc::borrow::ToOwned; | ||||
| use alloc::format; | ||||
| @@ -118,9 +119,8 @@ where | ||||
|             let mut offset = 0_usize; | ||||
|             let mut chunk = 0; | ||||
|             loop { | ||||
|                 let mut buf = [0_u8; 4096]; | ||||
|                 let to_write = conn.read(&mut buf).await?; | ||||
|                 if to_write == 0 { | ||||
|                 let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?; | ||||
|                 if buf.len() == 0 { | ||||
|                     info!("file request for {} finished", filename); | ||||
|                     break; | ||||
|                 } else { | ||||
| @@ -129,10 +129,10 @@ where | ||||
|                     board | ||||
|                         .board_hal | ||||
|                         .get_esp() | ||||
|                         .write_file(filename.to_owned(), offset as u32, &buf[0..to_write]) | ||||
|                         .write_file(filename.to_owned(), offset as u32, &buf) | ||||
|                         .await?; | ||||
|                 } | ||||
|                 offset = offset + to_write; | ||||
|                 offset = offset + buf.len(); | ||||
|                 chunk = chunk + 1; | ||||
|             } | ||||
|             BOARD_ACCESS | ||||
|   | ||||
| @@ -5,8 +5,8 @@ mod file_manager; | ||||
| mod get_json; | ||||
| mod get_log; | ||||
| mod get_static; | ||||
| mod post_json; | ||||
| mod ota; | ||||
| mod post_json; | ||||
|  | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use crate::webserver::backup_manager::{backup_config, backup_info, get_backup_config}; | ||||
| @@ -17,6 +17,7 @@ use crate::webserver::get_json::{ | ||||
| }; | ||||
| use crate::webserver::get_log::get_log; | ||||
| use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index}; | ||||
| use crate::webserver::ota::ota_operations; | ||||
| use crate::webserver::post_json::{ | ||||
|     board_test, night_lamp_test, pump_test, set_config, wifi_scan, write_time, | ||||
| }; | ||||
| @@ -37,7 +38,6 @@ use embassy_net::Stack; | ||||
| use embassy_time::Instant; | ||||
| use embedded_io_async::{Read, Write}; | ||||
| use log::{error, info}; | ||||
| use crate::webserver::ota::ota_operations; | ||||
| // fn ota( | ||||
| //     request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| @@ -109,11 +109,10 @@ impl Handler for HTTPRequestRouter { | ||||
|         let status = if path.starts_with(prefix) { | ||||
|             file_operations(conn, method, &path, &prefix).await? | ||||
|         } else if path == "/ota" { | ||||
|             ota_operations(conn,method).await.map_err(|e| { | ||||
|             ota_operations(conn, method).await.map_err(|e| { | ||||
|                 error!("Error handling ota: {}", e); | ||||
|                     e | ||||
|                 } | ||||
|             )? | ||||
|                 e | ||||
|             })? | ||||
|         } else { | ||||
|             match method { | ||||
|                 Method::Get => match path { | ||||
| @@ -202,12 +201,18 @@ where | ||||
|     let mut data_store = Vec::new(); | ||||
|     let mut total_read = 0; | ||||
|     loop { | ||||
|         let left = max_read - total_read; | ||||
|         let mut buf = [0_u8; 64]; | ||||
|         let read = request.read(&mut buf).await?; | ||||
|         let s_buf = if buf.len() <= left { | ||||
|             &mut buf | ||||
|         } else { | ||||
|             &mut buf[0..left] | ||||
|         }; | ||||
|         let read = request.read(s_buf).await?; | ||||
|         if read == 0 { | ||||
|             break; | ||||
|         } | ||||
|         let actual_data = &buf[0..read]; | ||||
|         let actual_data = &s_buf[0..read]; | ||||
|         total_read += read; | ||||
|         if total_read > max_read { | ||||
|             bail!("Request too large {total_read} > {max_read}"); | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| use crate::fat_error::FatError; | ||||
| use crate::webserver::read_up_to_bytes_from_request; | ||||
| use crate::BOARD_ACCESS; | ||||
| use edge_http::io::server::Connection; | ||||
| use edge_http::Method; | ||||
| use embedded_io_async::{Read, Write}; | ||||
| use log::info; | ||||
| use crate::BOARD_ACCESS; | ||||
| use crate::fat_error::FatError; | ||||
|  | ||||
| pub(crate) async fn ota_operations<T, const N: usize>( | ||||
|     conn: &mut Connection<'_, T, { N }>, | ||||
|     method: Method | ||||
|     method: Method, | ||||
| ) -> Result<Option<u32>, FatError> | ||||
| where | ||||
|     T: Read + Write, | ||||
| @@ -23,7 +24,7 @@ where | ||||
|                     ("Access-Control-Allow-Methods", "*"), | ||||
|                 ], | ||||
|             ) | ||||
|                 .await?; | ||||
|             .await?; | ||||
|             Some(200) | ||||
|         } | ||||
|         Method::Post => { | ||||
| @@ -33,9 +34,8 @@ where | ||||
|             // Erase only a single 4K block right before writing into it. | ||||
|             // The first block will be erased when offset == 0 below. | ||||
|             loop { | ||||
|                 let mut buf = [0_u8; 1024]; | ||||
|                 let to_write = conn.read(&mut buf).await?; | ||||
|                 if to_write == 0 { | ||||
|                 let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?; | ||||
|                 if buf.len() == 0 { | ||||
|                     info!("file request for  ota finished"); | ||||
|                     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|                     board.board_hal.get_esp().finalize_ota().await?; | ||||
| @@ -44,17 +44,14 @@ where | ||||
|                     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|                     board.board_hal.progress(chunk as u32).await; | ||||
|                     // Erase next block if we are at a 4K boundary (including the first block at offset 0) | ||||
|                     if offset % 4096 >= offset+to_write % 4096 { | ||||
|                         info!("erasing block {} during write between {} with size {}", offset / 4096, offset, to_write); | ||||
|                         board.board_hal.get_esp().ota_erase(offset as u32).await?; | ||||
|                     } | ||||
|                     info!("erasing and writing block 0x{offset:x}"); | ||||
|                     board | ||||
|                         .board_hal | ||||
|                         .get_esp() | ||||
|                         .write_ota(offset as u32, &buf[0..to_write]) | ||||
|                         .write_ota(offset as u32, &*buf) | ||||
|                         .await?; | ||||
|                 } | ||||
|                 offset = offset + to_write; | ||||
|                 offset = offset + buf.len(); | ||||
|                 chunk = chunk + 1; | ||||
|             } | ||||
|             BOARD_ACCESS | ||||
| @@ -74,7 +71,7 @@ where | ||||
|                     ("Access-Control-Allow-Methods", "*"), | ||||
|                 ], | ||||
|             ) | ||||
|                 .await?; | ||||
|             .await?; | ||||
|             Some(200) | ||||
|         } | ||||
|         _ => None, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user