blob: 39dcb12a0cfefdf9ba53952a6b585d989758bbab [file] [log] [blame]
// Copyright 2024, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::fastboot::{GblFastboot, GPT_NAME_LEN_U8};
use core::fmt::Write;
use core::str::{from_utf8, Split};
use fastboot::{next_arg, next_arg_u64, snprintf, CommandError, FormattedBytes};
use gbl_storage::{AsBlockDevice, AsMultiBlockDevices};
/// Internal trait that provides methods for getting and enumerating values for one or multiple
/// related fastboot variables.
pub(crate) trait Variable {
/// Get the variable value given variable name and arguments.
///
/// Return Ok(Some(`size`)) where `size` is the number of bytes written to `out`. Return
/// `Ok(None)` if the variable is not supported.
fn get(
&self,
gbl_fb: &mut GblFastboot,
name: &str,
args: Split<char>,
out: &mut [u8],
) -> Result<Option<usize>, CommandError>;
/// Iterates and calls `f` on all values/arguments combinations.
fn get_all(
&self,
gbl_fb: &mut GblFastboot,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
) -> Result<(), CommandError>;
}
// Constant fastboot variable
impl Variable for (&'static str, &'static str) {
fn get(
&self,
_: &mut GblFastboot,
name: &str,
_: Split<char>,
out: &mut [u8],
) -> Result<Option<usize>, CommandError> {
Ok((name == self.0).then_some(snprintf!(out, "{}", self.1).len()))
}
fn get_all(
&self,
_: &mut GblFastboot,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
) -> Result<(), CommandError> {
f(self.0, &[], self.1)
}
}
/// `Partition` variable provides information of GPT partitions
///
/// `fastboot getvar partition-size:<GBL Fastboot partition>`
/// `fastboot getvar partition-type:<GBL Fastboot partition>`
pub(crate) struct Partition {}
const PARTITION_SIZE: &str = "partition-size";
const PARTITION_TYPE: &str = "partition-type";
impl Variable for Partition {
fn get(
&self,
gbl_fb: &mut GblFastboot,
name: &str,
args: Split<char>,
out: &mut [u8],
) -> Result<Option<usize>, CommandError> {
let part = gbl_fb.parse_partition(args)?;
Ok(match name {
PARTITION_SIZE => Some(snprintf!(out, "{:#x}", gbl_fb.partition_io(part).size()).len()),
PARTITION_TYPE => Some(snprintf!(out, "raw").len()), // Image type not supported yet.
_ => None,
})
}
fn get_all(
&self,
gbl_fb: &mut GblFastboot,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
) -> Result<(), CommandError> {
// Though any sub range of a GPT partition or raw block counts as a partition in GBL
// Fastboot, for "getvar all" we only enumerate whole range GPT partitions.
let mut res: Result<(), CommandError> = Ok(());
let part_name = &mut [0u8; GPT_NAME_LEN_U8][..];
let mut size_str = [0u8; 32];
gbl_fb.storage().for_each_until(&mut |mut v, id| {
// `AsBlockDevice::partition_iter()` has `Self:Sized` constraint thus we make it into a
// `&mut &mut dyn AsBlockDevice` to meet the bound requirement.
let v = &mut v;
let mut id_str = [0u8; 32];
let id = snprintf!(id_str, "{:x}", id);
res = (|| {
for ptn in v.partition_iter() {
let sz = ptn.size()?;
let part = ptn.gpt_entry().name_to_str(part_name)?;
f(PARTITION_SIZE, &[part, id], snprintf!(size_str, "{:#x}", sz))?;
// Image type is not supported yet.
f(PARTITION_TYPE, &[part, id], snprintf!(size_str, "raw"))?;
}
Ok(())
})();
res.is_err()
})?;
res
}
}
/// `BlockDevice` variable provides information of block devices.
///
/// `fastboot getvar block-device:<id>:total-blocks`
/// `fastboot getvar block-device:<id>:block-size`
pub(crate) struct BlockDevice {}
const BLOCK_DEVICE: &str = "block-device";
const TOTAL_BLOCKS: &str = "total-blocks";
const BLOCK_SIZE: &str = "block-size";
impl Variable for BlockDevice {
fn get(
&self,
gbl_fb: &mut GblFastboot,
name: &str,
mut args: Split<char>,
out: &mut [u8],
) -> Result<Option<usize>, CommandError> {
Ok(match name {
BLOCK_DEVICE => {
let id = next_arg_u64(&mut args, Err("Missing block device ID".into()))?;
let val_type = next_arg(&mut args, Err("Missing value type".into()))?;
let val = match val_type {
TOTAL_BLOCKS => gbl_fb.storage().get(id)?.num_blocks()?,
BLOCK_SIZE => gbl_fb.storage().get(id)?.block_size()?,
_ => return Err("Invalid type".into()),
};
Some(snprintf!(out, "{:#x}", val).len())
}
_ => None,
})
}
fn get_all(
&self,
gbl_fb: &mut GblFastboot,
f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>,
) -> Result<(), CommandError> {
let mut val = [0u8; 32];
let mut res: Result<(), CommandError> = Ok(());
gbl_fb.storage().for_each_until(&mut |blk, id| {
let mut id_str = [0u8; 32];
let id = snprintf!(id_str, "{:x}", id);
res = (|| {
f(BLOCK_DEVICE, &[id, "total-blocks"], snprintf!(val, "{:#x}", blk.num_blocks()?))?;
f(BLOCK_DEVICE, &[id, "block-size"], snprintf!(val, "{:#x}", blk.block_size()?))
})();
res.is_err()
})?;
res
}
}