blob: 47a06dfb6e118fef35406677b07b070f6edda87f [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 Tosifd694c72023-08-14 14:24:07 +010021use static_assertions::const_assert_eq;
22use vmbase::util::{unchecked_align_up, RangeExt};
Alan Stokesa0e42962023-04-14 17:59:50 +010023use zerocopy::{FromBytes, LayoutVerified};
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010024
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000025/// Configuration data header.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010026#[repr(C, packed)]
Alan Stokesa0e42962023-04-14 17:59:50 +010027#[derive(Clone, Copy, Debug, FromBytes)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010028struct Header {
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000029 /// Magic number; must be `Header::MAGIC`.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010030 magic: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000031 /// Version of the header format.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090032 version: Version,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000033 /// Total size of the configuration data.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010034 total_size: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000035 /// Feature flags; currently reserved and must be zero.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010036 flags: u32,
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +000037 /// (offset, size) pairs used to locate individual entries appended to the header.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010038 entries: [HeaderEntry; Entry::COUNT],
39}
40
41#[derive(Debug)]
42pub enum Error {
43 /// Reserved region can't fit configuration header.
44 BufferTooSmall,
Alan Stokesa0e42962023-04-14 17:59:50 +010045 /// Header has the wrong alignment
46 HeaderMisaligned,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010047 /// Header doesn't contain the expect magic value.
48 InvalidMagic,
49 /// Version of the header isn't supported.
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090050 UnsupportedVersion(Version),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010051 /// Header sets flags incorrectly or uses reserved flags.
52 InvalidFlags(u32),
53 /// Header describes configuration data that doesn't fit in the expected buffer.
54 InvalidSize(usize),
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +000055 /// Header entry is missing.
56 MissingEntry(Entry),
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010057 /// Range described by entry does not fit within config data.
58 EntryOutOfBounds(Entry, Range<usize>, Range<usize>),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010059}
60
61impl fmt::Display for Error {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 match self {
64 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"),
Alan Stokesa0e42962023-04-14 17:59:50 +010065 Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010066 Self::InvalidMagic => write!(f, "Wrong magic number"),
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090067 Self::UnsupportedVersion(v) => write!(f, "Version {v} not supported"),
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010068 Self::InvalidFlags(v) => write!(f, "Flags value {v:#x} is incorrect or reserved"),
69 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 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010077 }
78 }
79}
80
81pub type Result<T> = result::Result<T, Error>;
82
83impl Header {
84 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +090085 const VERSION_1_0: Version = Version { major: 1, minor: 0 };
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 Tosifd694c72023-08-14 14:24:07 +010091 pub fn body_offset(&self) -> usize {
92 unchecked_align_up(mem::size_of::<Self>(), mem::size_of::<u64>())
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010093 }
94
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +000095 fn get_body_range(&self, entry: Entry) -> Result<Option<Range<usize>>> {
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +010096 let Some(range) = self.entries[entry as usize].as_range() else {
97 return Ok(None);
98 };
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +000099
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100100 let limits = self.body_offset()..self.total_size();
101 if range.start <= range.end && range.is_within(&limits) {
102 let start = range.start - limits.start;
103 let end = range.end - limits.start;
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +0000104
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100105 Ok(Some(start..end))
106 } else {
107 Err(Error::EntryOutOfBounds(entry, range, limits))
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000108 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100109 }
110}
111
112#[derive(Clone, Copy, Debug)]
113pub enum Entry {
114 Bcc = 0,
115 DebugPolicy = 1,
116}
117
118impl Entry {
119 const COUNT: usize = 2;
120}
121
122#[repr(packed)]
Alan Stokesa0e42962023-04-14 17:59:50 +0100123#[derive(Clone, Copy, Debug, FromBytes)]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100124struct HeaderEntry {
125 offset: u32,
126 size: u32,
127}
128
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100129impl HeaderEntry {
130 pub fn as_range(&self) -> Option<Range<usize>> {
131 let size = usize::try_from(self.size).unwrap();
132 if size != 0 {
133 let offset = self.offset.try_into().unwrap();
134 // Allow overflows here for the Range to properly describe the entry (validated later).
135 Some(offset..(offset + size))
136 } else {
137 None
138 }
139 }
140}
141
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900142#[repr(C, packed)]
143#[derive(Clone, Copy, Debug, Eq, FromBytes, PartialEq)]
144pub struct Version {
145 minor: u16,
146 major: u16,
147}
148
149impl fmt::Display for Version {
150 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151 // Copy the fields to local variables to prevent unaligned access.
152 let (major, minor) = (self.major, self.minor);
153 write!(f, "{}.{}", major, minor)
154 }
155}
156
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100157#[derive(Debug)]
158pub struct Config<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100159 body: &'a mut [u8],
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000160 bcc_range: Range<usize>,
161 dp_range: Option<Range<usize>>,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100162}
163
164impl<'a> Config<'a> {
165 /// Take ownership of a pvmfw configuration consisting of its header and following entries.
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100166 pub fn new(bytes: &'a mut [u8]) -> Result<Self> {
167 const HEADER_SIZE: usize = mem::size_of::<Header>();
168 if bytes.len() < HEADER_SIZE {
169 return Err(Error::BufferTooSmall);
170 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100171
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100172 let (header, rest) =
173 LayoutVerified::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
Alan Stokesa0e42962023-04-14 17:59:50 +0100174 let header = header.into_ref();
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100175
176 if header.magic != Header::MAGIC {
177 return Err(Error::InvalidMagic);
178 }
179
Pierre-Clément Tosi43592a42022-12-19 14:17:38 +0000180 if header.version != Header::VERSION_1_0 {
Jaewan Kim6b7fb3c2023-08-08 18:00:43 +0900181 return Err(Error::UnsupportedVersion(header.version));
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100182 }
183
184 if header.flags != 0 {
185 return Err(Error::InvalidFlags(header.flags));
186 }
187
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100188 // Validate that we won't get an invalid alignment in the following due to padding to u64.
189 const_assert_eq!(HEADER_SIZE % mem::size_of::<u64>(), 0);
190
191 // Ensure that Header::total_size isn't larger than anticipated by the caller and resize
192 // the &[u8] to catch OOB accesses to entries/blobs.
193 let total_size = header.total_size();
194 let rest = if let Some(rest_size) = total_size.checked_sub(HEADER_SIZE) {
195 rest.get_mut(..rest_size).ok_or(Error::InvalidSize(total_size))?
196 } else {
197 return Err(Error::InvalidSize(total_size));
198 };
199
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000200 let bcc_range =
Pierre-Clément Tosi292e0992022-12-12 13:01:27 +0000201 header.get_body_range(Entry::Bcc)?.ok_or(Error::MissingEntry(Entry::Bcc))?;
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000202 let dp_range = header.get_body_range(Entry::DebugPolicy)?;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100203
Pierre-Clément Tosifd694c72023-08-14 14:24:07 +0100204 Ok(Self { body: rest, bcc_range, dp_range })
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100205 }
206
207 /// Get slice containing the platform BCC.
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000208 pub fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
209 let bcc_start = self.bcc_range.start;
210 let bcc_end = self.bcc_range.len();
211 let (_, rest) = self.body.split_at_mut(bcc_start);
212 let (bcc, rest) = rest.split_at_mut(bcc_end);
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100213
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000214 let dp = if let Some(dp_range) = &self.dp_range {
215 let dp_start = dp_range.start.checked_sub(self.bcc_range.end).unwrap();
216 let dp_end = dp_range.len();
217 let (_, rest) = rest.split_at_mut(dp_start);
218 let (dp, _) = rest.split_at_mut(dp_end);
219 Some(dp)
220 } else {
221 None
222 };
223
224 (bcc, dp)
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100225 }
226}