Skip to main content

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}