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