platform/
dfu.rs

1use core::{
2    ptr,
3    sync::atomic::{self, Ordering},
4};
5
6/// Flag used to indicate that a reboot to DFU is requested.
7const DFU_FLAG: u32 = 0xDEAD_BEEF;
8
9unsafe extern "C" {
10    unsafe static mut _dfu_flag: u8;
11}
12
13/// Indicate a reboot to DFU is requested.
14pub fn dfu_reboot() {
15    unsafe {
16        ptr::write_unaligned(ptr::addr_of_mut!(_dfu_flag).cast(), DFU_FLAG);
17    }
18
19    cortex_m::peripheral::SCB::sys_reset();
20}
21
22/// Check if the DFU reboot flag is set, indicating a reboot to DFU is requested.
23pub fn dfu_flag_is_set() -> bool {
24    unsafe {
25        let start_ptr = ptr::addr_of_mut!(_dfu_flag).cast();
26        let set = DFU_FLAG == ptr::read_unaligned(start_ptr);
27
28        // Clear the boot flag after checking it to ensure it doesn't stick between reboots.
29        core::ptr::write_unaligned(start_ptr, 0);
30        atomic::fence(Ordering::SeqCst);
31        cortex_m::asm::dsb();
32        set
33    }
34}
35
36/// Execute the DFU bootloader stored in system memory.
37///
38/// # Note
39/// This function must be called before any system configuration is performed, as the DFU
40/// bootloader expects the system in an uninitialized state.
41pub fn bootload_dfu() {
42    // This process is largely adapted from
43    // https://community.st.com/t5/stm32-mcus/jump-to-bootloader-from-application-on-stm32h7-devices/ta-p/49510
44    cortex_m::interrupt::disable();
45
46    // Disable the SysTick peripheral.
47    let systick = unsafe { &*cortex_m::peripheral::SYST::PTR };
48    unsafe {
49        systick.csr.write(0);
50        systick.rvr.write(0);
51        systick.cvr.write(0);
52    }
53
54    // Clear NVIC interrupt flags and enables.
55    let nvic = unsafe { &*cortex_m::peripheral::NVIC::PTR };
56    for reg in nvic.icer.iter() {
57        unsafe {
58            reg.write(u32::MAX);
59        }
60    }
61
62    for reg in nvic.icpr.iter() {
63        unsafe {
64            reg.write(u32::MAX);
65        }
66    }
67
68    unsafe { cortex_m::interrupt::enable() };
69
70    log::info!("Jumping to DFU");
71
72    // The chip does not provide a means to modify the BOOT pins during
73    // run-time. Jump to the bootloader in system memory instead.
74    unsafe {
75        cortex_m::asm::bootload(0x1FF0_9800 as _);
76    }
77}