#[cfg(target_os = "linux")]
mod linux;
pub mod error;
pub mod firmware;
pub(crate) mod util;
use super::error::FirmwareError;
#[cfg(target_os = "linux")]
use linux::{ioctl::*, snp::*};
use std::{fmt::Display, marker::PhantomData, os::unix::io::AsRawFd, result::Result};
use bitfield::bitfield;
use bitflags::bitflags;
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))
}
}
#[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,
}
}
}
#[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,
}
#[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;
}
}
#[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,
}
}
}
bitfield! {
#[repr(C)]
#[derive(Default, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
pub struct GuestPolicy(u64);
impl Debug;
pub abi_minor, set_abi_minor: 7, 0;
pub abi_major, set_abi_major: 15, 8;
pub smt_allowed, set_smt_allowed: 16;
pub migrate_ma_allowed, set_migrate_ma_allowed: 18;
pub debug_allowed, set_debug_allowed: 19;
pub single_socket_required, set_single_socket_required: 20;
pub cxl_allowed, set_cxl_allowed: 21;
pub mem_aes_256_xts, set_mem_aes_256_xts: 22;
pub rapl_dis, set_rapl_dis: 23;
pub ciphertext_hiding, set_ciphertext_hiding: 24;
pub page_swap_disabled, set_page_swap_disabled: 25;
}
impl Display for GuestPolicy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
r#"Guest Policy (0x{:x}):
ABI Major: {}
ABI Minor: {}
SMT Allowed: {}
Migrate MA: {}
Debug Allowed: {}
Single Socket: {}
CXL Allowed: {}
AEX 256 XTS: {}
RAPL Allowed: {}
Ciphertext hiding: {}
Page Swap Disable: {}"#,
self.0,
self.abi_major(),
self.abi_minor(),
self.smt_allowed(),
self.migrate_ma_allowed(),
self.debug_allowed(),
self.single_socket_required(),
self.cxl_allowed(),
self.mem_aes_256_xts(),
self.rapl_dis(),
self.ciphertext_hiding(),
self.page_swap_disabled()
)
}
}
impl From<GuestPolicy> for u64 {
fn from(value: GuestPolicy) -> Self {
let reserved: u64 = 1 << 17;
value.0 | reserved
}
}
impl From<u64> for GuestPolicy {
fn from(value: u64) -> Self {
let reserved: u64 = 1 << 17;
GuestPolicy(value | reserved)
}
}