• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
17 use core::fmt;
18 use core::mem;
19 use core::num::NonZeroUsize;
20 use core::ops::Range;
21 use core::result;
22 use log::{info, warn};
23 use static_assertions::const_assert_eq;
24 use vmbase::util::RangeExt;
25 use zerocopy::FromBytes;
26 use zerocopy::Immutable;
27 use zerocopy::KnownLayout;
28 use zerocopy::Ref;
29 
30 /// Configuration data header.
31 #[repr(C, packed)]
32 #[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout)]
33 struct Header {
34     /// Magic number; must be `Header::MAGIC`.
35     magic: u32,
36     /// Version of the header format.
37     version: Version,
38     /// Total size of the configuration data.
39     total_size: u32,
40     /// Feature flags; currently reserved and must be zero.
41     flags: u32,
42 }
43 
44 #[derive(Debug)]
45 pub enum Error {
46     /// Reserved region can't fit configuration header.
47     BufferTooSmall,
48     /// Header has the wrong alignment
49     HeaderMisaligned,
50     /// Header doesn't contain the expect magic value.
51     InvalidMagic,
52     /// Version of the header isn't supported.
53     UnsupportedVersion(Version),
54     /// Header describes configuration data that doesn't fit in the expected buffer.
55     InvalidSize(usize),
56     /// Header entry is missing.
57     MissingEntry(Entry),
58     /// Range described by entry does not fit within config data.
59     EntryOutOfBounds(Entry, Range<usize>, Range<usize>),
60     /// Entries are in out of order
61     EntryOutOfOrder,
62 }
63 
64 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result65     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66         match self {
67             Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"),
68             Self::HeaderMisaligned => write!(f, "Reserved region is misaligned"),
69             Self::InvalidMagic => write!(f, "Wrong magic number"),
70             Self::UnsupportedVersion(v) => write!(f, "Version {v} not supported"),
71             Self::InvalidSize(sz) => write!(f, "Total size ({sz:#x}) overflows reserved region"),
72             Self::MissingEntry(entry) => write!(f, "Mandatory {entry:?} entry is missing"),
73             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             }
79             Self::EntryOutOfOrder => write!(f, "Entries are out of order"),
80         }
81     }
82 }
83 
84 pub type Result<T> = result::Result<T, Error>;
85 
86 impl Header {
87     const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
88     const VERSION_1_0: Version = Version { major: 1, minor: 0 };
89     const VERSION_1_1: Version = Version { major: 1, minor: 1 };
90     const VERSION_1_2: Version = Version { major: 1, minor: 2 };
91 
total_size(&self) -> usize92     pub fn total_size(&self) -> usize {
93         self.total_size as usize
94     }
95 
body_lowest_bound(&self) -> Result<usize>96     pub fn body_lowest_bound(&self) -> Result<usize> {
97         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())
106     }
107 
entry_count(&self) -> Result<usize>108     pub fn entry_count(&self) -> Result<usize> {
109         let last_entry = match self.version {
110             Self::VERSION_1_0 => Entry::DebugPolicy,
111             Self::VERSION_1_1 => Entry::VmDtbo,
112             Self::VERSION_1_2 => Entry::VmBaseDtbo,
113             v @ Version { major: 1, .. } => {
114                 const LATEST: Version = Header::VERSION_1_2;
115                 warn!("Parsing unknown config data version {v} as version {LATEST}");
116                 return Ok(Entry::COUNT);
117             }
118             v => return Err(Error::UnsupportedVersion(v)),
119         };
120 
121         Ok(last_entry as usize + 1)
122     }
123 }
124 
125 #[derive(Clone, Copy, Debug)]
126 pub enum Entry {
127     DiceHandover,
128     DebugPolicy,
129     VmDtbo,
130     VmBaseDtbo,
131     #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
132     _VARIANT_COUNT,
133 }
134 
135 impl Entry {
136     const COUNT: usize = Self::_VARIANT_COUNT as usize;
137 
138     const ALL_ENTRIES: [Entry; Self::COUNT] =
139         [Self::DiceHandover, Self::DebugPolicy, Self::VmDtbo, Self::VmBaseDtbo];
140 }
141 
142 #[derive(Default)]
143 pub struct Entries<'a> {
144     pub dice_handover: Option<&'a mut [u8]>,
145     pub debug_policy: Option<&'a [u8]>,
146     pub vm_dtbo: Option<&'a mut [u8]>,
147     pub vm_ref_dt: Option<&'a [u8]>,
148 }
149 
150 #[repr(packed)]
151 #[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout)]
152 struct HeaderEntry {
153     offset: u32,
154     size: u32,
155 }
156 
157 #[repr(C, packed)]
158 #[derive(Clone, Copy, Debug, Eq, FromBytes, Immutable, KnownLayout, PartialEq)]
159 pub struct Version {
160     minor: u16,
161     major: u16,
162 }
163 
164 impl fmt::Display for Version {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result165     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 
172 /// Range with non-empty length.
173 #[derive(Debug, Copy, Clone)]
174 struct NonEmptyRange {
175     start: usize,
176     size: NonZeroUsize,
177 }
178 
179 impl NonEmptyRange {
new(start: usize, size: usize) -> Option<Self>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 
end(&self) -> usize187     fn end(&self) -> usize {
188         self.start + self.len()
189     }
190 
len(&self) -> usize191     fn len(&self) -> usize {
192         self.size.into()
193     }
194 
as_range(&self) -> Range<usize>195     fn as_range(&self) -> Range<usize> {
196         self.start..self.end()
197     }
198 }
199 
200 #[derive(Debug)]
201 pub struct Config<'a> {
202     body: &'a mut [u8],
203     ranges: [Option<NonEmptyRange>; Entry::COUNT],
204 }
205 
206 impl<'a> Config<'a> {
207     /// Take ownership of a pvmfw configuration consisting of its header and following entries.
new(bytes: &'a mut [u8]) -> Result<Self>208     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         }
213 
214         let (header, rest) =
215             Ref::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
216         let header = Ref::into_ref(header);
217 
218         if header.magic != Header::MAGIC {
219             return Err(Error::InvalidMagic);
220         }
221 
222         let header_flags = header.flags;
223         if header_flags != 0 {
224             warn!("Ignoring unknown config flags: {header_flags:#x}");
225         }
226 
227         info!("pvmfw config version: {}", header.version);
228 
229         // 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 
241         let (header_entries, body) =
242             zerocopy::Ref::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?)
243                 .ok_or(Error::BufferTooSmall)?;
244 
245         // 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 
248         // Ensure entries are in the body.
249         let limits = header.body_lowest_bound()?..total_size;
250         let mut ranges: [Option<NonEmptyRange>; Entry::COUNT] = [None; Entry::COUNT];
251         let mut last_end = 0;
252         for entry in Entry::ALL_ENTRIES {
253             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 
262             if last_end > range.start {
263                 return Err(Error::EntryOutOfOrder);
264             }
265             last_end = range.end;
266 
267             ranges[entry as usize] = NonEmptyRange::new(
268                 entry_offset - limits.start, // is_within() validates safety of this.
269                 entry_size,
270             );
271         }
272         // Ensures that the DICE handover is present.
273         ranges[Entry::DiceHandover as usize].ok_or(Error::MissingEntry(Entry::DiceHandover))?;
274 
275         Ok(Self { body, ranges })
276     }
277 
278     /// Locate the various config entries.
get_entries(self) -> Entries<'a>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             }
295         }
296         let [dice_handover, debug_policy, vm_dtbo, vm_ref_dt] = entries;
297 
298         // We have no reason to mutate so drop the `mut`.
299         let debug_policy = debug_policy.map(|x| &*x);
300         let vm_ref_dt = vm_ref_dt.map(|x| &*x);
301 
302         Entries { dice_handover, debug_policy, vm_dtbo, vm_ref_dt }
303     }
304 }
305