#[cfg(target_os = "linux")]
use crate::{
error::FirmwareError,
firmware::guest::GuestPolicy,
launch::linux::{ioctl::*, shared::*, snp::*},
};
use std::{marker::PhantomData, os::unix::io::AsRawFd, result::Result};
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub struct New;
pub struct Started;
pub struct Launcher<T, U: AsRawFd, V: AsRawFd> {
vm_fd: U,
sev: V,
state: PhantomData<T>,
}
impl<T, U: AsRawFd, V: AsRawFd> AsRef<U> for Launcher<T, U, V> {
fn as_ref(&self) -> &U {
&self.vm_fd
}
}
impl<T, U: AsRawFd, V: AsRawFd> AsMut<U> for Launcher<T, U, V> {
fn as_mut(&mut self) -> &mut U {
&mut self.vm_fd
}
}
impl<U: AsRawFd, V: AsRawFd> Launcher<New, U, V> {
pub fn new(vm_fd: U, sev: V) -> Result<Self, FirmwareError> {
let mut launcher = Launcher {
vm_fd,
sev,
state: PhantomData,
};
let init = Init2::init_default_snp();
let mut cmd = Command::from(&launcher.sev, &init);
INIT2
.ioctl(&mut launcher.vm_fd, &mut cmd)
.map_err(|_| cmd.encapsulate())?;
Ok(launcher)
}
pub fn start(mut self, start: Start) -> Result<Launcher<Started, U, V>, FirmwareError> {
let launch_start = LaunchStart::from(start);
let mut cmd = Command::from(&self.sev, &launch_start);
SNP_LAUNCH_START
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|_| cmd.encapsulate())?;
let launcher = Launcher {
vm_fd: self.vm_fd,
sev: self.sev,
state: PhantomData,
};
Ok(launcher)
}
}
impl<U: AsRawFd, V: AsRawFd> Launcher<Started, U, V> {
pub fn update_data(
&mut self,
mut update: Update,
gpa: u64,
gpa_len: u64,
) -> Result<(), FirmwareError> {
loop {
let launch_update_data = LaunchUpdate::from(update);
let mut cmd = Command::from(&self.sev, &launch_update_data);
KvmEncRegion::new(update.uaddr).register(&mut self.vm_fd)?;
KvmSetMemoryAttributes::new(gpa, gpa_len, KVM_MEMORY_ATTRIBUTE_PRIVATE)
.set_attributes(&mut self.vm_fd)?;
match SNP_LAUNCH_UPDATE.ioctl(&mut self.vm_fd, &mut cmd) {
Ok(_) => {
if launch_update_data.len == 0 {
break;
}
update.start_gfn = launch_update_data.start_gfn;
update.uaddr = unsafe {
std::slice::from_raw_parts(
launch_update_data.uaddr as *const u8,
launch_update_data.len as usize,
)
};
}
Err(e) if e.raw_os_error() == Some(libc::EAGAIN) => {
continue;
}
Err(_) => {
return Err(cmd.encapsulate());
}
}
}
Ok(())
}
pub fn finish(mut self, finish: Finish) -> Result<(U, V), FirmwareError> {
let launch_finish = LaunchFinish::from(finish);
let mut cmd = Command::from(&self.sev, &launch_finish);
SNP_LAUNCH_FINISH
.ioctl(&mut self.vm_fd, &mut cmd)
.map_err(|_| cmd.encapsulate())?;
Ok((self.vm_fd, self.sev))
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct Start {
pub(crate) policy: GuestPolicy,
pub(crate) gosvw: [u8; 16],
pub(crate) flags: u16,
}
impl Start {
pub fn new(policy: GuestPolicy, gosvw: [u8; 16]) -> Self {
Self {
policy,
gosvw,
flags: 0,
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C)]
#[non_exhaustive]
pub enum PageType {
Normal = 0x1,
Vmsa = 0x2,
Zero = 0x3,
Unmeasured = 0x4,
Secrets = 0x5,
Cpuid = 0x6,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Update<'a> {
pub(crate) start_gfn: u64,
pub(crate) uaddr: &'a [u8],
pub(crate) page_type: PageType,
}
impl<'a> Update<'a> {
pub fn new(start_gfn: u64, uaddr: &'a [u8], page_type: PageType) -> Self {
Self {
start_gfn,
uaddr,
page_type,
}
}
}
bitflags! {
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct VmplPerms: u8 {
const READ = 1;
const WRITE = 1 << 1;
const EXECUTE_USER = 1 << 2;
const EXECUTE_SUPERVISOR = 1 << 3;
}
}
#[cfg(feature = "serde")]
impl Serialize for VmplPerms {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u8(self.bits())
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for VmplPerms {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let bits = u8::deserialize(deserializer)?;
Ok(VmplPerms::from_bits_truncate(bits))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Finish<'a, 'b> {
pub(crate) id_block: Option<&'a [u8]>,
pub(crate) id_auth: Option<&'b [u8]>,
pub(crate) host_data: [u8; KVM_SEV_SNP_FINISH_DATA_SIZE],
}
impl<'a, 'b> Finish<'a, 'b> {
pub fn new(
id_block: Option<&'a [u8]>,
id_auth: Option<&'b [u8]>,
host_data: [u8; KVM_SEV_SNP_FINISH_DATA_SIZE],
) -> Self {
Self {
id_block,
id_auth,
host_data,
}
}
}