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