Files
s390-tools/rust/pv_core/src/uvdevice.rs
Marc Hartmayer 4a76efe6d5 rust: Use AsRef<Path> and PathBuf in libraries
Use `AsRef<Path>` instead of `&Path`, &str, .... to be more versatile
and accept more input types. In addition, use `PathBuf` and `Path` for
paths instead of `String` and `str`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-05-27 16:53:47 +02:00

244 lines
7.1 KiB
Rust

// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
#![allow(non_camel_case_types)]
use crate::FileAccessErrorType;
use crate::{Error, Result};
use log::debug;
use std::{
convert::TryInto,
ffi::c_ulong,
fs::File,
os::unix::prelude::{AsRawFd, RawFd},
};
#[cfg(not(test))]
use ::libc::ioctl;
#[cfg(test)]
use test::mock_libc::ioctl;
/// Contains the rust representation of asm/uvdevice.h
/// from kernel version: 6.5 verify
mod ffi;
mod info;
mod test;
pub(crate) use ffi::uv_ioctl;
pub mod attest;
pub mod secret;
pub mod secret_list;
pub use info::UvDeviceInfo;
/// User data for the attestation UVC
pub type AttestationUserData = [u8; ffi::UVIO_ATT_USER_DATA_LEN];
/// Configuration Unique Id of the Secure Execution guest
pub type ConfigUid = [u8; ffi::UVIO_ATT_UID_LEN];
/// Bitflags as used by the Ultravisor in MSB0 ordering
///
/// Wraps an u64 to set/get individual bits
pub type UvFlags = crate::misc::Msb0Flags64;
/// Fire an ioctl.
///
/// # Safety:
/// Raw fd must point to an open file
fn ioctl_raw(raw_fd: RawFd, cmd: c_ulong, cb: &mut IoctlCb) -> Result<()> {
debug!("calling unsafe fn wrapper uv::ioctl_raw with {raw_fd:#x?}, {cmd:#x?}, {cb:?}");
let rc;
// Get the raw pointer and do an ioctl.
//
// SAFETY: the passed pointer points to a valid memory region that
// contains the expected C-struct. The struct outlives this function.
unsafe {
rc = ioctl(raw_fd, cmd, cb.as_ptr_mut());
}
debug!("ioctl resulted with {cb:?}");
match rc {
0 => Ok(()),
// NOTE io::Error handles all errnos ioctl uses
_ => Err(std::io::Error::last_os_error().into()),
}
}
/// Converts UV return codes into human readable error messages
fn rc_fmt<C: UvCmd>(rc: u16, rrc: u16, cmd: &mut C) -> &'static str {
let s = match (rc, rrc) {
(0x0000, _) => Some("invalid rc"),
(0x0002, _) => Some("invalid UV command"),
(0x0005, _) => Some("request has an invalid size"),
(0x0030, _) => Some("home address space control bit has R-bit set to one"),
(0x0031, _) => Some("access exception"),
(0x0032, _) => Some("request contains virtual address translating to an invalid address"),
(UvDevice::RC_MORE_DATA, _) => unreachable!("This is no Error!!!!"),
(UvDevice::RC_SUCCESS, _) => unreachable!("This is no Error!!!!"),
_ => cmd.rc_fmt(rc, rrc),
};
s.unwrap_or("unexpected error-code")
}
/// Ultravisor Command.
///
/// Implementers provide information on the specific Ultravisor command metadata and content.
/// API users do not need to interact directly with any functions provided by this trait and refer
/// to the specialized access and tweaking functionalities of the specific command.
pub trait UvCmd {
/// The UV IOCTL number of the UV call
const UV_IOCTL_NR: u8;
/// Returns the uvdevice IOCTL command that his command uses.
///
/// # Returns
///
/// The IOCTL cmd for this UvCmd usually something like `uv_ioctl!(CMD_NR)`
fn cmd(&self) -> u64 {
uv_ioctl(Self::UV_IOCTL_NR)
}
/// Converts UV return codes into human readable error messages
///
/// # Note for implementations
///
/// No need to handle `0x0000, 0x0001, 0x0002, 0x0005, 0x0030, 0x0031, 0x0032, 0x0100`
fn rc_fmt(&self, rc: u16, rrc: u16) -> Option<&'static str>;
/// Returns data used by this command if available.
fn data(&mut self) -> Option<&mut [u8]> {
None
}
}
/// [`UvDevice`] IOCTL control block.
#[derive(Debug)]
struct IoctlCb(ffi::uvio_ioctl_cb);
impl IoctlCb {
fn new(data: Option<&mut [u8]>) -> Result<Self> {
let (data_raw, data_size) = match data {
Some(data) => (
data.as_mut_ptr(),
data.len()
.try_into()
.map_err(|_| Error::Specification("passed data too large".to_string()))?,
),
None => (std::ptr::null_mut(), 0),
};
Ok(Self(ffi::uvio_ioctl_cb {
flags: 0,
uv_rc: 0,
uv_rrc: 0,
argument_addr: data_raw as u64,
argument_len: data_size,
reserved14: [0; 44],
}))
}
fn rc(&self) -> u16 {
self.0.uv_rc
}
fn rrc(&self) -> u16 {
self.0.uv_rrc
}
fn as_ptr_mut(&mut self) -> *mut ffi::uvio_ioctl_cb {
&mut self.0 as *mut _
}
}
/// The Ultravisor has two codes that represent a successful execution.
/// These are represented by this enum.
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UvcSuccess {
/// Command executed successfully
RC_SUCCESS = UvDevice::RC_SUCCESS,
/// Command executed successfully, but there is more data available and the buffer was to small
/// to hold it all. The returned data is still valid.
RC_MORE_DATA = UvDevice::RC_MORE_DATA,
}
/// The UvDevice is a (virtual) device on s390 machines to send Ultravisor commands(UVCs) from userspace.
///
/// On s390 machines with Ultravisor enabled (Secure Execution guest & hosts) the device at
/// `/dev/uv` will accept ioctls.
///
/// # Example
///
/// Use a implementation of [`UvCmd`] to send a specific Ultravisor command to the uvdevice to
/// forward to Firmware.
///
/// ```rust,no_run
/// # use pv_core::uv::UvDevice;
/// # use pv_core::uv::AddCmd;
/// # use std::fs::File;
/// # fn main() -> pv_core::Result<()> {
/// let mut file = File::open("request")?;
/// let uv = UvDevice::open()?;
/// let mut cmd = AddCmd::new(&mut file)?;
/// uv.send_cmd(&mut cmd)?;
/// # Ok(())
/// # }
/// // do something with the result
///
///
/// ```
#[derive(Debug)]
pub struct UvDevice(File);
impl UvDevice {
const RC_SUCCESS: u16 = 0x0001;
const RC_MORE_DATA: u16 = 0x0100;
const PATH: &'static str = "/dev/uv";
/// Open the uvdevice located at `/dev/uv`
///
/// # Errors
///
/// This function will return an error if the device file cannot be opened.
pub fn open() -> Result<Self> {
Ok(Self(
std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(UvDevice::PATH)
.map_err(|e| Error::FileAccess {
ty: FileAccessErrorType::Open,
path: (UvDevice::PATH).into(),
source: e,
})?,
))
}
/// Send an Ultravisor Command via this uvdevice.
///
/// This works by sending an IOCTL to the uvdevice.
///
/// # Errors
///
/// This function will return an error if the IOCTL fails or the Ultravisor does not report
/// a success.
///
/// # Returns
///
/// [`UvcSuccess`] if the UVC executed successfully
pub fn send_cmd<C: UvCmd>(&self, cmd: &mut C) -> Result<UvcSuccess> {
let mut cb = IoctlCb::new(cmd.data())?;
ioctl_raw(self.0.as_raw_fd(), cmd.cmd(), &mut cb)?;
match (cb.rc(), cb.rrc()) {
(Self::RC_SUCCESS, _) => Ok(UvcSuccess::RC_SUCCESS),
(Self::RC_MORE_DATA, _) => Ok(UvcSuccess::RC_MORE_DATA),
(rc, rrc) => Err(Error::Uv {
rc,
rrc,
msg: rc_fmt(rc, rrc, cmd),
}),
}
}
}