blob: dbfde155d374951bcb5f2038aca3e23ae53a50e9 [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;
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +010022use log::{info, warn};
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010023use static_assertions::const_assert_eq;
Pierre-Clément Tosi33001492023-08-14 17:57:30 +010024use vmbase::util::RangeExt;
Andrew Walbran47d316e2024-11-28 18:41:09 +000025use zerocopy::FromBytes;
26use zerocopy::Immutable;
27use zerocopy::KnownLayout;
28use zerocopy::Ref;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010029
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000030/// Configuration data header.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010031#[repr(C, packed)]
Andrew Walbran47d316e2024-11-28 18:41:09 +000032#[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010033struct Header {
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000034 /// Magic number; must be `Header::MAGIC`.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010035 magic: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000036 /// Version of the header format.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090037 version: Version,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000038 /// Total size of the configuration data.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010039 total_size: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000040 /// Feature flags; currently reserved and must be zero.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010041 flags: u32,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010042}
43
44#[derive(Debug)]
45pub enum Error {
46 /// Reserved region can't fit configuration header.
47 BufferTooSmall,
Alan Stokesa0e42962023-04-14 17:59:50 +010048 /// Header has the wrong alignment
49 HeaderMisaligned,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010050 /// Header doesn't contain the expect magic value.
51 InvalidMagic,
52 /// Version of the header isn't supported.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090053 UnsupportedVersion(Version),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010054 /// Header describes configuration data that doesn't fit in the expected buffer.
55 InvalidSize(usize),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000056 /// Header entry is missing.
57 MissingEntry(Entry),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010058 /// Range described by entry does not fit within config data.
59 EntryOutOfBounds(Entry, Range<usize>, Range<usize>),
Jaewan Kimb266e0e2023-09-27 15:44:53 +090060 /// Entries are in out of order
61 EntryOutOfOrder,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010062}
63
64impl fmt::Display for Error {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 match self {
67 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"),
Alan Stokesa0e42962023-04-14 17:59:50 +010068 Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010069 Self::InvalidMagic => write!(f, "Wrong magic number"),
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090070 Self::UnsupportedVersion(v) => write!(f, "Version {v} not supported"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010071 Self::InvalidSize(sz) => write!(f, "Total size ({sz:#x}) overflows reserved region"),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000072 Self::MissingEntry(entry) => write!(f, "Mandatory {entry:?} entry is missing"),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010073 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 }
Jaewan Kimb266e0e2023-09-27 15:44:53 +090079 Self::EntryOutOfOrder => write!(f, "Entries are out of order"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010080 }
81 }
82}
83
84pub type Result<T> = result::Result<T, Error>;
85
86impl Header {
87 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090088 const VERSION_1_0: Version = Version { major: 1, minor: 0 };
Jaewan Kimbf5978c2023-07-28 11:30:54 +000089 const VERSION_1_1: Version = Version { major: 1, minor: 1 };
Seungjae Yoo013f4c42024-01-02 13:04:19 +090090 const VERSION_1_2: Version = Version { major: 1, minor: 2 };
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010091
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010092 pub fn total_size(&self) -> usize {
93 self.total_size as usize
94 }
95
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +010096 pub fn body_lowest_bound(&self) -> Result<usize> {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +010097 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())
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100106 }
107
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100108 pub fn entry_count(&self) -> Result<usize> {
109 let last_entry = match self.version {
110 Self::VERSION_1_0 => Entry::DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000111 Self::VERSION_1_1 => Entry::VmDtbo,
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900112 Self::VERSION_1_2 => Entry::VmBaseDtbo,
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100113 v @ Version { major: 1, .. } => {
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900114 const LATEST: Version = Header::VERSION_1_2;
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100115 warn!("Parsing unknown config data version {v} as version {LATEST}");
116 return Ok(Entry::COUNT);
117 }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100118 v => return Err(Error::UnsupportedVersion(v)),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100119 };
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000120
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100121 Ok(last_entry as usize + 1)
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100122 }
123}
124
125#[derive(Clone, Copy, Debug)]
126pub enum Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100127 Bcc,
128 DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000129 VmDtbo,
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900130 VmBaseDtbo,
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100131 #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
132 _VARIANT_COUNT,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100133}
134
135impl Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100136 const COUNT: usize = Self::_VARIANT_COUNT as usize;
Alan Stokes65618332023-12-15 14:09:25 +0000137
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900138 const ALL_ENTRIES: [Entry; Self::COUNT] =
139 [Self::Bcc, Self::DebugPolicy, Self::VmDtbo, Self::VmBaseDtbo];
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100140}
141
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000142#[derive(Default)]
143pub struct Entries<'a> {
144 pub bcc: &'a mut [u8],
Alan Stokes65618332023-12-15 14:09:25 +0000145 pub debug_policy: Option<&'a [u8]>,
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000146 pub vm_dtbo: Option<&'a mut [u8]>,
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900147 pub vm_ref_dt: Option<&'a [u8]>,
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000148}
149
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100150#[repr(packed)]
Andrew Walbran47d316e2024-11-28 18:41:09 +0000151#[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100152struct HeaderEntry {
153 offset: u32,
154 size: u32,
155}
156
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900157#[repr(C, packed)]
Andrew Walbran47d316e2024-11-28 18:41:09 +0000158#[derive(Clone, Copy, Debug, Eq, FromBytes, Immutable, KnownLayout, PartialEq)]
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900159pub struct Version {
160 minor: u16,
161 major: u16,
162}
163
164impl fmt::Display for Version {
165 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
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900172/// Range with non-empty length.
173#[derive(Debug, Copy, Clone)]
174struct NonEmptyRange {
175 start: usize,
176 size: NonZeroUsize,
177}
178
179impl NonEmptyRange {
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
187 fn end(&self) -> usize {
188 self.start + self.len()
189 }
190
191 fn len(&self) -> usize {
192 self.size.into()
193 }
194
195 fn as_range(&self) -> Range<usize> {
196 self.start..self.end()
197 }
198}
199
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100200#[derive(Debug)]
201pub struct Config<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100202 body: &'a mut [u8],
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900203 ranges: [Option<NonEmptyRange>; Entry::COUNT],
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100204}
205
206impl<'a> Config<'a> {
207 /// Take ownership of a pvmfw configuration consisting of its header and following entries.
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100208 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 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100213
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100214 let (header, rest) =
Andrew Walbran47d316e2024-11-28 18:41:09 +0000215 Ref::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
216 let header = Ref::into_ref(header);
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100217
218 if header.magic != Header::MAGIC {
219 return Err(Error::InvalidMagic);
220 }
221
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100222 let header_flags = header.flags;
223 if header_flags != 0 {
224 warn!("Ignoring unknown config flags: {header_flags:#x}");
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100225 }
226
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000227 info!("pvmfw config version: {}", header.version);
228
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100229 // 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
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100241 let (header_entries, body) =
Alan Stokes65618332023-12-15 14:09:25 +0000242 zerocopy::Ref::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?)
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100243 .ok_or(Error::BufferTooSmall)?;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100244
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100245 // 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
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900248 // Ensure entries are in the body.
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100249 let limits = header.body_lowest_bound()?..total_size;
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900250 let mut ranges: [Option<NonEmptyRange>; Entry::COUNT] = [None; Entry::COUNT];
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900251 let mut last_end = 0;
Alan Stokes65618332023-12-15 14:09:25 +0000252 for entry in Entry::ALL_ENTRIES {
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900253 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
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900262 if last_end > range.start {
263 return Err(Error::EntryOutOfOrder);
264 }
265 last_end = range.end;
266
Jaewan Kim3dfc0ab2023-09-27 14:00:59 +0900267 ranges[entry as usize] = NonEmptyRange::new(
268 entry_offset - limits.start, // is_within() validates safety of this.
269 entry_size,
270 );
271 }
Jaewan Kimb266e0e2023-09-27 15:44:53 +0900272 // Ensures that BCC exists.
273 ranges[Entry::Bcc as usize].ok_or(Error::MissingEntry(Entry::Bcc))?;
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100274
275 Ok(Self { body, ranges })
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100276 }
277
Alan Stokes65618332023-12-15 14:09:25 +0000278 /// Locate the various config entries.
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 }
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000295 }
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900296 let [bcc, debug_policy, vm_dtbo, vm_ref_dt] = entries;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100297
Alan Stokes65618332023-12-15 14:09:25 +0000298 // The platform BCC has always been required.
299 let bcc = bcc.unwrap();
300
301 // We have no reason to mutate so drop the `mut`.
302 let debug_policy = debug_policy.map(|x| &*x);
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900303 let vm_ref_dt = vm_ref_dt.map(|x| &*x);
Alan Stokes82cefb32024-01-08 12:10:08 +0000304
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900305 Entries { bcc, debug_policy, vm_dtbo, vm_ref_dt }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100306 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100307}