firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
1use core::str::Utf8Error;
2
3#[cfg(feature = "defmt")]
4use defmt::{warn, debug};
5
6pub const XIP_BASE: *const u8 = 0x10000000 as *const u8;
7pub const PROGRAM_RAM_AREA_BASE: *mut u8 = 0x20020000 as *mut u8;
8
9pub const SLOT_SIZE: usize = 0x80000;
10
11pub const unsafe fn slot_ptr(id: u8) -> *const u8 { unsafe {
12 if id > 31 {
13 panic!("slot ID must be between 0 and 31");
14 }
15
16 if id == 0 {
17 // "Slot 0" is used for the launcher, which is stored 128K into the flash
18 XIP_BASE.add(128 * 1024)
19 } else {
20 XIP_BASE.add(SLOT_SIZE * id as usize)
21 }
22}}
23
24pub const unsafe fn slot(id: u8) -> *const ProgramSlotHeader { unsafe {
25 slot_ptr(id).cast()
26}}
27
28pub fn slot_of_addr<T>(addr: *const T) -> u8 {
29 ((addr as usize - XIP_BASE as usize) / SLOT_SIZE) as u8
30}
31
32
33pub struct Programs {
34 id: u8,
35}
36
37impl Programs {
38 pub fn new() -> Self {
39 Self { id: 0 }
40 }
41}
42
43impl Iterator for Programs {
44 type Item = *const ProgramSlotHeader;
45
46 fn next(&mut self) -> Option<Self::Item> {
47 loop {
48 self.id += 1;
49
50 if self.id > 31 {
51 return None;
52 }
53
54 let s = unsafe { slot(self.id) };
55
56 unsafe {
57 if (*s).is_valid() {
58 #[cfg(feature = "defmt")]
59 debug!("Found program {} version {} in slot {}", (*s).name().unwrap(), (*s).version().unwrap(), self.id);
60 return Some(s);
61 } else {
62 #[cfg(feature = "defmt")]
63 debug!("No program found in slot {}", self.id);
64 }
65 }
66 }
67 }
68
69 fn size_hint(&self) -> (usize, Option<usize>) {
70 (0, Some(32 - self.id as usize))
71 }
72}
73
74#[repr(C)]
75pub struct ProgramSlotHeader {
76 pub block_erase_cycles: usize,
77 pub crc: u32,
78 pub len: usize,
79
80 pub data_len: usize,
81 pub data_lma: *const u8,
82 pub data_vma: *mut u8,
83
84 pub bss_len: usize,
85 pub bss_vma: *mut u8,
86
87 pub name_len: usize,
88 pub name_ptr: *const u8,
89
90 pub version_len: usize,
91 pub version_ptr: *const u8,
92
93 pub entry: unsafe extern "C" fn(),
94}
95
96unsafe impl Sync for ProgramSlotHeader {}
97
98impl ProgramSlotHeader {
99 pub const fn partial(name: &'static str, version: &'static str, entry: unsafe extern "C" fn()) -> Self {
100 Self {
101 block_erase_cycles: 0,
102 crc: 0,
103 len: 0,
104 data_len: 0,
105 data_lma: core::ptr::null(),
106 data_vma: core::ptr::null_mut(),
107 bss_len: 0,
108 bss_vma: core::ptr::null_mut(),
109 name_len: name.len(),
110 name_ptr: name.as_ptr(),
111 version_len: version.len(),
112 version_ptr: version.as_ptr(),
113 entry,
114 }
115 }
116
117 pub fn is_valid(&self) -> bool {
118 // Erased flash contains all 1s
119 if self.len == 0 || self.len == usize::MAX {
120 return false;
121 }
122
123 if self.len > SLOT_SIZE {
124 #[cfg(feature = "defmt")]
125 warn!("Program header has invalid size");
126 return false;
127 }
128
129 if !self.check_crc() {
130 #[cfg(feature = "defmt")]
131 warn!("Program has invalid CRC");
132 return false;
133 }
134
135 if unsafe { self.name() }.is_err() {
136 #[cfg(feature = "defmt")]
137 warn!("Program name is not valid UTF-8");
138 return false;
139 }
140
141 if unsafe { self.version() }.is_err() {
142 #[cfg(feature = "defmt")]
143 warn!("Program version string is not valid UTF-8");
144 return false;
145 }
146
147 let slot_min = (&raw const *self) as usize;
148 let slot_max = slot_min + SLOT_SIZE;
149 let slot_range = slot_min..slot_max;
150 let ram_min = PROGRAM_RAM_AREA_BASE as usize;
151 let ram_max = ram_min + 136 * 1024;
152 let ram_range = ram_min..ram_max;
153
154 if self.data_len > 0 {
155 if !slot_range.contains(&(self.data_lma as usize)) || !slot_range.contains(&(self.data_lma as usize + self.data_len - 1)) {
156 #[cfg(feature = "defmt")]
157 warn!("Program has invalid data section addresses");
158 return false;
159 }
160
161 if !ram_range.contains(&(self.data_vma as usize)) || !ram_range.contains(&(self.data_vma as usize + self.data_len - 1)) {
162 #[cfg(feature = "defmt")]
163 warn!("Program has invalid data section load addresses");
164 return false;
165 }
166 }
167
168 if self.bss_len > 0 {
169 if !ram_range.contains(&(self.bss_vma as usize)) || !ram_range.contains(&(self.bss_vma as usize + self.bss_len - 1)) {
170 #[cfg(feature = "defmt")]
171 warn!("Program has invalid bss section addresses");
172 return false;
173 }
174 }
175
176 true
177 }
178
179 pub fn slot(&self) -> u8 {
180 (((&raw const *self as usize) - XIP_BASE as usize) / SLOT_SIZE) as u8
181 }
182
183 pub fn check_crc(&self) -> bool {
184 if self.len >= SLOT_SIZE || self.len < size_of::<ProgramSlotHeader>() {
185 return false;
186 }
187
188 let ptr = unsafe { (&raw const *self).cast::<u8>().add(8) };
189 let slice = unsafe { core::slice::from_raw_parts(ptr, self.len - 8) };
190 let crc = crc32fast::hash(slice);
191 crc == self.crc
192 }
193
194 pub unsafe fn name(&self) -> Result<&str, Utf8Error> {
195 unsafe {
196 core::str::from_utf8(core::slice::from_raw_parts(self.name_ptr, self.name_len))
197 }
198 }
199
200 pub unsafe fn version(&self) -> Result<&str, Utf8Error> {
201 unsafe {
202 core::str::from_utf8(core::slice::from_raw_parts(self.version_ptr, self.version_len))
203 }
204 }
205
206 pub unsafe fn load(&self) { unsafe {
207 if self.data_len > 0 {
208 core::ptr::copy_nonoverlapping(self.data_lma, self.data_vma, self.data_len);
209 }
210
211 if self.bss_len > 0 {
212 self.bss_vma.write_bytes(0, self.bss_len);
213 }
214 }}
215}
216
217/// Get the slot number of the (first) program with the specified name, if it exists.
218pub fn find_program_by_name(name: &str) -> Option<u8> {
219 Programs::new()
220 .find(|psh| {
221 // SAFETY: a header returned by Programs must be valid
222 let prog_name = unsafe { (**psh).name() };
223 prog_name == Ok(name)
224 })
225 .map(slot_of_addr)
226}