mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-22 14:09:16 +01:00
firmware verification
This commit is contained in:
parent
0b7ba63184
commit
a0a6423b92
9
build.sh
9
build.sh
@ -89,11 +89,10 @@ build_update () {
|
|||||||
rm -f ./sc64-firmware.bin
|
rm -f ./sc64-firmware.bin
|
||||||
fi
|
fi
|
||||||
GIT_INFO=""
|
GIT_INFO=""
|
||||||
if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+="branch: [$GIT_BRANCH] "; fi
|
if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+=$'\n'"branch: $GIT_BRANCH"; fi
|
||||||
if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+="tag: [$GIT_TAG] "; fi
|
if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+=$'\n'"tag: $GIT_TAG"; fi
|
||||||
if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+="sha: [$GIT_SHA] "; fi
|
if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+=$'\n'"sha: $GIT_SHA"; fi
|
||||||
if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+="message: [$GIT_MESSAGE] "; fi
|
if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+=$'\n'"commit msg: $GIT_MESSAGE"; fi
|
||||||
GIT_INFO=$(echo "$GIT_INFO" | xargs)
|
|
||||||
python3 update.py \
|
python3 update.py \
|
||||||
--git "$GIT_INFO" \
|
--git "$GIT_INFO" \
|
||||||
--mcu ../controller/build/app/app.bin \
|
--mcu ../controller/build/app/app.bin \
|
||||||
|
@ -53,6 +53,12 @@ impl From<DataType> for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<DataType> for u32 {
|
||||||
|
fn from(value: DataType) -> Self {
|
||||||
|
u8::from(value) as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
pub fn handle_debug_packet(&mut self, debug_packet: sc64::DebugPacket) {
|
pub fn handle_debug_packet(&mut self, debug_packet: sc64::DebugPacket) {
|
||||||
let sc64::DebugPacket { datatype, data } = debug_packet;
|
let sc64::DebugPacket { datatype, data } = debug_packet;
|
||||||
@ -97,13 +103,13 @@ impl Handler {
|
|||||||
println!("Invalid header length for screenshot datatype");
|
println!("Invalid header length for screenshot datatype");
|
||||||
return;
|
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");
|
println!("Invalid header datatype for screenshot datatype");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let pixel_format: u32 = u32::from_be_bytes(header[4..8].try_into().unwrap());
|
let pixel_format = u32::from_be_bytes(header[4..8].try_into().unwrap());
|
||||||
let width: u32 = u32::from_be_bytes(header[8..12].try_into().unwrap());
|
let width = u32::from_be_bytes(header[8..12].try_into().unwrap());
|
||||||
let height: u32 = u32::from_be_bytes(header[12..16].try_into().unwrap());
|
let height = u32::from_be_bytes(header[12..16].try_into().unwrap());
|
||||||
if pixel_format != 2 && pixel_format != 4 {
|
if pixel_format != 2 && pixel_format != 4 {
|
||||||
println!("Invalid pixel format for screenshot datatype");
|
println!("Invalid pixel format for screenshot datatype");
|
||||||
return;
|
return;
|
||||||
@ -141,7 +147,9 @@ impl Handler {
|
|||||||
let filename = &self.generate_filename("screenshot", "png");
|
let filename = &self.generate_filename("screenshot", "png");
|
||||||
if let Some(error) = image.save(filename).err() {
|
if let Some(error) = image.save(filename).err() {
|
||||||
println!("Error during image save: {error}");
|
println!("Error during image save: {error}");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
println!("Wrote {width}x{height} pixels to [{filename}]");
|
||||||
} else {
|
} else {
|
||||||
println!("Got screenshot packet without header data");
|
println!("Got screenshot packet without header data");
|
||||||
}
|
}
|
||||||
|
@ -451,9 +451,9 @@ fn handle_firmware_command(
|
|||||||
let mut firmware = vec![0u8; firmware_length as usize];
|
let mut firmware = vec![0u8; firmware_length as usize];
|
||||||
firmware_file.read_exact(&mut firmware)?;
|
firmware_file.read_exact(&mut firmware)?;
|
||||||
|
|
||||||
// TODO: print firmware metadata
|
let metadata = sc64::firmware::verify(&firmware)?;
|
||||||
|
println!("{}", "Firmware metadata:".bold());
|
||||||
println!("{}", "Sorry nothing".yellow());
|
println!("{}", metadata);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -463,12 +463,16 @@ fn handle_firmware_command(
|
|||||||
|
|
||||||
let (mut backup_file, backup_name) = create_file(&args.firmware)?;
|
let (mut backup_file, backup_name) = create_file(&args.firmware)?;
|
||||||
|
|
||||||
|
sc64.reset_state()?;
|
||||||
|
|
||||||
let firmware = log_wait(
|
let firmware = log_wait(
|
||||||
format!("Generating firmware backup, this might take a while [{backup_name}]"),
|
format!("Generating firmware backup, this might take a while [{backup_name}]"),
|
||||||
|| sc64.backup_firmware(),
|
|| sc64.backup_firmware(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO: print firmware metadata
|
let metadata = sc64::firmware::verify(&firmware)?;
|
||||||
|
println!("{}", "Firmware metadata:".bold());
|
||||||
|
println!("{}", metadata);
|
||||||
|
|
||||||
backup_file.write(&firmware)?;
|
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 update_file, update_name, update_length) = open_file(&args.firmware)?;
|
||||||
|
|
||||||
let mut fimware = vec![0u8; update_length as usize];
|
let mut firmware = vec![0u8; update_length as usize];
|
||||||
update_file.read_exact(&mut fimware)?;
|
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(
|
log_wait(
|
||||||
format!("Updating firmware, this might take a while [{update_name}]"),
|
format!("Updating firmware, this might take a while [{update_name}]"),
|
||||||
|| sc64.update_firmware(&fimware),
|
|| sc64.update_firmware(&firmware),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::io::{Error, Read, Seek};
|
use std::io::{Error, Read, Seek, SeekFrom};
|
||||||
|
|
||||||
pub enum SaveType {
|
pub enum SaveType {
|
||||||
None,
|
None,
|
||||||
@ -16,7 +16,7 @@ pub fn guess_save_type<T: Read + Seek>(
|
|||||||
) -> Result<(SaveType, Option<String>), Error> {
|
) -> Result<(SaveType, Option<String>), Error> {
|
||||||
let mut ed64_header = vec![0u8; 4];
|
let mut ed64_header = vec![0u8; 4];
|
||||||
|
|
||||||
reader.seek(std::io::SeekFrom::Start(0x3C))?;
|
reader.seek(SeekFrom::Start(0x3C))?;
|
||||||
reader.read(&mut ed64_header)?;
|
reader.read(&mut ed64_header)?;
|
||||||
|
|
||||||
if &ed64_header[0..2] == b"ED" {
|
if &ed64_header[0..2] == b"ED" {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use super::Error;
|
use super::Error;
|
||||||
use crc32fast::Hasher;
|
|
||||||
|
|
||||||
pub const IPL3_OFFSET: u32 = 0x40;
|
pub const IPL3_OFFSET: u32 = 0x40;
|
||||||
pub const IPL3_LENGTH: usize = 0xFC0;
|
pub const IPL3_LENGTH: usize = 0xFC0;
|
||||||
@ -66,11 +65,7 @@ pub fn guess_ipl3_seed(ipl3: &[u8]) -> Result<u8, Error> {
|
|||||||
return Err(Error::new("Invalid IPL3 length provided"));
|
return Err(Error::new("Invalid IPL3 length provided"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hasher = Hasher::new();
|
let cic_type: CicType = crc32fast::hash(ipl3).into();
|
||||||
|
|
||||||
hasher.update(ipl3);
|
|
||||||
|
|
||||||
let cic_type: CicType = hasher.finalize().into();
|
|
||||||
|
|
||||||
Ok(cic_type.into())
|
Ok(cic_type.into())
|
||||||
}
|
}
|
||||||
|
149
sw/deployer/src/sc64/firmware.rs
Normal file
149
sw/deployer/src/sc64/firmware.rs
Normal file
@ -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<u32> for ChunkId {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
|
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<String>,
|
||||||
|
mcu_data: Option<Vec<u8>>,
|
||||||
|
fpga_data: Option<Vec<u8>>,
|
||||||
|
bootloader_data: Option<Vec<u8>>,
|
||||||
|
primer_data: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Firmware, Error> {
|
||||||
|
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)
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
mod cic;
|
mod cic;
|
||||||
mod error;
|
mod error;
|
||||||
|
pub mod firmware;
|
||||||
mod link;
|
mod link;
|
||||||
mod types;
|
mod types;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
@ -219,13 +219,12 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
hostname = platform.node()
|
hostname = platform.node()
|
||||||
creation_datetime = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
|
creation_datetime = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
info = [
|
update_info = '\n'.join([
|
||||||
f'build system: [{hostname}]',
|
f'build system: {hostname}',
|
||||||
f'creation datetime: [{creation_datetime}]',
|
f'creation datetime: {creation_datetime}',
|
||||||
]
|
])
|
||||||
if (args.git):
|
if (args.git):
|
||||||
info.append(args.git)
|
update_info += args.git
|
||||||
update_info = ' '.join(info)
|
|
||||||
print(update_info)
|
print(update_info)
|
||||||
update.add_update_info(update_info.encode())
|
update.add_update_info(update_info.encode())
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user