bao1x_api/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub mod udma;
4pub use udma::*;
5// A bunch of these are gated on "std" because they include APIs that
6// aren't available in e.g. kernel or loader.
7pub mod iox;
8pub use iox::*;
9pub mod i2c;
10pub use i2c::*;
11pub mod api;
12pub use api::*;
13#[cfg(feature = "std")]
14pub mod keyboard;
15pub mod offsets;
16pub mod sce;
17pub mod signatures;
18pub use offsets::*;
19pub mod clocks;
20pub mod pubkeys;
21use arbitrary_int::u31;
22use bitbybit::bitfield;
23pub use clocks::*;
24pub mod bio;
25#[cfg(feature = "std")]
26pub mod bio_resources;
27
28/// UF2 Family ID. Randomly generated, no collisions with the known list, still to be merged
29/// into the "official" list
30pub const BAOCHIP_1X_UF2_FAMILY: u32 = 0xa7d7_6373;
31
32// density 18, memory type 20, mfg ID C2 ==> MX25L128833F
33// density 38, memory type 25, mfg ID C2 ==> MX25U12832F
34// mfg ID 0b ==> XT25Q64FWOIGT cost down option (8MiB)
35// mfg ID ba ==> ZD25Q64B super-cost down option (8MiB)
36pub const SPI_FLASH_IDS: [u32; 4] = [0x1820c2, 0x3825c2, 0x17600b, 0x1732ba];
37// KGD 5D, mfg ID 9D; remainder of bits are part of the EID
38pub const RAM_IDS: [u32; 3] = [0x5D9D, 0x559d, 0x5D0D];
39
40/// system preemption interval
41pub const SYSTEM_TICK_INTERVAL_MS: u32 = 10;
42
43/// standard baud rate
44pub const UART_BAUD: u32 = 1_000_000;
45
46/// Constants used by both emulation and hardware implementations
47pub const PERCLK: u32 = 100_000_000;
48pub const SERVER_NAME_KBD: &str = "_Matrix keyboard driver_";
49/// Do not change this constant, it is hard-coded into libraries in order to break
50/// circular dependencies on the IFRAM block.
51pub const SERVER_NAME_BAO1X_HAL: &str = "_bao1x-SoC HAL_";
52
53/// Flags register in the backup register bank. Used to track system state between soft resets.
54#[bitfield(u32)]
55#[derive(PartialEq, Eq, Debug)]
56pub struct BackupFlags {
57 #[bits(1..=31, rw)]
58 reserved: u31,
59 /// When `false`, indicates that the time in the RTC register is not synchronized to the offset
60 /// that is read from disk. Upon first encounter with an external time source, the offset should
61 /// be captured and recorded to disk.
62 #[bit(0, rw)]
63 rtc_synchronized: bool,
64}
65
66pub mod camera {
67 #[derive(Clone, Copy)]
68 pub enum Resolution {
69 Res480x272,
70 Res640x480,
71 Res320x240,
72 Res160x120,
73 Res256x256,
74 }
75
76 #[derive(Clone, Copy)]
77 #[repr(u32)]
78 pub enum Format {
79 Rgb565 = 0,
80 Rgb555 = 1,
81 Rgb444 = 2,
82 BypassLe = 4,
83 BypassBe = 5,
84 }
85
86 impl Into<(usize, usize)> for Resolution {
87 fn into(self) -> (usize, usize) {
88 match self {
89 Resolution::Res160x120 => (160, 120),
90 Resolution::Res320x240 => (320, 240),
91 Resolution::Res480x272 => (480, 272),
92 Resolution::Res640x480 => (640, 480),
93 Resolution::Res256x256 => (256, 256),
94 }
95 }
96 }
97}
98
99/// Version number of the below structure
100pub const STATICS_IN_ROM_VERSION: u16 = 1;
101/// This encodes to jal x0, 256 - jumps 256 bytes ahead from the current PC location.
102pub const JUMP_INSTRUCTION: u32 = 0x1000006f;
103/// In-ROM representation of static initialization data
104/// Placed by the image creation tool, and used for bootstrapping the Rust environment
105/// `usize` is *not* allowed because this structure is packed on a 64-bit host.
106#[repr(C, align(256))]
107pub struct StaticsInRom {
108 // reserved for a jump-over instruction so that the structure can be located in-line in ROM.
109 #[allow(dead_code)] // this should never actually be used
110 pub jump_instruction: u32,
111 // version number of this structure
112 pub version: u16,
113 // total number of valid pokes in `poke_table`
114 pub valid_pokes: u16,
115 // Origin of the data segment
116 pub data_origin: u32,
117 // overall size in bytes. [origin:origin+size] will be zeroized.
118 pub data_size_bytes: u32,
119 // poke table of values to stick in the data segment. This is needed in particular
120 // to initialize `static` variables, such as Atomics and Mutexes, required by the
121 // loader environment. Presented as (address, data) tuples, up to 40 of them.
122 // Only entries from [0..valid_pokes] are processed.
123 // The addresses and data are packed as u8's to avoid padding of the record.
124 pub poke_table: [([u8; 2], [u8; 4]); 40],
125}
126
127impl StaticsInRom {
128 pub fn as_bytes(&self) -> &[u8] {
129 unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, core::mem::size_of::<Self>()) }
130 }
131}
132
133/// Structure for recording message formats to be passed from interrupt handlers back to userspace.
134/// A specific handler may or may not use any or all of the arguments: this simply provides storage
135/// for all the possible arguments.
136#[derive(Copy, Clone)]
137pub struct IrqNotification {
138 /// Specifies the bit position of the event in the irq bank
139 pub bit: arbitrary_int::u4,
140 /// Connection to send notifications to
141 pub conn: xous::CID,
142 /// Opcode argument for the notification
143 pub opcode: usize,
144 /// Up to four arguments to be passed on
145 pub args: [usize; 4],
146}
147
148#[macro_export]
149macro_rules! bollard {
150 // A call with no args just inserts 4 illegal instructions
151 () => {
152 bollard!(4)
153 };
154
155 // A call with countermeasures specified interleaves countermeasures with illegal instructions
156 // Interleaving is done because the countermeasure jump is a single point of failure
157 // that could be bypassed. Leaving illegal instructions in hardens against that possibility.
158 //
159 // Note that the countermeasure routine needs to be within +/-1 MiB of the bollard
160 ($countermeasure:path, $count:literal) => {{
161 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
162
163 unsafe {
164 core::arch::asm!(
165 " j 2f",
166 "1:",
167 ".rept {count}",
168 " j {cm}",
169 // this is an "invalid opcode" -- will trigger an instruction page fault
170 " .word 0xffffffff",
171 ".endr",
172 "2:",
173 cm = sym $countermeasure,
174 count = const $count,
175 options(nomem, nostack, preserves_flags)
176 );
177 }
178
179 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
180 }};
181 ($count:literal) => {{
182 // Force a compiler barrier to prevent reordering around the tripwire
183 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
184
185 unsafe {
186 core::arch::asm!(
187 "j 2f",
188 // Label 1: illegal instruction sled
189 "1:",
190 ".rept {count}",
191 // this is an "invalid opcode" -- will trigger an instruction page fault
192 ".word 0xffffffff",
193 ".endr",
194 // Label 2: safe landing
195 "2:",
196 count = const $count,
197 options(nomem, nostack, preserves_flags)
198 );
199 }
200
201 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
202 }};
203}
204
205/// Hardened boolean type - values chosen for high Hamming distance
206/// and resistance to stuck-at-zero/one faults.
207#[derive(Clone, Copy)]
208#[repr(transparent)]
209pub struct HardenedBool(u32);
210
211impl HardenedBool {
212 pub const FALSE: Self = Self(Self::FALSE_VALUE);
213 const FALSE_VALUE: u32 = 0xA5A5_5A5A;
214 pub const TRUE: Self = Self(Self::TRUE_VALUE);
215 // Hamming distance of 16 between these values
216 // Also chosen to not be simple patterns (0x0000, 0xFFFF, etc.)
217 const TRUE_VALUE: u32 = 0x5A5A_A5A5;
218
219 /// Check if true - returns None if value is corrupted
220 #[inline(never)]
221 pub fn is_true(self) -> Option<bool> {
222 match self.0 {
223 Self::TRUE_VALUE => Some(true),
224 Self::FALSE_VALUE => Some(false),
225 _ => None, // Corruption detected
226 }
227 }
228
229 /// Constant-time equality check against TRUE
230 #[inline(never)]
231 pub fn check_true(self) -> bool {
232 // Use volatile to prevent optimizer from simplifying
233 let val = unsafe { core::ptr::read_volatile(&self.0) };
234 val == Self::TRUE_VALUE
235 }
236
237 /// Return the complement - for redundant checking
238 pub fn complement(self) -> u32 { !self.0 }
239
240 /// Verify internal consistency (value is one of the two valid states)
241 pub fn is_valid(self) -> bool { self.0 == Self::TRUE_VALUE || self.0 == Self::FALSE_VALUE }
242}