xref: /aosp_15_r20/bootable/libbootloader/gbl/libgbl/src/fastboot/vars.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
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