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