Skip to content

Commit d506889

Browse files
committed
feat: uninstall
Signed-off-by: addrian-77 <lunguadrian30@gmail.com>
1 parent 65e43ff commit d506889

7 files changed

Lines changed: 178 additions & 1 deletion

File tree

tockloader-cli/src/cli.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ fn get_subcommands() -> Vec<Command> {
5353
.args(get_app_args())
5454
.args(get_channel_args())
5555
.arg_required_else_help(false),
56+
Command::new("uninstall")
57+
.about("Uninstall apps")
58+
.args(get_app_args())
59+
.args(get_channel_args())
60+
.arg_required_else_help(false),
5661
]
5762
}
5863

tockloader-cli/src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use tockloader_lib::known_boards::KnownBoard;
1919
use tockloader_lib::tabs::tab::Tab;
2020
use tockloader_lib::{
2121
list_debug_probes, list_serial_ports, CommandInfo, CommandInstall, CommandList,
22+
CommandUninstall,
2223
};
2324

2425
fn get_serial_target_info(user_options: &ArgMatches) -> SerialTargetInfo {
@@ -228,6 +229,15 @@ async fn main() -> Result<()> {
228229
.await
229230
.context("Failed to install app.")?;
230231
}
232+
Some(("uninstall", sub_matches)) => {
233+
cli::validate(&mut cmd, sub_matches);
234+
let mut conn = open_connection(sub_matches).await?;
235+
let settings = get_board_settings(sub_matches);
236+
237+
conn.uninstall_app(&settings)
238+
.await
239+
.context("Failed to uninstall app.")?;
240+
}
231241
_ => {
232242
println!("Could not run the provided subcommand.");
233243
_ = make_cli().print_help();

tockloader-lib/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ serde = { version = "1.0.210", features = ["derive"] }
2121
thiserror = "1.0.63"
2222
async-trait = "0.1.88"
2323
log = "0.4.27"
24+
inquire = "0.7.5"
25+
anyhow = "1.0.99"

tockloader-lib/src/command_impl/generalized.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::board_settings::BoardSettings;
66
use crate::connection::TockloaderConnection;
77
use crate::errors::TockloaderError;
88
use crate::tabs::tab::Tab;
9-
use crate::{CommandInfo, CommandInstall, CommandList};
9+
use crate::{CommandInfo, CommandInstall, CommandList, CommandUninstall};
1010

1111
#[async_trait]
1212
impl CommandList for TockloaderConnection {
@@ -47,3 +47,13 @@ impl CommandInstall for TockloaderConnection {
4747
}
4848
}
4949
}
50+
51+
#[async_trait]
52+
impl CommandUninstall for TockloaderConnection {
53+
async fn uninstall_app(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> {
54+
match self {
55+
TockloaderConnection::ProbeRS(conn) => conn.uninstall_app(settings).await,
56+
TockloaderConnection::Serial(_conn) => todo!(),
57+
}
58+
}
59+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod info;
22
pub mod install;
33
pub mod list;
4+
pub mod uninstall;
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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+
}

tockloader-lib/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,8 @@ pub trait CommandInstall {
5959
tab_file: Tab,
6060
) -> Result<(), TockloaderError>;
6161
}
62+
63+
#[async_trait]
64+
pub trait CommandUninstall {
65+
async fn uninstall_app(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError>;
66+
}

0 commit comments

Comments
 (0)