1 // Copyright 2024, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use crate::fastboot::{GblFastboot, GPT_NAME_LEN_U8}; 16 use core::fmt::Write; 17 use core::str::{from_utf8, Split}; 18 use fastboot::{next_arg, next_arg_u64, snprintf, CommandError, FormattedBytes}; 19 use gbl_storage::{AsBlockDevice, AsMultiBlockDevices}; 20 21 /// Internal trait that provides methods for getting and enumerating values for one or multiple 22 /// related fastboot variables. 23 pub(crate) trait Variable { 24 /// Get the variable value given variable name and arguments. 25 /// 26 /// Return Ok(Some(`size`)) where `size` is the number of bytes written to `out`. Return 27 /// `Ok(None)` if the variable is not supported. get( &self, gbl_fb: &mut GblFastboot, name: &str, args: Split<char>, out: &mut [u8], ) -> Result<Option<usize>, CommandError>28 fn get( 29 &self, 30 gbl_fb: &mut GblFastboot, 31 name: &str, 32 args: Split<char>, 33 out: &mut [u8], 34 ) -> Result<Option<usize>, CommandError>; 35 36 /// Iterates and calls `f` on all values/arguments combinations. get_all( &self, gbl_fb: &mut GblFastboot, f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, ) -> Result<(), CommandError>37 fn get_all( 38 &self, 39 gbl_fb: &mut GblFastboot, 40 f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, 41 ) -> Result<(), CommandError>; 42 } 43 44 // Constant fastboot variable 45 impl Variable for (&'static str, &'static str) { get( &self, _: &mut GblFastboot, name: &str, _: Split<char>, out: &mut [u8], ) -> Result<Option<usize>, CommandError>46 fn get( 47 &self, 48 _: &mut GblFastboot, 49 name: &str, 50 _: Split<char>, 51 out: &mut [u8], 52 ) -> Result<Option<usize>, CommandError> { 53 Ok((name == self.0).then_some(snprintf!(out, "{}", self.1).len())) 54 } 55 get_all( &self, _: &mut GblFastboot, f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, ) -> Result<(), CommandError>56 fn get_all( 57 &self, 58 _: &mut GblFastboot, 59 f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, 60 ) -> Result<(), CommandError> { 61 f(self.0, &[], self.1) 62 } 63 } 64 65 /// `Partition` variable provides information of GPT partitions 66 /// 67 /// `fastboot getvar partition-size:<GBL Fastboot partition>` 68 /// `fastboot getvar partition-type:<GBL Fastboot partition>` 69 pub(crate) struct Partition {} 70 71 const PARTITION_SIZE: &str = "partition-size"; 72 const PARTITION_TYPE: &str = "partition-type"; 73 impl Variable for Partition { get( &self, gbl_fb: &mut GblFastboot, name: &str, args: Split<char>, out: &mut [u8], ) -> Result<Option<usize>, CommandError>74 fn get( 75 &self, 76 gbl_fb: &mut GblFastboot, 77 name: &str, 78 args: Split<char>, 79 out: &mut [u8], 80 ) -> Result<Option<usize>, CommandError> { 81 let part = gbl_fb.parse_partition(args)?; 82 Ok(match name { 83 PARTITION_SIZE => Some(snprintf!(out, "{:#x}", gbl_fb.partition_io(part).size()).len()), 84 PARTITION_TYPE => Some(snprintf!(out, "raw").len()), // Image type not supported yet. 85 _ => None, 86 }) 87 } 88 get_all( &self, gbl_fb: &mut GblFastboot, f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, ) -> Result<(), CommandError>89 fn get_all( 90 &self, 91 gbl_fb: &mut GblFastboot, 92 f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, 93 ) -> Result<(), CommandError> { 94 // Though any sub range of a GPT partition or raw block counts as a partition in GBL 95 // Fastboot, for "getvar all" we only enumerate whole range GPT partitions. 96 let mut res: Result<(), CommandError> = Ok(()); 97 let part_name = &mut [0u8; GPT_NAME_LEN_U8][..]; 98 let mut size_str = [0u8; 32]; 99 gbl_fb.storage().for_each_until(&mut |mut v, id| { 100 // `AsBlockDevice::partition_iter()` has `Self:Sized` constraint thus we make it into a 101 // `&mut &mut dyn AsBlockDevice` to meet the bound requirement. 102 let v = &mut v; 103 let mut id_str = [0u8; 32]; 104 let id = snprintf!(id_str, "{:x}", id); 105 res = (|| { 106 for ptn in v.partition_iter() { 107 let sz: u64 = ptn.size()?; 108 let part = ptn.gpt_entry().name_to_str(part_name)?; 109 f(PARTITION_SIZE, &[part, id], snprintf!(size_str, "{:#x}", sz))?; 110 // Image type is not supported yet. 111 f(PARTITION_TYPE, &[part, id], snprintf!(size_str, "raw"))?; 112 } 113 Ok(()) 114 })(); 115 res.is_err() 116 })?; 117 res 118 } 119 } 120 121 /// `BlockDevice` variable provides information of block devices. 122 /// 123 /// `fastboot getvar block-device:<id>:total-blocks` 124 /// `fastboot getvar block-device:<id>:block-size` 125 pub(crate) struct BlockDevice {} 126 127 const BLOCK_DEVICE: &str = "block-device"; 128 const TOTAL_BLOCKS: &str = "total-blocks"; 129 const BLOCK_SIZE: &str = "block-size"; 130 impl Variable for BlockDevice { get( &self, gbl_fb: &mut GblFastboot, name: &str, mut args: Split<char>, out: &mut [u8], ) -> Result<Option<usize>, CommandError>131 fn get( 132 &self, 133 gbl_fb: &mut GblFastboot, 134 name: &str, 135 mut args: Split<char>, 136 out: &mut [u8], 137 ) -> Result<Option<usize>, CommandError> { 138 Ok(match name { 139 BLOCK_DEVICE => { 140 let id = next_arg_u64(&mut args, Err("Missing block device ID".into()))?; 141 let val_type = next_arg(&mut args, Err("Missing value type".into()))?; 142 let val = match val_type { 143 TOTAL_BLOCKS => gbl_fb.storage().get(id)?.num_blocks()?, 144 BLOCK_SIZE => gbl_fb.storage().get(id)?.block_size()?, 145 _ => return Err("Invalid type".into()), 146 }; 147 Some(snprintf!(out, "{:#x}", val).len()) 148 } 149 _ => None, 150 }) 151 } 152 get_all( &self, gbl_fb: &mut GblFastboot, f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, ) -> Result<(), CommandError>153 fn get_all( 154 &self, 155 gbl_fb: &mut GblFastboot, 156 f: &mut dyn FnMut(&str, &[&str], &str) -> Result<(), CommandError>, 157 ) -> Result<(), CommandError> { 158 let mut val = [0u8; 32]; 159 let mut res: Result<(), CommandError> = Ok(()); 160 gbl_fb.storage().for_each_until(&mut |blk, id| { 161 let mut id_str = [0u8; 32]; 162 let id = snprintf!(id_str, "{:x}", id); 163 res = (|| { 164 f(BLOCK_DEVICE, &[id, "total-blocks"], snprintf!(val, "{:#x}", blk.num_blocks()?))?; 165 f(BLOCK_DEVICE, &[id, "block-size"], snprintf!(val, "{:#x}", blk.block_size()?)) 166 })(); 167 res.is_err() 168 })?; 169 res 170 } 171 } 172