summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYecheng Zhao <zyecheng@google.com>2024-03-21 05:16:05 +0000
committerYecheng Zhao <zyecheng@google.com>2024-03-22 22:22:31 +0000
commita86fa6086a31ffe0687ca87d23bff24d3156277b (patch)
tree6f56a25d375a754fbac0b2c91424e94d9fcfcb2f
parent7bda36f0c88cb817a70106331e8070f3f34b4897 (diff)
downloadlibbootloader-a86fa6086a31ffe0687ca87d23bff24d3156277b.tar.gz
Support fastboot upload/OEM command
Implements fastboot upload and OEM commands. This allows implementation to support staging datat with `fastboot get_staged` Change-Id: I00ded01e8b5cebe17922e4df5eae252265763d3c
-rw-r--r--gbl/libfastboot/src/lib.rs444
-rw-r--r--gbl/libgbl/src/fastboot/mod.rs46
2 files changed, 462 insertions, 28 deletions
diff --git a/gbl/libfastboot/src/lib.rs b/gbl/libfastboot/src/lib.rs
index 4e471a2..4316118 100644
--- a/gbl/libfastboot/src/lib.rs
+++ b/gbl/libfastboot/src/lib.rs
@@ -182,12 +182,21 @@ pub trait FastbootImplementation {
/// additional arguments in `args`.
///
/// Variable `max-download-size`, `version` are reserved by the library.
+ ///
+ /// # Args
+ ///
+ /// * `var`: Name of the variable.
+ /// * `args`: Additional arguments.
+ /// * `out`: Output buffer for storing the variable value.
+ /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
+ ///
/// TODO(b/322540167): Figure out other reserved variables.
fn get_var(
&mut self,
var: &str,
args: Split<char>,
out: &mut [u8],
+ utils: &mut FastbootUtils,
) -> Result<usize, CommandError>;
/// A helper API for getting the value of a fastboot variable and decoding it into string.
@@ -196,8 +205,9 @@ pub trait FastbootImplementation {
var: &str,
args: Split<char>,
out: &'s mut [u8],
+ utils: &mut FastbootUtils,
) -> Result<&'s str, CommandError> {
- let size = self.get_var(var, args, out)?;
+ let size = self.get_var(var, args, out, utils)?;
Ok(from_utf8(out.get(..size).ok_or("Invalid variable size")?)
.map_err(|_| "Value is not string")?)
}
@@ -215,7 +225,7 @@ pub trait FastbootImplementation {
/// error, the implementation should return it immediately. For example the following
/// implementation:
///
- /// fn get_var_all(&mut self, f: F) -> Result<(), CommandError> {
+ /// fn get_var_all(&mut self, f: F, utils: &mut FastbootUtils) -> Result<(), CommandError> {
/// f("partition-size", &["boot_a"], /* size string of boot_a */)?;
/// f("partition-size", &["boot_b"], /* size string of boot_b */)?;
/// f("partition-size", &["init_boot_a"], /* size string of init_boot_a */)?;
@@ -232,17 +242,70 @@ pub trait FastbootImplementation {
/// (bootloader) partition-size:init_boot_b: <size of init_boot_b>
/// ...
///
+ /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
+ ///
/// TODO(b/322540167): This and `get_var()` contain duplicated logic. Investigate if there can
/// be better solutions for doing the combination traversal.
fn get_var_all(
&mut self,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
+ utils: &mut FastbootUtils,
) -> Result<(), CommandError>;
+ /// Backend for `fastboot get_staged ...`
+ ///
+ /// # Args
+ ///
+ /// * `upload_builder`: An instance of `UploadBuilder` for initiating and uploading data. For
+ /// example:
+ ///
+ /// ```
+ /// fn upload(
+ /// &mut self,
+ /// upload_builder: UploadBuilder,
+ /// utils: &mut FastbootUtils,
+ /// ) -> Result<(), CommandError> {
+ /// // Sends a total of 1024 bytes data.
+ /// let mut uploader = upload_builder.start(1024)?;
+ /// // Can upload in multiple batches.
+ /// uploader.upload(&utils.download_buffer[..512])?;
+ /// uploader.upload(&utils.download_buffer[512..])?;
+ /// Ok(())
+ /// }
+ /// ```
+ ///
+ /// If implementation fails to upload enough, or upload more than expected data with
+ /// `Uploader::upload` a Fastboot error will be sent to the host.
+ /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
+ fn upload(
+ &mut self,
+ upload_builder: UploadBuilder,
+ utils: &mut FastbootUtils,
+ ) -> Result<(), CommandError>;
+
+ /// Backend for `fastboot oem ...`.
+ ///
+ /// # Args
+ ///
+ /// * `cmd`: The OEM command string that comes after "oem ".
+ /// * `utils`: A mutable reference to an instance of `FastbootUtils`.
+ /// * `res`: The response buffer. Upon success, implementation can use the buffer to
+ /// construct a valid UTF8 string which will be sent as "OKAY<string>"
+ ///
+ /// # Returns
+ ///
+ /// On success, returns the portion of `res` used by the construction of string message.
+ fn oem<'a>(
+ &mut self,
+ cmd: &str,
+ utils: &mut FastbootUtils,
+ res: &'a mut [u8],
+ ) -> Result<&'a [u8], CommandError>;
+
// TODO(b/322540167): Add methods for other commands.
}
-/// An internal convenient macro helper for `fastboot_okay` and `fastboot_fail`.
+/// An internal convenient macro helper for `fastboot_okay`, `fastboot_fail` and `fastboot_info`.
macro_rules! fastboot_msg {
( $arr:expr, $msg_type:expr, $( $x:expr ),* $(,)? ) => {
{
@@ -265,6 +328,143 @@ macro_rules! fastboot_fail {
( $arr:expr, $( $x:expr ),* ) => { fastboot_msg!($arr, "FAIL", $($x,)*) };
}
+/// An internal convenient macro that constructs a formatted fastboot INFO message.
+macro_rules! fastboot_info {
+ ( $arr:expr, $( $x:expr ),* ) => { fastboot_msg!($arr, "INFO", $($x,)*) };
+}
+
+/// `FastbootInfoSender` defines a method for sending Fastboot INFO messages.
+///
+/// The trait is for user to implement their mock `FastbootUtils` for testing implementation
+/// of the `FastbootImplementation` trait.
+pub trait FastbootInfoSend {
+ /// Sends a Fastboot "INFO<`msg`>" packet
+ fn send(&mut self, msg: &str) -> Result<(), CommandError>;
+}
+
+/// `FastbootUtils` contains download data/buffer and a `FastbootInfoSend` trait object for sending
+/// Fastboot INFO messages. It can be used in the implementation of `FastbootImplementation`.
+pub struct FastbootUtils<'a> {
+ // TODO(b/328784766): Consider using arrayvec crate or similar instead of passing download
+ // buffer and size separately.
+ /// The total download buffer.
+ pub download_buffer: &'a mut [u8],
+ /// Current downloaded data size.
+ pub download_data_size: usize,
+ /// When available, a trait object `FastbootInfoSend` for sending Fastboot INFO messages.
+ pub fb_info: Option<&'a mut dyn FastbootInfoSend>,
+}
+
+impl<'a> FastbootUtils<'a> {
+ fn new(fb: &'a mut Fastboot, fb_info: Option<&'a mut dyn FastbootInfoSend>) -> Self {
+ Self {
+ download_buffer: fb.download_buffer,
+ download_data_size: fb.total_download_size,
+ fb_info: fb_info,
+ }
+ }
+}
+
+/// `FastbootInfoSender` is an internal type that implements `FastbootInfoSend` with a `Transport`
+/// trait object.
+struct FastbootInfoSender<'a> {
+ transport: &'a mut dyn Transport,
+ transport_error: Result<(), TransportError>,
+}
+
+impl<'a> FastbootInfoSender<'a> {
+ /// Creates an new instance
+ fn new(transport: &'a mut dyn Transport) -> Self {
+ Self { transport: transport, transport_error: Ok(()) }
+ }
+
+ /// Returns the `Self:;transport_error`.
+ fn transport_error(&self) -> Result<(), TransportError> {
+ self.transport_error
+ }
+}
+
+impl FastbootInfoSend for FastbootInfoSender<'_> {
+ fn send(&mut self, msg: &str) -> Result<(), CommandError> {
+ self.transport_error?;
+ let mut res = [0u8; MAX_RESPONSE_SIZE];
+ self.transport_error = self.transport.send_packet(fastboot_info!(res, "{}", msg));
+ Ok(self.transport_error?)
+ }
+}
+
+/// `UploadBuilder` can be consumed to create an `Uploader` for sending data to the host during
+/// handling of command `fastboot get_staged`.
+pub struct UploadBuilder<'a> {
+ remaining: &'a mut u64,
+ // `send` sends a bytes array as fastboot packet.
+ send: &'a mut dyn FnMut(&[u8]) -> Result<(), CommandError>,
+}
+
+impl<'a> UploadBuilder<'a> {
+ /// Consumes the builder to create an `Uploader` to start uploading data.
+ pub fn start(self, data_size: u64) -> Result<Uploader<'a>, CommandError> {
+ let mut res = [0u8; 16];
+ (self.send)(snprintf!(res, "DATA{:08x}", data_size).as_bytes())?;
+ *self.remaining = data_size;
+ Ok(Uploader { remaining: self.remaining, send: self.send })
+ }
+}
+
+/// `UploadBuilder` provides APIs for sending data from the device in response to
+/// `fastboot get_staged`
+pub struct Uploader<'a> {
+ remaining: &'a mut u64,
+ send: &'a mut dyn FnMut(&[u8]) -> Result<(), CommandError>,
+}
+
+impl<'a> Uploader<'a> {
+ /// Uploads data. Returns error if accumulative amount exceeds `data_size` passed to
+ /// `UploadBuilder::start()`.
+ pub fn upload(&mut self, data: &[u8]) -> Result<(), CommandError> {
+ *self.remaining = self
+ .remaining
+ .checked_sub(data.len().try_into().map_err(|_| "usize -> u64 overflows")?)
+ .ok_or::<CommandError>("Upload more than expected".into())?;
+ (self.send)(data)
+ }
+}
+
+pub mod test_utils {
+ use crate::{CommandError, UploadBuilder};
+
+ /// Runs a closure with a mock uploader for user implementation to test
+ /// `FastbootImplementation::upload()`.
+ ///
+ /// The mock uploader simply uploads to a user provided buffer.
+ ///
+ /// Returns the total uploaded size and remaining size.
+ pub fn with_mock_upload_builder<F>(buffer: &mut [u8], mut f: F) -> (usize, usize)
+ where
+ F: FnMut(UploadBuilder),
+ {
+ let mut remaining = 0u64;
+ let mut sent = 0;
+ let mut send = |data: &[u8]| -> Result<(), CommandError> {
+ // Skips the first 12 bytes "DATAXXXXXXXX" fastboot message.
+ match sent == 0 {
+ true => {
+ assert_eq!(data.len(), 12);
+ assert!(data.starts_with(b"DATA"));
+ sent += data.len()
+ }
+ _ => {
+ buffer[sent - 12..][..data.len()].clone_from_slice(data);
+ sent += data.len();
+ }
+ };
+ Ok(())
+ };
+ f(UploadBuilder { remaining: &mut remaining, send: &mut send });
+ (core::cmp::max(sent, 12) - 12, remaining.try_into().unwrap())
+ }
+}
+
const MAX_DOWNLOAD_SIZE_NAME: &'static str = "max-download-size";
/// State of the fastboot protocol.
@@ -318,14 +518,14 @@ impl<'a> Fastboot<'a> {
}
let mut res = [0u8; MAX_RESPONSE_SIZE];
- let cmd = match from_utf8(&packet[..cmd_size]) {
+ let cmd_str = match from_utf8(&packet[..cmd_size]) {
Ok(s) => s,
_ => {
transport.send_packet(fastboot_fail!(res, "Invalid Command"))?;
continue;
}
};
- let mut args = cmd.split(':');
+ let mut args = cmd_str.split(':');
let Some(cmd) = args.next() else {
transport.send_packet(fastboot_fail!(res, "No command"))?;
continue;
@@ -333,6 +533,10 @@ impl<'a> Fastboot<'a> {
match cmd {
"getvar" => self.get_var(args, transport, fb_impl)?,
"download" => self.download(args, transport, fb_impl)?,
+ "upload" => self.upload(transport, fb_impl)?,
+ _ if cmd_str.starts_with("oem ") => {
+ self.oem(&cmd_str[4..], transport, fb_impl)?
+ }
_ => {
transport.send_packet(fastboot_fail!(res, "Command not found"))?;
}
@@ -400,9 +604,12 @@ impl<'a> Fastboot<'a> {
if var == "all" {
return self.get_var_all(transport, fb_impl);
+ } else if var == MAX_DOWNLOAD_SIZE_NAME {
+ return transport.send_packet(fastboot_okay!(res, "{:#x}", self.download_buffer.len()));
}
+
let mut val = [0u8; MAX_RESPONSE_SIZE];
- match self.get_var_str(var, args, &mut val[..], fb_impl) {
+ match self.get_var_str(var, args, &mut val[..], transport, fb_impl) {
Ok(s) => transport.send_packet(fastboot_okay!(res, "{}", s)),
Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
}
@@ -414,13 +621,12 @@ impl<'a> Fastboot<'a> {
var: &str,
args: Split<char>,
out: &'s mut [u8],
+ transport: &mut impl Transport,
fb_impl: &mut impl FastbootImplementation,
) -> Result<&'s str, CommandError> {
- if var == MAX_DOWNLOAD_SIZE_NAME {
- Ok(snprintf!(out, "{:#x}", self.download_buffer.len()))
- } else {
- fb_impl.get_var_as_str(var, args, out)
- }
+ let mut info_sender = FastbootInfoSender::new(transport);
+ let mut utils = FastbootUtils::new(self, Some(&mut info_sender));
+ fb_impl.get_var_as_str(var, args, out, &mut utils)
}
/// A wrapper of `get_var_all()` that first iterates reserved variables.
@@ -429,16 +635,11 @@ impl<'a> Fastboot<'a> {
fb_impl: &mut impl FastbootImplementation,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
) -> Result<(), CommandError> {
- let mut size_str = [0u8; 32];
-
// Process the built-in MAX_DOWNLOAD_SIZE_NAME variable.
- f(
- MAX_DOWNLOAD_SIZE_NAME,
- &[],
- self.get_var_str(MAX_DOWNLOAD_SIZE_NAME, "".split(':'), &mut size_str[..], fb_impl)?,
- )?;
+ let mut size_str = [0u8; 32];
+ f(MAX_DOWNLOAD_SIZE_NAME, &[], snprintf!(size_str, "{:#x}", self.download_buffer.len()))?;
- fb_impl.get_var_all(f)
+ fb_impl.get_var_all(f, &mut FastbootUtils::new(self, None))
}
/// Method for handling "fastboot getvar all"
@@ -501,6 +702,55 @@ impl<'a> Fastboot<'a> {
self.state = ProtocolState::Download;
Ok(())
}
+
+ /// Method for handling "fastboot get_staged ...".
+ pub fn upload(
+ &mut self,
+ transport: &mut impl Transport,
+ fb_impl: &mut impl FastbootImplementation,
+ ) -> Result<(), TransportError> {
+ let mut transport_error = Ok(());
+ let mut remaining = 0u64;
+ let mut send = |data: &[u8]| -> Result<(), CommandError> {
+ transport_error?;
+ transport_error = transport.send_packet(data);
+ Ok(transport_error?)
+ };
+ let mut utils = FastbootUtils::new(self, None);
+ let upload_res = fb_impl
+ .upload(UploadBuilder { remaining: &mut remaining, send: &mut send }, &mut utils);
+ transport_error?;
+ let mut res = [0u8; MAX_RESPONSE_SIZE];
+ match upload_res {
+ Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
+ Ok(()) if remaining > 0 => {
+ transport.send_packet(fastboot_fail!(res, "Failed to upload all data"))
+ }
+ _ => transport.send_packet(fastboot_okay!(res, "")),
+ }
+ }
+
+ /// Method for handling "fastboot oem ...".
+ pub fn oem(
+ &mut self,
+ cmd: &str,
+ transport: &mut impl Transport,
+ fb_impl: &mut impl FastbootImplementation,
+ ) -> Result<(), TransportError> {
+ let mut info_sender = FastbootInfoSender::new(transport);
+ let mut utils = FastbootUtils::new(self, Some(&mut info_sender));
+ let mut oem_out = [0u8; MAX_RESPONSE_SIZE - 4];
+ let oem_res = fb_impl.oem(cmd, &mut utils, &mut oem_out[..]);
+ info_sender.transport_error()?;
+ let mut res = [0u8; MAX_RESPONSE_SIZE];
+ match oem_res {
+ Ok(msg) => match from_utf8(msg) {
+ Ok(s) => transport.send_packet(fastboot_okay!(res, "{}", s)),
+ Err(e) => transport.send_packet(fastboot_fail!(res, "Invalid return string {}", e)),
+ },
+ Err(e) => transport.send_packet(fastboot_fail!(res, "{}", e.to_str())),
+ }
+ }
}
/// A helper data structure for writing formatted string to fixed size bytes array.
@@ -568,17 +818,24 @@ mod test {
use std::collections::{BTreeMap, VecDeque};
#[derive(Default)]
- struct FastbootTest {
+ struct FastbootTest<'a> {
// A mapping from (variable name, argument) to variable value.
vars: BTreeMap<(&'static str, &'static [&'static str]), &'static str>,
+ upload_cb: Option<
+ &'a mut dyn FnMut(UploadBuilder, &mut FastbootUtils) -> Result<(), CommandError>,
+ >,
+ oem_cb: Option<
+ &'a mut dyn FnMut(&str, &mut FastbootUtils, &mut [u8]) -> Result<usize, CommandError>,
+ >,
}
- impl FastbootImplementation for FastbootTest {
+ impl FastbootImplementation for FastbootTest<'_> {
fn get_var(
&mut self,
var: &str,
args: Split<char>,
out: &mut [u8],
+ _: &mut FastbootUtils,
) -> Result<usize, CommandError> {
let args = args.collect::<Vec<_>>();
match self.vars.get(&(var, &args[..])) {
@@ -593,12 +850,31 @@ mod test {
fn get_var_all(
&mut self,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
+ _: &mut FastbootUtils,
) -> Result<(), CommandError> {
for ((var, config), value) in &self.vars {
f(var, config, value)?;
}
Ok(())
}
+
+ fn upload(
+ &mut self,
+ upload_builder: UploadBuilder,
+ utils: &mut FastbootUtils,
+ ) -> Result<(), CommandError> {
+ (self.upload_cb.as_mut().unwrap())(upload_builder, utils)
+ }
+
+ fn oem<'b>(
+ &mut self,
+ cmd: &str,
+ utils: &mut FastbootUtils,
+ res: &'b mut [u8],
+ ) -> Result<&'b [u8], CommandError> {
+ let sz = (self.oem_cb.as_mut().unwrap())(cmd, utils, res)?;
+ Ok(&res[..sz])
+ }
}
struct TestTransport {
@@ -854,6 +1130,132 @@ mod test {
}
#[test]
+ fn test_oem_cmd() {
+ let mut fastboot_impl: FastbootTest = Default::default();
+ const DOWNLOAD_BUFFER_LEN: usize = 2048;
+ let mut download_buffer = vec![0u8; DOWNLOAD_BUFFER_LEN];
+ let download_content: Vec<u8> = (0..1024).into_iter().map(|v| v as u8).collect();
+ let mut fastboot = Fastboot::new(&mut download_buffer[..]);
+ let mut transport = TestTransport::new();
+ transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
+ transport.add_input(&download_content[..]);
+ transport.add_input(b"oem oem-command");
+
+ let mut oem_cb = |cmd: &str, utils: &mut FastbootUtils, res: &mut [u8]| {
+ assert_eq!(cmd, "oem-command");
+ assert_eq!(utils.download_buffer.len(), DOWNLOAD_BUFFER_LEN);
+ assert_eq!(
+ utils.download_buffer[..utils.download_data_size].to_vec(),
+ download_content
+ );
+ utils.fb_info.as_mut().unwrap().send("oem-info-1").unwrap();
+ utils.fb_info.as_mut().unwrap().send("oem-info-2").unwrap();
+ Ok(snprintf!(res, "oem-return").len())
+ };
+ fastboot_impl.oem_cb = Some(&mut oem_cb);
+ let _ = fastboot.run(&mut transport, &mut fastboot_impl);
+ assert_eq!(
+ transport.out_queue,
+ VecDeque::<Vec<u8>>::from([
+ b"DATA0x400".into(),
+ b"OKAY".into(),
+ b"INFOoem-info-1".into(),
+ b"INFOoem-info-2".into(),
+ b"OKAYoem-return".into(),
+ ])
+ );
+ }
+
+ #[test]
+ fn test_upload() {
+ let mut fastboot_impl: FastbootTest = Default::default();
+ const DOWNLOAD_BUFFER_LEN: usize = 2048;
+ let mut download_buffer = vec![0u8; DOWNLOAD_BUFFER_LEN];
+ let download_content: Vec<u8> = (0..1024).into_iter().map(|v| v as u8).collect();
+ let mut fastboot = Fastboot::new(&mut download_buffer[..]);
+ let mut transport = TestTransport::new();
+ transport.add_input(format!("download:{:#x}", download_content.len()).as_bytes());
+ transport.add_input(&download_content[..]);
+ transport.add_input(b"upload");
+
+ let mut upload_cb = |upload_builder: UploadBuilder, utils: &mut FastbootUtils| {
+ assert_eq!(utils.download_buffer.len(), DOWNLOAD_BUFFER_LEN);
+ assert_eq!(
+ utils.download_buffer[..utils.download_data_size].to_vec(),
+ download_content
+ );
+ let to_send = &utils.download_buffer[..utils.download_data_size];
+ let mut uploader = upload_builder.start(u64::try_from(to_send.len()).unwrap()).unwrap();
+ uploader.upload(&to_send[..to_send.len() / 2]).unwrap();
+ uploader.upload(&to_send[to_send.len() / 2..]).unwrap();
+ assert!(utils.fb_info.is_none());
+ Ok(())
+ };
+ fastboot_impl.upload_cb = Some(&mut upload_cb);
+ let _ = fastboot.run(&mut transport, &mut fastboot_impl);
+ assert_eq!(
+ transport.out_queue,
+ VecDeque::<Vec<u8>>::from([
+ b"DATA0x400".into(),
+ b"OKAY".into(),
+ b"DATA00000400".into(),
+ download_content[..download_content.len() / 2].to_vec(),
+ download_content[download_content.len() / 2..].to_vec(),
+ b"OKAY".into(),
+ ])
+ );
+ }
+
+ #[test]
+ fn test_upload_not_enough_data() {
+ let mut fastboot_impl: FastbootTest = Default::default();
+ let mut download_buffer = vec![0u8; 2048];
+ let mut fastboot = Fastboot::new(&mut download_buffer[..]);
+ let mut transport = TestTransport::new();
+ transport.add_input(b"upload");
+
+ let mut upload_cb = |upload_builder: UploadBuilder, _: &mut FastbootUtils| {
+ let mut uploader = upload_builder.start(0x400).unwrap();
+ uploader.upload(&[0u8; 0x400 - 1]).unwrap();
+ Ok(())
+ };
+ fastboot_impl.upload_cb = Some(&mut upload_cb);
+ let _ = fastboot.run(&mut transport, &mut fastboot_impl);
+ assert_eq!(
+ transport.out_queue,
+ VecDeque::<Vec<u8>>::from([
+ b"DATA00000400".into(),
+ [0u8; 0x400 - 1].to_vec(),
+ b"FAILFailed to upload all data".into(),
+ ])
+ );
+ }
+
+ #[test]
+ fn test_upload_more_data() {
+ let mut fastboot_impl: FastbootTest = Default::default();
+ let mut download_buffer = vec![0u8; 2048];
+ let mut fastboot = Fastboot::new(&mut download_buffer[..]);
+ let mut transport = TestTransport::new();
+ transport.add_input(b"upload");
+
+ let mut upload_cb = |upload_builder: UploadBuilder, _: &mut FastbootUtils| {
+ let mut uploader = upload_builder.start(0x400).unwrap();
+ uploader.upload(&[0u8; 0x400 + 1])?;
+ Ok(())
+ };
+ fastboot_impl.upload_cb = Some(&mut upload_cb);
+ let _ = fastboot.run(&mut transport, &mut fastboot_impl);
+ assert_eq!(
+ transport.out_queue,
+ VecDeque::<Vec<u8>>::from([
+ b"DATA00000400".into(),
+ b"FAILUpload more than expected".into(),
+ ])
+ );
+ }
+
+ #[test]
fn test_fastboot_tcp() {
let mut fastboot_impl: FastbootTest = Default::default();
let mut download_buffer = vec![0u8; 1024];
diff --git a/gbl/libgbl/src/fastboot/mod.rs b/gbl/libgbl/src/fastboot/mod.rs
index b779130..60febfb 100644
--- a/gbl/libgbl/src/fastboot/mod.rs
+++ b/gbl/libgbl/src/fastboot/mod.rs
@@ -13,7 +13,7 @@
// limitations under the License.
use core::str::Split;
-use fastboot::{CommandError, FastbootImplementation};
+use fastboot::{CommandError, FastbootImplementation, FastbootUtils, UploadBuilder};
use gbl_storage::AsMultiBlockDevices;
mod vars;
@@ -53,6 +53,7 @@ impl FastbootImplementation for GblFastboot<'_> {
var: &str,
args: Split<char>,
out: &mut [u8],
+ _utils: &mut FastbootUtils,
) -> Result<usize, CommandError> {
Self::NATIVE_VARS
.iter()
@@ -63,9 +64,28 @@ impl FastbootImplementation for GblFastboot<'_> {
fn get_var_all(
&mut self,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
+ _utils: &mut FastbootUtils,
) -> Result<(), CommandError> {
Self::NATIVE_VARS.iter().find_map(|v| v.get_all(self, f).err()).map_or(Ok(()), |e| Err(e))
}
+
+ fn upload(
+ &mut self,
+ _upload_builder: UploadBuilder,
+ _utils: &mut FastbootUtils,
+ ) -> Result<(), CommandError> {
+ Err("Unimplemented".into())
+ }
+
+ fn oem<'a>(
+ &mut self,
+ _cmd: &str,
+ utils: &mut FastbootUtils,
+ _res: &'a mut [u8],
+ ) -> Result<&'a [u8], CommandError> {
+ utils.fb_info.as_mut().unwrap().send("GBL OEM not implemented yet")?;
+ Err("Unimplemented".into())
+ }
}
/// A helper to convert a hex string into u64.
@@ -145,8 +165,13 @@ mod test {
/// Helper to test fastboot variable value.
fn check_var(gbl_fb: &mut GblFastboot, var: &str, args: &str, expected: &str) {
+ let mut utils =
+ FastbootUtils { download_buffer: &mut [], download_data_size: 0, fb_info: None };
let mut out = vec![0u8; fastboot::MAX_RESPONSE_SIZE];
- assert_eq!(gbl_fb.get_var_as_str(var, args.split(':'), &mut out[..]).unwrap(), expected);
+ assert_eq!(
+ gbl_fb.get_var_as_str(var, args.split(':'), &mut out[..], &mut utils).unwrap(),
+ expected
+ );
}
#[test]
@@ -163,9 +188,11 @@ mod test {
check_var(&mut gbl_fb, "partition-size", "vendor_boot_a:1", "0x1000");
check_var(&mut gbl_fb, "partition-size", "vendor_boot_b:1", "0x1800");
+ let mut utils =
+ FastbootUtils { download_buffer: &mut [], download_data_size: 0, fb_info: None };
let mut out = vec![0u8; fastboot::MAX_RESPONSE_SIZE];
assert!(gbl_fb
- .get_var_as_str("partition", "non-existent".split(':'), &mut out[..])
+ .get_var_as_str("partition", "non-existent".split(':'), &mut out[..], &mut utils)
.is_err());
}
@@ -178,12 +205,17 @@ mod test {
devs.sync_gpt_all(&mut |_, _, _| panic!("GPT sync failed"));
let mut gbl_fb = GblFastboot::new(&mut devs);
+ let mut utils =
+ FastbootUtils { download_buffer: &mut [], download_data_size: 0, fb_info: None };
let mut out: Vec<String> = Default::default();
gbl_fb
- .get_var_all(&mut |name, args, val| {
- out.push(format!("{}:{}: {}", name, args.join(":"), val));
- Ok(())
- })
+ .get_var_all(
+ &mut |name, args, val| {
+ out.push(format!("{}:{}: {}", name, args.join(":"), val));
+ Ok(())
+ },
+ &mut utils,
+ )
.unwrap();
assert_eq!(
out,