use std::io::{Read, Write};
use std::path::PathBuf;
use std::rc::Rc;
use std::thread;
use std::time::Duration;
use color_eyre::eyre::{Context, Result, eyre};
use color_eyre::owo_colors::OwoColorize;
use indicatif::{ProgressBar, ProgressStyle};
use log::{debug, error, info, warn};
use crate::bmp::{self, BmpDevice, FirmwareFormat, FirmwareType};
use crate::usb::PortId;
use crate::{AllowDangerous, BmpParams, FlashParams, elf};
pub struct Firmware
{
firmware_type: FirmwareType,
data: Vec<u8>,
length: u32,
}
impl Firmware
{
pub fn new<Params>(params: &Params, device: &BmpDevice, firmware_data: Vec<u8>) -> Result<Self>
where
Params: BmpParams + FlashParams,
{
let firmware_length = firmware_data.len();
let firmware_length = u32::try_from(firmware_length)
.expect("firmware filesize exceeded 32 bits! Firmware binary must be invalid (too big)");
Ok(Self {
firmware_type: Self::determine_firmware_type(params, device, &firmware_data)?,
data: firmware_data,
length: firmware_length,
})
}
fn determine_firmware_type<Params>(
params: &Params,
device: &BmpDevice,
firmware_data: &[u8],
) -> Result<FirmwareType>
where
Params: BmpParams + FlashParams,
{
let platform = device.platform();
let firmware_type =
FirmwareType::detect_from_firmware(platform, firmware_data).wrap_err("detecting firmware type")?;
debug!("Firmware file was detected as {}", firmware_type);
let firmware_type = match params.override_firmware_type() {
Some(override_firmware_type) => {
match params.allow_dangerous_options() {
AllowDangerous::Really => warn!(
"Overriding firmware-type detection and flashing to user-specified location ({}) instead!",
override_firmware_type
),
AllowDangerous::Never => {
eprintln!(
"{} --override-firmware-type is used to override the firmware type detection and flash a \
firmware binary to a location other than the one that it seems to be designed for.\nThis \
is a potentially destructive operation and can result in an unbootable device! (can \
require a second, external JTAG debugger and manual wiring to fix!)\n\nDo not use this \
option unless you are a firmware developer and really know what you are doing!\n\nIf you \
are sure this is really what you want to do, run again with \
--allow-dangerous-options=really",
"WARNING:".red()
);
std::process::exit(1);
},
}
override_firmware_type
},
None => firmware_type,
};
Ok(firmware_type)
}
pub fn program_firmware(&self, device: &mut BmpDevice) -> Result<()>
{
let progress_bar = ProgressBar::new(self.length as u64).with_style(
ProgressStyle::default_bar()
.template(" {percent:>3}% |{bar:50}| {bytes}/{total_bytes} [{binary_bytes_per_sec} {elapsed}]")
.unwrap(),
);
let progress_bar = Rc::new(progress_bar);
let enclosed = Rc::clone(&progress_bar);
let firmware_type = self.firmware_type;
let result = device.download(&*self.data, self.length, firmware_type, move |flash_pos_delta| {
if enclosed.position() == 0 {
if firmware_type == FirmwareType::Application {
enclosed.println("Flashing...");
} else {
enclosed.println("Flashing bootloader...");
}
}
enclosed.inc(flash_pos_delta as u64);
});
progress_bar.finish();
let dfu_iface = result?;
info!("Flash complete!");
if progress_bar.position() == (self.length as u64) {
device.reboot(dfu_iface)
} else {
Err(eyre!("Failed to flash device, download incomplete"))
}
}
}
fn intel_hex_error() -> !
{
eprintln!(
"{} The specified firmware file appears to be an Intel HEX file, but Intel HEX files are not currently \
supported. Please use a binary file (e.g. blackmagic.bin), or an ELF (e.g. blackmagic.elf) to flash.",
"Error:".red()
);
std::process::exit(1);
}
fn read_firmware(file_name: PathBuf) -> Result<Vec<u8>>
{
let firmware_file = std::fs::File::open(file_name.as_path())
.wrap_err_with(|| eyre!("Failed to read firmware file {} to Flash", file_name.display()))?;
let mut firmware_file = std::io::BufReader::new(firmware_file);
let mut firmware_data = Vec::new();
firmware_file.read_to_end(&mut firmware_data).unwrap();
if firmware_data.len() < 8 {
return Err(eyre!("Firmware file appears invalid: less than 8 bytes long"));
}
let firmware_data = match FirmwareFormat::detect_from_firmware(&firmware_data) {
FirmwareFormat::Binary => firmware_data,
FirmwareFormat::Elf => elf::extract_binary(&firmware_data)?,
FirmwareFormat::IntelHex => intel_hex_error(), };
Ok(firmware_data)
}
fn check_programming(port: PortId) -> Result<()>
{
let dev = bmp::wait_for_probe_reboot(port, Duration::from_secs(5), "flash").inspect_err(|_| {
error!("Black Magic Probe did not re-enumerate after flashing! Invalid firmware?");
})?;
let identity = dev.firmware_identity().inspect_err(|_| {
error!("Error reading firmware version after flash! Invalid firmware?");
})?;
println!(
"Black Magic Probe successfully rebooted into firmware version {}",
identity.version
);
Ok(())
}
pub fn flash_probe<Params>(params: &Params, mut device: BmpDevice, file_name: PathBuf) -> Result<()>
where
Params: BmpParams + FlashParams,
{
let firmware_data = read_firmware(file_name)?;
let port = device.port();
let firmware = Firmware::new(params, &device, firmware_data)?;
let _ = writeln!(std::io::stdout(), "Found: {}", device).map_err(|e| {
error!(
"Failed to read string data from Black Magic Probe: {}\nTrying to continue anyway...",
e
);
});
firmware.program_firmware(&mut device)?;
drop(device);
thread::sleep(Duration::from_millis(250));
check_programming(port)
}