|
| 1 | +use anyhow::Context; |
| 2 | +use async_trait::async_trait; |
| 3 | +use probe_rs::flashing::DownloadOptions; |
| 4 | +use probe_rs::MemoryInterface; |
| 5 | +use tbf_parser::parse::{parse_tbf_header, parse_tbf_header_lengths}; |
| 6 | + |
| 7 | +use crate::board_settings::BoardSettings; |
| 8 | +use crate::connection::{Connection, ProbeRSConnection}; |
| 9 | +use crate::errors::{InternalError, TockError, TockloaderError}; |
| 10 | +use crate::CommandUninstall; |
| 11 | + |
| 12 | +#[async_trait] |
| 13 | +impl CommandUninstall for ProbeRSConnection { |
| 14 | + async fn uninstall_app(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { |
| 15 | + if !self.is_open() { |
| 16 | + return Err(InternalError::ConnectionNotOpen.into()); |
| 17 | + } |
| 18 | + let session = self.session.as_mut().expect("Board must be open"); |
| 19 | + |
| 20 | + let mut installed_apps: Vec<AppData> = Vec::new(); |
| 21 | + let mut index: u8 = 1; |
| 22 | + let mut appaddr: u64 = settings.start_address; |
| 23 | + |
| 24 | + // make "Delete all" a part of this vector |
| 25 | + installed_apps.push(AppData { |
| 26 | + name: "Delete all".to_string(), |
| 27 | + address: settings.start_address, |
| 28 | + index: 0, |
| 29 | + size: 0, |
| 30 | + }); |
| 31 | + |
| 32 | + loop { |
| 33 | + let mut board_core = session.core(self.target_info.core)?; |
| 34 | + // Read the first 8 bytes, which is the length of a TLV header. |
| 35 | + let mut appdata = vec![0u8; 8]; |
| 36 | + |
| 37 | + board_core.read(appaddr, &mut appdata)?; |
| 38 | + let tbf_version: u16; |
| 39 | + let header_size: u16; |
| 40 | + let app_size: u32; |
| 41 | + |
| 42 | + // The first 8 bytes of the application data contain the TBF header |
| 43 | + // lengths and version. |
| 44 | + // |
| 45 | + // Note on expect: `read` always fills up the entire buffer, which |
| 46 | + // was previously declared as 8 bytes. |
| 47 | + match parse_tbf_header_lengths( |
| 48 | + &appdata[0..8] |
| 49 | + .try_into() |
| 50 | + .expect("Buffer length must be at least 8 bytes long."), |
| 51 | + ) { |
| 52 | + Ok(data) => { |
| 53 | + tbf_version = data.0; |
| 54 | + header_size = data.1; |
| 55 | + app_size = data.2; |
| 56 | + } |
| 57 | + _ => break, |
| 58 | + }; |
| 59 | + |
| 60 | + // Read the rest of the header |
| 61 | + let mut header_data = vec![0u8; header_size.into()]; |
| 62 | + board_core.read(appaddr, &mut header_data)?; |
| 63 | + |
| 64 | + let header = parse_tbf_header(&header_data, tbf_version) |
| 65 | + .map_err(TockError::InvalidAppTbfHeader)?; |
| 66 | + let pname = header.get_package_name().unwrap_or("").to_owned(); |
| 67 | + installed_apps.push(AppData { |
| 68 | + address: appaddr, |
| 69 | + name: pname, |
| 70 | + size: app_size, |
| 71 | + index, |
| 72 | + }); |
| 73 | + index += 1; |
| 74 | + appaddr += app_size as u64; |
| 75 | + } |
| 76 | + if installed_apps.len() == 1 { |
| 77 | + return Err(TockloaderError::Tock(TockError::MissingAttribute( |
| 78 | + "No apps installed".to_string(), |
| 79 | + ))); |
| 80 | + } |
| 81 | + let app = inquire::Select::new( |
| 82 | + "Which app do you want to uninstall?", |
| 83 | + installed_apps.iter().clone().collect(), |
| 84 | + ) |
| 85 | + .prompt() |
| 86 | + .context("No apps installed") |
| 87 | + .unwrap(); |
| 88 | + let address: u64; // here we'll write the remaining apps |
| 89 | + let buf_size: usize = if app.index > 0 { |
| 90 | + // buf_size is the size of all apps that are to the right of our target app |
| 91 | + address = app.address; // put remaining apps where target app starts |
| 92 | + installed_apps.iter().fold(0, |total_size, aux_app| { |
| 93 | + if aux_app.index > app.index { |
| 94 | + total_size + aux_app.size as usize // increase if app is to the right |
| 95 | + } else { |
| 96 | + total_size |
| 97 | + } |
| 98 | + }) |
| 99 | + } else { |
| 100 | + // Delete all case |
| 101 | + address = settings.start_address; // put a 0x0 byte here, all apps will become invalid |
| 102 | + 0 |
| 103 | + }; |
| 104 | + let mut buffer = vec![0u8; buf_size]; |
| 105 | + if buf_size > 0 { |
| 106 | + // we have apps to the right |
| 107 | + let mut board_core = session.core(self.target_info.core)?; |
| 108 | + board_core.read(installed_apps[app.index as usize + 1].address, &mut buffer)?; |
| 109 | + } |
| 110 | + |
| 111 | + buffer.push(0x0); |
| 112 | + let mut loader = session.target().flash_loader(); |
| 113 | + |
| 114 | + loader.add_data(address, &buffer)?; |
| 115 | + |
| 116 | + let mut options = DownloadOptions::default(); |
| 117 | + options.keep_unwritten_bytes = true; |
| 118 | + |
| 119 | + loader.commit(session, options)?; |
| 120 | + |
| 121 | + Ok(()) |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +struct AppData { |
| 126 | + name: String, |
| 127 | + address: u64, |
| 128 | + size: u32, |
| 129 | + index: u8, |
| 130 | +} |
| 131 | + |
| 132 | +impl std::fmt::Display for AppData { |
| 133 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 134 | + if self.name == "Delete all" { |
| 135 | + write!(f, "{}", self.name) |
| 136 | + } else { |
| 137 | + write!( |
| 138 | + f, |
| 139 | + "{}. {} - start: {:#x}, size: {}", |
| 140 | + self.index, self.name, self.address, self.size |
| 141 | + ) |
| 142 | + } |
| 143 | + } |
| 144 | +} |
0 commit comments