use super::super::Command;
use super::KeepPersonality;
use std::sync::{Arc, RwLock};
use anyhow::{anyhow, Result};
use kvm_ioctls::{VcpuExit, VcpuFd};
use mmarinus::{perms, Kind, Map};
use primordial::{Address, Register};
use sallyport::syscall::enarx::MemInfo;
use sallyport::syscall::{SYS_ENARX_BALLOON_MEMORY, SYS_ENARX_MEM_INFO};
use sallyport::Block;
use sallyport::{Request, KVM_SYSCALL_TRIGGER_PORT};
pub struct Thread<P: KeepPersonality> {
keep: Arc<RwLock<super::Keep<P>>>,
vcpu_fd: Option<VcpuFd>,
}
impl<P: KeepPersonality> Drop for Thread<P> {
fn drop(&mut self) {
let vcpu_fd = self.vcpu_fd.take().unwrap();
self.keep.write().unwrap().cpu_fds.push(vcpu_fd);
}
}
impl<P: KeepPersonality + 'static> super::super::Keep for RwLock<super::Keep<P>> {
fn spawn(self: Arc<Self>) -> Result<Option<Box<dyn super::super::Thread>>> {
let cpu_opt = self.write().unwrap().cpu_fds.pop();
match cpu_opt {
None => Ok(None),
Some(vcpu_fd) => Ok(Some(Box::new(Thread {
keep: self,
vcpu_fd: Some(vcpu_fd),
}))),
}
}
}
impl<P: KeepPersonality> Thread<P> {
pub fn balloon(&mut self, req: &Request) -> Result<[Register<usize>; 2], i32> {
let log2: usize = req.arg[0].into();
let npgs: usize = req.arg[1].into(); let addr: usize = req.arg[2].into(); let size: usize = 1 << log2;
let pgsz = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) } as usize;
assert!(pgsz.is_power_of_two());
if size != pgsz || addr % size != 0 {
return Err(libc::EINVAL);
}
let pages = Map::map(size * npgs)
.anywhere()
.anonymously()
.known::<perms::ReadWrite>(Kind::Private)
.map_err(|e| e.err.raw_os_error().unwrap_or(libc::ENOTSUP))?;
let mut keep = self.keep.write().unwrap();
let vaddr = keep
.map(pages, addr)
.map_err(|e| e.raw_os_error().unwrap_or(libc::ENOTSUP))?
.as_virt()
.start;
Ok([vaddr.as_u64().into(), 0.into()])
}
pub fn meminfo(&self, block: &mut Block) -> Result<[Register<usize>; 2], i32> {
let keep = self.keep.read().unwrap();
let mem_slots = keep.kvm_fd.get_nr_memslots() - keep.regions.len();
let virt_start = Address::from(keep.sallyport_start.as_mut_ptr());
let mem_info: MemInfo = MemInfo {
virt_start,
mem_slots,
};
let c = block.cursor();
c.write(&mem_info).map_err(|_| libc::ENOBUFS)?;
Ok([0.into(), 0.into()])
}
}
impl<P: KeepPersonality> super::super::Thread for Thread<P> {
fn enter(&mut self) -> Result<Command> {
let vcpu_fd = self.vcpu_fd.as_mut().unwrap();
match vcpu_fd.run()? {
VcpuExit::IoOut(KVM_SYSCALL_TRIGGER_PORT, data) => {
debug_assert_eq!(data.len(), 2);
let block_nr = data[0] as usize + ((data[1] as usize) << 8);
let block_virt = self.keep.write().unwrap().sallyports[block_nr]
.take()
.unwrap();
let block = unsafe { &mut *block_virt.as_mut_ptr::<Block>() };
let req = unsafe { block.msg.req };
let ret = match i64::from(req.num) {
SYS_ENARX_BALLOON_MEMORY => {
let rep = self.balloon(&req).into();
block.msg.rep = rep;
Ok(Command::Continue)
}
SYS_ENARX_MEM_INFO => {
block.msg.rep = self.meminfo(block).into();
Ok(Command::Continue)
}
_ => Ok(Command::SysCall(block)),
};
self.keep.write().unwrap().sallyports[block_nr].replace(block_virt);
ret
}
#[cfg(debug_assertions)]
reason => Err(anyhow!(
"{:?} {:#x?} {:#x?}",
reason,
vcpu_fd.get_regs(),
vcpu_fd.get_sregs()
)),
#[cfg(not(debug_assertions))]
reason => Err(anyhow!("{:?}", reason)),
}
}
}