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