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