Skip to main content

bao1x_api/
offsets.rs

1pub mod baosec;
2pub mod common;
3pub use common::*;
4pub mod dabao;
5use core::ops::Range;
6
7use arbitrary_int::{Number, u4};
8use bitbybit::bitfield;
9
10pub const DATA_SLOT_START: usize = 0x603E_0000;
11pub const DATA_SLOT_LEN: usize = 0x1_0000;
12pub const MAX_DATA_SLOTS: usize = DATA_SLOT_LEN / SLOT_ELEMENT_LEN_BYTES;
13pub const SLOT_ELEMENT_LEN_BYTES: usize = 256 / 8;
14
15pub const ACRAM_DATASLOT_START: usize = 0x603D_C000;
16pub const ACRAM_DATASLOT_LEN: usize = 0x2000;
17pub const IFR_BASE: usize = 0x6040_0000;
18pub const IFR_LEN: usize = 0x400;
19/// This number is special: it only appears during certain portions of the chip life cycle in
20/// some configurations, so it needs to be captured during manufacturing and copied elsewhere in the array.
21/// It contains the lot ID, wafer ID, and wafer x/y position of the die.
22pub const IFR_CP_ID_BASE: usize = 0x6040_00A0;
23
24#[bitfield(u32)]
25#[derive(PartialEq, Eq, Debug)]
26pub struct DataSlotAccess {
27    #[bit(24, rw)]
28    write_mode: bool,
29    #[bit(23, rw)]
30    fw1: bool,
31    #[bit(22, rw)]
32    fw0: bool,
33    #[bit(21, rw)]
34    boot1: bool,
35    #[bit(20, rw)]
36    boot0: bool,
37    #[bits(8..=15, rw)]
38    seg_id: u8,
39    #[bit(3, rw)]
40    sce_wr_dis: bool,
41    #[bit(2, rw)]
42    sce_rd_dis: bool,
43    #[bit(1, rw)]
44    core_wr_dis: bool,
45    #[bit(0, rw)]
46    core_rd_dis: bool,
47}
48
49impl DataSlotAccess {
50    // This method is only valid in no-std currently. Not sure if there is even meaning for us
51    // to access this in the Xous environment, as this is primarily a secure boot construct
52    #[cfg(not(feature = "std"))]
53    pub fn get_entry(slot: usize) -> Self {
54        let slot_array =
55            unsafe { core::slice::from_raw_parts(ACRAM_DATASLOT_START as *const DataSlotAccess, 2048) };
56        slot_array[slot]
57    }
58
59    pub fn get_partition_access(&self) -> PartitionAccess { PartitionAccess::from_raw_u32(self.raw_value()) }
60
61    pub fn set_partition_access(&mut self, pa: &PartitionAccess) {
62        *self = Self::new_with_raw_value((self.raw_value() & !(0xf << 20)) | (pa.to_raw_u4().as_u32() << 20));
63    }
64
65    pub fn get_rw_permissions(&self) -> RwPerms {
66        match [self.core_rd_dis(), self.core_wr_dis()] {
67            [true, true] => RwPerms::Denied,
68            [false, true] => RwPerms::ReadOnly,
69            [true, false] => RwPerms::WriteOnly,
70            [false, false] => RwPerms::ReadWrite,
71        }
72    }
73
74    pub fn set_rw_permissions(&mut self, spec: RwPerms) {
75        match spec {
76            RwPerms::Denied => {
77                self.set_core_rd_dis(true);
78                self.set_core_wr_dis(true);
79            }
80            RwPerms::ReadOnly => {
81                self.set_core_rd_dis(false);
82                self.set_core_wr_dis(true);
83            }
84            RwPerms::WriteOnly => {
85                self.set_core_rd_dis(true);
86                self.set_core_wr_dis(false);
87            }
88            RwPerms::ReadWrite => {
89                self.set_core_rd_dis(false);
90                self.set_core_wr_dis(false);
91            }
92            RwPerms::Unspecified => panic!("RwPerms must be specified"),
93        }
94    }
95}
96
97#[derive(PartialEq, Eq, Clone, Copy, Debug)]
98pub enum AccessError {
99    /// Access is anticipated to be denied by the hardware. Users can attempt to still
100    /// bypass the access control, but the result will either be invalid data, returned,
101    /// a failed, write, or a security alarm being raised, depending on the hardware
102    /// enforcement policy at play.
103    AccessDenied,
104    /// Returned when data is written to a slot that only supports 0->1 transitions
105    /// and the provided data contains 1->0 transitions, but the 0->1 transitions
106    /// were in fact correctly set
107    OnlyOnes,
108    /// Returned when data written did not verify
109    WriteError,
110    /// Returned when the wrong type of access settings are passed for setting access
111    TypeError,
112    /// Returned when an index request is out of valid bounds
113    OutOfBounds,
114    /// Returned when a buffer passed does not match the expected size
115    SizeError,
116    /// Returned when a set of slots that should have the same ACL don't. The value
117    /// inside is the value of the first ACL in the set.
118    DataAclInconsistency(DataSlotAccess),
119}
120
121/// Enum to specify read/write permissions to a given slot.
122#[derive(PartialEq, Eq, Clone, Copy, Debug)]
123pub enum RwPerms {
124    ReadOnly,
125    WriteOnly,
126    ReadWrite,
127    Denied,
128    Unspecified,
129}
130
131#[derive(PartialEq, Eq, Clone, Copy, Debug)]
132/// Specifies what partitions can access a given slot. Some common patterns are
133/// provided, a Custom field is also provided for other odd combinations.
134pub enum PartitionAccess {
135    /// Open disables not just PartitionAccess but also all other security controls
136    Open,
137    All,
138    Boot0,
139    Boot1,
140    Fw0,
141    Fw1,
142    AllBoots,
143    AllFws,
144    /// Convenience option for API calls and tests that don't care about this portion
145    /// of the access control field
146    Unspecified,
147    /// Stores directly the bit pattern as should be written into the field,
148    /// complete with the sense inversion where 0 == access allowed.
149    Custom(u4),
150}
151impl PartitionAccess {
152    /// Takes in a raw u32 pattern from either DataSlotAccess or KeySlotAccess and
153    /// extracts the PartitionAccess code
154    pub fn from_raw_u32(raw: u32) -> Self {
155        // The bitfield coding is fw1:fw0:boot1:boot0 from MSB to LSB.
156        let code: u4 = u4::new(((raw >> 20) & 0xF) as u8);
157        match code.value() {
158            0b0000 => Self::Open,
159            0b1111 => Self::All,
160            0b0001 => Self::Boot0,
161            0b0010 => Self::Boot1,
162            0b0100 => Self::Fw0,
163            0b1000 => Self::Fw1,
164            0b0011 => Self::AllBoots,
165            0b1100 => Self::AllFws,
166            _ => Self::Custom(u4::new(((raw >> 20) & 0xF) as u8)),
167        }
168    }
169
170    // internal function that translates the symbolic representation into a u4 that
171    // can be shifted into place.
172    fn to_raw_u4(&self) -> u4 {
173        match self {
174            Self::Open => u4::new(0b0000 & 0xF),
175            Self::All => u4::new(0b1111 & 0xF),
176            Self::Boot0 => u4::new(0b0001 & 0xF),
177            Self::Boot1 => u4::new(0b0010 & 0xF),
178            Self::Fw0 => u4::new(0b0100 & 0xF),
179            Self::Fw1 => u4::new(0b1000 & 0xF),
180            Self::AllBoots => u4::new(0b0011 & 0xF),
181            Self::AllFws => u4::new(0b1100 & 0xF),
182            // Panic is the correct behavior here because it's a static code bug to try and use this coding in
183            // this fashion.
184            Self::Unspecified => panic!("Attempt to resolve an unspecified access pattern"),
185            Self::Custom(f) => *f,
186        }
187    }
188}
189
190/// `SlotIndex` encodes the index of a given slot and the *specification* of what the access
191/// rights should be for that slot. The actual enforcement is done by hardware, so if someone
192/// tries to "lie" to the API by creating a SlotIndex specifier with an inaccurate `PartitionAccess`
193/// spec, hardware will ignore it.
194///
195/// The reason the two are bundled together is that semantic priority is given to getting the spec right
196/// in the constants in this crate that define the access control tables.
197#[derive(PartialEq, Eq, Clone, Debug)]
198pub enum SlotIndex {
199    Data(usize, PartitionAccess, RwPerms),
200    DataRange(Range<usize>, PartitionAccess, RwPerms),
201}
202#[derive(PartialEq, Eq, Clone, Debug)]
203pub enum SlotType {
204    Data,
205}
206impl SlotIndex {
207    pub fn get_access_spec(&self) -> (PartitionAccess, RwPerms) {
208        match self {
209            Self::Data(_, spec, rw) => (*spec, *rw),
210            Self::DataRange(_, spec, rw) => (*spec, *rw),
211        }
212    }
213
214    pub fn get_type(&self) -> SlotType {
215        match self {
216            Self::Data(_, _, _) | Self::DataRange(_, _, _) => SlotType::Data,
217        }
218    }
219
220    pub fn get_base(&self) -> usize {
221        match self {
222            Self::Data(base, _, _) => *base,
223            Self::DataRange(range, _, _) => range.start,
224        }
225    }
226
227    pub fn len(&self) -> usize {
228        match self {
229            Self::Data(_, _, _) => 1,
230            Self::DataRange(range, _, _) => range.len(),
231        }
232    }
233
234    /// Returns `OutOfBounds` error if the index specified in the slot is out of bounds.
235    /// For ranges, returns the offset of the first element of the range.
236    pub fn try_into_data_offset(&self) -> Result<usize, AccessError> {
237        match self {
238            Self::Data(index, _, _) => {
239                if *index < MAX_DATA_SLOTS {
240                    Ok(*index * SLOT_ELEMENT_LEN_BYTES)
241                } else {
242                    Err(AccessError::OutOfBounds)
243                }
244            }
245            Self::DataRange(range, _, _) => {
246                let index = range.start;
247                if range.end <= MAX_DATA_SLOTS {
248                    Ok(index * SLOT_ELEMENT_LEN_BYTES)
249                } else {
250                    Err(AccessError::OutOfBounds)
251                }
252            }
253        }
254    }
255
256    pub fn try_into_data_iter(&self) -> Result<SlotOffsetIter, AccessError> {
257        match self {
258            Self::Data(index, _, _) => {
259                if *index < MAX_DATA_SLOTS {
260                    Ok(SlotOffsetIter::Single(core::iter::once(*index * SLOT_ELEMENT_LEN_BYTES)))
261                } else {
262                    Err(AccessError::OutOfBounds)
263                }
264            }
265            Self::DataRange(range, _, _) => {
266                if range.end > MAX_DATA_SLOTS {
267                    return Err(AccessError::OutOfBounds);
268                }
269                Ok(SlotOffsetIter::Range(range.clone().map(|idx| idx * SLOT_ELEMENT_LEN_BYTES)))
270            }
271        }
272    }
273
274    pub fn try_into_acl_offset(&self) -> Result<usize, AccessError> {
275        match self {
276            Self::Data(index, _, _) => {
277                if *index < MAX_DATA_SLOTS {
278                    Ok(*index * size_of::<u32>())
279                } else {
280                    Err(AccessError::OutOfBounds)
281                }
282            }
283            Self::DataRange(range, _, _) => {
284                if range.end <= MAX_DATA_SLOTS {
285                    Ok(range.start * size_of::<u32>())
286                } else {
287                    Err(AccessError::OutOfBounds)
288                }
289            }
290        }
291    }
292
293    pub fn try_into_acl_iter(&self) -> Result<SlotOffsetIter, AccessError> {
294        const ACL_SIZE: usize = core::mem::size_of::<u32>();
295
296        match self {
297            Self::Data(index, _, _) => {
298                if *index < MAX_DATA_SLOTS {
299                    Ok(SlotOffsetIter::Single(core::iter::once(*index * ACL_SIZE)))
300                } else {
301                    Err(AccessError::OutOfBounds)
302                }
303            }
304            Self::DataRange(range, _, _) => {
305                if range.end > MAX_DATA_SLOTS {
306                    return Err(AccessError::OutOfBounds);
307                }
308                Ok(SlotOffsetIter::Range(range.clone().map(|idx| idx * ACL_SIZE)))
309            }
310        }
311    }
312}
313
314// Custom iterator enum for zero-cost abstraction
315pub enum SlotOffsetIter {
316    Single(core::iter::Once<usize>),
317    Range(core::iter::Map<Range<usize>, fn(usize) -> usize>),
318}
319
320impl Iterator for SlotOffsetIter {
321    type Item = usize;
322
323    fn next(&mut self) -> Option<Self::Item> {
324        match self {
325            Self::Single(iter) => iter.next(),
326            Self::Range(iter) => iter.next(),
327        }
328    }
329
330    fn size_hint(&self) -> (usize, Option<usize>) {
331        match self {
332            Self::Single(iter) => iter.size_hint(),
333            Self::Range(iter) => iter.size_hint(),
334        }
335    }
336}
337
338impl ExactSizeIterator for SlotOffsetIter {
339    fn len(&self) -> usize {
340        match self {
341            Self::Single(iter) => iter.len(),
342            Self::Range(iter) => iter.len(),
343        }
344    }
345}