1 // Copyright 2022, 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 //! Support for the pvmfw configuration data format. 16 17 use core::fmt; 18 use core::mem; 19 use core::num::NonZeroUsize; 20 use core::ops::Range; 21 use core::result; 22 use log::{info, warn}; 23 use static_assertions::const_assert_eq; 24 use vmbase::util::RangeExt; 25 use zerocopy::FromBytes; 26 use zerocopy::Immutable; 27 use zerocopy::KnownLayout; 28 use zerocopy::Ref; 29 30 /// Configuration data header. 31 #[repr(C, packed)] 32 #[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout)] 33 struct Header { 34 /// Magic number; must be `Header::MAGIC`. 35 magic: u32, 36 /// Version of the header format. 37 version: Version, 38 /// Total size of the configuration data. 39 total_size: u32, 40 /// Feature flags; currently reserved and must be zero. 41 flags: u32, 42 } 43 44 #[derive(Debug)] 45 pub enum Error { 46 /// Reserved region can't fit configuration header. 47 BufferTooSmall, 48 /// Header has the wrong alignment 49 HeaderMisaligned, 50 /// Header doesn't contain the expect magic value. 51 InvalidMagic, 52 /// Version of the header isn't supported. 53 UnsupportedVersion(Version), 54 /// Header describes configuration data that doesn't fit in the expected buffer. 55 InvalidSize(usize), 56 /// Header entry is missing. 57 MissingEntry(Entry), 58 /// Range described by entry does not fit within config data. 59 EntryOutOfBounds(Entry, Range<usize>, Range<usize>), 60 /// Entries are in out of order 61 EntryOutOfOrder, 62 } 63 64 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 match self { 67 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"), 68 Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"), 69 Self::InvalidMagic => write!(f, "Wrong magic number"), 70 Self::UnsupportedVersion(v) => write!(f, "Version {v} not supported"), 71 Self::InvalidSize(sz) => write!(f, "Total size ({sz:#x}) overflows reserved region"), 72 Self::MissingEntry(entry) => write!(f, "Mandatory {entry:?} entry is missing"), 73 Self::EntryOutOfBounds(entry, range, limits) => { 74 write!( 75 f, 76 "Entry {entry:?} out of bounds: {range:#x?} must be within range {limits:#x?}" 77 ) 78 } 79 Self::EntryOutOfOrder => write!(f, "Entries are out of order"), 80 } 81 } 82 } 83 84 pub type Result<T> = result::Result<T, Error>; 85 86 impl Header { 87 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf"); 88 const VERSION_1_0: Version = Version { major: 1, minor: 0 }; 89 const VERSION_1_1: Version = Version { major: 1, minor: 1 }; 90 const VERSION_1_2: Version = Version { major: 1, minor: 2 }; 91 total_size(&self) -> usize92 pub fn total_size(&self) -> usize { 93 self.total_size as usize 94 } 95 body_lowest_bound(&self) -> Result<usize>96 pub fn body_lowest_bound(&self) -> Result<usize> { 97 let entries_offset = mem::size_of::<Self>(); 98 99 // Ensure that the entries are properly aligned and do not require padding. 100 const_assert_eq!(mem::align_of::<Header>() % mem::align_of::<HeaderEntry>(), 0); 101 const_assert_eq!(mem::size_of::<Header>() % mem::align_of::<HeaderEntry>(), 0); 102 103 let entries_size = self.entry_count()?.checked_mul(mem::size_of::<HeaderEntry>()).unwrap(); 104 105 Ok(entries_offset.checked_add(entries_size).unwrap()) 106 } 107 entry_count(&self) -> Result<usize>108 pub fn entry_count(&self) -> Result<usize> { 109 let last_entry = match self.version { 110 Self::VERSION_1_0 => Entry::DebugPolicy, 111 Self::VERSION_1_1 => Entry::VmDtbo, 112 Self::VERSION_1_2 => Entry::VmBaseDtbo, 113 v @ Version { major: 1, .. } => { 114 const LATEST: Version = Header::VERSION_1_2; 115 warn!("Parsing unknown config data version {v} as version {LATEST}"); 116 return Ok(Entry::COUNT); 117 } 118 v => return Err(Error::UnsupportedVersion(v)), 119 }; 120 121 Ok(last_entry as usize + 1) 122 } 123 } 124 125 #[derive(Clone, Copy, Debug)] 126 pub enum Entry { 127 DiceHandover, 128 DebugPolicy, 129 VmDtbo, 130 VmBaseDtbo, 131 #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable. 132 _VARIANT_COUNT, 133 } 134 135 impl Entry { 136 const COUNT: usize = Self::_VARIANT_COUNT as usize; 137 138 const ALL_ENTRIES: [Entry; Self::COUNT] = 139 [Self::DiceHandover, Self::DebugPolicy, Self::VmDtbo, Self::VmBaseDtbo]; 140 } 141 142 #[derive(Default)] 143 pub struct Entries<'a> { 144 pub dice_handover: Option<&'a mut [u8]>, 145 pub debug_policy: Option<&'a [u8]>, 146 pub vm_dtbo: Option<&'a mut [u8]>, 147 pub vm_ref_dt: Option<&'a [u8]>, 148 } 149 150 #[repr(packed)] 151 #[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout)] 152 struct HeaderEntry { 153 offset: u32, 154 size: u32, 155 } 156 157 #[repr(C, packed)] 158 #[derive(Clone, Copy, Debug, Eq, FromBytes, Immutable, KnownLayout, PartialEq)] 159 pub struct Version { 160 minor: u16, 161 major: u16, 162 } 163 164 impl fmt::Display for Version { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result165 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 166 // Copy the fields to local variables to prevent unaligned access. 167 let (major, minor) = (self.major, self.minor); 168 write!(f, "{}.{}", major, minor) 169 } 170 } 171 172 /// Range with non-empty length. 173 #[derive(Debug, Copy, Clone)] 174 struct NonEmptyRange { 175 start: usize, 176 size: NonZeroUsize, 177 } 178 179 impl NonEmptyRange { new(start: usize, size: usize) -> Option<Self>180 pub fn new(start: usize, size: usize) -> Option<Self> { 181 // Ensure end() is safe. 182 start.checked_add(size).unwrap(); 183 184 Some(Self { start, size: NonZeroUsize::new(size)? }) 185 } 186 end(&self) -> usize187 fn end(&self) -> usize { 188 self.start + self.len() 189 } 190 len(&self) -> usize191 fn len(&self) -> usize { 192 self.size.into() 193 } 194 as_range(&self) -> Range<usize>195 fn as_range(&self) -> Range<usize> { 196 self.start..self.end() 197 } 198 } 199 200 #[derive(Debug)] 201 pub struct Config<'a> { 202 body: &'a mut [u8], 203 ranges: [Option<NonEmptyRange>; Entry::COUNT], 204 } 205 206 impl<'a> Config<'a> { 207 /// Take ownership of a pvmfw configuration consisting of its header and following entries. new(bytes: &'a mut [u8]) -> Result<Self>208 pub fn new(bytes: &'a mut [u8]) -> Result<Self> { 209 const HEADER_SIZE: usize = mem::size_of::<Header>(); 210 if bytes.len() < HEADER_SIZE { 211 return Err(Error::BufferTooSmall); 212 } 213 214 let (header, rest) = 215 Ref::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?; 216 let header = Ref::into_ref(header); 217 218 if header.magic != Header::MAGIC { 219 return Err(Error::InvalidMagic); 220 } 221 222 let header_flags = header.flags; 223 if header_flags != 0 { 224 warn!("Ignoring unknown config flags: {header_flags:#x}"); 225 } 226 227 info!("pvmfw config version: {}", header.version); 228 229 // Validate that we won't get an invalid alignment in the following due to padding to u64. 230 const_assert_eq!(HEADER_SIZE % mem::size_of::<u64>(), 0); 231 232 // Ensure that Header::total_size isn't larger than anticipated by the caller and resize 233 // the &[u8] to catch OOB accesses to entries/blobs. 234 let total_size = header.total_size(); 235 let rest = if let Some(rest_size) = total_size.checked_sub(HEADER_SIZE) { 236 rest.get_mut(..rest_size).ok_or(Error::InvalidSize(total_size))? 237 } else { 238 return Err(Error::InvalidSize(total_size)); 239 }; 240 241 let (header_entries, body) = 242 zerocopy::Ref::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?) 243 .ok_or(Error::BufferTooSmall)?; 244 245 // Validate that we won't get an invalid alignment in the following due to padding to u64. 246 const_assert_eq!(mem::size_of::<HeaderEntry>() % mem::size_of::<u64>(), 0); 247 248 // Ensure entries are in the body. 249 let limits = header.body_lowest_bound()?..total_size; 250 let mut ranges: [Option<NonEmptyRange>; Entry::COUNT] = [None; Entry::COUNT]; 251 let mut last_end = 0; 252 for entry in Entry::ALL_ENTRIES { 253 let Some(header_entry) = header_entries.get(entry as usize) else { continue }; 254 let entry_offset = header_entry.offset.try_into().unwrap(); 255 let entry_size = header_entry.size.try_into().unwrap(); 256 let Some(range) = NonEmptyRange::new(entry_offset, entry_size) else { continue }; 257 let range = range.as_range(); 258 if !range.is_within(&limits) { 259 return Err(Error::EntryOutOfBounds(entry, range, limits)); 260 } 261 262 if last_end > range.start { 263 return Err(Error::EntryOutOfOrder); 264 } 265 last_end = range.end; 266 267 ranges[entry as usize] = NonEmptyRange::new( 268 entry_offset - limits.start, // is_within() validates safety of this. 269 entry_size, 270 ); 271 } 272 // Ensures that the DICE handover is present. 273 ranges[Entry::DiceHandover as usize].ok_or(Error::MissingEntry(Entry::DiceHandover))?; 274 275 Ok(Self { body, ranges }) 276 } 277 278 /// Locate the various config entries. get_entries(self) -> Entries<'a>279 pub fn get_entries(self) -> Entries<'a> { 280 // We require the blobs to be in the same order as the `Entry` enum (and this is checked 281 // in `new` above) 282 // So we can just work through the body range and split off the parts we are interested in. 283 let mut offset = 0; 284 let mut body = self.body; 285 286 let mut entries: [Option<&mut [u8]>; Entry::COUNT] = Default::default(); 287 for (i, range) in self.ranges.iter().enumerate() { 288 if let Some(range) = range { 289 body = &mut body[range.start - offset..]; 290 let (chunk, rest) = body.split_at_mut(range.len()); 291 offset = range.end(); 292 body = rest; 293 entries[i] = Some(chunk); 294 } 295 } 296 let [dice_handover, debug_policy, vm_dtbo, vm_ref_dt] = entries; 297 298 // We have no reason to mutate so drop the `mut`. 299 let debug_policy = debug_policy.map(|x| &*x); 300 let vm_ref_dt = vm_ref_dt.map(|x| &*x); 301 302 Entries { dice_handover, debug_policy, vm_dtbo, vm_ref_dt } 303 } 304 } 305