Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions kernel/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>],
) -> Result<(), SvsmError>;
}
Expand Down
118 changes: 113 additions & 5 deletions kernel/src/platform/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u8>(vaddr, data);
}
}
2 => {
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
unsafe {
mmio_write_type::<u16>(vaddr, data);
}
}
4 => {
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
unsafe {
mmio_write_type::<u32>(vaddr, data);
}
}
8 => {
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
unsafe {
mmio_write_type::<u64>(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<u8>],
vaddr: VirtAddr,
data: &mut [MaybeUninit<u8>],
) -> Result<(), SvsmError> {
unimplemented!()
match data.len() {
1 => {
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
unsafe {
mmio_read_type::<u8>(vaddr, data);
}
}
2 => {
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
unsafe {
mmio_read_type::<u16>(vaddr, data);
}
}
4 => {
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
unsafe {
mmio_read_type::<u32>(vaddr, data);
}
}
8 => {
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
unsafe {
mmio_read_type::<u64>(vaddr, data);
}
}
_ => return Err(SvsmError::InvalidBytes),
};

Ok(())
}
}

unsafe fn mmio_write_type<T: Copy>(vaddr: VirtAddr, data: &[u8]) {
let data_ptr = data.as_ptr().cast::<T>();
let ptr = vaddr.as_mut_ptr::<T>();

// 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<T>(vaddr: VirtAddr, data: &mut [MaybeUninit<u8>]) {
let data_ptr = data.as_mut_ptr().cast::<T>();
let ptr = vaddr.as_mut_ptr::<T>();

// 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());
}
};
}
11 changes: 7 additions & 4 deletions kernel/src/platform/snp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}
Expand All @@ -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<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_read(paddr, data) }
}
Expand Down
4 changes: 2 additions & 2 deletions kernel/src/platform/tdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>],
) -> Result<(), SvsmError> {
unimplemented!()
Expand Down
19 changes: 6 additions & 13 deletions kernel/src/virtio/hal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use zerocopy::{FromBytes, Immutable, IntoBytes};

use crate::{
address::{PhysAddr, VirtAddr},
cpu::percpu::this_cpu,
mm::{page_visibility::*, *},
};

Expand Down Expand Up @@ -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<T: FromBytes + Immutable>(src: &T) -> T {
let paddr = this_cpu()
.get_pgtable()
.phys_addr(VirtAddr::from(addr_of!(*src)))
.unwrap();

let mut b = MaybeUninit::<T>::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
Expand All @@ -215,7 +209,9 @@ unsafe impl virtio_drivers::Hal for SvsmHal {
b.as_mut_ptr().cast::<MaybeUninit<u8>>(),
size_of::<T>(),
);
SVSM_PLATFORM.mmio_read(paddr, b_slice).unwrap();
SVSM_PLATFORM
.mmio_read(VirtAddr::from(addr_of!(*src)), b_slice)
.unwrap();
b.assume_init()
}
}
Expand All @@ -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<T: IntoBytes + Immutable>(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();
}
}
}