bootload.rs (3250B)
1 use std::path::Path; 2 3 use crc_fast::{CrcAlgorithm::Crc32IsoHdlc, checksum}; 4 use indicatif::ProgressStyle; 5 use tokio::{ 6 fs::File, 7 io::{AsyncReadExt, AsyncWriteExt}, 8 }; 9 use tokio_serial::SerialStream; 10 use tracing::{debug, instrument, trace}; 11 use tracing_indicatif::span_ext::IndicatifSpanExt; 12 13 #[repr(u32)] 14 #[derive(Debug)] 15 pub enum Message { 16 GetProgInfo = 0x11112222, 17 PutProgInfo = 0x33334444, 18 19 GetCode = 0x55556666, 20 PutCode = 0x77778888, 21 22 BootSuccess = 0x9999AAAA, 23 BadCodeAddr = 0xDEADBEEF, 24 BadCodeChecksum = 0xFEEDFACE, 25 } 26 27 #[derive(thiserror::Error, Debug)] 28 pub enum Error { 29 #[error("invalid boot address")] 30 BadAddress, 31 #[error("checksum did not match")] 32 BadChecksum, 33 #[error("io: {0}")] 34 Io(#[from] std::io::Error), 35 } 36 37 #[instrument(skip_all, fields(indicatif.pb_show))] 38 pub async fn boot(port: &mut SerialStream, bin: &Path, base_address: u32) -> Result<(), Error> { 39 let current = tracing::Span::current(); 40 41 let mut code = Vec::new(); 42 43 File::open(bin).await?.read_to_end(&mut code).await?; 44 45 let checksum = checksum(Crc32IsoHdlc, &code) as u32; 46 47 debug!("boot parameters: crc32={checksum:x}, file={bin:?}, base_address=0x{base_address:X}"); 48 49 current.pb_set_style(&ProgressStyle::default_spinner()); 50 current.pb_set_message("waiting for GetProgInfo"); 51 52 loop { 53 match port.read_u32_le().await? { 54 v if v == (Message::GetProgInfo as u32) => break, 55 v => { 56 trace!("got invalid GetProgInfo: {v:X}"); 57 // Read 1 to offset 58 port.read_u8().await?; 59 continue; 60 } 61 } 62 } 63 64 port.write_u32_le(Message::PutProgInfo as u32).await?; 65 port.write_u32_le(base_address).await?; 66 port.write_u32_le(code.len() as u32).await?; 67 port.write_u32_le(checksum).await?; 68 port.flush().await?; 69 70 loop { 71 match port.read_u32_le().await? { 72 v if v == (Message::GetCode as u32) => break, 73 v if v == (Message::BadCodeAddr as u32) => return Err(Error::BadAddress), 74 v => { 75 trace!("got invalid GetCode: {v:X}"); 76 continue; 77 } 78 } 79 } 80 81 let checksum_ret = port.read_u32_le().await?; 82 83 if checksum != checksum_ret { 84 return Err(Error::BadChecksum); 85 } 86 87 port.write_u32_le(Message::PutCode as u32).await?; 88 89 current.pb_set_style( 90 &ProgressStyle::with_template( 91 "[{elapsed}] [{wide_bar}] {binary_bytes}/{binary_total_bytes} (eta: {eta})", 92 ) 93 .unwrap() 94 .progress_chars("=> "), 95 ); 96 current.pb_set_length(code.len() as u64); 97 98 for b in code.iter() { 99 port.write_u8(*b).await?; 100 current.pb_inc(1); 101 } 102 port.flush().await?; 103 104 current.pb_set_style(&ProgressStyle::default_spinner()); 105 current.pb_set_message("waiting for BootSuccess"); 106 107 loop { 108 match port.read_u32_le().await? { 109 v if v == (Message::BootSuccess as u32) => break, 110 v if v == (Message::BadCodeChecksum as u32) => return Err(Error::BadAddress), 111 v => { 112 debug!("got invalid BootSuccess: {v}"); 113 continue; 114 } 115 } 116 } 117 118 Ok(()) 119 }