@@ -12,13 +12,20 @@ use crate::spec::NUM_REGISTERS;
1212use core:: arch:: asm;
1313use core:: fmt:: Debug ;
1414use core:: num:: NonZeroU8 ;
15- use core:: ptr:: { self , read_volatile , write_volatile } ;
15+ use core:: ptr:: NonNull ;
1616
1717mod private {
1818 pub trait Sealed { }
1919}
2020
2121/// Abstraction over register addresses in [`Backend`].
22+ ///
23+ /// # Safety
24+ ///
25+ /// All implementations and instances of this trait are created within this
26+ /// crate and do follow all safety invariants. API users don't get access to the
27+ /// underlying register addresses, nor can they construct one themselves, as this
28+ /// type et al. are sealed.
2229pub trait RegisterAddress : Copy + Clone + Debug + Sized + private:: Sealed {
2330 /// Adds a byte offset onto the base register address.
2431 fn add_offset ( self , offset : u8 ) -> Self ;
@@ -49,7 +56,7 @@ impl private::Sealed for PortIoAddress {}
4956///
5057/// See [`RegisterAddress`].
5158#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Hash ) ]
52- pub struct MmioAddress ( pub ( crate ) * mut u8 ) ;
59+ pub struct MmioAddress ( pub ( crate ) NonNull < u8 > ) ;
5360
5461// SAFETY: `Uart16550` is not `Sync`, so concurrent access from multiple
5562// threads is not possible through this type's API alone. Implementing `Send`
@@ -239,6 +246,67 @@ impl Backend for PioBackend {
239246 }
240247}
241248
249+ /// Arch-specific quirks to access hardware.
250+ ///
251+ /// On MMIO-access on aarch64, LLVM may emit instructions that are not properly
252+ /// virtualizable. We therefore need to be more explicit about the instruction.
253+ /// More info: <https://github.com/rust-lang/rust/issues/131894>
254+ mod arch {
255+ use crate :: backend:: MmioAddress ;
256+ #[ cfg( any( doc, not( target_arch = "aarch64" ) ) ) ]
257+ use core:: ptr;
258+
259+ /// Wrapper around [`ptr::read_volatile`].
260+ #[ cfg( target_arch = "aarch64" ) ]
261+ #[ inline( always) ]
262+ pub unsafe fn mmio_read_register ( address : MmioAddress ) -> u8 {
263+ let ptr = address. 0 . as_ptr ( ) ;
264+ let ret: u8 ;
265+ // SAFETY: Caller ensures the address is valid MMIO memory.
266+ unsafe {
267+ core:: arch:: asm!(
268+ "ldrb {ret:w}, [{ptr}]" ,
269+ ptr = in( reg) ptr,
270+ ret = out( reg) ret,
271+ options( nostack, preserves_flags)
272+ ) ;
273+ }
274+ ret
275+ }
276+
277+ /// Wrapper around [`ptr::read_volatile`].
278+ #[ cfg( not( target_arch = "aarch64" ) ) ]
279+ #[ inline( always) ]
280+ pub unsafe fn mmio_read_register ( address : MmioAddress ) -> u8 {
281+ // SAFETY: Caller ensures the address is valid MMIO memory.
282+ unsafe { ptr:: read_volatile ( address. 0 . as_ptr ( ) ) }
283+ }
284+
285+ /// Wrapper around [`ptr::write_volatile`].
286+ #[ cfg( target_arch = "aarch64" ) ]
287+ #[ inline( always) ]
288+ pub unsafe fn mmio_write_register ( address : MmioAddress , value : u8 ) {
289+ let ptr = address. 0 . as_ptr ( ) ;
290+ // SAFETY: Caller ensures the address is valid MMIO memory.
291+ unsafe {
292+ core:: arch:: asm!(
293+ "strb {val:w}, [{ptr}]" ,
294+ val = in( reg) value,
295+ ptr = in( reg) ptr,
296+ options( nostack, preserves_flags)
297+ ) ;
298+ }
299+ }
300+
301+ /// Wrapper around [`ptr::write_volatile`].
302+ #[ cfg( not( target_arch = "aarch64" ) ) ]
303+ #[ inline( always) ]
304+ pub unsafe fn mmio_write_register ( address : MmioAddress , value : u8 ) {
305+ // SAFETY: Caller ensures the address is valid MMIO memory.
306+ unsafe { ptr:: write_volatile ( address. 0 . as_ptr ( ) , value) }
307+ }
308+ }
309+
242310/// MMIO-mapped UART 16550.
243311#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Hash ) ]
244312pub struct MmioBackend {
@@ -264,23 +332,23 @@ impl Backend for MmioBackend {
264332
265333 #[ inline( always) ]
266334 unsafe fn _read_register ( & mut self , address : MmioAddress ) -> u8 {
267- debug_assert_ne ! ( address. 0 , ptr:: null_mut( ) ) ;
268335 debug_assert ! ( address >= self . base( ) ) ;
269336 let upper_bound_incl = ( NUM_REGISTERS - 1 ) * usize:: from ( u8:: from ( self . stride ) ) ;
270- debug_assert ! ( address. 0 <= self . base( ) . 0 . wrapping_add( upper_bound_incl) ) ;
337+ // Address is in the device's address range
338+ debug_assert ! ( address. 0 . as_ptr( ) <= self . base( ) . 0 . as_ptr( ) . wrapping_add( upper_bound_incl) ) ;
271339
272340 // SAFETY: The caller ensured that the MMIO address is safe to use.
273- unsafe { read_volatile ( address. 0 ) }
341+ unsafe { arch :: mmio_read_register ( address) }
274342 }
275343
276344 #[ inline( always) ]
277345 unsafe fn _write_register ( & mut self , address : MmioAddress , value : u8 ) {
278- debug_assert_ne ! ( address. 0 , ptr:: null_mut( ) ) ;
279346 debug_assert ! ( address >= self . base( ) ) ;
280347 let upper_bound_incl = ( NUM_REGISTERS - 1 ) * usize:: from ( u8:: from ( self . stride ) ) ;
281- debug_assert ! ( address. 0 <= self . base( ) . 0 . wrapping_add( upper_bound_incl) ) ;
348+ // Address is in the device's address range
349+ debug_assert ! ( address. 0 . as_ptr( ) <= self . base( ) . 0 . as_ptr( ) . wrapping_add( upper_bound_incl) ) ;
282350
283351 // SAFETY: The caller ensured that the MMIO address is safe to use.
284- unsafe { write_volatile ( address. 0 , value) }
352+ unsafe { arch :: mmio_write_register ( address, value) }
285353 }
286354}
0 commit comments