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