vexide_startup/lib.rs
1//! Startup routine and behavior in `vexide`.
2//!
3//! - User code begins at an assembly routine called `_boot`, which sets up the stack
4//! section before jumping to a user-provided `_start` symbol, which should be your
5//! rust entrypoint.
6//!
7//! - From there, the Rust entrypoint may call the [`startup`] function to finish the
8//! startup process by clearing the `.bss` section (intended for uninitialized data)
9//! and initializing vexide's heap allocator.
10//!
11//! This crate is NOT a crt0 implementation. No global constructors are called.
12
13#![no_std]
14#![allow(clippy::needless_doctest_main)]
15
16use banner::themes::BannerTheme;
17use bitflags::bitflags;
18
19pub mod banner;
20mod patcher;
21
22/// Load address of user programs.
23const USER_MEMORY_START: u32 = 0x0380_0000;
24
25/// Identifies the type of binary to VEXos.
26#[repr(u32)]
27#[non_exhaustive]
28pub enum ProgramType {
29 /// User program binary.
30 User = 0,
31}
32
33/// The owner (originator) of the user program
34#[repr(u32)]
35pub enum ProgramOwner {
36 /// Program is a system binary.
37 System = 0,
38
39 /// Program originated from VEX.
40 Vex = 1,
41
42 /// Program originated from a partner developer.
43 Partner = 2,
44}
45
46bitflags! {
47 /// Program Flags
48 ///
49 /// These bitflags are part of the [`CodeSignature`] that determine some small
50 /// aspects of program behavior when running under VEXos. This struct contains
51 /// the flags with publicly documented behavior.
52 #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
53 pub struct ProgramFlags: u32 {
54 /// Inverts the background color to pure white.
55 const INVERT_DEFAULT_GRAPHICS = 1 << 0;
56
57 /// VEXos scheduler simple tasks will be killed when the program requests exit.
58 const KILL_TASKS_ON_EXIT = 1 << 1;
59
60 /// If VEXos is using the Light theme, inverts the background color to pure white.
61 const THEMED_DEFAULT_GRAPHICS = 1 << 2;
62 }
63}
64
65/// Program Code Signature
66///
67/// The first 16 bytes of a VEX user code binary contain a user code signature,
68/// containing some basic metadata and startup flags about the program. This
69/// signature must be at the start of the binary for booting to occur.
70#[derive(Debug, Clone, Copy, Eq, PartialEq)]
71pub struct CodeSignature(vex_sdk::vcodesig, [u32; 4]);
72
73impl CodeSignature {
74 /// Creates a new signature given a program type, owner, and flags.
75 #[must_use]
76 pub const fn new(program_type: ProgramType, owner: ProgramOwner, flags: ProgramFlags) -> Self {
77 Self(
78 vex_sdk::vcodesig {
79 magic: vex_sdk::V5_SIG_MAGIC,
80 r#type: program_type as _,
81 owner: owner as _,
82 options: flags.bits(),
83 },
84 [0; 4],
85 )
86 }
87}
88
89unsafe extern "C" {
90 static mut __heap_start: u8;
91 static mut __heap_end: u8;
92
93 static mut __patcher_ram_start: u8;
94 static mut __patcher_ram_end: u8;
95
96 // These symbols don't have real types, so this is a little bit of a hack.
97 static mut __bss_start: u32;
98 static mut __bss_end: u32;
99}
100
101// This is the true entrypoint of vexide, containing the first two
102// instructions of user code executed before anything else.
103//
104// This function loads the stack pointer to the stack region specified
105// in our linkerscript, then immediately branches to the Rust entrypoint
106// created by our macro.
107core::arch::global_asm!(
108 r#"
109.section .boot, "ax"
110.global _boot
111
112_boot:
113 @ Load the user program stack.
114 @
115 @ This technically isn't required, as VEXos already sets up a stack for CPU1,
116 @ but that stack is relatively small and we have more than enough memory
117 @ available to us for this.
118 ldr sp, =__stack_top
119
120 @ Before any Rust code runs, we need to memcpy the currently running binary to
121 @ 0x07C00000 if a patch file is loaded into memory. See the documentation in
122 @ `patcher/mod.rs` for why we want to do this.
123
124 @ Check for patch magic at 0x07A00000.
125 mov r0, #0x07A00000
126 ldr r0, [r0]
127 ldr r1, =0xB1DF
128 cmp r0, r1
129
130 @ Prepare to memcpy binary to 0x07C00000
131 mov r0, #0x07C00000 @ memcpy dest -> r0
132 mov r1, #0x03800000 @ memcpy src -> r1
133 ldr r2, =0x07A0000C @ the length of the binary is stored at 0x07A0000C
134 ldr r2, [r2] @ memcpy size -> r2
135
136 @ Do the memcpy if patch magic is present (we checked this in our `cmp` instruction).
137 bleq __overwriter_aeabi_memcpy
138
139 @ Jump to the Rust entrypoint.
140 b _start
141"#
142);
143
144/// Zeroes the `.bss` section
145///
146/// # Arguments
147///
148/// - `sbss`. Pointer to the start of the `.bss` section.
149/// - `ebss`. Pointer to the open/non-inclusive end of the `.bss` section.
150/// (The value behind this pointer will not be modified)
151/// - Use `T` to indicate the alignment of the `.bss` section.
152///
153/// # Safety
154///
155/// - Must be called exactly once
156/// - `mem::size_of::<T>()` must be non-zero
157/// - `ebss >= sbss`
158/// - `sbss` and `ebss` must be `T` aligned.
159#[inline]
160#[allow(clippy::similar_names)]
161#[cfg(target_vendor = "vex")]
162unsafe fn zero_bss<T>(mut sbss: *mut T, ebss: *mut T)
163where
164 T: Copy,
165{
166 while sbss < ebss {
167 // NOTE: volatile to prevent this from being transformed into `memclr`
168 unsafe {
169 core::ptr::write_volatile(sbss, core::mem::zeroed());
170 sbss = sbss.offset(1);
171 }
172 }
173}
174
175/// Startup Routine
176///
177/// - Sets up the heap allocator if necessary.
178/// - Zeroes the `.bss` section if necessary.
179/// - Prints the startup banner with a specified theme, if enabled.
180///
181/// # Safety
182///
183/// Must be called once at the start of program execution after the stack has been setup.
184#[inline]
185#[allow(clippy::too_many_lines)]
186pub unsafe fn startup<const BANNER: bool>(theme: BannerTheme) {
187 #[cfg(target_vendor = "vex")]
188 unsafe {
189 // Fill the `.bss` section of our program's memory with zeroes to ensure that
190 // uninitialized data is allocated properly.
191 zero_bss(&raw mut __bss_start, &raw mut __bss_end);
192
193 // Initialize the heap allocator using normal bounds
194 #[cfg(feature = "allocator")]
195 vexide_core::allocator::claim(&raw mut __heap_start, &raw mut __heap_end);
196
197 // If this link address is 0x03800000, this implies we were uploaded using
198 // differential uploads by cargo-v5 and may have a patch to apply.
199 if vex_sdk::vexSystemLinkAddrGet() == USER_MEMORY_START {
200 patcher::patch();
201 }
202
203 // Reclaim 6mb memory region occupied by patches and program copies as heap space.
204 #[cfg(feature = "allocator")]
205 vexide_core::allocator::claim(&raw mut __patcher_ram_start, &raw mut __patcher_ram_end);
206 }
207
208 // Print the banner
209 if BANNER {
210 banner::print(theme);
211 }
212}