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 crate::helpers; 18 use core::fmt; 19 use core::mem; 20 use core::ops::Range; 21 use core::result; 22 use zerocopy::{FromBytes, LayoutVerified}; 23 24 /// Configuration data header. 25 #[repr(C, packed)] 26 #[derive(Clone, Copy, Debug, FromBytes)] 27 struct Header { 28 /// Magic number; must be `Header::MAGIC`. 29 magic: u32, 30 /// Version of the header format. 31 version: u32, 32 /// Total size of the configuration data. 33 total_size: u32, 34 /// Feature flags; currently reserved and must be zero. 35 flags: u32, 36 /// (offset, size) pairs used to locate individual entries appended to the header. 37 entries: [HeaderEntry; Entry::COUNT], 38 } 39 40 #[derive(Debug)] 41 pub enum Error { 42 /// Reserved region can't fit configuration header. 43 BufferTooSmall, 44 /// Header has the wrong alignment 45 HeaderMisaligned, 46 /// Header doesn't contain the expect magic value. 47 InvalidMagic, 48 /// Version of the header isn't supported. 49 UnsupportedVersion(u16, u16), 50 /// Header sets flags incorrectly or uses reserved flags. 51 InvalidFlags(u32), 52 /// Header describes configuration data that doesn't fit in the expected buffer. 53 InvalidSize(usize), 54 /// Header entry is missing. 55 MissingEntry(Entry), 56 /// Header entry is invalid. 57 InvalidEntry(Entry, EntryError), 58 } 59 60 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 62 match self { 63 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"), 64 Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"), 65 Self::InvalidMagic => write!(f, "Wrong magic number"), 66 Self::UnsupportedVersion(x, y) => write!(f, "Version {x}.{y} not supported"), 67 Self::InvalidFlags(v) => write!(f, "Flags value {v:#x} is incorrect or reserved"), 68 Self::InvalidSize(sz) => write!(f, "Total size ({sz:#x}) overflows reserved region"), 69 Self::MissingEntry(entry) => write!(f, "Mandatory {entry:?} entry is missing"), 70 Self::InvalidEntry(entry, e) => write!(f, "Invalid {entry:?} entry: {e}"), 71 } 72 } 73 } 74 75 pub type Result<T> = result::Result<T, Error>; 76 77 #[derive(Debug)] 78 pub enum EntryError { 79 /// Offset isn't between the fixed minimum value and size of configuration data. 80 InvalidOffset(usize), 81 /// Size must be zero when offset is and not be when it isn't. 82 InvalidSize(usize), 83 /// Entry isn't fully within the configuration data structure. 84 OutOfBounds { offset: usize, size: usize, limit: usize }, 85 } 86 87 impl fmt::Display for EntryError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 89 match self { 90 Self::InvalidOffset(offset) => write!(f, "Invalid offset: {offset:#x?}"), 91 Self::InvalidSize(sz) => write!(f, "Invalid size: {sz:#x?}"), 92 Self::OutOfBounds { offset, size, limit } => { 93 let range = Header::PADDED_SIZE..*limit; 94 let entry = *offset..(*offset + *size); 95 write!(f, "Out of bounds: {entry:#x?} must be within range {range:#x?}") 96 } 97 } 98 } 99 } 100 101 impl Header { 102 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf"); 103 const VERSION_1_0: u32 = Self::version(1, 0); 104 const PADDED_SIZE: usize = 105 helpers::unchecked_align_up(mem::size_of::<Self>(), mem::size_of::<u64>()); 106 version(major: u16, minor: u16) -> u32107 pub const fn version(major: u16, minor: u16) -> u32 { 108 ((major as u32) << 16) | (minor as u32) 109 } 110 version_tuple(&self) -> (u16, u16)111 pub const fn version_tuple(&self) -> (u16, u16) { 112 ((self.version >> 16) as u16, self.version as u16) 113 } 114 total_size(&self) -> usize115 pub fn total_size(&self) -> usize { 116 self.total_size as usize 117 } 118 body_size(&self) -> usize119 pub fn body_size(&self) -> usize { 120 self.total_size() - Self::PADDED_SIZE 121 } 122 get_body_range(&self, entry: Entry) -> Result<Option<Range<usize>>>123 fn get_body_range(&self, entry: Entry) -> Result<Option<Range<usize>>> { 124 let e = self.entries[entry as usize]; 125 let offset = e.offset as usize; 126 let size = e.size as usize; 127 128 match self._get_body_range(offset, size) { 129 Ok(r) => Ok(r), 130 Err(EntryError::InvalidSize(0)) => { 131 // As our bootloader currently uses this (non-compliant) case, permit it for now. 132 log::warn!("Config entry {entry:?} uses non-zero offset with zero size"); 133 // TODO(b/262181812): Either make this case valid or fix the bootloader. 134 Ok(None) 135 } 136 Err(e) => Err(Error::InvalidEntry(entry, e)), 137 } 138 } 139 _get_body_range( &self, offset: usize, size: usize, ) -> result::Result<Option<Range<usize>>, EntryError>140 fn _get_body_range( 141 &self, 142 offset: usize, 143 size: usize, 144 ) -> result::Result<Option<Range<usize>>, EntryError> { 145 match (offset, size) { 146 (0, 0) => Ok(None), 147 (0, size) | (_, size @ 0) => Err(EntryError::InvalidSize(size)), 148 _ => { 149 let start = offset 150 .checked_sub(Header::PADDED_SIZE) 151 .ok_or(EntryError::InvalidOffset(offset))?; 152 let end = start 153 .checked_add(size) 154 .filter(|x| *x <= self.body_size()) 155 .ok_or(EntryError::OutOfBounds { offset, size, limit: self.total_size() })?; 156 157 Ok(Some(start..end)) 158 } 159 } 160 } 161 } 162 163 #[derive(Clone, Copy, Debug)] 164 pub enum Entry { 165 Bcc = 0, 166 DebugPolicy = 1, 167 } 168 169 impl Entry { 170 const COUNT: usize = 2; 171 } 172 173 #[repr(packed)] 174 #[derive(Clone, Copy, Debug, FromBytes)] 175 struct HeaderEntry { 176 offset: u32, 177 size: u32, 178 } 179 180 #[derive(Debug)] 181 pub struct Config<'a> { 182 body: &'a mut [u8], 183 bcc_range: Range<usize>, 184 dp_range: Option<Range<usize>>, 185 } 186 187 impl<'a> Config<'a> { 188 /// Take ownership of a pvmfw configuration consisting of its header and following entries. 189 /// 190 /// SAFETY - 'data' should respect the alignment of Header. new(data: &'a mut [u8]) -> Result<Self>191 pub unsafe fn new(data: &'a mut [u8]) -> Result<Self> { 192 let header = data.get(..Header::PADDED_SIZE).ok_or(Error::BufferTooSmall)?; 193 194 let (header, _) = 195 LayoutVerified::<_, Header>::new_from_prefix(header).ok_or(Error::HeaderMisaligned)?; 196 let header = header.into_ref(); 197 198 if header.magic != Header::MAGIC { 199 return Err(Error::InvalidMagic); 200 } 201 202 if header.version != Header::VERSION_1_0 { 203 let (major, minor) = header.version_tuple(); 204 return Err(Error::UnsupportedVersion(major, minor)); 205 } 206 207 if header.flags != 0 { 208 return Err(Error::InvalidFlags(header.flags)); 209 } 210 211 let bcc_range = 212 header.get_body_range(Entry::Bcc)?.ok_or(Error::MissingEntry(Entry::Bcc))?; 213 let dp_range = header.get_body_range(Entry::DebugPolicy)?; 214 215 let body_size = header.body_size(); 216 let total_size = header.total_size(); 217 let body = data 218 .get_mut(Header::PADDED_SIZE..) 219 .ok_or(Error::BufferTooSmall)? 220 .get_mut(..body_size) 221 .ok_or(Error::InvalidSize(total_size))?; 222 223 Ok(Self { body, bcc_range, dp_range }) 224 } 225 226 /// Get slice containing the platform BCC. get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>)227 pub fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) { 228 let bcc_start = self.bcc_range.start; 229 let bcc_end = self.bcc_range.len(); 230 let (_, rest) = self.body.split_at_mut(bcc_start); 231 let (bcc, rest) = rest.split_at_mut(bcc_end); 232 233 let dp = if let Some(dp_range) = &self.dp_range { 234 let dp_start = dp_range.start.checked_sub(self.bcc_range.end).unwrap(); 235 let dp_end = dp_range.len(); 236 let (_, rest) = rest.split_at_mut(dp_start); 237 let (dp, _) = rest.split_at_mut(dp_end); 238 Some(dp) 239 } else { 240 None 241 }; 242 243 (bcc, dp) 244 } 245 } 246