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::PinFutContainerTyped; 16 use crate::{ 17 fastboot::{BufferPool, GblFastboot}, 18 GblOps, 19 }; 20 use core::{ffi::CStr, future::Future, ops::DerefMut, str::from_utf8}; 21 use fastboot::{next_arg, next_arg_u64, CommandResult, VarInfoSender}; 22 use gbl_async::{block_on, select, yield_now}; 23 use gbl_storage::BlockIo; 24 use libutils::snprintf; 25 26 // See definition of [GblFastboot] for docs on lifetimes and generics parameters. 27 impl<'a: 'c, 'b: 'c, 'c, 'd, 'e, G, B, S, T, P, C, F> 28 GblFastboot<'a, 'b, 'c, 'd, 'e, G, B, S, T, P, C, F> 29 where 30 G: GblOps<'a, 'e>, 31 B: BlockIo, 32 S: DerefMut<Target = [u8]>, 33 T: DerefMut<Target = [u8]>, 34 P: BufferPool, 35 C: PinFutContainerTyped<'c, F>, 36 F: Future<Output = ()> + 'c, 37 { 38 const VERSION_BOOTLOADER: &'static str = "version-bootloader"; 39 const VERSION_BOOTLOADER_VAL: &'static str = "1.0"; 40 41 const MAX_FETCH_SIZE: &'static str = "max-fetch-size"; 42 const MAX_FETCH_SIZE_VAL: &'static str = "0xffffffffffffffff"; 43 44 /// Entry point for "fastboot getvar <variable>..." get_var_internal<'s, 't>( &mut self, name: &CStr, args: impl Iterator<Item = &'t CStr> + Clone, out: &'s mut [u8], ) -> CommandResult<&'s str>45 pub(crate) fn get_var_internal<'s, 't>( 46 &mut self, 47 name: &CStr, 48 args: impl Iterator<Item = &'t CStr> + Clone, 49 out: &'s mut [u8], 50 ) -> CommandResult<&'s str> { 51 let args_str = args.clone().map(|v| v.to_str()); 52 // Checks that all arguments are valid str first. 53 args_str.clone().find(|v| v.is_err()).unwrap_or(Ok(""))?; 54 let args_str = args_str.map(|v| v.unwrap()); 55 Ok(match name.to_str()? { 56 Self::VERSION_BOOTLOADER => snprintf!(out, "{}", Self::VERSION_BOOTLOADER_VAL), 57 Self::MAX_FETCH_SIZE => snprintf!(out, "{}", Self::MAX_FETCH_SIZE_VAL), 58 Self::PARTITION_SIZE => self.get_var_partition_size(args_str, out)?, 59 Self::PARTITION_TYPE => self.get_var_partition_type(args_str, out)?, 60 Self::BLOCK_DEVICE => self.get_var_block_device(args_str, out)?, 61 Self::DEFAULT_BLOCK => self.get_var_default_block(out)?, 62 _ => { 63 let sz = self.gbl_ops.fastboot_variable(name, args, out)?; 64 from_utf8(out.get(..sz).ok_or("Invalid variable value size")?)? 65 } 66 }) 67 } 68 69 /// Entry point for "fastboot getvar all..." get_var_all_internal( &mut self, send: &mut impl VarInfoSender, ) -> CommandResult<()>70 pub(crate) async fn get_var_all_internal( 71 &mut self, 72 send: &mut impl VarInfoSender, 73 ) -> CommandResult<()> { 74 send.send_var_info(Self::VERSION_BOOTLOADER, [], Self::VERSION_BOOTLOADER_VAL).await?; 75 send.send_var_info(Self::MAX_FETCH_SIZE, [], Self::MAX_FETCH_SIZE_VAL).await?; 76 self.get_all_block_device(send).await?; 77 let mut buf = [0u8; 32]; 78 send.send_var_info(Self::DEFAULT_BLOCK, [], self.get_var_default_block(&mut buf)?).await?; 79 self.get_all_partition_size_type(send).await?; 80 81 // Gets platform specific variables 82 let tasks = self.tasks(); 83 Ok(self.gbl_ops.fastboot_visit_all_variables(|args, val| { 84 if let Some((name, args)) = args.split_first_chunk::<1>() { 85 let name = name[0].to_str().unwrap_or("?"); 86 let args = args.iter().map(|v| v.to_str().unwrap_or("?")); 87 let val = val.to_str().unwrap_or("?"); 88 // Manually polls async tasks so that we can still get parallelism while running in 89 // the context of backend. 90 let _ = block_on(select(send.send_var_info(name, args, val), async { 91 loop { 92 tasks.borrow_mut().poll_all(); 93 yield_now().await; 94 } 95 })); 96 } 97 })?) 98 } 99 100 const PARTITION_SIZE: &'static str = "partition-size"; 101 const PARTITION_TYPE: &'static str = "partition-type"; 102 103 /// "fastboot getvar partition-size" get_var_partition_size<'s, 't>( &mut self, mut args: impl Iterator<Item = &'t str> + Clone, out: &'s mut [u8], ) -> CommandResult<&'s str>104 fn get_var_partition_size<'s, 't>( 105 &mut self, 106 mut args: impl Iterator<Item = &'t str> + Clone, 107 out: &'s mut [u8], 108 ) -> CommandResult<&'s str> { 109 let (_, _, _, sz) = self.parse_partition(args.next().ok_or("Missing partition")?)?; 110 Ok(snprintf!(out, "{:#x}", sz)) 111 } 112 113 /// "fastboot getvar partition-type" get_var_partition_type<'s, 't>( &mut self, mut args: impl Iterator<Item = &'t str> + Clone, out: &'s mut [u8], ) -> CommandResult<&'s str>114 fn get_var_partition_type<'s, 't>( 115 &mut self, 116 mut args: impl Iterator<Item = &'t str> + Clone, 117 out: &'s mut [u8], 118 ) -> CommandResult<&'s str> { 119 self.parse_partition(args.next().ok_or("Missing partition")?)?; 120 Ok(snprintf!(out, "raw")) 121 } 122 123 /// Gets all "partition-size/partition-type" get_all_partition_size_type( &mut self, responder: &mut impl VarInfoSender, ) -> CommandResult<()>124 async fn get_all_partition_size_type( 125 &mut self, 126 responder: &mut impl VarInfoSender, 127 ) -> CommandResult<()> { 128 // Though any sub range of a GPT partition or raw block counts as a partition in GBL 129 // Fastboot, for "getvar all" we only enumerate whole range GPT partitions. 130 let disks = self.disks; 131 let mut size_str = [0u8; 32]; 132 for (idx, blk) in disks.iter().enumerate() { 133 for ptn_idx in 0..blk.num_partitions().unwrap_or(0) { 134 let ptn = blk.get_partition_by_idx(ptn_idx)?; 135 let sz: u64 = ptn.size()?; 136 let part = ptn.name()?; 137 // Assumes max partition name length of 72 plus max u64 hex string length 18. 138 let mut part_id_buf = [0u8; 128]; 139 let part = snprintf!(part_id_buf, "{}/{:x}", part, idx); 140 responder 141 .send_var_info(Self::PARTITION_SIZE, [part], snprintf!(size_str, "{:#x}", sz)) 142 .await?; 143 // Image type is not supported yet. 144 responder 145 .send_var_info(Self::PARTITION_TYPE, [part], snprintf!(size_str, "raw")) 146 .await?; 147 } 148 } 149 Ok(()) 150 } 151 152 const BLOCK_DEVICE: &'static str = "block-device"; 153 const TOTAL_BLOCKS: &'static str = "total-blocks"; 154 const BLOCK_SIZE: &'static str = "block-size"; 155 const BLOCK_DEVICE_STATUS: &'static str = "status"; 156 157 /// Block device related information. 158 /// 159 /// `fastboot getvar block-device:<id>:total-blocks` 160 /// `fastboot getvar block-device:<id>:block-size` 161 /// `fastboot getvar block-device:<id>:status` get_var_block_device<'s, 't>( &mut self, mut args: impl Iterator<Item = &'t str> + Clone, out: &'s mut [u8], ) -> CommandResult<&'s str>162 fn get_var_block_device<'s, 't>( 163 &mut self, 164 mut args: impl Iterator<Item = &'t str> + Clone, 165 out: &'s mut [u8], 166 ) -> CommandResult<&'s str> { 167 let id = next_arg_u64(&mut args)?.ok_or("Missing block device ID")?; 168 let id = usize::try_from(id)?; 169 let val_type = next_arg(&mut args).ok_or("Missing value type")?; 170 let blk = &self.disks[id]; 171 let info = blk.block_info(); 172 Ok(match val_type { 173 Self::TOTAL_BLOCKS => snprintf!(out, "{:#x}", info.num_blocks), 174 Self::BLOCK_SIZE => snprintf!(out, "{:#x}", info.block_size), 175 Self::BLOCK_DEVICE_STATUS => { 176 snprintf!(out, "{}", blk.status().to_str()) 177 } 178 _ => return Err("Invalid type".into()), 179 }) 180 } 181 182 /// Gets all "block-device" variables. get_all_block_device( &mut self, responder: &mut impl VarInfoSender, ) -> CommandResult<()>183 async fn get_all_block_device( 184 &mut self, 185 responder: &mut impl VarInfoSender, 186 ) -> CommandResult<()> { 187 let mut val = [0u8; 32]; 188 for (idx, blk) in self.gbl_ops.disks().iter().enumerate() { 189 let mut id_str = [0u8; 32]; 190 let id = snprintf!(id_str, "{:x}", idx); 191 let info = blk.block_info(); 192 responder 193 .send_var_info( 194 Self::BLOCK_DEVICE, 195 [id, Self::TOTAL_BLOCKS], 196 snprintf!(val, "{:#x}", info.num_blocks), 197 ) 198 .await?; 199 responder 200 .send_var_info( 201 Self::BLOCK_DEVICE, 202 [id, Self::BLOCK_SIZE], 203 snprintf!(val, "{:#x}", info.block_size), 204 ) 205 .await?; 206 responder 207 .send_var_info( 208 Self::BLOCK_DEVICE, 209 [id, Self::BLOCK_DEVICE_STATUS], 210 snprintf!(val, "{}", blk.status().to_str()), 211 ) 212 .await?; 213 } 214 Ok(()) 215 } 216 217 const DEFAULT_BLOCK: &'static str = "gbl-default-block"; 218 219 /// "fastboot getvar gbl-default-block" get_var_default_block<'s>(&mut self, out: &'s mut [u8]) -> CommandResult<&'s str>220 fn get_var_default_block<'s>(&mut self, out: &'s mut [u8]) -> CommandResult<&'s str> { 221 Ok(match self.default_block { 222 Some(v) => snprintf!(out, "{:#x}", v), 223 None => snprintf!(out, "None"), 224 }) 225 } 226 } 227