diff --git a/examples/dma.rs b/examples/dma.rs index 66bf8e6c..c385d5e7 100644 --- a/examples/dma.rs +++ b/examples/dma.rs @@ -6,6 +6,9 @@ use core::mem::MaybeUninit; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use cortex_m_rt::entry; #[macro_use] mod utilities; @@ -24,9 +27,11 @@ use log::info; // // The runtime does not initialise these SRAM banks. #[link_section = ".sram4.buffers"] -static mut SOURCE_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit(); +static SOURCE_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[link_section = ".axisram.buffers"] -static mut TARGET_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit(); +static TARGET_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -53,29 +58,37 @@ fn main() -> ! { info!("stm32h7xx-hal example - Memory to Memory DMA"); info!(""); - // Initialise the source buffer with truly random data, without taking any - // references to uninitialisated memory - let source_buffer: &'static mut [u32; 20] = { - let buf: &mut [MaybeUninit; 20] = unsafe { - &mut *(core::ptr::addr_of_mut!(SOURCE_BUFFER) - as *mut [MaybeUninit; 20]) - }; - - for value in buf.iter_mut() { - unsafe { - value.as_mut_ptr().write(rng.gen().unwrap()); - } - } - #[allow(static_mut_refs)] // TODO: Fix this - unsafe { - SOURCE_BUFFER.assume_init_mut() + // SOURCE_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = SOURCE_BUFFER.as_ptr(); + for i in 0..20 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(rng.gen().unwrap()); } - }; + } + // Now we can take a mutable reference to SOURCE_BUFFER. To avoid aliasing, + // this reference must only be taken once + let source_buffer = + unsafe { &mut *SyncUnsafeCell::raw_get(SOURCE_BUFFER.as_ptr()) }; // Save a copy on the stack so we can check it later let source_buffer_cloned = *source_buffer; - // NOTE(unsafe): TARGET_BUFFER must also be initialised to prevent undefined - // behaviour (taking a mutable reference to uninitialised memory) + // TARGET_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = TARGET_BUFFER.as_ptr(); + for i in 0..20 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); + } + } + // Now we can take a mutable reference to TARGET_BUFFER. To avoid aliasing, + // this reference must only be taken once + let target_buffer = + unsafe { &mut *SyncUnsafeCell::raw_get(TARGET_BUFFER.as_ptr()) }; // Setup DMA // @@ -92,9 +105,7 @@ fn main() -> ! { Transfer::init( streams.4, MemoryToMemory::new(), - unsafe { - (*core::ptr::addr_of_mut!(TARGET_BUFFER)).assume_init_mut() - }, // Uninitialised memory + target_buffer, Some(source_buffer), config, ); @@ -104,10 +115,11 @@ fn main() -> ! { // Wait for transfer to complete while !transfer.get_transfer_complete_flag() {} - // Now the target memory is actually initialised - #[allow(static_mut_refs)] // TODO: Fix this - let target_buffer: &'static mut [u32; 20] = - unsafe { TARGET_BUFFER.assume_init_mut() }; + // Take a second(!) reference to the target buffer. Because the transfer has + // stopped, we can reason that the first reference is no longer + // active. Therefore we can take another reference without causing aliasing + let target_buffer: &[u32; 20] = + unsafe { &*SyncUnsafeCell::raw_get(TARGET_BUFFER.as_ptr()) }; // Comparison check assert_eq!(&source_buffer_cloned, target_buffer); diff --git a/examples/ethernet-nucleo-h743zi2.rs b/examples/ethernet-nucleo-h743zi2.rs index 787ca35c..f09e487e 100644 --- a/examples/ethernet-nucleo-h743zi2.rs +++ b/examples/ethernet-nucleo-h743zi2.rs @@ -17,6 +17,9 @@ use core::mem::MaybeUninit; use core::sync::atomic::{AtomicU32, Ordering}; use rt::{entry, exception}; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + extern crate cortex_m; #[macro_use] @@ -52,7 +55,7 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; /// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] -static mut DES_RING: MaybeUninit> = +static DES_RING: MaybeUninit>> = MaybeUninit::uninit(); // the program entry point @@ -113,9 +116,18 @@ fn main() -> ! { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (_eth_dma, eth_mac) = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.write(ethernet::DesRing::new()); + let (_eth_dma, eth_mac) = { + // DES_RING is located in .axisram.eth, which is not initialized by + // the runtime. We must manually initialize it to a valid value, + // without taking a reference to the uninitialized value + unsafe { + SyncUnsafeCell::raw_get(DES_RING.as_ptr()) + .write(ethernet::DesRing::new()); + } + // Now we can take a mutable reference to DES_RING. To avoid + // aliasing, this reference must only be taken once + let des_ring = + unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) }; ethernet::new( dp.ETHERNET_MAC, @@ -132,8 +144,7 @@ fn main() -> ! { rmii_txd0, rmii_txd1, ), - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.assume_init_mut(), + des_ring, mac_addr, ccdr.peripheral.ETH1MAC, &ccdr.clocks, diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs index 4927115c..feb74320 100644 --- a/examples/ethernet-rtic-nucleo-h723zg.rs +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -20,9 +20,11 @@ mod utilities; use core::mem::MaybeUninit; -use core::ptr::addr_of_mut; use core::sync::atomic::AtomicU32; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; @@ -49,17 +51,19 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; /// Ethernet descriptor rings are a global singleton #[link_section = ".axisram.eth"] -static mut DES_RING: MaybeUninit> = +static DES_RING: MaybeUninit>> = MaybeUninit::uninit(); /// Net storage with static initialisation - another global singleton +#[derive(Default)] pub struct NetStorageStatic<'a> { socket_storage: [SocketStorage<'a>; 8], } // MaybeUninit allows us write code that is correct even if STORE is not // initialised by the runtime -static mut STORE: MaybeUninit = MaybeUninit::uninit(); +static STORE: MaybeUninit> = + MaybeUninit::uninit(); pub struct Net<'a> { iface: Interface, @@ -163,9 +167,18 @@ mod app { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (eth_dma, eth_mac) = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.write(ethernet::DesRing::new()); + let (eth_dma, eth_mac) = { + // DES_RING is located in .axisram.eth, which is not initialized by + // the runtime. We must manually initialize it to a valid value, + // without taking a reference to the uninitialized value + unsafe { + SyncUnsafeCell::raw_get(DES_RING.as_ptr()) + .write(ethernet::DesRing::new()); + } + // Now we can take a mutable reference to DES_RING. To avoid + // aliasing, this reference must only be taken once + let des_ring = + unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) }; ethernet::new( ctx.device.ETHERNET_MAC, @@ -182,8 +195,7 @@ mod app { rmii_txd0, rmii_txd1, ), - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.assume_init_mut(), + des_ring, mac_addr, ccdr.peripheral.ETH1MAC, &ccdr.clocks, @@ -198,22 +210,14 @@ mod app { unsafe { ethernet::enable_interrupt() }; - // unsafe: mutable reference to static storage, we only do this once - let store = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - let store_ptr = STORE.as_mut_ptr(); - - // Initialise the socket_storage field. Using `write` instead of - // assignment via `=` to not call `drop` on the old, uninitialised - // value - addr_of_mut!((*store_ptr).socket_storage) - .write([SocketStorage::EMPTY; 8]); - - // Now that all fields are initialised we can safely use - // assume_init_mut to return a mutable reference to STORE - #[allow(static_mut_refs)] // TODO: Fix this - STORE.assume_init_mut() - }; + // Initialize STORE + unsafe { + SyncUnsafeCell::raw_get(STORE.as_ptr()) + .write(NetStorageStatic::default()); + } + // Now we can take a mutable reference to STORE. To avoid aliasing, this + // reference must only be taken once + let store = unsafe { &mut *SyncUnsafeCell::raw_get(STORE.as_ptr()) }; let net = Net::new(store, eth_dma, mac_addr.into()); diff --git a/examples/ethernet-rtic-stm32h735g-dk.rs b/examples/ethernet-rtic-stm32h735g-dk.rs index d7449504..ff8c0cda 100644 --- a/examples/ethernet-rtic-stm32h735g-dk.rs +++ b/examples/ethernet-rtic-stm32h735g-dk.rs @@ -16,9 +16,11 @@ mod utilities; use core::mem::MaybeUninit; -use core::ptr::addr_of_mut; use core::sync::atomic::AtomicU32; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; @@ -45,16 +47,19 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; /// Ethernet descriptor rings are a global singleton #[link_section = ".axisram.eth"] -static mut DES_RING: MaybeUninit> = +static DES_RING: MaybeUninit>> = MaybeUninit::uninit(); // This data will be held by Net through a mutable reference +#[derive(Default)] pub struct NetStorageStatic<'a> { socket_storage: [SocketStorage<'a>; 8], } + // MaybeUninit allows us write code that is correct even if STORE is not // initialised by the runtime -static mut STORE: MaybeUninit = MaybeUninit::uninit(); +static STORE: MaybeUninit> = + MaybeUninit::uninit(); pub struct Net<'a> { iface: Interface, @@ -157,9 +162,18 @@ mod app { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (eth_dma, eth_mac) = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.write(ethernet::DesRing::new()); + let (eth_dma, eth_mac) = { + // DES_RING is located in .axisram.eth, which is not initialized by + // the runtime. We must manually initialize it to a valid value, + // without taking a reference to the uninitialized value + unsafe { + SyncUnsafeCell::raw_get(DES_RING.as_ptr()) + .write(ethernet::DesRing::new()); + } + // Now we can take a mutable reference to DES_RING. To avoid + // aliasing, this reference must only be taken once + let des_ring = + unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) }; ethernet::new( ctx.device.ETHERNET_MAC, @@ -176,8 +190,7 @@ mod app { rmii_txd0, rmii_txd1, ), - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.assume_init_mut(), + des_ring, mac_addr, ccdr.peripheral.ETH1MAC, &ccdr.clocks, @@ -192,22 +205,14 @@ mod app { unsafe { ethernet::enable_interrupt() }; - // unsafe: mutable reference to static storage, we only do this once - let store = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - let store_ptr = STORE.as_mut_ptr(); - - // Initialise the socket_storage field. Using `write` instead of - // assignment via `=` to not call `drop` on the old, uninitialised - // value - addr_of_mut!((*store_ptr).socket_storage) - .write([SocketStorage::EMPTY; 8]); - - // Now that all fields are initialised we can safely use - // assume_init_mut to return a mutable reference to STORE - #[allow(static_mut_refs)] // TODO: Fix this - STORE.assume_init_mut() - }; + // Initialize STORE + unsafe { + SyncUnsafeCell::raw_get(STORE.as_ptr()) + .write(NetStorageStatic::default()); + } + // Now we can take a mutable reference to STORE. To avoid aliasing, this + // reference must only be taken once + let store = unsafe { &mut *SyncUnsafeCell::raw_get(STORE.as_ptr()) }; let net = Net::new(store, eth_dma, mac_addr.into(), Instant::ZERO); diff --git a/examples/ethernet-rtic-stm32h747i-disco.rs b/examples/ethernet-rtic-stm32h747i-disco.rs index 43cdc6a3..74e30cb3 100644 --- a/examples/ethernet-rtic-stm32h747i-disco.rs +++ b/examples/ethernet-rtic-stm32h747i-disco.rs @@ -23,9 +23,11 @@ mod utilities; use core::mem::MaybeUninit; -use core::ptr::addr_of_mut; use core::sync::atomic::AtomicU32; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; @@ -52,16 +54,18 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; /// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] -static mut DES_RING: MaybeUninit> = +static DES_RING: MaybeUninit>> = MaybeUninit::uninit(); // This data will be held by Net through a mutable reference +#[derive(Default)] pub struct NetStorageStatic<'a> { socket_storage: [SocketStorage<'a>; 8], } // MaybeUninit allows us write code that is correct even if STORE is not // initialised by the runtime -static mut STORE: MaybeUninit = MaybeUninit::uninit(); +static STORE: MaybeUninit> = + MaybeUninit::uninit(); pub struct Net<'a> { iface: Interface, @@ -167,9 +171,18 @@ mod app { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (eth_dma, eth_mac) = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.write(ethernet::DesRing::new()); + let (eth_dma, eth_mac) = { + // DES_RING is located in .axisram.eth, which is not initialized by + // the runtime. We must manually initialize it to a valid value, + // without taking a reference to the uninitialized value + unsafe { + SyncUnsafeCell::raw_get(DES_RING.as_ptr()) + .write(ethernet::DesRing::new()); + } + // Now we can take a mutable reference to DES_RING. To avoid + // aliasing, this reference must only be taken once + let des_ring = + unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) }; ethernet::new( ctx.device.ETHERNET_MAC, @@ -186,8 +199,7 @@ mod app { rmii_txd0, rmii_txd1, ), - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.assume_init_mut(), + des_ring, mac_addr, ccdr.peripheral.ETH1MAC, &ccdr.clocks, @@ -202,22 +214,14 @@ mod app { unsafe { ethernet::enable_interrupt() }; - // unsafe: mutable reference to static storage, we only do this once - let store = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - let store_ptr = STORE.as_mut_ptr(); - - // Initialise the socket_storage field. Using `write` instead of - // assignment via `=` to not call `drop` on the old, uninitialised - // value - addr_of_mut!((*store_ptr).socket_storage) - .write([SocketStorage::EMPTY; 8]); - - // Now that all fields are initialised we can safely use - // assume_init_mut to return a mutable reference to STORE - #[allow(static_mut_refs)] // TODO: Fix this - STORE.assume_init_mut() - }; + // Initialize STORE + unsafe { + SyncUnsafeCell::raw_get(STORE.as_ptr()) + .write(NetStorageStatic::default()); + } + // Now we can take a mutable reference to STORE. To avoid aliasing, this + // reference must only be taken once + let store = unsafe { &mut *SyncUnsafeCell::raw_get(STORE.as_ptr()) }; let net = Net::new(store, eth_dma, mac_addr.into(), Instant::ZERO); diff --git a/examples/ethernet-stm32h747i-disco.rs b/examples/ethernet-stm32h747i-disco.rs index 7fd027bc..8befad42 100644 --- a/examples/ethernet-stm32h747i-disco.rs +++ b/examples/ethernet-stm32h747i-disco.rs @@ -15,6 +15,9 @@ use core::mem::MaybeUninit; use cortex_m_rt as rt; use rt::{entry, exception}; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + #[macro_use] #[allow(unused)] mod utilities; @@ -29,7 +32,7 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; /// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] -static mut DES_RING: MaybeUninit> = +static DES_RING: MaybeUninit>> = MaybeUninit::uninit(); // the program entry point @@ -86,9 +89,18 @@ fn main() -> ! { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (_eth_dma, eth_mac) = unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.write(ethernet::DesRing::new()); + let (_eth_dma, eth_mac) = { + // DES_RING is located in .axisram.eth, which is not initialized by + // the runtime. We must manually initialize it to a valid value, + // without taking a reference to the uninitialized value + unsafe { + SyncUnsafeCell::raw_get(DES_RING.as_ptr()) + .write(ethernet::DesRing::new()); + } + // Now we can take a mutable reference to DES_RING. To avoid + // aliasing, this reference must only be taken once + let des_ring = + unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) }; ethernet::new( dp.ETHERNET_MAC, @@ -105,8 +117,7 @@ fn main() -> ! { rmii_txd0, rmii_txd1, ), - #[allow(static_mut_refs)] // TODO: Fix this - DES_RING.assume_init_mut(), + des_ring, mac_addr, ccdr.peripheral.ETH1MAC, &ccdr.clocks, diff --git a/examples/i2c4_bdma.rs b/examples/i2c4_bdma.rs index 50e596ec..22c4da1a 100644 --- a/examples/i2c4_bdma.rs +++ b/examples/i2c4_bdma.rs @@ -1,7 +1,6 @@ //! I2C4 in low power mode. //! //! - #![deny(warnings)] #![no_std] #![no_main] @@ -11,6 +10,9 @@ use core::mem::MaybeUninit; #[macro_use] mod utilities; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use stm32h7xx_hal::dma::{ bdma::{BdmaConfig, StreamsTuple}, PeripheralToMemory, Transfer, @@ -27,7 +29,7 @@ use log::info; // // The runtime does not initialise this SRAM bank #[link_section = ".sram4.buffers"] -static mut BUFFER: MaybeUninit<[u8; 10]> = MaybeUninit::uninit(); +static BUFFER: MaybeUninit> = MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -91,26 +93,23 @@ fn main() -> ! { let config = BdmaConfig::default().memory_increment(true); - // Initialise buffer + // BUFFER is located in .sram4.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value unsafe { - // Convert an uninitialised array into an array of uninitialised - let buf: &mut [core::mem::MaybeUninit; 10] = - &mut *(core::ptr::addr_of_mut!(BUFFER) as *mut _); - buf.iter_mut().for_each(|x| x.as_mut_ptr().write(0)); + let cell = BUFFER.as_ptr(); + for i in 0..10 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); + } } + // Now we can take a mutable reference to BUFFER. To avoid aliasing, + // this reference must only be taken once + let buffer = unsafe { &mut *SyncUnsafeCell::raw_get(BUFFER.as_ptr()) }; // We need to specify the direction with a type annotation let mut transfer: Transfer<_, _, PeripheralToMemory, &mut [u8; 10], _> = - Transfer::init( - streams.0, - i2c, - #[allow(static_mut_refs)] // TODO: Fix this - unsafe { - BUFFER.assume_init_mut() - }, - None, - config, - ); + Transfer::init(streams.0, i2c, buffer, None, config); transfer.start(|i2c| { // This closure runs right after enabling the stream @@ -138,9 +137,11 @@ fn main() -> ! { fn I2C4_EV() { info!("I2C transfer complete!"); - // Look at BUFFER, which we expect to be initialised - #[allow(static_mut_refs)] // TODO: Fix this - let buffer: &'static [u8; 10] = unsafe { BUFFER.assume_init_mut() }; + // Take a second(!) reference to the buffer. Because the transfer has + // stopped, we can reason that the first reference is no longer + // active. Therefore we can take another reference without causing aliasing + let buffer: &[u8; 10] = + unsafe { &*SyncUnsafeCell::raw_get(BUFFER.as_ptr()) }; assert_eq!(buffer[0], 0xBE); diff --git a/examples/mdma.rs b/examples/mdma.rs index 348d780f..fa32546d 100644 --- a/examples/mdma.rs +++ b/examples/mdma.rs @@ -6,6 +6,9 @@ use core::{mem, mem::MaybeUninit}; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use cortex_m_rt::entry; #[macro_use] mod utilities; @@ -24,7 +27,8 @@ use log::info; // // The runtime does not initialise AXI SRAM banks. #[link_section = ".axisram.buffers"] -static mut SOURCE_BUFFER: MaybeUninit<[u32; 200]> = MaybeUninit::uninit(); +static SOURCE_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -48,24 +52,20 @@ fn main() -> ! { info!("stm32h7xx-hal example - Memory to TCM with Master DMA"); info!(""); - // Initialise the source buffer without taking any references to - // uninitialisated memory - let source_buffer: &'static mut [u32; 200] = { - let buf: &mut [MaybeUninit; 200] = unsafe { - &mut *(core::ptr::addr_of_mut!(SOURCE_BUFFER) - as *mut [MaybeUninit; 200]) - }; - - for value in buf.iter_mut() { - unsafe { - value.as_mut_ptr().write(0x11223344u32); - } + // SOURCE_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = SOURCE_BUFFER.as_ptr(); + for i in 0..200 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0x11223344u32); } - #[allow(static_mut_refs)] // TODO: Fix this - unsafe { - SOURCE_BUFFER.assume_init_mut() - } - }; + } + // Now we can take a mutable reference to SOURCE_BUFFER. To avoid aliasing, + // this reference must only be taken once + let source_buffer = + unsafe { &mut *SyncUnsafeCell::raw_get(SOURCE_BUFFER.as_ptr()) }; // // Example 1: Memory to TCM @@ -108,7 +108,7 @@ fn main() -> ! { while !transfer.get_transfer_complete_flag() {} // Decompose the stream to get the source buffer back - let (stream, _mem2mem, _target, _) = transfer.free(); + let (stream, _mem2mem, _target, source) = transfer.free(); for a in target_buffer.iter() { assert_eq!(*a, 0x11223344); @@ -121,8 +121,7 @@ fn main() -> ! { // // Reset source buffer - #[allow(static_mut_refs)] // TODO: Fix this - let source_buffer = unsafe { SOURCE_BUFFER.assume_init_mut() }; + let source_buffer = source.unwrap(); *source_buffer = [0xAABBCCDD; 200]; // New target buffer on the stack diff --git a/examples/mdma_bursts.rs b/examples/mdma_bursts.rs index 2b6c3641..cb262c8c 100644 --- a/examples/mdma_bursts.rs +++ b/examples/mdma_bursts.rs @@ -11,6 +11,9 @@ use core::mem::MaybeUninit; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use cortex_m_rt::entry; #[macro_use] mod utilities; @@ -29,9 +32,11 @@ use log::info; // // The runtime does not initialise AXI SRAM banks. #[link_section = ".axisram.buffers"] -static mut SOURCE_BUFFER: MaybeUninit<[u32; 200]> = MaybeUninit::uninit(); +static SOURCE_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[link_section = ".axisram.buffers"] -static mut TARGET_BUFFER: MaybeUninit<[u32; 200]> = MaybeUninit::uninit(); +static TARGET_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -62,25 +67,27 @@ fn main() -> ! { // Initialise the source buffer without taking any references to // uninitialised memory - let _source_buffer: &'static mut [u32; 200] = { - let buf: &mut [MaybeUninit; 200] = unsafe { - &mut *(core::ptr::addr_of_mut!(SOURCE_BUFFER) - as *mut [MaybeUninit; 200]) - }; - for value in buf.iter_mut() { - unsafe { - value.as_mut_ptr().write(0x11223344u32); - } + // SOURCE_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = SOURCE_BUFFER.as_ptr(); + for i in 0..200 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0x11223344u32); } - #[allow(static_mut_refs)] // TODO: Fix this - unsafe { - SOURCE_BUFFER.assume_init_mut() + } + // TARGET_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = TARGET_BUFFER.as_ptr(); + for i in 0..200 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); } - }; - - // NOTE(unsafe): TARGET_BUFFER must also be initialised to prevent undefined - // behaviour (taking a mutable reference to uninitialised memory) + } // Setup DMA let streams = StreamsTuple::new(dp.MDMA, ccdr.peripheral.MDMA); @@ -93,20 +100,23 @@ fn main() -> ! { config: MdmaConfig, ) { let mut transfer: Transfer<_, _, MemoryToMemory, _, _> = { - // unsafe: Both source and destination live at least as long as this - // transfer - #[allow(static_mut_refs)] // TODO: Fix this - let source: &'static mut [u32; 200] = - unsafe { SOURCE_BUFFER.assume_init_mut() }; - #[allow(static_mut_refs)] // TODO: Fix this - let target: &'static mut [u32; 200] = - unsafe { TARGET_BUFFER.assume_init_mut() }; // uninitialised memory + // SOURCE_BUFFER and TARGET_BUFFER are initialized, so we can take a + // mutable reference. To avoid aliasing, this reference must only be + // taken once, but here we take it multiple times as we know it will + // be dropped later in this method + let source_buffer = unsafe { + &mut *SyncUnsafeCell::raw_get(SOURCE_BUFFER.as_ptr()) + }; + + let target_buffer = unsafe { + &mut *SyncUnsafeCell::raw_get(TARGET_BUFFER.as_ptr()) + }; Transfer::init_master( stream, MemoryToMemory::new(), - target, // Destination: AXISRAM - Some(source), // Source: AXISRAM + target_buffer, // Destination: AXISRAM + Some(source_buffer), // Source: AXISRAM config, ) }; diff --git a/examples/sai_dma_passthru.rs b/examples/sai_dma_passthru.rs index c4d2952b..b3f568da 100644 --- a/examples/sai_dma_passthru.rs +++ b/examples/sai_dma_passthru.rs @@ -8,6 +8,9 @@ use core::mem::MaybeUninit; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use cortex_m::asm; use cortex_m_rt::entry; @@ -45,10 +48,10 @@ const PLL3_P_HZ: Hertz = Hertz::from_raw(AUDIO_SAMPLE_HZ.raw() * 257); // = static data ============================================================== #[link_section = ".sram3"] -static mut TX_BUFFER: MaybeUninit<[u32; DMA_BUFFER_LENGTH]> = +static TX_BUFFER: MaybeUninit> = MaybeUninit::uninit(); #[link_section = ".sram3"] -static mut RX_BUFFER: MaybeUninit<[u32; DMA_BUFFER_LENGTH]> = +static RX_BUFFER: MaybeUninit> = MaybeUninit::uninit(); pub const CLOCK_RATE_HZ: Hertz = Hertz::MHz(400); @@ -110,9 +113,12 @@ fn main() -> ! { dma::dma::StreamsTuple::new(dp.DMA1, ccdr.peripheral.DMA1); // dma1 stream 0 - #[allow(static_mut_refs)] // TODO: Fix this + // + // SRAM3 is not initialised by the runtime, so here we are creating a + // reference to uninitialised memory. This is UB! let tx_buffer: &'static mut [u32; DMA_BUFFER_LENGTH] = - unsafe { TX_BUFFER.assume_init_mut() }; // uninitialised memory + unsafe { &mut *SyncUnsafeCell::raw_get(TX_BUFFER.as_ptr()) }; + let dma_config = dma::dma::DmaConfig::default() .priority(dma::config::Priority::High) .memory_increment(true) @@ -129,9 +135,12 @@ fn main() -> ! { ); // dma1 stream 1 - #[allow(static_mut_refs)] // TODO: Fix this + // + // SRAM3 is not initialised by the runtime, so here we are creating a + // reference to uninitialised memory. This is UB! let rx_buffer: &'static mut [u32; DMA_BUFFER_LENGTH] = - unsafe { RX_BUFFER.assume_init_mut() }; // uninitialised memory + unsafe { &mut *SyncUnsafeCell::raw_get(RX_BUFFER.as_ptr()) }; + let dma_config = dma_config .transfer_complete_interrupt(true) .half_transfer_interrupt(true); @@ -178,12 +187,8 @@ fn main() -> ! { pac::NVIC::unmask(pac::Interrupt::DMA1_STR1); } - static mut TRANSFER_DMA1_STR1: MaybeUninit> = - MaybeUninit::uninit(); - unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - TRANSFER_DMA1_STR1.write(None); - } + static mut TRANSFER_DMA1_STR1: SyncUnsafeCell> = + SyncUnsafeCell::new(None); dma1_str1.start(|_sai1_rb| { sai1.enable_dma(SaiChannel::ChannelB); @@ -223,29 +228,38 @@ fn main() -> ! { >; unsafe { - #[allow(static_mut_refs)] // TODO: Fix this - TRANSFER_DMA1_STR1.write(Some(dma1_str1)); // drops previous None + // Write to TRANSFER_DMA1_STR1, without taking a reference to it + SyncUnsafeCell::raw_get(core::ptr::addr_of!(TRANSFER_DMA1_STR1)) + .write(Some(dma1_str1)); + + let tx_buffer = &*SyncUnsafeCell::raw_get(TX_BUFFER.as_ptr()); + let rx_buffer = &*SyncUnsafeCell::raw_get(RX_BUFFER.as_ptr()); info!( "{:?}, {:?}", - TX_BUFFER.assume_init()[0] as *const u32, - RX_BUFFER.assume_init()[0] as *const u32 + tx_buffer[0] as *const u32, rx_buffer[0] as *const u32 ); } #[interrupt] fn DMA1_STR1() { - #[allow(static_mut_refs)] // TODO: Fix this + // Here we take mutable references to static variables. We assert that + // any previous references are no longer in scope and that these + // references are unique + let tx_buffer: &'static mut [u32; DMA_BUFFER_LENGTH] = - unsafe { TX_BUFFER.assume_init_mut() }; - #[allow(static_mut_refs)] // TODO: Fix this + unsafe { &mut *SyncUnsafeCell::raw_get(TX_BUFFER.as_ptr()) }; let rx_buffer: &'static mut [u32; DMA_BUFFER_LENGTH] = - unsafe { RX_BUFFER.assume_init_mut() }; + unsafe { &mut *SyncUnsafeCell::raw_get(RX_BUFFER.as_ptr()) }; + let transfer_dma1_str1 = unsafe { + &mut *SyncUnsafeCell::raw_get(core::ptr::addr_of!( + TRANSFER_DMA1_STR1 + )) + }; let stereo_block_length = tx_buffer.len() / 2; #[allow(static_mut_refs)] // TODO: Fix this - if let Some(transfer) = unsafe { TRANSFER_DMA1_STR1.assume_init_mut() } - { + if let Some(transfer) = transfer_dma1_str1 { let skip = if transfer.get_half_transfer_flag() { transfer.clear_half_transfer_interrupt(); (0, stereo_block_length) diff --git a/examples/serial-dma.rs b/examples/serial-dma.rs index 1621fe0d..99ae5eb6 100644 --- a/examples/serial-dma.rs +++ b/examples/serial-dma.rs @@ -13,6 +13,9 @@ use core::{mem, mem::MaybeUninit}; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use cortex_m_rt::entry; #[macro_use] mod utilities; @@ -31,10 +34,12 @@ use log::info; // // The runtime does not initialise these SRAM banks #[link_section = ".axisram.buffers"] -static mut SHORT_BUFFER: MaybeUninit<[u8; 10]> = MaybeUninit::uninit(); +static SHORT_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[link_section = ".axisram.buffers"] -static mut LONG_BUFFER: MaybeUninit<[u32; 0x8000]> = MaybeUninit::uninit(); +static LONG_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -78,36 +83,35 @@ fn main() -> ! { let (tx, _rx) = serial.split(); - // Initialise the source buffer, without taking any references to - // uninitialised memory - let short_buffer: &'static mut [u8; 10] = { - let buf: &mut [MaybeUninit; 10] = - unsafe { &mut *(core::ptr::addr_of_mut!(SHORT_BUFFER) as *mut _) }; - - for (i, value) in buf.iter_mut().enumerate() { - unsafe { - value.as_mut_ptr().write(i as u8 + 96); // 0x60, 0x61, 0x62... - } - } - #[allow(static_mut_refs)] // TODO: Fix this - unsafe { - SHORT_BUFFER.assume_init_mut() + // SHORT_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = SHORT_BUFFER.as_ptr(); + for i in 0..10 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(i as u8 + 96); // 0x60, 0x61, 0x62... } - }; - // view u32 buffer as u8. Endianess is undefined (little-endian on STM32H7) - let long_buffer: &'static mut [u8; 0x2_0010] = { - let buf: &mut [MaybeUninit; 0x8004] = - unsafe { &mut *(core::ptr::addr_of_mut!(LONG_BUFFER) as *mut _) }; - - for (i, value) in buf.iter_mut().enumerate() { - unsafe { - value.as_mut_ptr().write(i as u32); - } - } - unsafe { - &mut *(core::ptr::addr_of_mut!(LONG_BUFFER) as *mut [u8; 0x2_0010]) + } + // Now we can take a mutable reference to SHORT_BUFFER. To avoid aliasing, + // this reference must only be taken once + let short_buffer: &mut [u8; 10] = + unsafe { &mut *SyncUnsafeCell::raw_get(SHORT_BUFFER.as_ptr()) }; + + // LONG_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = LONG_BUFFER.as_ptr(); + for i in 0..0x2_0010 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(i as u8); } - }; + } + // Now we can take a mutable reference to LONG_BUFFER. To avoid aliasing, + // this reference must only be taken once + let long_buffer: &mut [u8; 0x2_0010] = + unsafe { &mut *SyncUnsafeCell::raw_get(LONG_BUFFER.as_ptr()) }; // Setup the DMA transfer on stream 0 // diff --git a/examples/spi-dma-rtic.rs b/examples/spi-dma-rtic.rs index 47f243e6..38e3d169 100644 --- a/examples/spi-dma-rtic.rs +++ b/examples/spi-dma-rtic.rs @@ -12,17 +12,6 @@ use core::mem::MaybeUninit; #[macro_use] mod utilities; -// The number of bytes to transfer. -const BUFFER_SIZE: usize = 100; - -// DMA1/DMA2 cannot interact with our stack. Instead, buffers for use with the -// DMA must be placed somewhere that DMA1/DMA2 can access. In this case we use -// AXI SRAM. -// -// The runtime does not initialise these SRAM banks -#[link_section = ".axisram.buffers"] -static mut BUFFER: MaybeUninit<[u8; BUFFER_SIZE]> = MaybeUninit::uninit(); - #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)] mod app { use hal::prelude::*; @@ -30,6 +19,21 @@ mod app { use super::*; + // TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 + use utilities::sync_unsafe_cell::SyncUnsafeCell; + + // The number of bytes to transfer. + const BUFFER_SIZE: usize = 100; + + // DMA1/DMA2 cannot interact with our stack. Instead, buffers for use with the + // DMA must be placed somewhere that DMA1/DMA2 can access. In this case we use + // AXI SRAM. + // + // The runtime does not initialise these SRAM banks + #[link_section = ".axisram.buffers"] + static BUFFER: MaybeUninit> = + MaybeUninit::uninit(); + #[shared] struct SharedResources { cs: hal::gpio::gpiob::PB12>, @@ -94,24 +98,19 @@ mod app { .speed(hal::gpio::Speed::VeryHigh); cs.set_high(); - // Initialize our transmit buffer. - let buffer: &'static mut [u8; BUFFER_SIZE] = { - let buf: &mut [MaybeUninit; BUFFER_SIZE] = unsafe { - &mut *(core::ptr::addr_of_mut!(BUFFER) - as *mut [MaybeUninit; BUFFER_SIZE]) - }; - - for (i, value) in buf.iter_mut().enumerate() { - unsafe { - value.as_mut_ptr().write(i as u8 + 0x60); // 0x60, 0x61, 0x62... - } + // BUFFER is located in .axisram.buffers, which is not initialized by the + // runtime. We must manually initialize it to a valid value, without taking + // a reference to the uninitialized value + unsafe { + let cell = BUFFER.as_ptr(); + for i in 0..BUFFER_SIZE { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(i as u8 + 96); // 0x60, 0x61, 0x62... } - - #[allow(static_mut_refs)] // TODO: Fix this - unsafe { - BUFFER.assume_init_mut() - } - }; + } + // Now we can take a mutable reference to BUFFER. To avoid aliasing, + // this reference must only be taken once + let buffer = unsafe { &mut *SyncUnsafeCell::raw_get(BUFFER.as_ptr()) }; let streams = hal::dma::dma::StreamsTuple::new( ctx.device.DMA1, diff --git a/examples/spi-dma.rs b/examples/spi-dma.rs index 40f17dea..f1d4991e 100644 --- a/examples/spi-dma.rs +++ b/examples/spi-dma.rs @@ -6,13 +6,15 @@ //! hardware. The second part of this example demonstrates splitting a transfer //! into chunks and using the `next_transfer_with` method to start each part of //! the transfer. - #![deny(warnings)] #![no_main] #![no_std] use core::{mem, mem::MaybeUninit}; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use cortex_m_rt::entry; #[macro_use] mod utilities; @@ -31,10 +33,12 @@ use log::info; // // The runtime does not initialise these SRAM banks #[link_section = ".axisram.buffers"] -static mut SHORT_BUFFER: MaybeUninit<[u8; 10]> = MaybeUninit::uninit(); +static SHORT_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[link_section = ".axisram.buffers"] -static mut LONG_BUFFER: MaybeUninit<[u32; 0x8000]> = MaybeUninit::uninit(); +static LONG_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -80,36 +84,35 @@ fn main() -> ! { // SPI must be disabled to configure DMA let spi = spi.disable(); - // Initialise the source buffer, without taking any references to - // uninitialisated memory - let short_buffer: &'static mut [u8; 10] = { - let buf: &mut [MaybeUninit; 10] = - unsafe { &mut *(core::ptr::addr_of_mut!(SHORT_BUFFER) as *mut _) }; - - for (i, value) in buf.iter_mut().enumerate() { - unsafe { - value.as_mut_ptr().write(i as u8 + 96); // 0x60, 0x61, 0x62... - } - } - #[allow(static_mut_refs)] // TODO: Fix this - unsafe { - SHORT_BUFFER.assume_init_mut() + // SHORT_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = SHORT_BUFFER.as_ptr(); + for i in 0..10 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(i as u8 + 96); // 0x60, 0x61, 0x62... } - }; - // view u32 buffer as u8. Endianess is undefined (little-endian on STM32H7) - let long_buffer: &'static mut [u8; 0x2_0010] = { - let buf: &mut [MaybeUninit; 0x8004] = - unsafe { &mut *(core::ptr::addr_of_mut!(LONG_BUFFER) as *mut _) }; - - for (i, value) in buf.iter_mut().enumerate() { - unsafe { - value.as_mut_ptr().write(i as u32); - } - } - unsafe { - &mut *(core::ptr::addr_of_mut!(LONG_BUFFER) as *mut [u8; 0x2_0010]) + } + // Now we can take a mutable reference to SHORT_BUFFER. To avoid aliasing, + // this reference must only be taken once + let short_buffer: &mut [u8; 10] = + unsafe { &mut *SyncUnsafeCell::raw_get(SHORT_BUFFER.as_ptr()) }; + + // LONG_BUFFER is located in .axisram.buffers, which is not initialized by + // the runtime. We must manually initialize it to a valid value, without + // taking a reference to the uninitialized value + unsafe { + let cell = LONG_BUFFER.as_ptr(); + for i in 0..0x2_0010 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(i as u8); } - }; + } + // Now we can take a mutable reference to LONG_BUFFER. To avoid aliasing, + // this reference must only be taken once + let long_buffer: &mut [u8; 0x2_0010] = + unsafe { &mut *SyncUnsafeCell::raw_get(LONG_BUFFER.as_ptr()) }; // Setup the DMA transfer on stream 0 // diff --git a/examples/usb_passthrough.rs b/examples/usb_passthrough.rs index 7052b44e..421423e0 100644 --- a/examples/usb_passthrough.rs +++ b/examples/usb_passthrough.rs @@ -8,10 +8,14 @@ #![no_std] #![no_main] -use panic_itm as _; +#[allow(unused)] +mod utilities; use core::mem::MaybeUninit; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use cortex_m_rt::entry; use stm32h7xx_hal::rcc::rec::UsbClkSel; @@ -20,8 +24,10 @@ use stm32h7xx_hal::{prelude::*, stm32}; use usb_device::prelude::*; -static mut EP_MEMORY_1: MaybeUninit<[u32; 1024]> = MaybeUninit::uninit(); -static mut EP_MEMORY_2: MaybeUninit<[u32; 1024]> = MaybeUninit::uninit(); +static EP_MEMORY_1: MaybeUninit> = + MaybeUninit::uninit(); +static EP_MEMORY_2: MaybeUninit> = + MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -73,24 +79,32 @@ fn main() -> ! { // Initialise EP_MEMORY_1 to zero unsafe { - let buf: &mut [MaybeUninit; 1024] = - &mut *(core::ptr::addr_of_mut!(EP_MEMORY_1) as *mut _); - for value in buf.iter_mut() { - value.as_mut_ptr().write(0); + let cell = EP_MEMORY_1.as_ptr(); + for i in 0..1024 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); } } + // Now we can take a mutable reference to EP_MEMORY_1. To avoid + // aliasing, this reference must only be taken once + let ep_memory_1 = + unsafe { &mut *SyncUnsafeCell::raw_get(EP_MEMORY_1.as_ptr()) }; + // Initialise EP_MEMORY_2 to zero unsafe { - let buf: &mut [MaybeUninit; 1024] = - &mut *(core::ptr::addr_of_mut!(EP_MEMORY_2) as *mut _); - for value in buf.iter_mut() { - value.as_mut_ptr().write(0); + let cell = EP_MEMORY_2.as_ptr(); + for i in 0..1024 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); } } + // Now we can take a mutable reference to EP_MEMORY_1. To avoid + // aliasing, this reference must only be taken once + let ep_memory_2 = + unsafe { &mut *SyncUnsafeCell::raw_get(EP_MEMORY_2.as_ptr()) }; // Port 1 - #[allow(static_mut_refs)] // TODO: Fix this - let usb1_bus = UsbBus::new(usb1, unsafe { EP_MEMORY_1.assume_init_mut() }); + let usb1_bus = UsbBus::new(usb1, ep_memory_1); let mut serial1 = usbd_serial::SerialPort::new(&usb1_bus); let mut usb1_dev = UsbDeviceBuilder::new(&usb1_bus, UsbVidPid(0x16c0, 0x27dd)) @@ -103,8 +117,7 @@ fn main() -> ! { .build(); // Port 2 - #[allow(static_mut_refs)] // TODO: Fix this - let usb2_bus = UsbBus::new(usb2, unsafe { EP_MEMORY_2.assume_init_mut() }); + let usb2_bus = UsbBus::new(usb2, ep_memory_2); let mut serial2 = usbd_serial::SerialPort::new(&usb2_bus); let mut usb2_dev = UsbDeviceBuilder::new(&usb2_bus, UsbVidPid(0x16c0, 0x27dd)) diff --git a/examples/usb_phy_serial_interrupt.rs b/examples/usb_phy_serial_interrupt.rs index e25e8dc5..56499e0f 100644 --- a/examples/usb_phy_serial_interrupt.rs +++ b/examples/usb_phy_serial_interrupt.rs @@ -7,9 +7,13 @@ #![no_std] #![no_main] +use core::cell::RefCell; +use core::mem::MaybeUninit; + +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + use { - core::cell::RefCell, - core::mem::MaybeUninit, cortex_m::interrupt::{free as interrupt_free, Mutex}, stm32h7xx_hal::{ interrupt, pac, @@ -32,9 +36,17 @@ mod utilities; pub const VID: u16 = 0x2341; pub const PID: u16 = 0x025b; -pub static mut USB_MEMORY_1: MaybeUninit<[u32; 1024]> = MaybeUninit::uninit(); -pub static mut USB_BUS_ALLOCATOR: Option>> = - None; +// It's safe to use the link_section attribute to place this in an alternative +// memory area. MaybeUninit is used to mark that the contents are not +// initialized, and they are explictly initialized before use. +pub static USB_MEMORY_1: MaybeUninit> = + MaybeUninit::uninit(); + +// Not safe to use link_section attribute here, see above. +pub static mut USB_BUS_ALLOCATOR: SyncUnsafeCell< + Option>>, +> = SyncUnsafeCell::new(None); + pub static SERIAL_PORT: Mutex< RefCell< Option< @@ -148,28 +160,32 @@ unsafe fn main() -> ! { &ccdr.clocks, ); - // Initialise USB_MEMORY_1 to zero - { - let buf: &mut [MaybeUninit; 1024] = - &mut *(core::ptr::addr_of_mut!(USB_MEMORY_1) as *mut _); - for value in buf.iter_mut() { - value.as_mut_ptr().write(0); + // Initialize USB_MEMORY_1 to zero + unsafe { + let cell = USB_MEMORY_1.as_ptr(); + for i in 0..1024 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); } } + // Now we can take a mutable reference to USB_MEMORY_1. To avoid aliasing, + // this reference must only be taken once + let usb_memory_1 = + unsafe { &mut *SyncUnsafeCell::raw_get(USB_MEMORY_1.as_ptr()) }; + + // USB bus + let usb_bus_allocator: &Option> = unsafe { + // Initialize + SyncUnsafeCell::raw_get(core::ptr::addr_of!(USB_BUS_ALLOCATOR)) + .write(Some(UsbBus::new(usb, usb_memory_1))); + // Get reference + &*SyncUnsafeCell::raw_get(core::ptr::addr_of!(USB_BUS_ALLOCATOR)) + }; - #[allow(static_mut_refs)] // TODO: Fix this - { - USB_BUS_ALLOCATOR = - Some(UsbBus::new(usb, USB_MEMORY_1.assume_init_mut())); - } - - #[allow(static_mut_refs)] // TODO: Fix this let usb_serial = - usbd_serial::SerialPort::new(USB_BUS_ALLOCATOR.as_ref().unwrap()); - - #[allow(static_mut_refs)] // TODO: Fix this + usbd_serial::SerialPort::new(usb_bus_allocator.as_ref().unwrap()); let usb_dev = UsbDeviceBuilder::new( - USB_BUS_ALLOCATOR.as_ref().unwrap(), + usb_bus_allocator.as_ref().unwrap(), UsbVidPid(VID, PID), ) .strings(&[usb_device::device::StringDescriptors::default() diff --git a/examples/usb_rtic.rs b/examples/usb_rtic.rs index 1c6a210e..0bd6a040 100644 --- a/examples/usb_rtic.rs +++ b/examples/usb_rtic.rs @@ -23,8 +23,12 @@ mod app { use stm32h7xx_hal::usb_hs::{UsbBus, USB1}; use usb_device::prelude::*; - static mut EP_MEMORY: MaybeUninit<[u32; 1024]> = MaybeUninit::uninit(); use super::utilities; + // TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 + use super::utilities::sync_unsafe_cell::SyncUnsafeCell; + + static EP_MEMORY: MaybeUninit> = + MaybeUninit::uninit(); #[shared] struct SharedResources {} @@ -87,18 +91,18 @@ mod app { // Initialise EP_MEMORY to zero unsafe { - let buf: &mut [MaybeUninit; 1024] = - &mut *(core::ptr::addr_of_mut!(EP_MEMORY) as *mut _); - for value in buf.iter_mut() { - value.as_mut_ptr().write(0); + let cell = EP_MEMORY.as_ptr(); + for i in 0..1024 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); } } + // Now we can take a mutable reference to EP_MEMORY. To avoid + // aliasing, this reference must only be taken once - // Now we may assume that EP_MEMORY is initialised - #[allow(static_mut_refs)] // TODO: Fix this let usb_bus = cortex_m::singleton!( : usb_device::class_prelude::UsbBusAllocator> = - UsbBus::new(usb, unsafe { EP_MEMORY.assume_init_mut() }) + UsbBus::new(usb, unsafe { &mut *SyncUnsafeCell::raw_get(EP_MEMORY.as_ptr()) }) ) .unwrap(); let serial = usbd_serial::SerialPort::new(usb_bus); diff --git a/examples/usb_serial.rs b/examples/usb_serial.rs index 90dff296..e8bf5787 100644 --- a/examples/usb_serial.rs +++ b/examples/usb_serial.rs @@ -14,6 +14,9 @@ use core::mem::MaybeUninit; +// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439 +use utilities::sync_unsafe_cell::SyncUnsafeCell; + #[macro_use] #[allow(unused)] mod utilities; @@ -26,7 +29,8 @@ use stm32h7xx_hal::{prelude::*, stm32}; use usb_device::prelude::*; -static mut EP_MEMORY: MaybeUninit<[u32; 1024]> = MaybeUninit::uninit(); +static EP_MEMORY: MaybeUninit> = + MaybeUninit::uninit(); #[entry] fn main() -> ! { @@ -77,17 +81,18 @@ fn main() -> ! { // Initialise EP_MEMORY to zero unsafe { - let buf: &mut [MaybeUninit; 1024] = - &mut *(core::ptr::addr_of_mut!(EP_MEMORY) as *mut _); - for value in buf.iter_mut() { - value.as_mut_ptr().write(0); + let cell = EP_MEMORY.as_ptr(); + for i in 0..1024 { + core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i]) + .write(0); } } + // Now we can take a mutable reference to EP_MEMORY. To avoid aliasing, this + // reference must only be taken once + let ep_memory = + unsafe { &mut *SyncUnsafeCell::raw_get(EP_MEMORY.as_ptr()) }; - // Now we may assume that EP_MEMORY is initialised - #[allow(static_mut_refs)] // TODO: Fix this - let usb_bus = UsbBus::new(usb, unsafe { EP_MEMORY.assume_init_mut() }); - + let usb_bus = UsbBus::new(usb, ep_memory); let mut serial = usbd_serial::SerialPort::new(&usb_bus); let mut usb_dev = diff --git a/examples/utilities/mod.rs b/examples/utilities/mod.rs index 63bfbc0c..3e05b8b6 100644 --- a/examples/utilities/mod.rs +++ b/examples/utilities/mod.rs @@ -1,6 +1,9 @@ //! Utilities for examples pub mod logger; + +pub mod sync_unsafe_cell; + #[macro_use] mod power; diff --git a/examples/utilities/sync_unsafe_cell.rs b/examples/utilities/sync_unsafe_cell.rs new file mode 100644 index 00000000..ccfc17b1 --- /dev/null +++ b/examples/utilities/sync_unsafe_cell.rs @@ -0,0 +1,25 @@ +#![allow(unused)] +#![allow(unsafe_code)] + +/// Create our own SyncUnsafeCell +/// +/// TODO: Replace with core::cell::SyncUnsafeCell when stabilized +/// rust-lang/rust#95439 +#[repr(transparent)] +pub struct SyncUnsafeCell(core::cell::UnsafeCell); + +unsafe impl Sync for SyncUnsafeCell {} +impl SyncUnsafeCell { + /// Constructs a new instance of UnsafeCell which will wrap the specified value + pub const fn new(value: T) -> Self { + Self(core::cell::UnsafeCell::new(value)) + } + /// Get a mutable pointer to the wrapped value, without creating a temporary + /// reference + pub unsafe fn raw_get(this: *const Self) -> *mut T { + let ucell: *const core::cell::UnsafeCell = + core::ptr::addr_of!((*this).0); + + core::cell::UnsafeCell::raw_get(ucell) + } +}