#[cfg(feature = "backend-kvm")]
pub mod kvm;
#[cfg(feature = "backend-sev")]
pub mod sev;
#[cfg(feature = "backend-sgx")]
pub mod sgx;
mod binary;
mod probe;
use binary::Binary;
use std::convert::TryFrom;
use std::sync::Arc;
use anyhow::{Error, Result};
use mmarinus::{perms, Map};
use sallyport::Block;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use spinning::Lazy;
trait Config: Sized {
type Flags;
fn flags(flags: u32) -> Self::Flags;
fn new(shim: &Binary<'_>, exec: &Binary<'_>) -> Result<Self>;
}
trait Mapper: Sized + TryFrom<Self::Config, Error = Error> {
type Config: Config;
type Output: TryFrom<Self, Error = Error>;
fn map(
&mut self,
pages: Map<perms::ReadWrite>,
to: usize,
with: <Self::Config as Config>::Flags,
) -> Result<()>;
}
trait Loader: Mapper {
fn load(shim: impl AsRef<[u8]>, exec: impl AsRef<[u8]>) -> Result<Self::Output>;
}
pub trait Backend: Sync + Send {
fn name(&self) -> &'static str;
fn shim(&self) -> &'static [u8];
fn data(&self) -> Vec<Datum>;
fn keep(&self, shim: &[u8], exec: &[u8]) -> Result<Arc<dyn Keep>>;
fn hash(&self, shim: &[u8], exec: &[u8]) -> Result<Vec<u8>>;
fn have(&self) -> bool {
!self.data().iter().fold(false, |e, d| e | !d.pass)
}
}
impl Serialize for dyn Backend {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut backend = serializer.serialize_struct("Backend", 2)?;
backend.serialize_field("backend", self.name())?;
backend.serialize_field("data", &self.data())?;
backend.end()
}
}
#[derive(serde::Serialize)]
pub struct Datum {
pub name: String,
pub pass: bool,
pub info: Option<String>,
pub mesg: Option<String>,
}
pub trait Keep {
fn spawn(self: Arc<Self>) -> Result<Option<Box<dyn Thread>>>;
}
pub trait Thread {
fn enter(&mut self) -> Result<Command<'_>>;
}
pub enum Command<'a> {
#[allow(dead_code)]
SysCall(&'a mut Block),
#[allow(dead_code)]
CpuId(&'a mut Block),
#[cfg(feature = "gdb")]
#[allow(dead_code)]
Gdb(&'a mut Block, &'a mut Option<std::net::TcpStream>),
#[allow(dead_code)]
Continue,
}
pub static BACKENDS: Lazy<Vec<Box<dyn Backend>>> = Lazy::new(|| {
vec![
#[cfg(feature = "backend-sgx")]
Box::new(sgx::Backend),
#[cfg(feature = "backend-sev")]
Box::new(sev::Backend),
#[cfg(feature = "backend-kvm")]
Box::new(kvm::Backend),
]
});
#[cfg(feature = "gdb")]
pub fn wait_for_gdb_connection(sockaddr: &str) -> std::io::Result<std::net::TcpStream> {
use std::net::TcpListener;
eprintln!("Waiting for a GDB connection on {:?}...", sockaddr);
let sock = TcpListener::bind(sockaddr)?;
let (stream, addr) = sock.accept()?;
eprintln!("Debugger connected from {}", addr);
Ok(stream) }
#[cfg(feature = "gdb")]
pub fn handle_gdb(block: &mut Block, gdb_fd: &mut Option<std::net::TcpStream>, sockaddr: &str) {
use gdbstub::Connection;
let req = unsafe { block.msg.req };
match req.num.into() {
sallyport::syscall::SYS_ENARX_GDB_START => {
if gdb_fd.is_none() {
let mut stream = wait_for_gdb_connection(sockaddr).unwrap();
let res = stream
.on_session_start()
.map(|_| [0usize.into(), 0usize.into()])
.map_err(|e| e.raw_os_error().unwrap_or(libc::EINVAL));
if res.is_ok() {
gdb_fd.replace(stream);
}
block.msg.rep = res.into();
} else {
block.msg.rep = Ok([0usize.into(), 0usize.into()]).into();
}
}
sallyport::syscall::SYS_ENARX_GDB_PEEK => {
let stream = gdb_fd.as_mut().unwrap();
let ret = Connection::peek(stream)
.map(|v| {
let v = v.map(|v| v as usize).unwrap_or(u8::MAX as usize + 1);
[v.into(), 0usize.into()]
})
.map_err(|e| e.raw_os_error().unwrap_or(libc::EINVAL));
block.msg.rep = ret.into();
}
sallyport::syscall::SYS_ENARX_GDB_READ => {
let stream = gdb_fd.as_mut().unwrap();
let buf_ptr: *mut u8 = req.arg[0].into();
let buf_len: usize = req.arg[1].into();
let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr, buf_len) };
let ret = stream
.read_exact(buf)
.map(|_| [buf_len.into(), 0usize.into()])
.map_err(|e| e.raw_os_error().unwrap_or(libc::EINVAL));
block.msg.rep = ret.into();
}
sallyport::syscall::SYS_ENARX_GDB_WRITE => {
let stream = gdb_fd.as_mut().unwrap();
let buf_ptr: *mut u8 = req.arg[0].into();
let buf_len: usize = req.arg[1].into();
let buf = unsafe { core::slice::from_raw_parts(buf_ptr, buf_len) };
let ret = Connection::write_all(stream, buf)
.map(|_| [buf_len.into(), 0usize.into()])
.map_err(|e| e.raw_os_error().unwrap_or(libc::EINVAL));
block.msg.rep = ret.into();
}
_ => {}
}
}