blob: db27001db72842bb306120d95d521f05ff1710c4 [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;
Jaewan Kimbf5978c2023-07-28 11:30:54 +000021use log::info;
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 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),
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>),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010058}
59
60impl fmt::Display for Error {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 match self {
63 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"),
Alan Stokesa0e42962023-04-14 17:59:50 +010064 Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010065 Self::InvalidMagic => write!(f, "Wrong magic number"),
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090066 Self::UnsupportedVersion(v) => write!(f, "Version {v} not supported"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010067 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"),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000069 Self::MissingEntry(entry) => write!(f, "Mandatory {entry:?} entry is missing"),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010070 Self::EntryOutOfBounds(entry, range, limits) => {
71 write!(
72 f,
73 "Entry {entry:?} out of bounds: {range:#x?} must be within range {limits:#x?}"
74 )
75 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010076 }
77 }
78}
79
80pub type Result<T> = result::Result<T, Error>;
81
82impl Header {
83 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090084 const VERSION_1_0: Version = Version { major: 1, minor: 0 };
Jaewan Kimbf5978c2023-07-28 11:30:54 +000085 const VERSION_1_1: Version = Version { major: 1, minor: 1 };
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010086
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010087 pub fn total_size(&self) -> usize {
88 self.total_size as usize
89 }
90
Pierre-Clément Tosi33001492023-08-14 17:57:30 +010091 pub fn body_offset(&self) -> Result<usize> {
92 let entries_offset = mem::size_of::<Self>();
93
94 // Ensure that the entries are properly aligned and do not require padding.
95 const_assert_eq!(mem::align_of::<Header>() % mem::align_of::<HeaderEntry>(), 0);
96 const_assert_eq!(mem::size_of::<Header>() % mem::align_of::<HeaderEntry>(), 0);
97
98 let entries_size = self.entry_count()?.checked_mul(mem::size_of::<HeaderEntry>()).unwrap();
99
100 Ok(entries_offset.checked_add(entries_size).unwrap())
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100101 }
102
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100103 pub fn entry_count(&self) -> Result<usize> {
104 let last_entry = match self.version {
105 Self::VERSION_1_0 => Entry::DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000106 Self::VERSION_1_1 => Entry::VmDtbo,
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100107 v => return Err(Error::UnsupportedVersion(v)),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100108 };
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000109
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100110 Ok(last_entry as usize + 1)
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100111 }
112}
113
114#[derive(Clone, Copy, Debug)]
115pub enum Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100116 Bcc,
117 DebugPolicy,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000118 VmDtbo,
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100119 #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
120 _VARIANT_COUNT,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100121}
122
123impl Entry {
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100124 const COUNT: usize = Self::_VARIANT_COUNT as usize;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100125}
126
127#[repr(packed)]
Alan Stokesa0e42962023-04-14 17:59:50 +0100128#[derive(Clone, Copy, Debug, FromBytes)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100129struct HeaderEntry {
130 offset: u32,
131 size: u32,
132}
133
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100134impl HeaderEntry {
135 pub fn as_range(&self) -> Option<Range<usize>> {
136 let size = usize::try_from(self.size).unwrap();
137 if size != 0 {
138 let offset = self.offset.try_into().unwrap();
139 // Allow overflows here for the Range to properly describe the entry (validated later).
140 Some(offset..(offset + size))
141 } else {
142 None
143 }
144 }
145}
146
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900147#[repr(C, packed)]
148#[derive(Clone, Copy, Debug, Eq, FromBytes, PartialEq)]
149pub struct Version {
150 minor: u16,
151 major: u16,
152}
153
154impl fmt::Display for Version {
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 // Copy the fields to local variables to prevent unaligned access.
157 let (major, minor) = (self.major, self.minor);
158 write!(f, "{}.{}", major, minor)
159 }
160}
161
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100162#[derive(Debug)]
163pub struct Config<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100164 body: &'a mut [u8],
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100165 ranges: [Option<Range<usize>>; Entry::COUNT],
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100166}
167
168impl<'a> Config<'a> {
169 /// Take ownership of a pvmfw configuration consisting of its header and following entries.
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100170 pub fn new(bytes: &'a mut [u8]) -> Result<Self> {
171 const HEADER_SIZE: usize = mem::size_of::<Header>();
172 if bytes.len() < HEADER_SIZE {
173 return Err(Error::BufferTooSmall);
174 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100175
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100176 let (header, rest) =
177 LayoutVerified::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
Alan Stokesa0e42962023-04-14 17:59:50 +0100178 let header = header.into_ref();
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100179
180 if header.magic != Header::MAGIC {
181 return Err(Error::InvalidMagic);
182 }
183
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100184 if header.flags != 0 {
185 return Err(Error::InvalidFlags(header.flags));
186 }
187
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000188 info!("pvmfw config version: {}", header.version);
189
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100190 // Validate that we won't get an invalid alignment in the following due to padding to u64.
191 const_assert_eq!(HEADER_SIZE % mem::size_of::<u64>(), 0);
192
193 // Ensure that Header::total_size isn't larger than anticipated by the caller and resize
194 // the &[u8] to catch OOB accesses to entries/blobs.
195 let total_size = header.total_size();
196 let rest = if let Some(rest_size) = total_size.checked_sub(HEADER_SIZE) {
197 rest.get_mut(..rest_size).ok_or(Error::InvalidSize(total_size))?
198 } else {
199 return Err(Error::InvalidSize(total_size));
200 };
201
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100202 let (header_entries, body) =
203 LayoutVerified::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?)
204 .ok_or(Error::BufferTooSmall)?;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100205
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100206 // Validate that we won't get an invalid alignment in the following due to padding to u64.
207 const_assert_eq!(mem::size_of::<HeaderEntry>() % mem::size_of::<u64>(), 0);
208
209 let limits = header.body_offset()?..total_size;
210 let ranges = [
211 // TODO: Find a way to do this programmatically even if the trait
212 // `core::marker::Copy` is not implemented for `core::ops::Range<usize>`.
213 Self::validated_body_range(Entry::Bcc, &header_entries, &limits)?,
214 Self::validated_body_range(Entry::DebugPolicy, &header_entries, &limits)?,
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000215 Self::validated_body_range(Entry::VmDtbo, &header_entries, &limits)?,
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100216 ];
217
218 Ok(Self { body, ranges })
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100219 }
220
221 /// Get slice containing the platform BCC.
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100222 pub fn get_entries(&mut self) -> Result<(&mut [u8], Option<&mut [u8]>)> {
223 // This assumes that the blobs are in-order w.r.t. the entries.
224 let bcc_range = self.get_entry_range(Entry::Bcc).ok_or(Error::MissingEntry(Entry::Bcc))?;
225 let dp_range = self.get_entry_range(Entry::DebugPolicy);
Jaewan Kimbf5978c2023-07-28 11:30:54 +0000226 let vm_dtbo_range = self.get_entry_range(Entry::VmDtbo);
227 // TODO(b/291191157): Provision device assignment with this.
228 if let Some(vm_dtbo_range) = vm_dtbo_range {
229 info!("Found VM DTBO at {:?}", vm_dtbo_range);
230 }
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100231 let bcc_start = bcc_range.start;
232 let bcc_end = bcc_range.len();
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000233 let (_, rest) = self.body.split_at_mut(bcc_start);
234 let (bcc, rest) = rest.split_at_mut(bcc_end);
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100235
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100236 let dp = if let Some(dp_range) = dp_range {
237 let dp_start = dp_range.start.checked_sub(bcc_range.end).unwrap();
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000238 let dp_end = dp_range.len();
239 let (_, rest) = rest.split_at_mut(dp_start);
240 let (dp, _) = rest.split_at_mut(dp_end);
241 Some(dp)
242 } else {
243 None
244 };
245
Pierre-Clément Tosi33001492023-08-14 17:57:30 +0100246 Ok((bcc, dp))
247 }
248
249 pub fn get_entry_range(&self, entry: Entry) -> Option<Range<usize>> {
250 self.ranges[entry as usize].clone()
251 }
252
253 fn validated_body_range(
254 entry: Entry,
255 header_entries: &[HeaderEntry],
256 limits: &Range<usize>,
257 ) -> Result<Option<Range<usize>>> {
258 if let Some(header_entry) = header_entries.get(entry as usize) {
259 if let Some(r) = header_entry.as_range() {
260 return if r.start <= r.end && r.is_within(limits) {
261 let start = r.start - limits.start;
262 let end = r.end - limits.start;
263
264 Ok(Some(start..end))
265 } else {
266 Err(Error::EntryOutOfBounds(entry, r, limits.clone()))
267 };
268 }
269 }
270
271 Ok(None)
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100272 }
273}