• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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