1
0
Fork 0
mirror of https://github.com/hermitcore/libhermit.git synced 2025-03-09 00:00:03 +01:00

Replace the Isle variant with a proper Trait.

The proxy library was moved into an own directory. Then the IsleKind
enum was replaced with an Isle trait which is implemented by
qemu/multi/uhyve.
This commit is contained in:
bytesnake 2017-05-20 14:12:31 +02:00
parent 66332a1337
commit abbcee0b46
18 changed files with 247 additions and 184 deletions

View file

@ -1,5 +1,5 @@
[package]
name = "hermitcore_proxy"
name = "hermit_proxy"
version = "0.0.1"
authors = ["bytesnake <bytesnake@mailbox.org>"]

View file

@ -1,127 +0,0 @@
use std::env;
use std::fs::File;
use std::path::Path;
use std::io::{Write, Read, BufReader, BufRead};
use inotify::{Inotify, watch_mask};
use hermit_env;
use qemu::QEmu;
use multi::Multi;
use uhyve::Uhyve;
use uhyve::vm::VirtualMachine;
use error::*;
pub enum IsleKind {
QEMU(QEmu),
UHYVE((Uhyve, VirtualMachine)),
MULTI(Multi)
}
impl IsleKind {
pub fn new(path: &str) -> Result<IsleKind> {
let isle = hermit_env::isle_kind();
debug!("Create a new {} isle", isle);
let isle = match isle.as_str() {
"qemu"=>IsleKind::QEMU(QEmu::new(path)?),
"uhyve" => {
let uhyve = Uhyve::new();
let mut vm = uhyve.create_vm(0x20000000)?;
vm.load_kernel(path)?;
vm.init()?;
IsleKind::UHYVE((uhyve, vm))
},
s => IsleKind::MULTI(Multi::new(s.parse::<u8>().unwrap_or(0), path)?)
};
Ok(isle)
}
fn get_num(&self) -> u8 {
match *self {
IsleKind::QEMU(_) => 0,
IsleKind::UHYVE(_) => 0,
IsleKind::MULTI(ref s) => s.get_num()
}
}
pub fn is_available(&self) -> Result<bool> {
let mut file = match *self {
IsleKind::QEMU(ref q) => File::open(q.tmp_path()),
_ => File::open(format!("/sys/hermit/isle{}/log", self.get_num()))
};
let mut file = file.map_err(|x| Error::InvalidFile(format!("{:?}",x)))?;
let mut reader = BufReader::new(file);
//let mut result = String::new();
//file.read_to_string(&mut result).map_err(|_| Error::InvalidFile)?;
for line in reader.lines() {
if line.unwrap().contains("TCP server is listening.") {
debug!("Found key token, continue");
return Ok(true);
}
}
Ok(false)
}
pub fn wait_available(&mut self) -> Result<()> {
debug!("HERMIT - wait to be available");
let mut ino = Inotify::init().unwrap();
match *self {
IsleKind::QEMU(_) => ino.add_watch(Path::new("/tmp"), watch_mask::MODIFY | watch_mask::CREATE).unwrap(),
IsleKind::MULTI(_) => ino.add_watch(Path::new("/sys/hermit"), watch_mask::MODIFY | watch_mask::CREATE).unwrap(),
IsleKind::UHYVE(_) => return Ok(())
};
let mut buffer = [0; 1024];
loop {
//debug!("Wait ... ");
if let Some(_) = ino.read_events(&mut buffer).unwrap().next() {
if self.is_available()? {
return Ok(());
}
}
if let IsleKind::QEMU(ref mut obj) = *self {
let (stdout,stderr) = obj.output();
if stderr != "" {
return Err(Error::QEmu((stdout, stderr)));
}
if stdout != "" {
debug!("stdout: {}", stdout);
}
}
}
}
pub fn stop(&self) -> Result<()> {
let cpu_path = format!("/sys/hermit/isle{}/cpus", self.get_num());
let mut cpus_file = File::create(&cpu_path)
.map_err(|_| Error::InvalidFile(cpu_path.clone()))?;
cpus_file.write("-1".as_bytes())
.map_err(|_| Error::InvalidFile(cpu_path));
Ok(())
}
pub fn run(mut self) -> Result<()> {
match self {
IsleKind::UHYVE((_, mut vm)) => vm.run()?,
IsleKind::QEMU(qemu) => qemu.run(),
IsleKind::MULTI(multi) => multi.run()
}
Ok(())
}
}

View file

@ -0,0 +1,105 @@
pub mod error;
mod utils;
mod hermit_env;
mod qemu;
mod multi;
mod proto;
mod socket;
mod uhyve;
use std::fs::File;
use std::path::Path;
use std::io::{Write, Read, BufReader, BufRead};
use inotify::{Inotify, watch_mask};
use hermit::qemu::QEmu;
use hermit::multi::Multi;
use hermit::uhyve::uhyve::Uhyve;
use hermit::error::*;
pub fn new_isle(path: &str) -> Result<Box<Isle>> {
let isle = hermit_env::isle_kind();
debug!("Create a new {} isle", isle);
match isle.as_str() {
"qemu"=> Ok(Box::new(QEmu::new(path)?)),
"uhyve" => Ok(Box::new(Uhyve::new(path)?)),
s => Ok(Box::new(Multi::new(s.parse::<u8>().unwrap_or(0), path)?))
}
}
pub trait Isle {
fn num(&self) -> u8;
fn log_file(&self) -> Result<String>;
fn log_path(&self) -> Result<String>;
fn cpu_path(&self) -> Result<String>;
fn run(&mut self) -> Result<()>;
fn is_available(&self) -> Result<bool> {
let log = self.log_file()?;
// open the log file
let mut file = File::open(log)
.map_err(|x| Error::InvalidFile(format!("{:?}",x)))?;
let mut reader = BufReader::new(file);
for line in reader.lines() {
if line.unwrap().contains("TCP server is listening.") {
debug!("Found key token, continue");
return Ok(true);
}
}
Ok(false)
}
fn wait_until_available(&self) -> Result<()> {
debug!("Wait for the HermitIsle to be available");
let mut ino = Inotify::init().unwrap();
// watch on the log path
let log_path = self.log_path()?;
ino.add_watch(Path::new(&log_path), watch_mask::MODIFY | watch_mask::CREATE).unwrap();
let mut buffer = [0; 1024];
loop {
if let Some(_) = ino.read_events(&mut buffer).unwrap().next() {
if self.is_available()? {
return Ok(());
}
}
/*
if let IsleKind::QEMU(ref mut obj) = *self {
let (stdout,stderr) = obj.output();
if stderr != "" {
return Err(Error::QEmu((stdout, stderr)));
}
if stdout != "" {
debug!("stdout: {}", stdout);
}
}*/
}
}
fn stop(&self) -> Result<()> {
debug!("Stop the HermitIsle");
let cpu_path = self.cpu_path()?;
let mut cpus_file = File::create(&cpu_path)
.map_err(|_| Error::InvalidFile(cpu_path.clone()))?;
cpus_file.write("-1".as_bytes())
.map_err(|_| Error::InvalidFile(cpu_path));
Ok(())
}
}

View file

@ -2,10 +2,10 @@ use std::fs::File;
use std::env;
use std::io::{Write, Read};
use error::*;
use hermit_env;
use socket::Socket;
use hermit::Isle;
use hermit::error::*;
use hermit::hermit_env;
use hermit::socket::Socket;
pub struct Multi {
num: u8,
@ -49,12 +49,28 @@ impl Multi {
Ok(Multi { num: num, socket: Socket::new_multi(num) })
}
}
pub fn get_num(&self) -> u8 {
impl Isle for Multi {
fn num(&self) -> u8 {
self.num
}
pub fn run(&self) {
fn log_file(&self) -> Result<String> {
Ok(format!("/sys/hermit/isle{}/log", self.num))
}
fn log_path(&self) -> Result<String> {
Ok("/sys/hermit/".into())
}
fn cpu_path(&self) -> Result<String> {
Ok(format!("/sys/hermit/isle{}/cpus", self.num))
}
fn run(&mut self) -> Result<()> {
self.socket.connect().run();
Ok(())
}
}

View file

@ -1,14 +1,15 @@
use std::env;
use utils;
use error::*;
use std::process::{Stdio, Child, Command};
use libc;
use std::fs::File;
use std::io::Read;
use std::process::{ChildStdout, ChildStderr};
use hermit_env;
use socket::Socket;
use hermit::Isle;
use hermit::utils;
use hermit::error::*;
use hermit::hermit_env;
use hermit::socket::Socket;
const PIDNAME: &'static str = "/tmp/hpid-XXXXXX";
const TMPNAME: &'static str = "/tmp/hermit-XXXXXX";
@ -42,14 +43,6 @@ impl QEmu {
})
}
pub fn run(&self) {
self.socket.connect().run();
}
pub fn tmp_path(&self) -> &str {
&self.tmp_file
}
pub fn start_with(path: &str, tmp_file: &str, pid_file: &str) -> Result<Command> {
let hostfwd = format!("user,hostfwd=tcp:127.0.0.1:{}-:{}", hermit_env::port(), hermit_env::port());
let monitor_str = format!("telnet:127.0.0.1:{},server,nowait", (hermit_env::port().parse::<u32>().unwrap()+1).to_string());
@ -132,6 +125,25 @@ impl QEmu {
}
}
impl Isle for QEmu {
fn num(&self) -> u8 { 0 }
fn log_file(&self) -> Result<String> {
Ok(self.tmp_file.clone())
}
fn log_path(&self) -> Result<String> {
Ok("/tmp".into())
}
fn cpu_path(&self) -> Result<String> {
Err(Error::InternalError)
}
fn run(&mut self) -> Result<()> {
self.socket.connect().run();
Ok(())
}
}
impl Drop for QEmu {
fn drop(&mut self) {
let mut id_str = String::new();

View file

@ -6,8 +6,8 @@ use std::ffi::CString;
use std::process;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use proto;
use proto::Packet;
use hermit::proto;
use hermit::proto::Packet;
use libc;

View file

@ -2,7 +2,7 @@
//! GDT. The Global Descriptor Table contains information about the memory structure used by the
//! X86 family.
use uhyve::kvm_header::kvm_segment;
use super::kvm_header::kvm_segment;
/// Used segments in order
pub const BOOT_NULL: isize = 0;

View file

@ -1,6 +1,6 @@
//gdt.rs kvm_header.rs kvm.rs mod.rs vcpu.rs vm.rs
mod gdt;
mod kvm_header;
pub mod kvm_header;
pub mod uhyve;
pub mod vcpu;
pub mod vm;
@ -8,5 +8,6 @@ pub mod proto;
// reexport Uhyve to show up in the root namespace of our module
pub use self::uhyve::*;
// reexport the Error enum and Result type
pub use error::*;
pub use hermit::error::*;

View file

@ -1,5 +1,5 @@
use libc::{write, read, lseek, exit, open, close, c_int, c_void};
use uhyve::kvm_header::{kvm_run, KVM_EXIT_IO, KVM_EXIT_HLT, KVM_EXIT_MMIO,KVM_EXIT_FAIL_ENTRY, KVM_EXIT_INTERNAL_ERROR, KVM_EXIT_SHUTDOWN };
use super::kvm_header::{kvm_run, KVM_EXIT_IO, KVM_EXIT_HLT, KVM_EXIT_MMIO,KVM_EXIT_FAIL_ENTRY, KVM_EXIT_INTERNAL_ERROR, KVM_EXIT_SHUTDOWN };
use std::ffi::CStr;

View file

@ -7,15 +7,16 @@ use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use libc;
use uhyve::{Error, Result, NameIOCTL};
use uhyve::vm::VirtualMachine;
use hermit::Isle;
use super::{Error, Result, NameIOCTL};
use super::vm::VirtualMachine;
/// The normal way of defining a IOCTL interface is provided by C macros. In Rust we have our own
/// flawed macro system. The module below wraps a bunch of functions which are generated by the
/// ioctl! macro and need to be wrapped further to provide a safe interface.
pub mod ioctl {
use std::mem;
use uhyve::kvm_header::{KVMIO, kvm_msr_list, kvm_cpuid2_header, kvm_memory_region, kvm_dirty_log, kvm_memory_alias, kvm_userspace_memory_region, kvm_regs,kvm_sregs};
use hermit::uhyve::kvm_header::{KVMIO, kvm_msr_list, kvm_cpuid2_header, kvm_memory_region, kvm_dirty_log, kvm_memory_alias, kvm_userspace_memory_region, kvm_regs,kvm_sregs};
ioctl!(get_version with io!(KVMIO, 0x00));
ioctl!(create_vm with io!(KVMIO, 0x01));
@ -55,13 +56,13 @@ pub enum Version{
/// This is the entry point of our module, it connects to the KVM device and wraps the functions
/// which accept the global file descriptor.
pub struct Uhyve {
pub struct KVM {
file: File
}
impl Uhyve {
impl KVM {
// Connects to the KVM hypervisor, by opening the virtual device /dev/kvm
pub fn new() -> Uhyve {
pub fn new() -> KVM {
let kvm_file = OpenOptions::new()
.read(true)
@ -71,7 +72,7 @@ impl Uhyve {
debug!("UHYVE - The connection to KVM was established.");
Uhyve { file: kvm_file }
KVM { file: kvm_file }
}
// Acquires the KVM version to seperate ancient systems
@ -97,3 +98,46 @@ impl Uhyve {
}
}
pub struct Uhyve {
kvm: KVM,
vm: VirtualMachine
}
impl Uhyve {
pub fn new(path: &str) -> Result<Uhyve> {
let kvm = KVM::new();
let mut vm = kvm.create_vm(0x20000000)?;
vm.load_kernel(path)?;
vm.init()?;
Ok(Uhyve {
kvm: kvm,
vm: vm
})
}
}
impl Isle for Uhyve {
fn num(&self) -> u8 {
0
}
fn log_file(&self) -> Result<String> {
Err(Error::InternalError)
}
fn log_path(&self) -> Result<String> {
Err(Error::InternalError)
}
fn cpu_path(&self) -> Result<String> {
Err(Error::InternalError)
}
fn run(&mut self) -> Result<()> {
self.vm.run();
Ok(())
}
}

View file

@ -6,11 +6,11 @@ use std::os::unix::io::FromRawFd;
use memmap::{Mmap, Protection};
use errno::errno;
use uhyve;
use uhyve::kvm_header::{kvm_sregs, kvm_regs, kvm_segment, kvm_cpuid2,kvm_cpuid2_header};
use uhyve::{Result, Error, NameIOCTL};
use uhyve::gdt;
use uhyve::proto;
use hermit::uhyve;
use super::kvm_header::{kvm_sregs, kvm_regs, kvm_segment, kvm_cpuid2,kvm_cpuid2_header};
use super::{Result, Error, NameIOCTL};
use super::gdt;
use super::proto;
pub const GUEST_OFFSET: usize = 0x0;
pub const CPUID_FUNC_PERFMON: usize = 0x0A;

View file

@ -8,12 +8,13 @@ use std::io::{Read, Cursor};
use memmap::{Mmap, Protection};
use elf;
use elf::types::{ELFCLASS64, OSABI, PT_LOAD};
use std::ffi::CStr;
use utils;
use uhyve;
use uhyve::kvm_header::{kvm_userspace_memory_region };
use uhyve::{Result, Error, NameIOCTL};
use uhyve::vcpu::VirtualCPU;
use hermit::utils;
use hermit::uhyve;
use super::kvm_header::{kvm_userspace_memory_region };
use super::{Result, Error, NameIOCTL};
use super::vcpu::VirtualCPU;
//use byteorder::ByteOrder;
// guest offset?
@ -23,7 +24,8 @@ pub struct VirtualMachine {
kvm_fd: libc::c_int,
vm_fd: libc::c_int,
mem: Mmap,
elf_entry: Option<u64>,
elf_header: Option<elf::types::FileHeader>,
klog: Option<*const i8>,
vcpus: Vec<VirtualCPU>,
size: usize
}
@ -35,7 +37,7 @@ impl VirtualMachine {
// create a new memory region to map the memory of our guest
Mmap::anonymous(size, Protection::ReadWrite)
.map_err(|_| Error::NotEnoughMemory)
.map(|mem| VirtualMachine { kvm_fd: kvm_fd, vm_fd: fd, mem: mem, elf_entry: None, vcpus: Vec::new(), size: size })
.map(|mem| VirtualMachine { kvm_fd: kvm_fd, vm_fd: fd, mem: mem, elf_header: None, klog: None, vcpus: Vec::new(), size: size })
}
/// Loads a kernel from path and initialite mem and elf_entry
@ -56,7 +58,7 @@ impl VirtualMachine {
return Err(Error::InvalidFile(path.into()));
}
self.elf_entry = Some(file_efi.ehdr.entry);
self.elf_header = Some(file_efi.ehdr);
// acquire the slices of the user memory and kernel file
let vm_mem_length = self.mem.len() as u64;
@ -93,6 +95,8 @@ impl VirtualMachine {
*(ptr.offset(0x38) as *mut u64) = header.memsz; //
*(ptr.offset(0x60) as *mut u32) = 1; // NUMA nodes
*(ptr.offset(0x94) as *mut u32) = 1; // announce uhyve
self.klog = Some(vm_mem.as_ptr().offset(header.paddr as isize + 0x5000) as *const i8);
}
}
@ -132,13 +136,20 @@ impl VirtualMachine {
}
pub fn create_vcpu(&mut self, id: u8) -> Result<VirtualCPU> {
let entry = self.elf_entry.ok_or(Error::KernelNotLoaded)?;
let entry = self.elf_header.ok_or(Error::KernelNotLoaded)?.entry;
let cpu = VirtualCPU::new(self.kvm_fd, self.vm_fd, id, entry, &mut self.mem)?;
Ok(cpu)
}
pub fn output(&self) -> Result<String> {
let paddr = self.klog.ok_or(Error::KernelNotLoaded)?;
let c_str = unsafe { CStr::from_ptr(paddr).to_str().map_err(|_| Error::KernelNotLoaded)? };
Ok(c_str.into())
}
pub fn run(&mut self) -> Result<()> {
let mut guest_mem = unsafe { self.mem.as_mut_slice() };
@ -149,3 +160,11 @@ impl VirtualMachine {
Ok(())
}
}
impl Drop for VirtualMachine {
fn drop(&mut self) {
if let Ok(output) = self.output() {
debug!("{}", output);
}
}
}

View file

@ -3,7 +3,7 @@ use std::os::unix::io::FromRawFd;
use std::ffi::CString;
use std::io::Read;
use error::*;
use hermit::error::*;
use libc;

View file

@ -17,18 +17,11 @@ extern crate byteorder;
#[macro_use]
extern crate nix;
mod error;
mod utils;
mod hermit;
mod uhyve;
mod hermit_env;
mod qemu;
mod multi;
mod proto;
mod socket;
use nix::sys::signal;
use std::{env, process};
use hermit::error;
extern fn exit(_:i32) {
panic!("Aborting ..");
@ -46,9 +39,9 @@ fn main() {
// create the isle, wait to be available and start it
env::args().skip(1).next().ok_or(error::Error::MissingBinary)
.and_then(|path| hermit::IsleKind::new(&path))
.and_then(|path| hermit::new_isle(&path))
.and_then(|mut isle| {
isle.wait_available()?;
isle.wait_until_available()?;
isle.run()?;
Ok(())