use super::{error::*, types::*};
use std::cmp::min;
use nix::{ioctl_read, ioctl_readwrite, ioctl_write_ptr};
pub const NE_MAGIC: u8 = 0xAE;
pub const NE_CREATE_VM: u8 = 0x20;
ioctl_read!(ne_create_vm, NE_MAGIC, NE_CREATE_VM, u64);
pub const NE_ADD_VCPU: u8 = 0x21;
ioctl_readwrite!(ne_add_vcpu, NE_MAGIC, NE_ADD_VCPU, u32);
pub const NE_GET_IMAGE_LOAD_INFO: u8 = 0x22;
ioctl_readwrite!(
ne_get_image_load_info,
NE_MAGIC,
NE_GET_IMAGE_LOAD_INFO,
ImageLoadInfo
);
pub const NE_SET_USER_MEMORY_REGION: u8 = 0x23;
ioctl_write_ptr!(
ne_set_user_memory_region,
NE_MAGIC,
NE_SET_USER_MEMORY_REGION,
UserMemoryRegion
);
pub const NE_START_ENCLAVE: u8 = 0x24;
ioctl_readwrite!(ne_start_enclave, NE_MAGIC, NE_START_ENCLAVE, StartInfo);
const NE_DEFAULT_MEMORY_REGION: u64 = 0;
const HUGE_FLAG_SIZE: [(libc::c_int, usize); 9] = [
(libc::MAP_HUGE_16GB, 16 << 30),
(libc::MAP_HUGE_2GB, 2 << 30),
(libc::MAP_HUGE_1GB, 1 << 30),
(libc::MAP_HUGE_512MB, 512 << 20),
(libc::MAP_HUGE_256MB, 256 << 20),
(libc::MAP_HUGE_32MB, 32 << 20),
(libc::MAP_HUGE_16MB, 16 << 20),
(libc::MAP_HUGE_8MB, 8 << 20),
(libc::MAP_HUGE_2MB, 2 << 20),
];
#[derive(Debug, Default)]
#[repr(C)]
pub struct ImageLoadInfo {
flags: u64,
pub memory_offset: u64,
}
impl From<&ImageType<'_>> for ImageLoadInfo {
fn from(image_type: &ImageType) -> Self {
let flags = match image_type {
ImageType::Eif(_) => 0x01,
};
Self {
flags,
..Default::default()
}
}
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct UserMemoryRegion {
pub flags: u64,
pub size: u64,
pub uaddr: u64,
}
impl UserMemoryRegion {
pub fn image_fill(
&mut self,
image: &[u8],
offset: usize,
image_size: usize,
written: &mut usize,
) -> Result<(), MemInitError> {
let Some(location) = written.checked_add(self.size as usize) else {
return Err(MemInitError::OffsetCheckOverflow);
};
if location > offset {
let region_offset = offset.saturating_sub(*written);
let image_offset = written.saturating_sub(offset);
let write_amount = min(
self.size as usize - region_offset,
image_size - image_offset,
);
let (region_start, region_end) = (region_offset, region_offset + write_amount);
let (image_start, image_end) = (image_offset, image_offset + write_amount);
let bytes = unsafe {
std::slice::from_raw_parts_mut(self.uaddr as *mut u8, self.size as usize)
};
bytes[region_start..region_end].copy_from_slice(&image[image_start..image_end]);
}
*written += self.size as usize;
Ok(())
}
}
pub struct UserMemoryRegions(Vec<UserMemoryRegion>);
impl UserMemoryRegions {
pub fn new(size_mib: usize) -> Result<Self, MemInitError> {
let mut regions = Vec::new();
let mut size = size_mib << 20;
let mut found: bool;
while size > 0 {
found = false;
for (hp_flag, reg_size) in HUGE_FLAG_SIZE {
if size < reg_size {
continue;
}
let addr = unsafe {
libc::mmap(
std::ptr::null_mut(),
reg_size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_HUGETLB | hp_flag,
-1,
0,
)
};
if addr == libc::MAP_FAILED {
continue;
}
let region = UserMemoryRegion {
flags: NE_DEFAULT_MEMORY_REGION,
size: reg_size as _,
uaddr: addr as _,
};
regions.push(region);
size -= reg_size;
found = true;
}
if !found {
return Err(MemInitError::NoHugePageFound);
}
}
Ok(Self(regions))
}
pub fn image_fill(&mut self, offset: usize, image: ImageType) -> Result<(), MemInitError> {
let ImageType::Eif(image) = image;
let image_size = image.len();
let Some(limit) = offset.checked_add(image_size) else {
return Err(MemInitError::ImagePlacementOverflow);
};
let mut written: usize = 0;
for region in &mut self.0 {
region.image_fill(image, offset, image_size, &mut written)?;
if written >= limit {
break;
}
}
if written < limit {
return Err(MemInitError::ImageWriteIncomplete);
}
Ok(())
}
pub fn inner_ref(&self) -> &Vec<UserMemoryRegion> {
&self.0
}
}
#[repr(C)]
pub struct StartInfo {
flags: u64,
pub cid: u64,
}
impl StartInfo {
pub fn new(flags: StartFlags, cid: u64) -> Self {
let flags = flags.bits();
Self { flags, cid }
}
}