blob: 2fe4ec996f4cb3c8708a78a8d322bab29f6a97d6 [file] [log] [blame]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +01001// 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
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010017use core::fmt;
18use core::mem;
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +090019use core::num::NonZeroUsize;
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +000020use core::ops::Range;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010021use core::result;
Jaewan Kimb266e0e2023-09-27 15:44:53 +090022use core::slice;
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +010023use log::{info, warn};
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010024use static_assertions::const_assert_eq;
Pierre-Clément Tosi33001492023-08-14 17:57:30 +010025use vmbase::util::RangeExt;
Frederick Mayle2e779942023-10-15 18:27:31 +000026use zerocopy::{FromBytes, FromZeroes, LayoutVerified};
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010027
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000028/// Configuration data header.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010029#[repr(C, packed)]
Frederick Mayle2e779942023-10-15 18:27:31 +000030#[derive(Clone, Copy, Debug, FromZeroes, FromBytes)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010031struct Header {
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000032 /// Magic number; must be `Header::MAGIC`.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010033 magic: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000034 /// Version of the header format.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090035 version: Version,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000036 /// Total size of the configuration data.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010037 total_size: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000038 /// Feature flags; currently reserved and must be zero.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010039 flags: u32,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010040}
41
42#[derive(Debug)]
43pub enum Error {
44 /// Reserved region can't fit configuration header.
45 BufferTooSmall,
Alan Stokesa0e42962023-04-14 17:59:50 +010046 /// Header has the wrong alignment
47 HeaderMisaligned,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010048 /// Header doesn't contain the expect magic value.
49 InvalidMagic,
50 /// Version of the header isn't supported.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090051 UnsupportedVersion(Version),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010052 /// Header describes configuration data that doesn't fit in the expected buffer.
53 InvalidSize(usize),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000054 /// Header entry is missing.
55 MissingEntry(Entry),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010056 /// Range described by entry does not fit within config data.
57 EntryOutOfBounds(Entry, Range<usize>, Range<usize>),
Jaewan Kimb266e0e2023-09-27 15:44:53 +090058 /// Entries are in out of order
59 EntryOutOfOrder,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010060}
61
62impl fmt::Display for Error {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 match self {
65 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"),
Alan Stokesa0e42962023-04-14 17:59:50 +010066 Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010067 Self::InvalidMagic => write!(f, "Wrong magic number"),
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090068 Self::UnsupportedVersion(v) => write!(f, "Version {v} not supported"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010069 Self::InvalidSize(sz) => write!(f, "Total size ({sz:#x}) overflows reserved region"),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000070 Self::MissingEntry(entry) => write!(f, "Mandatory {entry:?} entry is missing"),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010071 Self::EntryOutOfBounds(entry, range, limits) => {
72 write!(
73 f,
74 "Entry {entry:?} out of bounds: {range:#x?} must be within range {limits:#x?}"
75 )
76 }
Jaewan Kimb266e0e2023-09-27 15:44:53 +090077 Self::EntryOutOfOrder => write!(f, "Entries are out of order"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010078 }
79 }
80}
81
82pub type Result<T> = result::Result<T, Error>;
83
84impl Header {
85 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090086 const VERSION_1_0: Version = Version { major: 1, minor: 0 };
Jaewan Kimbf5978c2023-07-28 11:30:54 +000087 const VERSION_1_1: Version = Version { major: 1, minor: 1 };
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010088
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010089 pub fn total_size(&self) -> usize {
90 self.total_size as usize
91 }
92
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +010093 pub fn body_lowest_bound(&self) -> Result<usize> {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +010094 let entries_offset = mem::size_of::<Self>();
95
96 // Ensure that the entries are properly aligned and do not require padding.
97 const_assert_eq!(mem::align_of::<Header>() % mem::align_of::<HeaderEntry>(), 0);
98 const_assert_eq!(mem::size_of::<Header>() % mem::align_of::<HeaderEntry>(), 0);
99
100 let entries_size = self.entry_count()?.checked_mul(mem::size_of::<HeaderEntry>()).unwrap();
101
102 Ok(entries_offset.checked_add(entries_size).unwrap())
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100103 }
104
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100105 pub fn entry_count(&self) -> Result<usize> {
106 let last_entry = match self.version {
107 Self::VERSION_1_0 => Entry::DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000108 Self::VERSION_1_1 => Entry::VmDtbo,
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100109 v @ Version { major: 1, .. } => {
110 const LATEST: Version = Header::VERSION_1_1;
111 warn!("Parsing unknown config data version {v} as version {LATEST}");
112 return Ok(Entry::COUNT);
113 }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100114 v => return Err(Error::UnsupportedVersion(v)),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100115 };
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000116
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100117 Ok(last_entry as usize + 1)
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100118 }
119}
120
121#[derive(Clone, Copy, Debug)]
122pub enum Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100123 Bcc,
124 DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000125 VmDtbo,
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100126 #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
127 _VARIANT_COUNT,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100128}
129
130impl Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100131 const COUNT: usize = Self::_VARIANT_COUNT as usize;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100132}
133
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000134#[derive(Default)]
135pub struct Entries<'a> {
136 pub bcc: &'a mut [u8],
137 pub debug_policy: Option<&'a mut [u8]>,
138 pub vm_dtbo: Option<&'a mut [u8]>,
139}
140
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100141#[repr(packed)]
Frederick Mayle2e779942023-10-15 18:27:31 +0000142#[derive(Clone, Copy, Debug, FromZeroes, FromBytes)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100143struct HeaderEntry {
144 offset: u32,
145 size: u32,
146}
147
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900148#[repr(C, packed)]
Frederick Mayle2e779942023-10-15 18:27:31 +0000149#[derive(Clone, Copy, Debug, Eq, FromZeroes, FromBytes, PartialEq)]
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900150pub struct Version {
151 minor: u16,
152 major: u16,
153}
154
155impl fmt::Display for Version {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 // Copy the fields to local variables to prevent unaligned access.
158 let (major, minor) = (self.major, self.minor);
159 write!(f, "{}.{}", major, minor)
160 }
161}
162
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900163/// Range with non-empty length.
164#[derive(Debug, Copy, Clone)]
165struct NonEmptyRange {
166 start: usize,
167 size: NonZeroUsize,
168}
169
170impl NonEmptyRange {
171 pub fn new(start: usize, size: usize) -> Option<Self> {
172 // Ensure end() is safe.
173 start.checked_add(size).unwrap();
174
175 Some(Self { start, size: NonZeroUsize::new(size)? })
176 }
177
178 fn end(&self) -> usize {
179 self.start + self.len()
180 }
181
182 fn len(&self) -> usize {
183 self.size.into()
184 }
185
186 fn as_range(&self) -> Range<usize> {
187 self.start..self.end()
188 }
189}
190
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100191#[derive(Debug)]
192pub struct Config<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100193 body: &'a mut [u8],
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900194 ranges: [Option<NonEmptyRange>; Entry::COUNT],
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100195}
196
197impl<'a> Config<'a> {
198 /// Take ownership of a pvmfw configuration consisting of its header and following entries.
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100199 pub fn new(bytes: &'a mut [u8]) -> Result<Self> {
200 const HEADER_SIZE: usize = mem::size_of::<Header>();
201 if bytes.len() < HEADER_SIZE {
202 return Err(Error::BufferTooSmall);
203 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100204
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100205 let (header, rest) =
206 LayoutVerified::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
Alan Stokesa0e42962023-04-14 17:59:50 +0100207 let header = header.into_ref();
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100208
209 if header.magic != Header::MAGIC {
210 return Err(Error::InvalidMagic);
211 }
212
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100213 let header_flags = header.flags;
214 if header_flags != 0 {
215 warn!("Ignoring unknown config flags: {header_flags:#x}");
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100216 }
217
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000218 info!("pvmfw config version: {}", header.version);
219
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100220 // Validate that we won't get an invalid alignment in the following due to padding to u64.
221 const_assert_eq!(HEADER_SIZE % mem::size_of::<u64>(), 0);
222
223 // Ensure that Header::total_size isn't larger than anticipated by the caller and resize
224 // the &[u8] to catch OOB accesses to entries/blobs.
225 let total_size = header.total_size();
226 let rest = if let Some(rest_size) = total_size.checked_sub(HEADER_SIZE) {
227 rest.get_mut(..rest_size).ok_or(Error::InvalidSize(total_size))?
228 } else {
229 return Err(Error::InvalidSize(total_size));
230 };
231
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100232 let (header_entries, body) =
233 LayoutVerified::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?)
234 .ok_or(Error::BufferTooSmall)?;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100235
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100236 // Validate that we won't get an invalid alignment in the following due to padding to u64.
237 const_assert_eq!(mem::size_of::<HeaderEntry>() % mem::size_of::<u64>(), 0);
238
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900239 // Ensure entries are in the body.
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100240 let limits = header.body_lowest_bound()?..total_size;
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900241 let mut ranges: [Option<NonEmptyRange>; Entry::COUNT] = [None; Entry::COUNT];
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900242 let mut last_end = 0;
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900243 for entry in [Entry::Bcc, Entry::DebugPolicy, Entry::VmDtbo] {
244 let Some(header_entry) = header_entries.get(entry as usize) else { continue };
245 let entry_offset = header_entry.offset.try_into().unwrap();
246 let entry_size = header_entry.size.try_into().unwrap();
247 let Some(range) = NonEmptyRange::new(entry_offset, entry_size) else { continue };
248 let range = range.as_range();
249 if !range.is_within(&limits) {
250 return Err(Error::EntryOutOfBounds(entry, range, limits));
251 }
252
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900253 if last_end > range.start {
254 return Err(Error::EntryOutOfOrder);
255 }
256 last_end = range.end;
257
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900258 ranges[entry as usize] = NonEmptyRange::new(
259 entry_offset - limits.start, // is_within() validates safety of this.
260 entry_size,
261 );
262 }
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900263 // Ensures that BCC exists.
264 ranges[Entry::Bcc as usize].ok_or(Error::MissingEntry(Entry::Bcc))?;
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100265
266 Ok(Self { body, ranges })
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100267 }
268
269 /// Get slice containing the platform BCC.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000270 pub fn get_entries(&mut self) -> Entries<'_> {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100271 // This assumes that the blobs are in-order w.r.t. the entries.
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900272 let bcc_range = self.get_entry_range(Entry::Bcc);
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100273 let dp_range = self.get_entry_range(Entry::DebugPolicy);
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000274 let vm_dtbo_range = self.get_entry_range(Entry::VmDtbo);
275 // TODO(b/291191157): Provision device assignment with this.
276 if let Some(vm_dtbo_range) = vm_dtbo_range {
277 info!("Found VM DTBO at {:?}", vm_dtbo_range);
278 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100279
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000280 // SAFETY: When instantiated, ranges are validated to be in the body range without
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900281 // overlapping.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000282 let (bcc, debug_policy, vm_dtbo) = unsafe {
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900283 let ptr = self.body.as_mut_ptr() as usize;
284 (
285 Self::from_raw_range_mut(ptr, bcc_range.unwrap()),
286 dp_range.map(|dp_range| Self::from_raw_range_mut(ptr, dp_range)),
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900287 vm_dtbo_range.map(|vm_dtbo_range| Self::from_raw_range_mut(ptr, vm_dtbo_range)),
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900288 )
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000289 };
290 Entries { bcc, debug_policy, vm_dtbo }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100291 }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100292
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900293 fn get_entry_range(&self, entry: Entry) -> Option<NonEmptyRange> {
294 self.ranges[entry as usize]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100295 }
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900296
297 unsafe fn from_raw_range_mut(ptr: usize, range: NonEmptyRange) -> &'a mut [u8] {
298 // SAFETY: The caller must ensure that the range is valid from ptr.
Seungjae Yoo13615ba2023-12-07 11:21:19 +0900299 unsafe { slice::from_raw_parts_mut((ptr + range.start) as *mut u8, range.len()) }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100300 }
301}