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