···11+use core::sync::atomic::Ordering;
22+use core::fmt::Write;
33+use portable_atomic::AtomicBool;
44+use usb_device::device::UsbDevice;
55+use usbd_serial::SerialPort;
66+use eepy_serial::{Response, SerialCommand};
77+use eepy_sys::flash::{erase_and_program, invalidate_cache};
88+use eepy_sys::image::{refresh, write_image, RefreshBlockMode};
99+use eepy_sys::IMAGE_BYTES;
1010+use eepy_sys::misc::{debug, trace};
1111+use eepy_sys::usb::UsbBus;
1212+use crate::{USB_DEVICE, USB_SERIAL};
1313+1414+#[derive(Copy, Clone, Debug)]
1515+enum SerialState {
1616+ ReadyForCommand,
1717+1818+ ReceivingImage {
1919+ fast_refresh: bool,
2020+ index: usize,
2121+ },
2222+2323+ FlashingProgram {
2424+ index: usize,
2525+ page: usize,
2626+ num_pages: Option<usize>,
2727+ remainder: Option<usize>,
2828+ },
2929+}
3030+3131+unsafe fn write_flash(buf: &[u8], slot: u8, page: usize) {
3232+ erase_and_program((slot as u32) * 512 * 1024 + (page as u32) * 4096, buf);
3333+}
3434+3535+fn write_all(serial: &mut SerialPort<UsbBus>, mut buf: &[u8]) {
3636+ while !buf.is_empty() {
3737+ let _ = serial.write(buf).map(|len| buf = &buf[len..]);
3838+ }
3939+}
4040+4141+pub(crate) static NEEDS_REFRESH_PROGRAMS: AtomicBool = AtomicBool::new(false);
4242+4343+pub(crate) extern "C" fn usb_handler() {
4444+ trace("USB handler");
4545+4646+ static mut STATE: SerialState = SerialState::ReadyForCommand;
4747+ #[allow(static_mut_refs)]
4848+ let state = unsafe { &mut STATE };
4949+5050+ #[allow(static_mut_refs)]
5151+ let dev: &mut UsbDevice<UsbBus> = unsafe { USB_DEVICE.as_mut().unwrap() };
5252+ #[allow(static_mut_refs)]
5353+ let serial: &mut SerialPort<UsbBus> = unsafe { USB_SERIAL.as_mut().unwrap() };
5454+5555+ // Receive buffer. Size equal to IMAGE_BYTES so it can store an entire frame; also used for
5656+ // receiving flash applications.
5757+ static mut BUF: [u8; IMAGE_BYTES] = [0; IMAGE_BYTES];
5858+ #[allow(static_mut_refs)]
5959+ let buf = unsafe { &mut BUF };
6060+6161+ if dev.poll(&mut [serial]) {
6262+ match state {
6363+ SerialState::ReadyForCommand => {
6464+ let mut cmd_buf = [0u8];
6565+ if let Ok(count) = serial.read(&mut cmd_buf) {
6666+ if count == 0 {
6767+ return;
6868+ }
6969+7070+ match SerialCommand::try_from(cmd_buf[0]) {
7171+ Ok(SerialCommand::RefreshNormal) => *state = SerialState::ReceivingImage { fast_refresh: false, index: 0 },
7272+ Ok(SerialCommand::RefreshFast) => *state = SerialState::ReceivingImage { fast_refresh: true, index: 0 },
7373+ Ok(SerialCommand::UploadProgram) => *state = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None },
7474+ Ok(_) => write_all(serial, &[Response::UnknownCommand as u8]),
7575+ Err(_) => write_all(serial, &[Response::UnknownCommand as u8]),
7676+ }
7777+ }
7878+ }
7979+8080+ SerialState::ReceivingImage { fast_refresh, index } => {
8181+ if let Ok(count) = serial.read(&mut buf[*index..]) {
8282+ *index += count;
8383+ if *index == IMAGE_BYTES {
8484+ write_image(buf);
8585+ refresh(*fast_refresh, RefreshBlockMode::NonBlocking);
8686+ write_all(serial, &[Response::Ack as u8]);
8787+ *state = SerialState::ReadyForCommand;
8888+ }
8989+ }
9090+ }
9191+9292+ SerialState::FlashingProgram { index, page, num_pages, remainder } => {
9393+ // Write page 0 last - this is the header, so we only want to write it once everything
9494+ // else is written successfully
9595+ // Keep page 0 in the first 4096 bytes of BUF for the end
9696+ if *page == 0 {
9797+ debug("receiving page 0");
9898+ if let Ok(count) = serial.read(&mut buf[*index..4096]) {
9999+ *index += count;
100100+101101+ if num_pages.is_none() && *index >= 12 {
102102+ let mut b = [0u8; 4];
103103+ b.copy_from_slice(&buf[8..12]);
104104+ let num_bytes = usize::from_le_bytes(b);
105105+ *num_pages = Some(num_bytes.div_ceil(4096));
106106+ *remainder = Some(num_bytes % 4096);
107107+ }
108108+109109+ if *index == 4096 {
110110+ *index = 0;
111111+ *page += 1;
112112+ }
113113+ }
114114+ } else {
115115+ if let Ok(count) = serial.read(&mut buf[(4096 + *index)..8192]) {
116116+ let mut message = heapless::String::<32>::new();
117117+ write!(message, "receiving page {page}").unwrap();
118118+ debug(&message);
119119+120120+ *index += count;
121121+122122+ let num_pages = num_pages.unwrap();
123123+ let remainder = remainder.unwrap();
124124+ if *index == 4096 || (*page == num_pages - 1 && *index == remainder) {
125125+ *index = 0;
126126+127127+ // Actually write the flash page
128128+ // TODO: get next slot instead of always using slot 1
129129+ // TODO: wear levelling
130130+ unsafe { write_flash(&buf[4096..8192], 1, *page) };
131131+132132+ *page += 1;
133133+ // If this is the last page, also flash the first page which we didn't
134134+ // do at the start
135135+ if *page == num_pages {
136136+ unsafe { write_flash(&buf[0..4096], 1, 0) };
137137+138138+ // Invalidate the XIP cache, in case something from the flash area
139139+ // we just wrote is in there
140140+ unsafe { invalidate_cache() }
141141+142142+ NEEDS_REFRESH_PROGRAMS.store(true, Ordering::Relaxed);
143143+ debug("Finished writing program");
144144+145145+ *state = SerialState::ReadyForCommand;
146146+ }
147147+ }
148148+ }
149149+ }
150150+ }
151151+ }
152152+ }
153153+}
+2-2
eepy-sys/src/critical_section_impl.rs
···7788unsafe impl critical_section::Impl for EepyCs {
99 unsafe fn acquire() -> bool {
1010- let mut state: bool;
1010+ let mut state: usize;
1111 syscall!(
1212 SyscallNumber::CriticalSection,
1313 out state in CsSyscall::Acquire,
1414 );
1515- state
1515+ state != 0
1616 }
17171818 unsafe fn release(state: bool) {
+62
eepy-sys/src/flash.rs
···11+use crate::syscall;
22+use crate::syscall::SyscallNumber;
33+44+#[repr(usize)]
55+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
66+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77+pub enum FlashSyscall {
88+ Erase = 0,
99+ Program = 1,
1010+ EraseAndProgram = 2,
1111+ InvalidateCache = 3,
1212+}
1313+1414+impl TryFrom<usize> for FlashSyscall {
1515+ type Error = ();
1616+1717+ fn try_from(value: usize) -> Result<Self, Self::Error> {
1818+ match value {
1919+ x if x == FlashSyscall::Erase as usize => Ok(FlashSyscall::Erase),
2020+ x if x == FlashSyscall::Program as usize => Ok(FlashSyscall::Program),
2121+ x if x == FlashSyscall::EraseAndProgram as usize => Ok(FlashSyscall::EraseAndProgram),
2222+ x if x == FlashSyscall::InvalidateCache as usize => Ok(FlashSyscall::InvalidateCache),
2323+ _ => Err(()),
2424+ }
2525+ }
2626+}
2727+2828+pub unsafe fn erase(start_addr: u32, len: u32) {
2929+ syscall!(
3030+ SyscallNumber::Flash,
3131+ in FlashSyscall::Erase,
3232+ in start_addr,
3333+ in len,
3434+ );
3535+}
3636+3737+pub unsafe fn program(start_addr: u32, data: &[u8]) {
3838+ syscall!(
3939+ SyscallNumber::Flash,
4040+ in FlashSyscall::Program,
4141+ in start_addr,
4242+ in data.len(),
4343+ in data.as_ptr(),
4444+ );
4545+}
4646+4747+pub unsafe fn erase_and_program(start_addr: u32, data: &[u8]) {
4848+ syscall!(
4949+ SyscallNumber::Flash,
5050+ in FlashSyscall::EraseAndProgram,
5151+ in start_addr,
5252+ in data.len(),
5353+ in data.as_ptr(),
5454+ );
5555+}
5656+5757+pub unsafe fn invalidate_cache() {
5858+ syscall!(
5959+ SyscallNumber::Flash,
6060+ in FlashSyscall::InvalidateCache,
6161+ );
6262+}
+1
eepy-sys/src/lib.rs
···88pub mod usb;
99pub mod exec;
1010pub mod critical_section;
1111+pub mod flash;
11121213#[cfg(feature = "critical-section-impl")]
1314mod critical_section_impl;
+2
eepy-sys/src/syscall.rs
···88 Usb = 3,
99 Exec = 4,
1010 CriticalSection = 5,
1111+ Flash = 6,
1112}
12131314impl TryFrom<u8> for SyscallNumber {
···2122 x if x == SyscallNumber::Usb as u8 => Ok(SyscallNumber::Usb),
2223 x if x == SyscallNumber::Exec as u8 => Ok(SyscallNumber::Exec),
2324 x if x == SyscallNumber::CriticalSection as u8 => Ok(SyscallNumber::CriticalSection),
2525+ x if x == SyscallNumber::Flash as u8 => Ok(SyscallNumber::Flash),
2426 _ => Err(()),
2527 }
2628 }
-182
eepy/src/serial.rs
···11-use core::sync::atomic::Ordering;
22-use crate::{interrupt, FLASHING, FLASHING_ACK};
33-use defmt::{debug, trace};
44-use usbd_serial::SerialPort;
55-use fw16_epd_bsp::hal::usb::UsbBus;
66-use fw16_epd_bsp::pac;
77-use eepy_sys::header::ProgramSlotHeader;
88-use eepy_serial::{Response, SerialCommand};
99-use eepy_sys::image::{refresh, write_image, RefreshBlockMode};
1010-use tp370pgh01::IMAGE_BYTES;
1111-#[derive(Copy, Clone, Debug, defmt::Format)]
1212-enum SerialState {
1313- ReadyForCommand,
1414-1515- ReceivingImage {
1616- fast_refresh: bool,
1717- index: usize,
1818- },
1919-2020- FlashingProgram {
2121- index: usize,
2222- page: usize,
2323- num_pages: Option<usize>,
2424- remainder: Option<usize>,
2525- },
2626-}
2727-2828-fn write_all(serial: &mut SerialPort<UsbBus>, mut buf: &[u8]) {
2929- while !buf.is_empty() {
3030- let _ = serial.write(buf).map(|len| buf = &buf[len..]);
3131- }
3232-}
3333-3434-/// Safety:
3535-///
3636-/// This function takes care of the main safety requirements of flashing, but the
3737-/// caller must ensure that the `slot` and `page` parameters are valid and do
3838-/// not produce an address outside the flash's range. Additionally, do not write
3939-/// to slot 0 as this contains the firmware.
4040-unsafe fn write_flash(buf: &[u8], slot: u8, page: usize) {
4141- debug!("Begin write slot {} page {}", slot, page);
4242-4343- // Make sure core1 is running code from RAM with interrupts disabled
4444- FLASHING.store(true, Ordering::Relaxed);
4545- cortex_m::asm::sev();
4646- // Wait until core1 has acknowledged that it is now in RAM code
4747- while !FLASHING_ACK.load(Ordering::Relaxed) {}
4848- // Disable interrupts on this core
4949- cortex_m::interrupt::disable();
5050-5151- unsafe {
5252- rp2040_flash::flash::flash_range_erase_and_program(
5353- (slot as u32) * 512 * 1024 + (page as u32) * 4096,
5454- buf,
5555- true
5656- );
5757- }
5858-5959- // Enable interrupts
6060- unsafe { cortex_m::interrupt::enable() }
6161- // Wake up core1
6262- FLASHING.store(false, Ordering::Relaxed);
6363- cortex_m::asm::sev();
6464-6565- debug!("End write slot {} page {}", slot, page);
6666-}
6767-6868-/*
6969-#[interrupt]
7070-fn USBCTRL_IRQ() {
7171- static mut STATE: SerialState = SerialState::ReadyForCommand;
7272-7373- // Receive buffer. Size equal to IMAGE_BYTES so it can store an entire frame; also used for
7474- // receiving flash applications.
7575- static mut BUF: [u8; IMAGE_BYTES] = [0; IMAGE_BYTES];
7676-7777- trace!("USBCTRL_IRQ");
7878-7979- // Safety: These are only accessed within this interrupt handler, or in main() before the
8080- // interrupt is enabled.
8181- #[allow(static_mut_refs)]
8282- let usb_dev = unsafe { GLOBAL_USB_DEVICE.as_mut().unwrap() };
8383- #[allow(static_mut_refs)]
8484- let serial = unsafe { GLOBAL_USB_SERIAL.as_mut().unwrap() };
8585-8686- if usb_dev.poll(&mut [serial]) {
8787- match STATE {
8888- SerialState::ReadyForCommand => {
8989- let mut cmd_buf = [0u8];
9090- if let Ok(count) = serial.read(&mut cmd_buf) {
9191- if count == 0 {
9292- return;
9393- }
9494-9595- match SerialCommand::try_from(cmd_buf[0]) {
9696- Ok(SerialCommand::RefreshNormal) => *STATE = SerialState::ReceivingImage { fast_refresh: false, index: 0 },
9797- Ok(SerialCommand::RefreshFast) => *STATE = SerialState::ReceivingImage { fast_refresh: true, index: 0 },
9898- Ok(SerialCommand::UploadProgram) => *STATE = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None },
9999- Ok(_) => write_all(serial, &[Response::UnknownCommand as u8]),
100100- Err(_) => write_all(serial, &[Response::UnknownCommand as u8]),
101101- }
102102- }
103103- }
104104-105105- SerialState::ReceivingImage { fast_refresh, index } => {
106106- if let Ok(count) = serial.read(&mut BUF[*index..]) {
107107- *index += count;
108108- if *index == IMAGE_BYTES {
109109- write_image(BUF);
110110- refresh(*fast_refresh, RefreshBlockMode::NonBlocking);
111111- write_all(serial, &[Response::Ack as u8]);
112112- *STATE = SerialState::ReadyForCommand;
113113- }
114114- }
115115- }
116116-117117- SerialState::FlashingProgram { index, page, num_pages, remainder } => {
118118- debug!("Flashing program - page {}", *page);
119119- // Write page 0 last - this is the header, so we only want to write it once everything
120120- // else is written successfully
121121- // Keep page 0 in the first 4096 bytes of BUF for the end
122122- debug!("{} {} {} {}", index, page, num_pages, remainder);
123123- if *page == 0 {
124124- if let Ok(count) = serial.read(&mut BUF[*index..4096]) {
125125- *index += count;
126126-127127- if num_pages.is_none() && *index >= 12 {
128128- let mut b = [0u8; 4];
129129- b.copy_from_slice(&BUF[8..12]);
130130- let num_bytes = usize::from_le_bytes(b);
131131- debug!("Program is {} bytes ({} pages) long", num_bytes, num_bytes.div_ceil(4096));
132132- *num_pages = Some(num_bytes.div_ceil(4096));
133133- *remainder = Some(num_bytes % 4096);
134134- }
135135-136136- if *index == 4096 {
137137- *index = 0;
138138- *page += 1;
139139- }
140140- }
141141- } else {
142142- if let Ok(count) = serial.read(&mut BUF[(4096 + *index)..8192]) {
143143- *index += count;
144144-145145- let num_pages = num_pages.unwrap();
146146- let remainder = remainder.unwrap();
147147- if *index == 4096 || (*page == num_pages - 1 && *index == remainder) {
148148- *index = 0;
149149-150150- // Actually write the flash page
151151- // TODO: get next slot instead of always using slot 1
152152- // TODO: wear levelling
153153- unsafe { write_flash(&BUF[4096..8192], 1, *page) };
154154-155155- *page += 1;
156156- // If this is the last page, also flash the first page which we didn't
157157- // do at the start
158158- if *page == num_pages {
159159- unsafe { write_flash(&BUF[0..4096], 1, 0) };
160160-161161- // Invalidate the XIP cache, in case something from the flash area
162162- // we just wrote is in there
163163- unsafe {
164164- // FIXME: steal
165165- let xip = pac::Peripherals::steal().XIP_CTRL;
166166- xip.flush().write(|w| w.flush().set_bit());
167167- xip.flush().read();
168168- };
169169-170170- let program = unsafe { &*(0x10080000 as *const ProgramSlotHeader) };
171171- debug!("{} {}", program.name().unwrap(), program.version().unwrap());
172172-173173- *STATE = SerialState::ReadyForCommand;
174174- }
175175- }
176176- }
177177- }
178178- }
179179- }
180180- }
181181-}
182182- */
+111
eepy/src/syscall.rs
···3333 Ok(SyscallNumber::Usb) => crate::usb::handle_usb(stack_values),
3434 Ok(SyscallNumber::Exec) => handle_exec(stack_values, using_psp),
3535 Ok(SyscallNumber::CriticalSection) => cs::handle_cs(stack_values),
3636+ Ok(SyscallNumber::Flash) => flash::handle_flash(stack_values),
3637 Err(_) => panic!("illegal syscall"),
3738 }
3839}
···234235 }
235236 }
236237 core::sync::atomic::compiler_fence(Ordering::SeqCst);
238238+ }
239239+}
240240+241241+mod flash {
242242+ use core::sync::atomic::Ordering;
243243+ use eepy_sys::flash::FlashSyscall;
244244+ use eepy_sys::header::{SLOT_SIZE, XIP_BASE};
245245+ use fw16_epd_bsp::pac;
246246+ use crate::exception::StackFrame;
247247+ use crate::{FLASHING, FLASHING_ACK};
248248+249249+ pub(super) fn handle_flash(stack_values: &mut StackFrame) {
250250+ match FlashSyscall::try_from(stack_values.r0) {
251251+ Ok(FlashSyscall::Erase) => handle_erase(stack_values),
252252+ Ok(FlashSyscall::Program) => handle_program(stack_values),
253253+ Ok(FlashSyscall::EraseAndProgram) => handle_erase_and_program(stack_values),
254254+ Ok(FlashSyscall::InvalidateCache) => handle_invalidate_cache(),
255255+ Err(_) => panic!("illegal syscall"),
256256+ }
257257+ }
258258+259259+ fn assert_permissions(stack_values: &mut StackFrame, start: u32, len: u32) {
260260+ let slot_n = (stack_values.pc as usize - XIP_BASE as usize) / SLOT_SIZE;
261261+ let start_addr_xip = start as usize + XIP_BASE as usize;
262262+ let end_addr_xip = start_addr_xip + len as usize;
263263+264264+ if slot_n == 0 {
265265+ // Slot 0 (launcher) can write any flash except kernel
266266+ if start_addr_xip < (XIP_BASE as usize + 128 * 1024) {
267267+ panic!("illegal flash write");
268268+ }
269269+ return;
270270+ }
271271+272272+ let slot_start = XIP_BASE as usize + (slot_n * SLOT_SIZE);
273273+ let slot_end = slot_start + SLOT_SIZE;
274274+ if start_addr_xip < slot_start || end_addr_xip > slot_end {
275275+ panic!("illegal flash write");
276276+ }
277277+ }
278278+279279+ fn begin() {
280280+ // Make sure core1 is running code from RAM with interrupts disabled
281281+ FLASHING.store(true, Ordering::Relaxed);
282282+ cortex_m::asm::sev();
283283+ // Wait until core1 has acknowledged that it is now in RAM code
284284+ while !FLASHING_ACK.load(Ordering::Relaxed) {}
285285+ // Disable interrupts on this core
286286+ cortex_m::interrupt::disable();
287287+ }
288288+289289+ fn end() {
290290+ // Enable interrupts
291291+ unsafe { cortex_m::interrupt::enable() }
292292+ // Wake up core1
293293+ FLASHING.store(false, Ordering::Relaxed);
294294+ cortex_m::asm::sev();
295295+ }
296296+297297+ fn handle_erase(stack_values: &mut StackFrame) {
298298+ let start_addr = stack_values.r1 as u32;
299299+ let len = stack_values.r2 as u32;
300300+ assert_permissions(stack_values, start_addr, len);
301301+ if start_addr % 4096 != 0 || len % 4096 != 0 {
302302+ panic!("unaligned flash erase");
303303+ }
304304+305305+ begin();
306306+ unsafe {
307307+ rp2040_flash::flash::flash_range_erase(start_addr, len, true);
308308+ }
309309+ end();
310310+ }
311311+312312+ fn handle_program(stack_values: &mut StackFrame) {
313313+ let start_addr = stack_values.r1 as u32;
314314+ let data = unsafe { core::slice::from_raw_parts(stack_values.r3 as *const u8, stack_values.r2) };
315315+ assert_permissions(stack_values, start_addr, data.len() as u32);
316316+ if start_addr % 256 != 0 || data.len() % 256 != 0 {
317317+ panic!("unaligned flash program");
318318+ }
319319+320320+ begin();
321321+ unsafe {
322322+ rp2040_flash::flash::flash_range_program(start_addr, data, true);
323323+ }
324324+ end();
325325+ }
326326+327327+ fn handle_erase_and_program(stack_values: &mut StackFrame) {
328328+ let start_addr = stack_values.r1 as u32;
329329+ let data = unsafe { core::slice::from_raw_parts(stack_values.r3 as *const u8, stack_values.r2) };
330330+ assert_permissions(stack_values, start_addr, data.len() as u32);
331331+ if start_addr % 4096 != 0 || data.len() % 4096 != 0 {
332332+ panic!("unaligned flash erase");
333333+ }
334334+335335+ begin();
336336+ unsafe {
337337+ rp2040_flash::flash::flash_range_erase_and_program(start_addr, data, true);
338338+ }
339339+ end();
340340+ }
341341+342342+ fn handle_invalidate_cache() {
343343+ unsafe {
344344+ let xip = pac::Peripherals::steal().XIP_CTRL;
345345+ xip.flush().write(|w| w.flush().set_bit());
346346+ xip.flush().read();
347347+ };
237348 }
238349}