use core::mem::MaybeUninit; use core::ptr; #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] use core::ops::{BitAnd, BitOr, Not}; use super::io::Io; #[repr(packed)] pub struct Mmio { value: MaybeUninit, } impl Mmio { /// Create a new Mmio without initializing #[deprecated = "unsound because it's possible to read even though it's uninitialized"] pub fn new() -> Self { unsafe { Self::uninit() } } pub unsafe fn zeroed() -> Self { Self { value: MaybeUninit::zeroed(), } } pub unsafe fn uninit() -> Self { Self { value: MaybeUninit::uninit(), } } pub const fn from(value: T) -> Self { Self { value: MaybeUninit::new(value), } } } // Generic implementation (WARNING: requires aligned pointers!) #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] impl Io for Mmio where T: Copy + PartialEq + BitAnd + BitOr + Not { type Value = T; fn read(&self) -> T { unsafe { ptr::read_volatile(ptr::addr_of!(self.value).cast::()) } } fn write(&mut self, value: T) { unsafe { ptr::write_volatile(ptr::addr_of_mut!(self.value).cast::(), value) }; } } // x86 u8 implementation #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl Io for Mmio { type Value = u8; fn read(&self) -> Self::Value { unsafe { let value: Self::Value; let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::(); core::arch::asm!( "mov {}, [{}]", out(reg_byte) value, in(reg) ptr ); value } } fn write(&mut self, value: Self::Value) { unsafe { let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::(); core::arch::asm!( "mov [{}], {}", in(reg) ptr, in(reg_byte) value, ); } } } // x86 u16 implementation #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl Io for Mmio { type Value = u16; fn read(&self) -> Self::Value { unsafe { let value: Self::Value; let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::(); core::arch::asm!( "mov {:x}, [{}]", out(reg) value, in(reg) ptr ); value } } fn write(&mut self, value: Self::Value) { unsafe { let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::(); core::arch::asm!( "mov [{}], {:x}", in(reg) ptr, in(reg) value, ); } } } // x86 u32 implementation #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl Io for Mmio { type Value = u32; fn read(&self) -> Self::Value { unsafe { let value: Self::Value; let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::(); core::arch::asm!( "mov {:e}, [{}]", out(reg) value, in(reg) ptr ); value } } fn write(&mut self, value: Self::Value) { unsafe { let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::(); core::arch::asm!( "mov [{}], {:e}", in(reg) ptr, in(reg) value, ); } } } // x86 u64 implementation (x86_64 only) #[cfg(target_arch = "x86_64")] impl Io for Mmio { type Value = u64; fn read(&self) -> Self::Value { unsafe { let value: Self::Value; let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::(); core::arch::asm!( "mov {:r}, [{}]", out(reg) value, in(reg) ptr ); value } } fn write(&mut self, value: Self::Value) { unsafe { let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::(); core::arch::asm!( "mov [{}], {:r}", in(reg) ptr, in(reg) value, ); } } }