1 // Copyright 2023, 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 //! Library for constructing kernel bootconfig. See the following for more detail:
16 //!
17 //!   https://source.android.com/docs/core/architecture/bootloader/implementing-bootconfig#bootloader-changes
18 
19 #![cfg_attr(not(test), no_std)]
20 
21 /// Library error type.
22 #[derive(Debug)]
23 pub enum BootConfigError {
24     ArithmeticOverflow,
25     BufferTooSmall,
26     InvalidConfigString,
27     GenericReaderError(i64),
28 }
29 
30 /// The type of Result used in this library.
31 pub type Result<T> = core::result::Result<T, BootConfigError>;
32 
33 /// A class for constructing bootconfig section.
34 pub struct BootConfigBuilder<'a> {
35     current_size: usize,
36     buffer: &'a mut [u8],
37 }
38 
39 const BOOTCONFIG_MAGIC: &str = "#BOOTCONFIG\n";
40 // Trailer structure:
41 // struct {
42 //     config_size: u32,
43 //     checksum: u32,
44 //     bootconfig_magic: [u8]
45 // }
46 const BOOTCONFIG_TRAILER_SIZE: usize = 4 + 4 + BOOTCONFIG_MAGIC.len();
47 
48 impl<'a> BootConfigBuilder<'a> {
49     /// Initialize with a given buffer.
new(buffer: &'a mut [u8]) -> Result<Self>50     pub fn new(buffer: &'a mut [u8]) -> Result<Self> {
51         if buffer.len() < BOOTCONFIG_TRAILER_SIZE {
52             return Err(BootConfigError::BufferTooSmall);
53         }
54         let mut ret = Self { current_size: 0, buffer: buffer };
55         ret.update_trailer()?;
56         Ok(ret)
57     }
58 
59     /// Get the remaining capacity for adding new bootconfig.
remaining_capacity(&self) -> usize60     pub fn remaining_capacity(&self) -> usize {
61         self.buffer
62             .len()
63             .checked_sub(self.current_size)
64             .unwrap()
65             .checked_sub(BOOTCONFIG_TRAILER_SIZE)
66             .unwrap()
67     }
68 
69     /// Get the whole config bytes including trailer.
config_bytes(&self) -> &[u8]70     pub fn config_bytes(&self) -> &[u8] {
71         // Arithmetic not expected to fail.
72         &self.buffer[..self.current_size.checked_add(BOOTCONFIG_TRAILER_SIZE).unwrap()]
73     }
74 
75     /// Append a new config via a reader callback.
76     ///
77     /// A `&mut [u8]` that covers the remaining space is passed to the callback for reading the
78     /// config bytes. It should return the total size read if operation is successful or a custom
79     /// error code via `BootConfigError::GenericReaderError(<code>)`. Attempting to return a size
80     /// greater than the input will cause it to panic. Empty read is allowed. It's up to the caller
81     /// to make sure the read content will eventually form a valid boot config. The API is for
82     /// situations where configs are read from sources such as disk and separate buffer allocation
83     /// is not possible or desired.
add_with<F>(&mut self, reader: F) -> Result<()> where F: FnOnce(&mut [u8]) -> Result<usize>,84     pub fn add_with<F>(&mut self, reader: F) -> Result<()>
85     where
86         F: FnOnce(&mut [u8]) -> Result<usize>,
87     {
88         let remains = self.remaining_capacity();
89         let size = reader(&mut self.buffer[self.current_size..][..remains])?;
90         assert!(size <= remains);
91         self.current_size += size;
92         // Content may have been modified. Re-compute trailer.
93         self.update_trailer()
94     }
95 
96     /// Append a new config from string.
add(&mut self, config: &str) -> Result<()>97     pub fn add(&mut self, config: &str) -> Result<()> {
98         if self.remaining_capacity() < config.len() {
99             return Err(BootConfigError::BufferTooSmall);
100         }
101         self.add_with(|out| {
102             out[..config.len()].clone_from_slice(config.as_bytes());
103             Ok(config.len())
104         })
105     }
106 
107     /// Update the boot config trailer at the end of parameter list.
108     /// See specification at:
109     /// https://source.android.com/docs/core/architecture/bootloader/implementing-bootconfig#bootloader-changes
update_trailer(&mut self) -> Result<()>110     fn update_trailer(&mut self) -> Result<()> {
111         // Config size
112         let size: u32 =
113             self.current_size.try_into().map_err(|_| BootConfigError::ArithmeticOverflow)?;
114         // Check sum.
115         let checksum = self.checksum();
116         let trailer = &mut self.buffer[self.current_size..];
117         trailer[..4].clone_from_slice(&size.to_le_bytes());
118         trailer[4..8].clone_from_slice(&checksum.to_le_bytes());
119         trailer[8..][..BOOTCONFIG_MAGIC.len()].clone_from_slice(BOOTCONFIG_MAGIC.as_bytes());
120         Ok(())
121     }
122 
123     /// Compute the checksum value.
checksum(&self) -> u32124     fn checksum(&self) -> u32 {
125         self.buffer[..self.current_size]
126             .iter()
127             .map(|v| *v as u32)
128             .reduce(|acc, v| acc.overflowing_add(v).0)
129             .unwrap_or(0)
130     }
131 }
132 
133 impl core::fmt::Display for BootConfigBuilder<'_> {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result134     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135         let bytes = self.config_bytes();
136         for val in &bytes[..bytes.len().checked_sub(BOOTCONFIG_TRAILER_SIZE).unwrap()] {
137             write!(f, "{}", core::ascii::escape_default(*val))?;
138         }
139         Ok(())
140     }
141 }
142 
143 impl core::fmt::Write for BootConfigBuilder<'_> {
write_str(&mut self, s: &str) -> core::fmt::Result144     fn write_str(&mut self, s: &str) -> core::fmt::Result {
145         self.add_with(|out| {
146             out.get_mut(..s.len())
147                 .ok_or(BootConfigError::BufferTooSmall)?
148                 .clone_from_slice(s.as_bytes());
149             Ok(s.len())
150         })
151         .map_err(|_| core::fmt::Error)?;
152         Ok(())
153     }
154 }
155 
156 #[cfg(test)]
157 mod test {
158     use super::*;
159     use core::fmt::Write;
160 
161     // Taken from Cuttlefish on QEMU aarch64.
162     const TEST_CONFIG: &str = "androidboot.hardware=cutf_cvm
163 kernel.mac80211_hwsim.radios=0
164 kernel.vmw_vsock_virtio_transport_common.virtio_transport_max_vsock_pkt_buf_size=16384
165 androidboot.vendor.apex.com.google.emulated.camera.provider.hal=com.google.emulated.camera.provider.hal
166 androidboot.slot_suffix=_a
167 androidboot.force_normal_boot=1
168 androidboot.hw_timeout_multiplier=50
169 androidboot.fstab_suffix=cf.f2fs.hctr2
170 androidboot.hypervisor.protected_vm.supported=0
171 androidboot.modem_simulator_ports=9600
172 androidboot.vsock_lights_port=6900
173 androidboot.lcd_density=320
174 androidboot.vendor.audiocontrol.server.port=9410
175 androidboot.vendor.audiocontrol.server.cid=3
176 androidboot.cuttlefish_config_server_port=6800
177 androidboot.hardware.gralloc=minigbm
178 androidboot.vsock_lights_cid=3
179 androidboot.enable_confirmationui=0
180 androidboot.hypervisor.vm.supported=0
181 androidboot.setupwizard_mode=DISABLED
182 androidboot.serialno=CUTTLEFISHCVD011
183 androidboot.enable_bootanimation=1
184 androidboot.hardware.hwcomposer.display_finder_mode=drm
185 androidboot.hardware.angle_feature_overrides_enabled=preferLinearFilterForYUV:mapUnspecifiedColorSpaceToPassThrough
186 androidboot.hardware.egl=mesa
187 androidboot.boot_devices=4010000000.pcie
188 androidboot.opengles.version=196608
189 androidboot.wifi_mac_prefix=5554
190 androidboot.vsock_tombstone_port=6600
191 androidboot.hardware.hwcomposer=ranchu
192 androidboot.hardware.hwcomposer.mode=client
193 androidboot.console=ttyAMA0
194 androidboot.ddr_size=4915MB
195 androidboot.cpuvulkan.version=0
196 androidboot.serialconsole=1
197 androidboot.vbmeta.device=PARTUUID=2b7e273a-42a1-654b-bbad-8cb6ab2b6911
198 androidboot.vbmeta.avb_version=1.1
199 androidboot.vbmeta.device_state=unlocked
200 androidboot.vbmeta.hash_alg=sha256
201 androidboot.vbmeta.size=23040
202 androidboot.vbmeta.digest=6d6cdbad779475dd945ed79e6bd79c0574541d34ff488fa5aeeb024d739dd0d2
203 androidboot.vbmeta.invalidate_on_error=yes
204 androidboot.veritymode=enforcing
205 androidboot.verifiedbootstate=orange
206 ";
207 
208     const TEST_CONFIG_TRAILER: &[u8; BOOTCONFIG_TRAILER_SIZE] =
209         b"i\x07\x00\x00\xf9\xc4\x02\x00#BOOTCONFIG\n";
210 
211     #[test]
test_add()212     fn test_add() {
213         let mut buffer = [0u8; TEST_CONFIG.len() + TEST_CONFIG_TRAILER.len()];
214         let mut builder = BootConfigBuilder::new(&mut buffer[..]).unwrap();
215         builder.add(TEST_CONFIG).unwrap();
216         assert_eq!(
217             builder.config_bytes().to_vec(),
218             [TEST_CONFIG.as_bytes(), TEST_CONFIG_TRAILER].concat().to_vec()
219         );
220     }
221 
222     #[test]
test_add_incremental()223     fn test_add_incremental() {
224         let mut buffer = [0u8; TEST_CONFIG.len() + TEST_CONFIG_TRAILER.len()];
225         let mut builder = BootConfigBuilder::new(&mut buffer[..]).unwrap();
226         for ele in TEST_CONFIG.strip_suffix('\n').unwrap().split('\n') {
227             let config = std::string::String::from(ele) + "\n";
228             builder.add(config.as_str()).unwrap();
229         }
230         assert_eq!(
231             builder.config_bytes().to_vec(),
232             [TEST_CONFIG.as_bytes(), TEST_CONFIG_TRAILER].concat().to_vec()
233         );
234     }
235 
236     #[test]
test_add_incremental_via_fmt_write()237     fn test_add_incremental_via_fmt_write() {
238         let mut buffer = [0u8; TEST_CONFIG.len() + TEST_CONFIG_TRAILER.len()];
239         let mut builder = BootConfigBuilder::new(&mut buffer[..]).unwrap();
240         for ele in TEST_CONFIG.strip_suffix('\n').unwrap().split('\n') {
241             write!(builder, "{}\n", ele).unwrap();
242         }
243         assert_eq!(
244             builder.config_bytes().to_vec(),
245             [TEST_CONFIG.as_bytes(), TEST_CONFIG_TRAILER].concat().to_vec()
246         );
247     }
248 
249     #[test]
test_new_buffer_too_small()250     fn test_new_buffer_too_small() {
251         let mut buffer = [0u8; BOOTCONFIG_TRAILER_SIZE - 1];
252         assert!(BootConfigBuilder::new(&mut buffer[..]).is_err());
253     }
254 
255     #[test]
test_add_buffer_too_small()256     fn test_add_buffer_too_small() {
257         let mut buffer = [0u8; BOOTCONFIG_TRAILER_SIZE + 1];
258         let mut builder = BootConfigBuilder::new(&mut buffer[..]).unwrap();
259         assert!(builder.add("a\n").is_err());
260     }
261 
262     #[test]
test_add_empty_string()263     fn test_add_empty_string() {
264         let mut buffer = [0u8; BOOTCONFIG_TRAILER_SIZE + 1];
265         let mut builder = BootConfigBuilder::new(&mut buffer[..]).unwrap();
266         builder.add("").unwrap();
267     }
268 
269     #[test]
test_add_with_error()270     fn test_add_with_error() {
271         let mut buffer = [0u8; BOOTCONFIG_TRAILER_SIZE + 1];
272         let mut builder = BootConfigBuilder::new(&mut buffer[..]).unwrap();
273         assert!(builder.add_with(|_| Err(BootConfigError::GenericReaderError(123))).is_err());
274     }
275 }
276