diff --git a/kernel/src/platform/mod.rs b/kernel/src/platform/mod.rs index 1edb77472d..fca13fd6c0 100644 --- a/kernel/src/platform/mod.rs +++ b/kernel/src/platform/mod.rs @@ -217,19 +217,19 @@ pub trait SvsmPlatform: Sync { /// /// # Safety /// - /// Caller must ensure that `pa` points to a properly aligned memory location and the + /// Caller must ensure that `vaddr` points to a properly aligned memory location and the /// memory accessed is part of a valid MMIO range. - unsafe fn mmio_write(&self, _paddr: PhysAddr, _data: &[u8]) -> Result<(), SvsmError>; + unsafe fn mmio_write(&self, vaddr: VirtAddr, data: &[u8]) -> Result<(), SvsmError>; /// Perfrom a read from a memory-mapped IO area /// /// # Safety /// - /// Caller must ensure that `paddr` points to a properly aligned memory location and the + /// Caller must ensure that `vaddr` points to a properly aligned memory location and the /// memory accessed is part of a valid MMIO range. unsafe fn mmio_read( &self, - paddr: PhysAddr, + vaddr: VirtAddr, data: &mut [MaybeUninit], ) -> Result<(), SvsmError>; } diff --git a/kernel/src/platform/native.rs b/kernel/src/platform/native.rs index 8aa6137d0a..60aedd9d4b 100644 --- a/kernel/src/platform/native.rs +++ b/kernel/src/platform/native.rs @@ -233,15 +233,123 @@ impl SvsmPlatform for NativePlatform { Ok(()) } - unsafe fn mmio_write(&self, _paddr: PhysAddr, _data: &[u8]) -> Result<(), SvsmError> { - unimplemented!() + /// Perform a write to a memory-mapped IO area + /// + /// This function expects data to be 1, 2, 4 or 8 bytes long. + /// + /// It is not possible to loop and write one byte at a time because mmio devices (e.g., those emulated by QEMU) + /// expect certain registers to be written with a single operation. Using a generic on SvsmPlatform is + /// not possible because it uses the dyn trait. + /// + /// # Safety + /// + /// Caller must ensure that `vaddr` points to a properly aligned memory location and the + /// memory accessed is part of a valid MMIO range. + unsafe fn mmio_write(&self, vaddr: VirtAddr, data: &[u8]) -> Result<(), SvsmError> { + match data.len() { + 1 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_write_type::(vaddr, data); + } + } + 2 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_write_type::(vaddr, data); + } + } + 4 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_write_type::(vaddr, data); + } + } + 8 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_write_type::(vaddr, data); + } + } + _ => return Err(SvsmError::InvalidBytes), + }; + + Ok(()) } + /// Perform a read from a memory-mapped IO area + /// + /// This function expects reads to be 1, 2, 4 or 8 bytes long. + /// + /// It is not possible to loop and read one byte at a time because mmio devices (e.g., those emulated by QEMU) + /// expect certain registers to be read with a single operation. Using a generic on SvsmPlatform is + /// not possible because it uses the dyn trait. + /// + /// # Safety + /// + /// Caller must ensure that `vaddr` points to a properly aligned memory location and the + /// memory accessed is part of a valid MMIO range. unsafe fn mmio_read( &self, - _paddr: PhysAddr, - _data: &mut [MaybeUninit], + vaddr: VirtAddr, + data: &mut [MaybeUninit], ) -> Result<(), SvsmError> { - unimplemented!() + match data.len() { + 1 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_read_type::(vaddr, data); + } + } + 2 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_read_type::(vaddr, data); + } + } + 4 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_read_type::(vaddr, data); + } + } + 8 => { + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + mmio_read_type::(vaddr, data); + } + } + _ => return Err(SvsmError::InvalidBytes), + }; + + Ok(()) } } + +unsafe fn mmio_write_type(vaddr: VirtAddr, data: &[u8]) { + let data_ptr = data.as_ptr().cast::(); + let ptr = vaddr.as_mut_ptr::(); + + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + if data_ptr.is_aligned() { + ptr.write_volatile(data_ptr.read()); + } else { + ptr.write_volatile(data_ptr.read_unaligned()); + } + }; +} + +unsafe fn mmio_read_type(vaddr: VirtAddr, data: &mut [MaybeUninit]) { + let data_ptr = data.as_mut_ptr().cast::(); + let ptr = vaddr.as_mut_ptr::(); + + // SAFETY: We are trusting the caller to ensure validity of `vaddr`. + unsafe { + if data_ptr.is_aligned() { + data_ptr.write(ptr.read_volatile()); + } else { + data_ptr.write_unaligned(ptr.read_volatile()); + } + }; +} diff --git a/kernel/src/platform/snp.rs b/kernel/src/platform/snp.rs index 303bb64a07..3c22009249 100644 --- a/kernel/src/platform/snp.rs +++ b/kernel/src/platform/snp.rs @@ -381,9 +381,11 @@ impl SvsmPlatform for SnpPlatform { /// /// # Safety /// - /// Caller must ensure that `paddr` points to a properly aligned memory location and the + /// Caller must ensure that `vaddr` points to a properly aligned memory location and the /// memory accessed is part of a valid MMIO range. - unsafe fn mmio_write(&self, paddr: PhysAddr, data: &[u8]) -> Result<(), SvsmError> { + unsafe fn mmio_write(&self, vaddr: VirtAddr, data: &[u8]) -> Result<(), SvsmError> { + let paddr = this_cpu().get_pgtable().phys_addr(vaddr)?; + // SAFETY: We are trusting the caller to ensure validity of `paddr` and alignment of data. unsafe { crate::cpu::percpu::current_ghcb().mmio_write(paddr, data) } } @@ -392,13 +394,14 @@ impl SvsmPlatform for SnpPlatform { /// /// # Safety /// - /// Caller must ensure that `paddr` points to a properly aligned memory location and the + /// Caller must ensure that `vaddr` points to a properly aligned memory location and the /// memory accessed is part of a valid MMIO range. unsafe fn mmio_read( &self, - paddr: PhysAddr, + vaddr: VirtAddr, data: &mut [MaybeUninit], ) -> Result<(), SvsmError> { + let paddr = this_cpu().get_pgtable().phys_addr(vaddr)?; // SAFETY: We are trusting the caller to ensure validity of `paddr` and alignment of data. unsafe { crate::cpu::percpu::current_ghcb().mmio_read(paddr, data) } } diff --git a/kernel/src/platform/tdp.rs b/kernel/src/platform/tdp.rs index 61792947f5..0dff69685f 100644 --- a/kernel/src/platform/tdp.rs +++ b/kernel/src/platform/tdp.rs @@ -280,13 +280,13 @@ impl SvsmPlatform for TdpPlatform { Ok(()) } - unsafe fn mmio_write(&self, _paddr: PhysAddr, _data: &[u8]) -> Result<(), SvsmError> { + unsafe fn mmio_write(&self, _vaddr: VirtAddr, _data: &[u8]) -> Result<(), SvsmError> { unimplemented!() } unsafe fn mmio_read( &self, - _paddr: PhysAddr, + _vaddr: VirtAddr, _data: &mut [MaybeUninit], ) -> Result<(), SvsmError> { unimplemented!() diff --git a/kernel/src/virtio/hal.rs b/kernel/src/virtio/hal.rs index 76af5fd906..39d9d78b47 100644 --- a/kernel/src/virtio/hal.rs +++ b/kernel/src/virtio/hal.rs @@ -16,7 +16,6 @@ use zerocopy::{FromBytes, Immutable, IntoBytes}; use crate::{ address::{PhysAddr, VirtAddr}, - cpu::percpu::this_cpu, mm::{page_visibility::*, *}, }; @@ -198,11 +197,6 @@ unsafe impl virtio_drivers::Hal for SvsmHal { /// /// `src` must be properly aligned and reside at a readable memory address. unsafe fn mmio_read(src: &T) -> T { - let paddr = this_cpu() - .get_pgtable() - .phys_addr(VirtAddr::from(addr_of!(*src))) - .unwrap(); - let mut b = MaybeUninit::::uninit(); // SAFETY: We are trusting the caller (the virtio driver) to ensure `src` is a valid MMIO // address and that it is aligned properly. If SVSM_PLATFORM.mmio_read() doesn't fail @@ -215,7 +209,9 @@ unsafe impl virtio_drivers::Hal for SvsmHal { b.as_mut_ptr().cast::>(), size_of::(), ); - SVSM_PLATFORM.mmio_read(paddr, b_slice).unwrap(); + SVSM_PLATFORM + .mmio_read(VirtAddr::from(addr_of!(*src)), b_slice) + .unwrap(); b.assume_init() } } @@ -229,14 +225,11 @@ unsafe impl virtio_drivers::Hal for SvsmHal { /// /// `dst` must be properly aligned and reside at a writable memory address. unsafe fn mmio_write(dst: &mut T, v: T) { - let paddr = this_cpu() - .get_pgtable() - .phys_addr(VirtAddr::from(addr_of!(*dst))) - .unwrap(); - // SAFETY: We are trusting the caller (the virtio driver) to ensure validity of `paddr` and alignment of data. unsafe { - SVSM_PLATFORM.mmio_write(paddr, v.as_bytes()).unwrap(); + SVSM_PLATFORM + .mmio_write(VirtAddr::from(addr_of!(*dst)), v.as_bytes()) + .unwrap(); } } }