Skip to main content

bao1x_api/offsets/
common.rs

1use crate::offsets::PartitionAccess;
2use crate::offsets::RwPerms;
3use crate::offsets::SlotIndex;
4
5// These define the start region of the partition. In general, each partition has signature
6// metadata in them, but the first word in the signature is a jump instruction that takes you
7// to the actual boot code.
8pub const BOOT0_START: usize = 0x6000_0000;
9pub const BOOT1_START: usize = 0x6002_0000;
10pub const LOADER_START: usize = 0x6006_0000;
11pub const BAREMETAL_START: usize = LOADER_START;
12// kernel needs to start on a page boundary, so eat into the loader area a bit to allow that to happen.
13pub const KERNEL_START: usize = 0x600A_0000 - crate::signatures::SIGBLOCK_LEN;
14
15// total storage area available in RRAM. Above this are reserved vectors for security apparatus.
16pub const RRAM_STORAGE_LEN: usize = 0x3D_A000;
17
18// loadable swap "starts" at these address for UF2 updates. They are are interpreted as
19// zero-offsets from their respective "partitions" after masking for the top address location.
20pub const SWAP_START_UF2: usize = 0x7000_0000;
21pub const SWAP_UF2_LEN: usize = 0x0800_0000; // 128 MiB
22
23// Define a trait with just the offset
24pub trait OneWayEncoding: TryFrom<u32> {
25    const OFFSET: usize;
26}
27macro_rules! encode_oneway {
28    (
29        #[offset = $offset:literal]
30        $(#[$meta:meta])*
31        pub enum $name:ident {
32            $(
33                $variant:ident
34            ),* $(,)?
35        }
36    ) => {
37        $(#[$meta])*
38        #[repr(u32)]
39        #[derive(Debug, Clone, Copy, PartialEq)]
40        pub enum $name {
41            $(
42                $variant
43            ),*
44        }
45
46        impl $name {
47            const ALL: &'static [Self] = &[
48                $(Self::$variant),*
49            ];
50        }
51
52        impl TryFrom<u32> for $name {
53            type Error = ();
54
55            fn try_from(value: u32) -> Result<Self, Self::Error> {
56                let index = (value % Self::ALL.len() as u32) as usize;
57                Ok(Self::ALL[index])
58            }
59        }
60
61        impl OneWayEncoding for $name {
62            const OFFSET: usize = $offset;
63        }
64    };
65}
66
67// =========== ONE WAY COUNTER SLOTS ==============
68
69/// Anti-rollback counters only count up and are used to note what is the minimum rollback
70/// version allowed for a firmware stage. Note that this is different from a semver because
71/// semvers can change without there being security problems. Anti-rollback counter is in
72/// principle limited to 10k increments due to wear out of the underlying memory, so we
73/// use a separate counter for that.
74pub const RESERVED0_ANTI_ROLLBACK: usize = 56;
75pub const RESERVED1_ANTI_ROLLBACK: usize = 57;
76pub const BOOT0_ANTI_ROLLBACK: usize = 58;
77pub const BOOT1_ANTI_ROLLBACK: usize = 59;
78pub const LOADER_ANTI_ROLLBACK: usize = 60;
79pub const KERNEL_ANTI_ROLLBACK: usize = 61;
80pub const SWAP_ANTI_ROLLBACK: usize = 62;
81pub const APP_ANTI_ROLLBACK: usize = 63;
82pub const BAREMETAL_ANTI_ROLLBACK: usize = 64;
83
84/// Paranoid mode is written twice; either being non-zero invokes paranoid mode
85/// In paranoid mode, the glitch detectors are set to trigger aggressively and reset the
86/// system through automatic hardware means. This can lead to false positives that can
87/// degrade user experience, which is why it's left as a setting.
88pub const PARANOID_MODE: usize = 65;
89
90/// Counter that logs the possible number of attacks seen. This is used in conjunction
91/// with paranoid mode to initiate a system wipe. The reason this can be non-zero is that
92/// stray light or environmental factors can trigger the system, and so this threshold is
93/// tunable.
94pub const POSSIBLE_ATTACKS: usize = 66;
95
96/// Paranoid mode is written twice
97pub const PARANOID_MODE_DUPE: usize = 67;
98
99/// Total number of public key slots in the system. Pubkey revocations are duplicated because
100/// being able to override a pubkey revocation is a strong attack vector.
101pub const PUBKEY_SLOTS: usize = 4;
102
103/// The layout of the below duplicate revocation bits *MUST* match that of the primary keys.
104/// This property is relied upon by the signature checking routine.
105/// Offset in the one-way counter array for loader key revocations. Provisions for up to four
106/// key slots, from [68..=72].
107pub const LOADER_REVOCATION_DUPE_OFFSET: usize = 68;
108/// Offset in the one-way counter array for boot1 key revocations. Provisions for up to four
109/// key slots, from [72..=76].
110pub const BOOT1_REVOCATION_DUPE_OFFSET: usize = LOADER_REVOCATION_DUPE_OFFSET + PUBKEY_SLOTS;
111/// Offset in the one-way counter array for boot0 key revocations. Provisions for up to four
112/// key slots, from [76..=80].
113pub const BOOT0_REVOCATION_DUPE_OFFSET: usize = BOOT1_REVOCATION_DUPE_OFFSET + PUBKEY_SLOTS;
114
115/// Fixed offset between the main key and the duplicate key
116pub const REVOCATION_DUPE_DISTANCE: usize = LOADER_REVOCATION_OFFSET - LOADER_REVOCATION_DUPE_OFFSET;
117
118encode_oneway! {
119    #[offset = 80]
120    pub enum BootWaitCoding {
121        Disable,
122        Enable,
123    }
124}
125
126encode_oneway! {
127    #[offset = 81]
128    pub enum BoardTypeCoding {
129        Dabao,
130        Baosec,
131        Oem,
132    }
133}
134
135encode_oneway! {
136    #[offset = 82]
137    pub enum AltBootCoding {
138        PrimaryPartition,
139        AlternatePartition,
140    }
141}
142
143/// Incremented from 0 if chip-probe boot setup has been finished.
144pub const CP_BOOT_SETUP_DONE: usize = 83;
145
146/// Incremented from 0 if system boot setup has been finished. This is for systems that have
147/// a supplemental entropy source and want to replace the generated keys with keys that are
148/// derived from a blend of entropy sources. It's only useful on platforms like baosec.
149pub const IN_SYSTEM_BOOT_SETUP_DONE: usize = 84;
150
151/// When non-zero, the system had, at least one point in time, been challenged to boot
152/// from a developer image. Thus, the state of the system cannot be attested to based on
153/// the original signing keys burned from the factory. The value of this is also
154/// included as AAD in key derivations.
155pub const DEVELOPER_MODE: usize = 85;
156
157/// This is flipped when a trust transfer happens to a third party. i.e. any OEMs that
158/// come to Baochip to sign an image (that may then have their public keys in it) are
159/// required to set this bit as part of their signed code. It's a half-baked work-around
160/// for folks that are paranoid about DEVELOPER_MODE and for whatever reason think it's
161/// more trustworthy if someone they've never met used some cryptography to bless a bag
162/// of bits, but at the least they can say that a person they don't know or trust most likely
163/// did bless the bag of bits.
164pub const OEM_MODE: usize = 86;
165
166/// This is incremented if the boot0 public keys failed to compare against the static keys in
167/// the data store.
168pub const BOOT0_PUBKEY_FAIL: usize = 87;
169/// This is incremented if the boot1 public keys failed to compare against the static keys in
170/// the data store. Note that on Alpha-rev dabaos, this was used to track the initialization
171/// of dabao security state, and so this may be set already due to that use.
172pub const BOOT1_PUBKEY_FAIL: usize = 88;
173
174encode_oneway! {
175    #[offset = 90]
176    /// When set, the system will prefer to present generic, fixed identifiers when challenged
177    /// by external systems. The canonical use case for this is the serial number field in the
178    /// USB device descriptor: normally, ExternalIdentifiers is `0`, which means the device will
179    /// present a semi-unique serial number (this is useful for users who plug in multiple devices
180    /// and want to tell them apart). However, privacy-conscious users who don't need or want
181    /// to tell devices apart can increment this OWC and then the USB serial number will be
182    /// replaced with a fixed pattern that is common across all devices.
183    pub enum ExternalIdentifiers{
184        SerialNumber,
185        Anonymous,
186    }
187}
188
189/// Offset in the one-way counter array for loader key revocations. Provisions for up to four
190/// key slots, from [116..=120].
191pub const LOADER_REVOCATION_OFFSET: usize = 116;
192/// Offset in the one-way counter array for boot1 key revocations. Provisions for up to four
193/// key slots, from [120..=123].
194pub const BOOT1_REVOCATION_OFFSET: usize = LOADER_REVOCATION_OFFSET + PUBKEY_SLOTS;
195/// Offset in the one-way counter array for boot0 key revocations. Provisions for up to four
196/// key slots, from [124..=127].
197pub const BOOT0_REVOCATION_OFFSET: usize = BOOT1_REVOCATION_OFFSET + PUBKEY_SLOTS;
198
199// slots from 128..=255 are totally unused by the boot logic
200
201// =========== DATA SLOTS ==============
202
203/// The 'SERIAL_NUMBER` is a publicly readable number that has a "weak" guarantee of
204/// uniqueness, in that there is nothing essentially that prevents duplicates, forgeries
205/// or procedural errors replicating this. The serial number also is not strictly incrementing
206/// nor does it have any guarantee of being a monotonic or smoothly spaced out. It could
207/// even be all zeros (in which case LOT_CODE should be used). However, nominally, the plan
208/// is for SERIAL_NUMBER to be exactly the CP_ID field.
209pub const SERIAL_NUMBER: SlotIndex = SlotIndex::Data(0, PartitionAccess::Open, RwPerms::ReadWrite);
210
211/// `UUID` is a 256-bit random number that can be used as a UUID for the chip. It is publicly
212/// readable and generated by a TRNG. This is suitable for putting into a KDF and generating
213/// salts for algorithms that require such a parameter. The UUID is changeable, but changing
214/// will cause all derived keys to be changed as well.
215pub const UUID: SlotIndex = SlotIndex::Data(1, PartitionAccess::Open, RwPerms::ReadWrite);
216
217/// `IFR_HASH` is a provisional slot for a hash of the IFR region. Whether the hash is meaningful
218/// or not depends on if the chip is booted before it is sealed. At the time of writing, it's
219/// not clear if the wafer probe infrastructure will allow this.
220pub const IFR_HASH: SlotIndex = SlotIndex::Data(2, PartitionAccess::Open, RwPerms::ReadWrite);
221
222/// `WAFER_ID` is a copy of the lot ID + wafer ID + x/y position data that should be captured
223/// during CP.
224pub const CP_ID: SlotIndex = SlotIndex::Data(3, PartitionAccess::Open, RwPerms::ReadWrite);
225
226/// Indelible versions of the public keys. The problem with the pubkeys in boot0 region is that
227/// boot0 itself has the ability to modify its own memory. A copy here can have a bit set in
228/// the IFR that blocks any attempt to modify these keys.
229pub const BAO1_PUBKEY: SlotIndex = SlotIndex::Data(4, PartitionAccess::All, RwPerms::ReadOnly);
230pub const BAO2_PUBKEY: SlotIndex = SlotIndex::Data(5, PartitionAccess::All, RwPerms::ReadOnly);
231pub const BETA_PUBKEY: SlotIndex = SlotIndex::Data(6, PartitionAccess::All, RwPerms::ReadOnly);
232pub const DEV_PUBKEY: SlotIndex = SlotIndex::Data(7, PartitionAccess::All, RwPerms::ReadOnly);
233
234/// Collateral is a data range that is *always* erased every time the system transitions from boot0
235/// to a Baochip-signed boot1. The only condition under which it is not erased is if *all* the boot1 keys
236/// in slots 0, 1, *and* 2 do *not* match the Baochip set.
237///
238/// The purpose of the collateral keys is to ensure that no Baochip-signed images can be used to
239/// decrypt third-party signed images. Thus, the conditions for signing a third-party image with
240/// its own signing keys are as follows:
241///
242/// - It must be a Boot1 image
243/// - Keys 0, 1, and 2 must all be different from the Baochip keys
244/// - The third-party firmware must generate and populate all the COLLATERAL data slots.
245/// - The third-party firmware must incorporate at least one of the keys in slots 261, 262, or 263 into their
246///   root key mechanism.
247/// - Collateral key in slot 264 must be made disclosable through a public inspection mechanism. The purpose
248///   of the inspection is to verify that in fact the collateral keys have been populated with non-zero value
249///   by the third-party firmware, and to verify erase of the key range when necessary. Erasure always
250///   progresses from low slot to high slot, and thus one can infer the erasure state of the collateral by
251///   inspecting the value of the high key slot.
252pub const COLLATERAL: SlotIndex = SlotIndex::DataRange(261..265, PartitionAccess::Fw0, RwPerms::ReadWrite);
253
254/// Boot1 pubkey `receipt` fields record the last accepted public key used when running boot1.
255/// If this changes, the collateral keys need to be erased. This prevents one third-party signed firmware
256/// from being used to attack another third-party signed firmware. These keys have a 1:1 correlation
257/// with the BAO1, BAO2, BETA, and DEV _PUBKEY slots; the data type doesn't use a range so that iterators
258/// can have a cleaner parallel structure when comparing against these.
259pub const BOOT1_PK_RECEIPT_SLOT0: SlotIndex = SlotIndex::Data(265, PartitionAccess::Open, RwPerms::ReadWrite);
260pub const BOOT1_PK_RECEIPT_SLOT1: SlotIndex = SlotIndex::Data(266, PartitionAccess::Open, RwPerms::ReadWrite);
261pub const BOOT1_PK_RECEIPT_SLOT2: SlotIndex = SlotIndex::Data(267, PartitionAccess::Open, RwPerms::ReadWrite);
262pub const BOOT1_PK_RECEIPT_SLOT3: SlotIndex = SlotIndex::Data(268, PartitionAccess::Open, RwPerms::ReadWrite);
263pub const BOOT1_RECEIPT_SLOTS: [SlotIndex; 4] =
264    [BOOT1_PK_RECEIPT_SLOT0, BOOT1_PK_RECEIPT_SLOT1, BOOT1_PK_RECEIPT_SLOT2, BOOT1_PK_RECEIPT_SLOT3];
265
266// Notes on defining the boot0 IFR region.
267// RISC-V boot0 region start is defined by IFR slot 6, bits [55:48]
268// RISC-V boot1 region start (which is boot0 end) is defined by IFR slot 6, bits[47:40]
269// These bits are compared against address [21:14], which means the RRAM region is
270// sub-dividable into 256 16k blocks
271//   ** Boot0 should be from 0x0 - 0x2_0000 (128k reserved as R/O data)
272//    - "start" bits [55:48] == 0x00
273//    - "end" bits [47:40] == 0x08
274//
275// Furthermore, IFR slot 0x14, bits [127:120] should have 0x3a in it to enforce write disable on boot0