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(¤t_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