use super::super::{Error, Indeterminate};
use super::{Finish, Start, Update};
use iocuddle::{Group, Ioctl, Write, WriteRead};
use std::marker::PhantomData;
use std::os::raw::c_ulong;
use std::os::unix::io::AsRawFd;
impl_const_id! {
pub Id => u32;
Init = 22,
LaunchStart<'_> = 23,
LaunchUpdate<'_> = 24,
LaunchFinish<'_> = 25,
}
const KVM: Group = Group::new(0xAE);
const ENC_OP: Ioctl<WriteRead, &c_ulong> = unsafe { KVM.write_read(0xBA) };
pub const ENC_REG_REGION: Ioctl<Write, &KvmEncRegion> =
unsafe { KVM.read::<KvmEncRegion>(0xBB).lie() };
pub const SNP_INIT: Ioctl<WriteRead, &Command<Init>> = unsafe { ENC_OP.lie() };
pub const SNP_LAUNCH_START: Ioctl<WriteRead, &Command<LaunchStart>> = unsafe { ENC_OP.lie() };
pub const SNP_LAUNCH_UPDATE: Ioctl<WriteRead, &Command<LaunchUpdate>> = unsafe { ENC_OP.lie() };
pub const SNP_LAUNCH_FINISH: Ioctl<WriteRead, &Command<LaunchFinish>> = unsafe { ENC_OP.lie() };
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, PartialEq)]
pub struct KvmEncRegion<'a> {
addr: u64,
size: u64,
phantom: PhantomData<&'a [u8]>,
}
impl<'a> KvmEncRegion<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self {
addr: data.as_ptr() as _,
size: data.len() as _,
phantom: PhantomData,
}
}
pub fn register(&mut self, vm_fd: &mut impl AsRawFd) -> std::io::Result<std::os::raw::c_uint> {
ENC_REG_REGION.ioctl(vm_fd, self)
}
}
#[repr(C)]
pub struct Command<'a, T: Id> {
code: u32,
data: u64,
error: u32,
sev_fd: u32,
_phantom: PhantomData<&'a T>,
}
impl<'a, T: Id> Command<'a, T> {
pub fn from_mut(sev: &'a mut impl AsRawFd, subcmd: &'a mut T) -> Self {
Self {
code: T::ID,
data: subcmd as *mut T as _,
error: 0,
sev_fd: sev.as_raw_fd() as _,
_phantom: PhantomData,
}
}
pub fn from(sev: &'a mut impl AsRawFd, subcmd: &'a T) -> Self {
Self {
code: T::ID,
data: subcmd as *const T as _,
error: 0,
sev_fd: sev.as_raw_fd() as _,
_phantom: PhantomData,
}
}
pub fn encapsulate(&self, err: std::io::Error) -> Indeterminate<Error> {
match self.error {
0 => Indeterminate::<Error>::from(err),
_ => Indeterminate::<Error>::from(self.error as u32),
}
}
}
#[derive(Default)]
#[repr(C, packed)]
pub struct Init {
flags: u64,
}
#[repr(C)]
pub struct LaunchStart<'a> {
policy: u64,
ma_uaddr: u64,
ma_en: u8,
imi_en: u8,
gosvw: [u8; 16],
pad: [u8; 6],
_phantom: PhantomData<&'a [u8]>,
}
impl From<Start<'_>> for LaunchStart<'_> {
fn from(start: Start) -> Self {
Self {
policy: start.policy.into(),
ma_uaddr: if let Some(addr) = start.ma_uaddr {
addr.as_ptr() as u64
} else {
0
},
ma_en: if start.ma_uaddr.is_some() { 1 } else { 0 },
imi_en: start.imi_en as _,
gosvw: start.gosvw,
pad: [0u8; 6],
_phantom: PhantomData,
}
}
}
#[repr(C)]
pub struct LaunchUpdate<'a> {
start_gfn: u64,
uaddr: u64,
len: u32,
imi_page: u8,
page_type: u8,
vmpl3_perms: u8,
vmpl2_perms: u8,
vmpl1_perms: u8,
_phantom: PhantomData<&'a ()>,
}
impl From<Update<'_>> for LaunchUpdate<'_> {
fn from(update: Update) -> Self {
Self {
start_gfn: update.start_gfn,
uaddr: update.uaddr.as_ptr() as _,
len: update.uaddr.len() as _,
imi_page: if update.imi_page { 1 } else { 0 },
page_type: update.page_type as _,
vmpl3_perms: update.vmpl3_perms.bits(),
vmpl2_perms: update.vmpl2_perms.bits(),
vmpl1_perms: update.vmpl1_perms.bits(),
_phantom: PhantomData,
}
}
}
pub const KVM_SEV_SNP_FINISH_DATA_SIZE: usize = 32;
#[repr(C)]
pub struct LaunchFinish<'a> {
id_block_uaddr: u64,
id_auth_uaddr: u64,
id_block_en: u8,
auth_key_en: u8,
host_data: [u8; KVM_SEV_SNP_FINISH_DATA_SIZE],
pad: [u8; 6],
_phantom: PhantomData<&'a [u8]>,
}
impl From<Finish<'_, '_>> for LaunchFinish<'_> {
fn from(finish: Finish) -> Self {
let id_block = if let Some(addr) = finish.id_block {
addr.as_ptr() as u64
} else {
0
};
let id_auth = if let Some(addr) = finish.id_auth {
addr.as_ptr() as u64
} else {
0
};
Self {
id_block_uaddr: id_block,
id_auth_uaddr: id_auth,
id_block_en: if finish.id_block.is_some() { 1 } else { 0 },
auth_key_en: if finish.id_auth.is_some() { 1 } else { 0 },
host_data: finish.host_data,
pad: [0u8; 6],
_phantom: PhantomData,
}
}
}