firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 229 lines 6.5 kB view raw
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}