use super::super::Command;
use super::KeepPersonality;
#[cfg(feature = "gdb")]
use crate::backend::execute_gdb;
use std::io;
use std::iter;
use std::mem::size_of;
use std::sync::{Arc, RwLock};
use anyhow::{bail, Context, Result};
use kvm_ioctls::{VcpuExit, VcpuFd};
use mmarinus::{perms, Kind, Map};
use sallyport::item::enarxcall::Payload;
use sallyport::item::{Block, Item};
use sallyport::{item, KVM_SYSCALL_TRIGGER_PORT};
pub struct Thread<P: KeepPersonality> {
keep: Arc<RwLock<super::Keep<P>>>,
vcpu_fd: Option<VcpuFd>,
#[cfg(feature = "gdb")]
gdb_fd: Option<std::net::TcpStream>,
}
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),
#[cfg(feature = "gdb")]
gdb_fd: None,
}))),
}
}
}
impl<P: KeepPersonality> Thread<P> {
pub fn balloon(&mut self, log2: usize, npgs: usize, addr: usize) -> sallyport::Result<usize> {
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() as _)
}
pub fn meminfo(&self) -> sallyport::Result<usize> {
let keep = self.keep.read().unwrap();
Ok(keep.kvm_fd.get_nr_memslots() - keep.regions.len())
}
fn kvm_enarxcall<'a>(
&mut self,
enarxcall: &'a mut Payload,
data: &'a mut [u8],
) -> Result<Option<Item<'a>>> {
match enarxcall {
item::Enarxcall {
num: item::enarxcall::Number::MemInfo,
ret,
..
} => {
*ret = match self.meminfo() {
Ok(n) => n as usize,
Err(e) => -e as usize,
};
Ok(None)
}
item::Enarxcall {
num: item::enarxcall::Number::BalloonMemory,
argv: [log2, npgs, addr, ..],
ret,
} => {
*ret = match self.balloon(*log2, *npgs, *addr) {
Ok(n) => n as usize,
Err(e) => -e as usize,
};
Ok(None)
}
_ => return Ok(Some(Item::Enarxcall(enarxcall, data))),
}
}
}
impl<P: KeepPersonality> super::super::Thread for Thread<P> {
fn enter(&mut self, _gdblisten: &Option<String>) -> 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: Block = unsafe {
std::slice::from_raw_parts_mut(
block_virt.as_mut_ptr::<usize>(),
self.keep.read().unwrap().sallyport_block_size as usize
/ size_of::<usize>(),
)
}
.into();
for item in block {
match item {
Item::Gdbcall(_gdbcall, _data) => {
#[cfg(feature = "gdb")]
unsafe {
execute_gdb(
_gdbcall,
_data,
&mut self.gdb_fd,
_gdblisten.as_ref().unwrap(),
)
.map_err(io::Error::from_raw_os_error)
.context("execute_gdb")?;
}
}
Item::Enarxcall(enarxcall, data) => {
sallyport::host::execute(
self.kvm_enarxcall(enarxcall, data)?.into_iter(),
)
.map_err(io::Error::from_raw_os_error)
.context("sallyport::host::execute")?;
}
Item::Syscall(syscall, ..)
if (syscall.num == libc::SYS_exit as usize
|| syscall.num == libc::SYS_exit_group as usize) =>
{
if cfg!(feature = "dbg") {
dbg!(&syscall);
}
return Ok(Command::Exit(syscall.argv[0] as _));
}
Item::Syscall(ref _syscall, ..) => {
#[cfg(feature = "dbg")]
match (
_syscall.num as libc::c_long,
_syscall.argv[1] as libc::c_int,
) {
(
libc::SYS_write | libc::SYS_read,
libc::STDIN_FILENO | libc::STDOUT_FILENO | libc::STDERR_FILENO,
) => {}
_ => {
dbg!(&_syscall);
}
}
sallyport::host::execute(iter::once(item))
.map_err(io::Error::from_raw_os_error)
.context("sallyport::host::execute")?;
}
}
}
self.keep.write().unwrap().sallyports[block_nr].replace(block_virt);
Ok(Command::Continue)
}
#[cfg(debug_assertions)]
reason => bail!(
"KVM error: {:?} {:#x?} {:#x?}",
reason,
vcpu_fd.get_regs(),
vcpu_fd.get_sregs()
),
#[cfg(not(debug_assertions))]
reason => bail!("KVM error: {:?}", reason),
}
}
}