diff --git a/build.sh b/build.sh index efc7260..cc37fbb 100755 --- a/build.sh +++ b/build.sh @@ -89,11 +89,10 @@ build_update () { rm -f ./sc64-firmware.bin fi GIT_INFO="" - if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+="branch: [$GIT_BRANCH] "; fi - if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+="tag: [$GIT_TAG] "; fi - if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+="sha: [$GIT_SHA] "; fi - if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+="message: [$GIT_MESSAGE] "; fi - GIT_INFO=$(echo "$GIT_INFO" | xargs) + if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+=$'\n'"branch: $GIT_BRANCH"; fi + if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+=$'\n'"tag: $GIT_TAG"; fi + if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+=$'\n'"sha: $GIT_SHA"; fi + if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+=$'\n'"commit msg: $GIT_MESSAGE"; fi python3 update.py \ --git "$GIT_INFO" \ --mcu ../controller/build/app/app.bin \ diff --git a/sw/deployer/src/debug.rs b/sw/deployer/src/debug.rs index 87433ac..6d068c7 100644 --- a/sw/deployer/src/debug.rs +++ b/sw/deployer/src/debug.rs @@ -53,6 +53,12 @@ impl From for u8 { } } +impl From for u32 { + fn from(value: DataType) -> Self { + u8::from(value) as u32 + } +} + impl Handler { pub fn handle_debug_packet(&mut self, debug_packet: sc64::DebugPacket) { let sc64::DebugPacket { datatype, data } = debug_packet; @@ -97,13 +103,13 @@ impl Handler { println!("Invalid header length for screenshot datatype"); return; } - if u32::from_be_bytes(header[0..4].try_into().unwrap()) != DataType::Screenshot as u32 { + if u32::from_be_bytes(header[0..4].try_into().unwrap()) != DataType::Screenshot.into() { println!("Invalid header datatype for screenshot datatype"); return; } - let pixel_format: u32 = u32::from_be_bytes(header[4..8].try_into().unwrap()); - let width: u32 = u32::from_be_bytes(header[8..12].try_into().unwrap()); - let height: u32 = u32::from_be_bytes(header[12..16].try_into().unwrap()); + let pixel_format = u32::from_be_bytes(header[4..8].try_into().unwrap()); + let width = u32::from_be_bytes(header[8..12].try_into().unwrap()); + let height = u32::from_be_bytes(header[12..16].try_into().unwrap()); if pixel_format != 2 && pixel_format != 4 { println!("Invalid pixel format for screenshot datatype"); return; @@ -141,7 +147,9 @@ impl Handler { let filename = &self.generate_filename("screenshot", "png"); if let Some(error) = image.save(filename).err() { println!("Error during image save: {error}"); + return; } + println!("Wrote {width}x{height} pixels to [{filename}]"); } else { println!("Got screenshot packet without header data"); } diff --git a/sw/deployer/src/main.rs b/sw/deployer/src/main.rs index 3c8ca6c..bff2f65 100644 --- a/sw/deployer/src/main.rs +++ b/sw/deployer/src/main.rs @@ -451,9 +451,9 @@ fn handle_firmware_command( let mut firmware = vec![0u8; firmware_length as usize]; firmware_file.read_exact(&mut firmware)?; - // TODO: print firmware metadata - - println!("{}", "Sorry nothing".yellow()); + let metadata = sc64::firmware::verify(&firmware)?; + println!("{}", "Firmware metadata:".bold()); + println!("{}", metadata); Ok(()) } @@ -463,12 +463,16 @@ fn handle_firmware_command( let (mut backup_file, backup_name) = create_file(&args.firmware)?; + sc64.reset_state()?; + let firmware = log_wait( format!("Generating firmware backup, this might take a while [{backup_name}]"), || sc64.backup_firmware(), )?; - // TODO: print firmware metadata + let metadata = sc64::firmware::verify(&firmware)?; + println!("{}", "Firmware metadata:".bold()); + println!("{}", metadata); backup_file.write(&firmware)?; @@ -480,14 +484,18 @@ fn handle_firmware_command( let (mut update_file, update_name, update_length) = open_file(&args.firmware)?; - let mut fimware = vec![0u8; update_length as usize]; - update_file.read_exact(&mut fimware)?; + let mut firmware = vec![0u8; update_length as usize]; + update_file.read_exact(&mut firmware)?; - // TODO: print firmware metadata + let metadata = sc64::firmware::verify(&firmware)?; + println!("{}", "Firmware metadata:".bold()); + println!("{}", metadata); + + sc64.reset_state()?; log_wait( format!("Updating firmware, this might take a while [{update_name}]"), - || sc64.update_firmware(&fimware), + || sc64.update_firmware(&firmware), )?; Ok(()) diff --git a/sw/deployer/src/n64.rs b/sw/deployer/src/n64.rs index 6486072..37dfca8 100644 --- a/sw/deployer/src/n64.rs +++ b/sw/deployer/src/n64.rs @@ -1,4 +1,4 @@ -use std::io::{Error, Read, Seek}; +use std::io::{Error, Read, Seek, SeekFrom}; pub enum SaveType { None, @@ -16,7 +16,7 @@ pub fn guess_save_type( ) -> Result<(SaveType, Option), Error> { let mut ed64_header = vec![0u8; 4]; - reader.seek(std::io::SeekFrom::Start(0x3C))?; + reader.seek(SeekFrom::Start(0x3C))?; reader.read(&mut ed64_header)?; if &ed64_header[0..2] == b"ED" { diff --git a/sw/deployer/src/sc64/cic.rs b/sw/deployer/src/sc64/cic.rs index f1ebe78..abebd68 100644 --- a/sw/deployer/src/sc64/cic.rs +++ b/sw/deployer/src/sc64/cic.rs @@ -1,5 +1,4 @@ use super::Error; -use crc32fast::Hasher; pub const IPL3_OFFSET: u32 = 0x40; pub const IPL3_LENGTH: usize = 0xFC0; @@ -66,11 +65,7 @@ pub fn guess_ipl3_seed(ipl3: &[u8]) -> Result { return Err(Error::new("Invalid IPL3 length provided")); } - let mut hasher = Hasher::new(); - - hasher.update(ipl3); - - let cic_type: CicType = hasher.finalize().into(); + let cic_type: CicType = crc32fast::hash(ipl3).into(); Ok(cic_type.into()) } diff --git a/sw/deployer/src/sc64/firmware.rs b/sw/deployer/src/sc64/firmware.rs new file mode 100644 index 0000000..8c74526 --- /dev/null +++ b/sw/deployer/src/sc64/firmware.rs @@ -0,0 +1,149 @@ +use super::Error; +use std::{ + fmt::Display, + io::{Cursor, Read, Seek, SeekFrom}, +}; + +enum ChunkId { + UpdateInfo, + McuData, + FpgaData, + BootloaderData, + PrimerData, +} + +impl TryFrom for ChunkId { + type Error = Error; + fn try_from(value: u32) -> Result { + Ok(match value { + 1 => Self::UpdateInfo, + 2 => Self::McuData, + 3 => Self::FpgaData, + 4 => Self::BootloaderData, + 5 => Self::PrimerData, + _ => return Err(Error::new("Unknown chunk id inside firmware update file")), + }) + } +} + +impl Display for ChunkId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + ChunkId::UpdateInfo => "Update info", + ChunkId::McuData => "MCU data", + ChunkId::FpgaData => "FPGA data", + ChunkId::BootloaderData => "Bootloader data", + ChunkId::PrimerData => "Primer data", + }) + } +} + +pub struct Firmware { + update_info: Option, + mcu_data: Option>, + fpga_data: Option>, + bootloader_data: Option>, + primer_data: Option>, +} + +impl Display for Firmware { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(update_info) = &self.update_info { + f.write_fmt(format_args!("{}", &update_info))?; + } else { + f.write_str("No update info data included")?; + } + if let Some(data) = &self.mcu_data { + f.write_fmt(format_args!( + "\nMCU data present, length: 0x{:X}", + data.len() + ))?; + } else { + f.write_str("\nNo MCU data included")?; + } + if let Some(data) = &self.fpga_data { + f.write_fmt(format_args!( + "\nFPGA data present, length: 0x{:X}", + data.len() + ))?; + } else { + f.write_str("\nNo FPGA data included")?; + } + if let Some(data) = &self.bootloader_data { + f.write_fmt(format_args!( + "\nBootloader data present, length: 0x{:X}", + data.len() + ))?; + } else { + f.write_str("\nNo bootloader data included")?; + } + if let Some(data) = &self.primer_data { + f.write_fmt(format_args!( + "\nPrimer data present, length: 0x{:X}", + data.len() + ))?; + } else { + f.write_str("\nNo primer data included")?; + } + Ok(()) + } +} + +const SC64_FIRMWARE_UPDATE_TOKEN: &[u8; 16] = b"SC64 Update v2.0"; + +pub fn verify(data: &[u8]) -> Result { + let mut buffer = vec![0u8; 16]; + + let mut reader = Cursor::new(data); + + reader.read(&mut buffer)?; + if buffer != SC64_FIRMWARE_UPDATE_TOKEN { + return Err(Error::new("Invalid firmware update header")); + } + + let mut firmware = Firmware { + update_info: None, + mcu_data: None, + fpga_data: None, + bootloader_data: None, + primer_data: None, + }; + + loop { + let bytes = reader.read(&mut buffer)?; + if bytes == 16 { + let id: ChunkId = u32::from_le_bytes(buffer[0..4].try_into().unwrap()).try_into()?; + let aligned_length = u32::from_le_bytes(buffer[4..8].try_into().unwrap()); + let checksum = u32::from_le_bytes(buffer[8..12].try_into().unwrap()); + let data_length = u32::from_le_bytes(buffer[12..16].try_into().unwrap()); + + let mut data = vec![0u8; data_length as usize]; + reader.read(&mut data)?; + + let align = aligned_length - 4 - 4 - data_length; + reader.seek(SeekFrom::Current(align as i64))?; + + if crc32fast::hash(&data) != checksum { + return Err(Error::new( + format!("Invalid checksum for chunk [{id}]").as_str(), + )); + } + + match id { + ChunkId::UpdateInfo => { + firmware.update_info = Some(String::from_utf8_lossy(&data).to_string()) + } + ChunkId::McuData => firmware.mcu_data = Some(data), + ChunkId::FpgaData => firmware.fpga_data = Some(data), + ChunkId::BootloaderData => firmware.bootloader_data = Some(data), + ChunkId::PrimerData => firmware.primer_data = Some(data), + } + } else if bytes == 0 { + break; + } else { + return Err(Error::new("Unexpected end of data in firmware update")); + } + } + + Ok(firmware) +} diff --git a/sw/deployer/src/sc64/mod.rs b/sw/deployer/src/sc64/mod.rs index ec506da..7058666 100644 --- a/sw/deployer/src/sc64/mod.rs +++ b/sw/deployer/src/sc64/mod.rs @@ -1,5 +1,6 @@ mod cic; mod error; +pub mod firmware; mod link; mod types; mod utils; diff --git a/sw/update/update.py b/sw/update/update.py index 2889056..e6b08cb 100755 --- a/sw/update/update.py +++ b/sw/update/update.py @@ -219,13 +219,12 @@ if __name__ == "__main__": hostname = platform.node() creation_datetime = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') - info = [ - f'build system: [{hostname}]', - f'creation datetime: [{creation_datetime}]', - ] + update_info = '\n'.join([ + f'build system: {hostname}', + f'creation datetime: {creation_datetime}', + ]) if (args.git): - info.append(args.git) - update_info = ' '.join(info) + update_info += args.git print(update_info) update.add_update_info(update_info.encode())