blob: d0a6b7f4214fec6a3d0df14c996b98cd4e7a0ee0 [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;
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +000019use core::ops::Range;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010020use core::result;
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +010021use log::{info, warn};
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010022use static_assertions::const_assert_eq;
Pierre-Clément Tosi33001492023-08-14 17:57:30 +010023use vmbase::util::RangeExt;
Alan Stokesa0e42962023-04-14 17:59:50 +010024use zerocopy::{FromBytes, LayoutVerified};
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010025
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000026/// Configuration data header.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010027#[repr(C, packed)]
Alan Stokesa0e42962023-04-14 17:59:50 +010028#[derive(Clone, Copy, Debug, FromBytes)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010029struct Header {
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000030 /// Magic number; must be `Header::MAGIC`.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010031 magic: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000032 /// Version of the header format.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090033 version: Version,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000034 /// Total size of the configuration data.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010035 total_size: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000036 /// Feature flags; currently reserved and must be zero.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010037 flags: u32,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010038}
39
40#[derive(Debug)]
41pub enum Error {
42 /// Reserved region can't fit configuration header.
43 BufferTooSmall,
Alan Stokesa0e42962023-04-14 17:59:50 +010044 /// Header has the wrong alignment
45 HeaderMisaligned,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010046 /// Header doesn't contain the expect magic value.
47 InvalidMagic,
48 /// Version of the header isn't supported.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090049 UnsupportedVersion(Version),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010050 /// Header describes configuration data that doesn't fit in the expected buffer.
51 InvalidSize(usize),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000052 /// Header entry is missing.
53 MissingEntry(Entry),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010054 /// Range described by entry does not fit within config data.
55 EntryOutOfBounds(Entry, Range<usize>, Range<usize>),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010056}
57
58impl fmt::Display for Error {
59 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60 match self {
61 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"),
Alan Stokesa0e42962023-04-14 17:59:50 +010062 Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010063 Self::InvalidMagic => write!(f, "Wrong magic number"),
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090064 Self::UnsupportedVersion(v) => write!(f, "Version {v} not supported"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010065 Self::InvalidSize(sz) => write!(f, "Total size ({sz:#x}) overflows reserved region"),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000066 Self::MissingEntry(entry) => write!(f, "Mandatory {entry:?} entry is missing"),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010067 Self::EntryOutOfBounds(entry, range, limits) => {
68 write!(
69 f,
70 "Entry {entry:?} out of bounds: {range:#x?} must be within range {limits:#x?}"
71 )
72 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010073 }
74 }
75}
76
77pub type Result<T> = result::Result<T, Error>;
78
79impl Header {
80 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090081 const VERSION_1_0: Version = Version { major: 1, minor: 0 };
Jaewan Kimbf5978c2023-07-28 11:30:54 +000082 const VERSION_1_1: Version = Version { major: 1, minor: 1 };
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010083
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010084 pub fn total_size(&self) -> usize {
85 self.total_size as usize
86 }
87
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +010088 pub fn body_lowest_bound(&self) -> Result<usize> {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +010089 let entries_offset = mem::size_of::<Self>();
90
91 // Ensure that the entries are properly aligned and do not require padding.
92 const_assert_eq!(mem::align_of::<Header>() % mem::align_of::<HeaderEntry>(), 0);
93 const_assert_eq!(mem::size_of::<Header>() % mem::align_of::<HeaderEntry>(), 0);
94
95 let entries_size = self.entry_count()?.checked_mul(mem::size_of::<HeaderEntry>()).unwrap();
96
97 Ok(entries_offset.checked_add(entries_size).unwrap())
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010098 }
99
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100100 pub fn entry_count(&self) -> Result<usize> {
101 let last_entry = match self.version {
102 Self::VERSION_1_0 => Entry::DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000103 Self::VERSION_1_1 => Entry::VmDtbo,
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100104 v @ Version { major: 1, .. } => {
105 const LATEST: Version = Header::VERSION_1_1;
106 warn!("Parsing unknown config data version {v} as version {LATEST}");
107 return Ok(Entry::COUNT);
108 }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100109 v => return Err(Error::UnsupportedVersion(v)),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100110 };
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000111
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100112 Ok(last_entry as usize + 1)
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100113 }
114}
115
116#[derive(Clone, Copy, Debug)]
117pub enum Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100118 Bcc,
119 DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000120 VmDtbo,
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100121 #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
122 _VARIANT_COUNT,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100123}
124
125impl Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100126 const COUNT: usize = Self::_VARIANT_COUNT as usize;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100127}
128
129#[repr(packed)]
Alan Stokesa0e42962023-04-14 17:59:50 +0100130#[derive(Clone, Copy, Debug, FromBytes)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100131struct HeaderEntry {
132 offset: u32,
133 size: u32,
134}
135
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100136impl HeaderEntry {
137 pub fn as_range(&self) -> Option<Range<usize>> {
138 let size = usize::try_from(self.size).unwrap();
139 if size != 0 {
140 let offset = self.offset.try_into().unwrap();
141 // Allow overflows here for the Range to properly describe the entry (validated later).
142 Some(offset..(offset + size))
143 } else {
144 None
145 }
146 }
147}
148
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900149#[repr(C, packed)]
150#[derive(Clone, Copy, Debug, Eq, FromBytes, PartialEq)]
151pub struct Version {
152 minor: u16,
153 major: u16,
154}
155
156impl fmt::Display for Version {
157 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158 // Copy the fields to local variables to prevent unaligned access.
159 let (major, minor) = (self.major, self.minor);
160 write!(f, "{}.{}", major, minor)
161 }
162}
163
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100164#[derive(Debug)]
165pub struct Config<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100166 body: &'a mut [u8],
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100167 ranges: [Option<Range<usize>>; Entry::COUNT],
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100168}
169
170impl<'a> Config<'a> {
171 /// Take ownership of a pvmfw configuration consisting of its header and following entries.
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100172 pub fn new(bytes: &'a mut [u8]) -> Result<Self> {
173 const HEADER_SIZE: usize = mem::size_of::<Header>();
174 if bytes.len() < HEADER_SIZE {
175 return Err(Error::BufferTooSmall);
176 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100177
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100178 let (header, rest) =
179 LayoutVerified::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
Alan Stokesa0e42962023-04-14 17:59:50 +0100180 let header = header.into_ref();
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100181
182 if header.magic != Header::MAGIC {
183 return Err(Error::InvalidMagic);
184 }
185
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100186 let header_flags = header.flags;
187 if header_flags != 0 {
188 warn!("Ignoring unknown config flags: {header_flags:#x}");
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100189 }
190
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000191 info!("pvmfw config version: {}", header.version);
192
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100193 // Validate that we won't get an invalid alignment in the following due to padding to u64.
194 const_assert_eq!(HEADER_SIZE % mem::size_of::<u64>(), 0);
195
196 // Ensure that Header::total_size isn't larger than anticipated by the caller and resize
197 // the &[u8] to catch OOB accesses to entries/blobs.
198 let total_size = header.total_size();
199 let rest = if let Some(rest_size) = total_size.checked_sub(HEADER_SIZE) {
200 rest.get_mut(..rest_size).ok_or(Error::InvalidSize(total_size))?
201 } else {
202 return Err(Error::InvalidSize(total_size));
203 };
204
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100205 let (header_entries, body) =
206 LayoutVerified::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?)
207 .ok_or(Error::BufferTooSmall)?;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100208
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100209 // Validate that we won't get an invalid alignment in the following due to padding to u64.
210 const_assert_eq!(mem::size_of::<HeaderEntry>() % mem::size_of::<u64>(), 0);
211
Pierre-Clément Tosice3e3132023-08-17 10:24:22 +0100212 let limits = header.body_lowest_bound()?..total_size;
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100213 let ranges = [
214 // TODO: Find a way to do this programmatically even if the trait
215 // `core::marker::Copy` is not implemented for `core::ops::Range<usize>`.
216 Self::validated_body_range(Entry::Bcc, &header_entries, &limits)?,
217 Self::validated_body_range(Entry::DebugPolicy, &header_entries, &limits)?,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000218 Self::validated_body_range(Entry::VmDtbo, &header_entries, &limits)?,
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100219 ];
220
221 Ok(Self { body, ranges })
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100222 }
223
224 /// Get slice containing the platform BCC.
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100225 pub fn get_entries(&mut self) -> Result<(&mut [u8], Option<&mut [u8]>)> {
226 // This assumes that the blobs are in-order w.r.t. the entries.
227 let bcc_range = self.get_entry_range(Entry::Bcc).ok_or(Error::MissingEntry(Entry::Bcc))?;
228 let dp_range = self.get_entry_range(Entry::DebugPolicy);
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000229 let vm_dtbo_range = self.get_entry_range(Entry::VmDtbo);
230 // TODO(b/291191157): Provision device assignment with this.
231 if let Some(vm_dtbo_range) = vm_dtbo_range {
232 info!("Found VM DTBO at {:?}", vm_dtbo_range);
233 }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100234 let bcc_start = bcc_range.start;
235 let bcc_end = bcc_range.len();
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000236 let (_, rest) = self.body.split_at_mut(bcc_start);
237 let (bcc, rest) = rest.split_at_mut(bcc_end);
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100238
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100239 let dp = if let Some(dp_range) = dp_range {
240 let dp_start = dp_range.start.checked_sub(bcc_range.end).unwrap();
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000241 let dp_end = dp_range.len();
242 let (_, rest) = rest.split_at_mut(dp_start);
243 let (dp, _) = rest.split_at_mut(dp_end);
244 Some(dp)
245 } else {
246 None
247 };
248
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100249 Ok((bcc, dp))
250 }
251
252 pub fn get_entry_range(&self, entry: Entry) -> Option<Range<usize>> {
253 self.ranges[entry as usize].clone()
254 }
255
256 fn validated_body_range(
257 entry: Entry,
258 header_entries: &[HeaderEntry],
259 limits: &Range<usize>,
260 ) -> Result<Option<Range<usize>>> {
261 if let Some(header_entry) = header_entries.get(entry as usize) {
262 if let Some(r) = header_entry.as_range() {
263 return if r.start <= r.end && r.is_within(limits) {
264 let start = r.start - limits.start;
265 let end = r.end - limits.start;
266
267 Ok(Some(start..end))
268 } else {
269 Err(Error::EntryOutOfBounds(entry, r, limits.clone()))
270 };
271 }
272 }
273
274 Ok(None)
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100275 }
276}