blob: ffa6e9d21cbd714f2a0520e89fdeb533b242f604 [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
17use crate::helpers;
18use core::fmt;
19use core::mem;
20use core::num::NonZeroUsize;
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +000021use core::ops::Range;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010022use core::result;
23
24#[repr(C, packed)]
25#[derive(Clone, Copy, Debug)]
26struct Header {
27 magic: u32,
28 version: u32,
29 total_size: u32,
30 flags: u32,
31 entries: [HeaderEntry; Entry::COUNT],
32}
33
34#[derive(Debug)]
35pub enum Error {
36 /// Reserved region can't fit configuration header.
37 BufferTooSmall,
38 /// Header doesn't contain the expect magic value.
39 InvalidMagic,
40 /// Version of the header isn't supported.
41 UnsupportedVersion(u16, u16),
42 /// Header sets flags incorrectly or uses reserved flags.
43 InvalidFlags(u32),
44 /// Header describes configuration data that doesn't fit in the expected buffer.
45 InvalidSize(usize),
46 /// Header entry is invalid.
47 InvalidEntry(Entry),
48}
49
50impl fmt::Display for Error {
51 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52 match self {
53 Self::BufferTooSmall => write!(f, "Reserved region is smaller than config header"),
54 Self::InvalidMagic => write!(f, "Wrong magic number"),
55 Self::UnsupportedVersion(x, y) => write!(f, "Version {x}.{y} not supported"),
56 Self::InvalidFlags(v) => write!(f, "Flags value {v:#x} is incorrect or reserved"),
57 Self::InvalidSize(sz) => write!(f, "Total size ({sz:#x}) overflows reserved region"),
58 Self::InvalidEntry(e) => write!(f, "Entry {e:?} is invalid"),
59 }
60 }
61}
62
63pub type Result<T> = result::Result<T, Error>;
64
65impl Header {
66 const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
67 const PADDED_SIZE: usize =
68 helpers::unchecked_align_up(mem::size_of::<Self>(), mem::size_of::<u64>());
69
70 pub const fn version(major: u16, minor: u16) -> u32 {
71 ((major as u32) << 16) | (minor as u32)
72 }
73
74 pub const fn version_tuple(&self) -> (u16, u16) {
75 ((self.version >> 16) as u16, self.version as u16)
76 }
77
78 pub fn total_size(&self) -> usize {
79 self.total_size as usize
80 }
81
82 pub fn body_size(&self) -> usize {
83 self.total_size() - Self::PADDED_SIZE
84 }
85
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +000086 fn get_body_range(&self, entry: Entry) -> Result<Option<Range<usize>>> {
87 let e = self.entries[entry as usize];
88
89 if e.is_empty() {
90 Ok(None)
91 } else if e.fits_in(self.total_size()) {
92 Ok(Some(e.as_body_range()))
93 } else {
94 Err(Error::InvalidEntry(entry))
95 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010096 }
97}
98
99#[derive(Clone, Copy, Debug)]
100pub enum Entry {
101 Bcc = 0,
102 DebugPolicy = 1,
103}
104
105impl Entry {
106 const COUNT: usize = 2;
107}
108
109#[repr(packed)]
110#[derive(Clone, Copy, Debug)]
111struct HeaderEntry {
112 offset: u32,
113 size: u32,
114}
115
116impl HeaderEntry {
117 pub fn is_empty(&self) -> bool {
118 self.offset() == 0 && self.size() == 0
119 }
120
121 pub fn fits_in(&self, max_size: usize) -> bool {
122 (Header::PADDED_SIZE..max_size).contains(&self.offset())
123 && NonZeroUsize::new(self.size())
124 .and_then(|s| s.checked_add(self.offset()))
125 .filter(|&x| x.get() <= max_size)
126 .is_some()
127 }
128
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000129 pub fn as_body_range(&self) -> Range<usize> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100130 let start = self.offset() - Header::PADDED_SIZE;
131
132 start..(start + self.size())
133 }
134
135 pub fn offset(&self) -> usize {
136 self.offset as usize
137 }
138
139 pub fn size(&self) -> usize {
140 self.size as usize
141 }
142}
143
144#[derive(Debug)]
145pub struct Config<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100146 body: &'a mut [u8],
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000147 bcc_range: Range<usize>,
148 dp_range: Option<Range<usize>>,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100149}
150
151impl<'a> Config<'a> {
152 /// Take ownership of a pvmfw configuration consisting of its header and following entries.
153 ///
154 /// SAFETY - 'data' should respect the alignment of Header.
155 pub unsafe fn new(data: &'a mut [u8]) -> Result<Self> {
156 let header = data.get(..Header::PADDED_SIZE).ok_or(Error::BufferTooSmall)?;
157
158 let header = &*(header.as_ptr() as *const Header);
159
160 if header.magic != Header::MAGIC {
161 return Err(Error::InvalidMagic);
162 }
163
164 if header.version != Header::version(1, 0) {
165 let (major, minor) = header.version_tuple();
166 return Err(Error::UnsupportedVersion(major, minor));
167 }
168
169 if header.flags != 0 {
170 return Err(Error::InvalidFlags(header.flags));
171 }
172
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000173 let bcc_range =
174 header.get_body_range(Entry::Bcc)?.ok_or(Error::InvalidEntry(Entry::Bcc))?;
175 let dp_range = header.get_body_range(Entry::DebugPolicy)?;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100176
177 let body = data
178 .get_mut(Header::PADDED_SIZE..)
179 .ok_or(Error::BufferTooSmall)?
180 .get_mut(..header.body_size())
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000181 .ok_or_else(|| Error::InvalidSize(header.total_size()))?;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100182
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000183 Ok(Self { body, bcc_range, dp_range })
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100184 }
185
186 /// Get slice containing the platform BCC.
187 pub fn get_bcc_mut(&mut self) -> &mut [u8] {
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000188 &mut self.body[self.bcc_range.clone()]
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100189 }
190
191 /// Get slice containing the platform debug policy.
192 pub fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
Pierre-Clément Tosi71d7d5b2022-12-12 13:15:45 +0000193 self.dp_range.as_ref().map(|r| &mut self.body[r.clone()])
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100194 }
195}