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

The first isle is usable. This commit exists to check in a heavily WIP

version of the HermitCore proxy, rewritten in Rust. Any attempt to use
the current version in a serious manner should be avoided. The Multi and
UHyve isles aren't tested yet.
This commit is contained in:
l_schmid 2017-05-09 23:49:13 +02:00
parent 47f5261b94
commit 5226d4a19a
20 changed files with 7564 additions and 0 deletions

1
tools/hermit_proxy/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target

216
tools/hermit_proxy/Cargo.lock generated Normal file
View file

@ -0,0 +1,216 @@
[root]
name = "hermitcore_proxy"
version = "0.0.1"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"errno 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aho-corasick"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "elf"
version = "0.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "env_logger"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "errno"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fs2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "inotify"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memmap"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4841de15dbe0e49b9b62a417589299e3be0d557e0900d36acb87e6dae47197f5"
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
"checksum errno 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2c858c42ac0b88532f48fca88b0ed947cad4f1f64d904bcd6c9f138f7b95d70"
"checksum fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34edaee07555859dc13ca387e6ae05686bb4d0364c95d649b6dab959511f4baf"
"checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502"
"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad"
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
"checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b"
"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487"
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View file

@ -0,0 +1,15 @@
[package]
name = "hermitcore_proxy"
version = "0.0.1"
authors = ["bytesnake <bytesnake@mailbox.org>"]
[dependencies]
libc = "0.2.21"
nix = "0.8.0"
memmap = "0.5.2"
elf = "0.0.10"
errno = "0.2.3"
inotify = "0.3"
byteorder = "1"
log = "0.3"
env_logger = "0.3"

View file

@ -0,0 +1,4 @@
pub mod qemu;
pub mod multi;
pub mod proto;
pub mod socket;

View file

@ -0,0 +1,48 @@
use std::fs::File;
use std::env;
use std::io::{Write, Read};
use error::*;
use hermit_env;
use dedicate::socket::Socket;
pub struct Multi {
num: u8,
socket: Socket
}
impl Multi {
pub fn new(num: u8, path: &str) -> Result<Multi> {
// request a new isle, enforce close
{
let mut path_file = File::create(format!("/sys/hermit/isle{}/path", num)).map_err(|_| Error::InvalidFile)?;
let mut cpus_file = File::create(format!("/sys/hermit/isle{}/cpus", num)).map_err(|_| Error::InvalidFile)?;
let cpus = hermit_env::num_cpus();
path_file.write_all(path.as_bytes()).map_err(|_| Error::InvalidFile)?;
cpus_file.write_all(cpus.as_bytes()).map_err(|_| Error::InvalidFile)?;
}
// check the result
let mut path_file = File::create(format!("/sys/hermit/isle{}/cpus", num)).map_err(|_| Error::InvalidFile)?;
let mut result = String::new();
path_file.read_to_string(&mut result).map_err(|_| Error::InvalidFile)?;
if result.parse::<i32>().map_err(|_| Error::InvalidFile)? == -1 {
return Err(Error::MultiIsleFailed);
}
Ok(Multi { num: num, socket: Socket::new_multi(num) })
}
pub fn get_num(&self) -> u8 {
self.num
}
pub fn run(&self) {
self.socket.connect().run();
}
}

View file

@ -0,0 +1,139 @@
use std::io::{Read, Seek, Cursor};
use std::mem;
use byteorder::{ReadBytesExt, WriteBytesExt,LittleEndian,BigEndian};
use std::ffi::CString;
const PACKET_LENGTH: &'static [u64] = &[1,3,1,1,2,3];
#[derive(Debug)]
pub enum PartialPacket {
Exit { arg: i32 },
Write { fd: i32, len: u64 },
Open { len: i64 },
Close { fd: i32 },
Read { fd: i32, len: u64 },
LSeek { fd: i32, offset: i64, whence: i32 }
}
impl PartialPacket {
pub fn from_buf(id: i32, buf: &mut Cursor<Vec<u8>>) -> PartialPacket {
match id {
0 => PartialPacket::Exit {
arg: buf.read_i32::<LittleEndian>().unwrap()
},
1 => PartialPacket::Write {
fd: buf.read_i32::<LittleEndian>().unwrap(),
len: buf.read_u64::<LittleEndian>().unwrap()
},
2 => PartialPacket::Open {
len: buf.read_i64::<LittleEndian>().unwrap()
},
3 => PartialPacket::Close {
fd: buf.read_i32::<LittleEndian>().unwrap()
},
4 => PartialPacket::Read {
fd: buf.read_i32::<LittleEndian>().unwrap(),
len: buf.read_u64::<LittleEndian>().unwrap()
},
5 => PartialPacket::LSeek {
fd: buf.read_i32::<LittleEndian>().unwrap(),
offset: buf.read_i64::<LittleEndian>().unwrap(),
whence: buf.read_i32::<LittleEndian>().unwrap()
},
_ => panic!("")
}
}
pub fn additional_size(&self) -> usize {
match *self {
PartialPacket::Write { fd, len } => len as usize,
PartialPacket::Open { len } => len as usize + 8,
_ => 0
}
}
}
#[derive(Debug)]
pub enum Packet {
Exit { arg: i32 },
Write { fd: i32, buf: Vec<u8> },
Open { name: CString, flags: i32, mode: i32 },
Close { fd: i32 },
Read { fd: i32, len: u64 },
LSeek { fd: i32, whence: i32, offset: i64 }
}
impl Packet {
pub fn from_buf(obj: &PartialPacket, buf: &mut Cursor<Vec<u8>>) -> Packet {
match *obj {
PartialPacket::Write { fd, len } => {
debug!("Read write packet with length {}", len);
let mut content = vec![0; len as usize];
buf.read(&mut content);
Packet::Write { fd: fd, buf: content }
},
PartialPacket::Open { len } => {
let mut name_buf = vec![0; len as usize];
buf.read(&mut name_buf);
name_buf.pop();
let c_str = CString::new(name_buf).unwrap();
Packet::Open {
name: c_str,
flags: buf.read_i32::<LittleEndian>().unwrap(),
mode: buf.read_i32::<LittleEndian>().unwrap()
}
},
PartialPacket::Exit { arg } => Packet::Exit { arg },
PartialPacket::Close { fd } => Packet::Close { fd },
PartialPacket::Read { fd, len } => Packet::Read { fd, len },
PartialPacket::LSeek { fd, whence, offset } => Packet::LSeek { fd, whence, offset }
}
}
}
#[derive(Debug)]
pub enum State {
Id,
Type { id: i32, len: u64 },
Partial(PartialPacket),
Finished(Packet)
}
impl State {
pub fn read_in(self, buf: &mut Cursor<Vec<u8>>) -> State {
let length = buf.get_ref().len() - buf.position() as usize;
match self {
State::Id if length < mem::size_of::<isize>() => {
State::Id
},
State::Id => {
let id = buf.read_i32::<LittleEndian>().unwrap();
State::Type {
id: id,
len: match id as usize {
x @ 0...5 => PACKET_LENGTH[x],
_ => panic!("")
}
}
},
State::Type { id, len } => {
if length >= (len as usize) * mem::size_of::<i32>() {
let par_packet = State::Partial(PartialPacket::from_buf(id, buf));
debug!("Partial packet {:?} pos {}", par_packet, buf.position());
par_packet
} else {
self
}
},
State::Partial(ref packet) if length >= packet.additional_size() => {
State::Finished(Packet::from_buf(packet, buf))
},
_ => { self }
}
}
}

View file

@ -0,0 +1,134 @@
use std::env;
use utils;
use error::*;
use std::process::{Stdio, Child, Command};
use libc;
use std::fs::File;
use std::io::Read;
use hermit_env;
use dedicate::socket::Socket;
const PIDNAME: &'static str = "/tmp/hpid-XXXXXX";
const TMPNAME: &'static str = "/tmp/hermit-XXXXXX";
#[derive(Debug)]
pub struct QEmu {
socket: Socket,
child: Child,
tmp_file: String,
pid_file: String,
}
impl QEmu {
pub fn new(path: &str) -> Result<QEmu> {
let tmpf = utils::create_tmp_file(TMPNAME)?;
let pidf = utils::create_tmp_file(PIDNAME)?;
let mut child = QEmu::start_with(path, &tmpf, &pidf)?.spawn().expect("Couldn't find qemu binary!");
Ok(QEmu {
socket: Socket::new_qemu(),
child: child,
tmp_file: tmpf,
pid_file: pidf
})
}
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());
let chardev = format!("file,id=gnc0,path={}",&tmp_file);
let freq = format!("\"-freq{} -proxy\"",(utils::cpufreq().unwrap()/1000).to_string());
let cpus = hermit_env::num_cpus();
let mem_size = hermit_env::mem_size();
let exe = env::current_exe().unwrap();
let name = exe.to_str().unwrap();
let exe_path = name.split("/").take(name.matches('/').count()).collect::<Vec<&str>>().join("/");
let exe = format!("{}/ldhermit.elf", exe_path);
let mut port_str;
let mut args: Vec<&str> = vec![
"-daemonize",
"-display", "none",
"-smp", &cpus,
"-m", &mem_size,
"-pidfile", pid_file,
"-net", "nic,model=rtl8139",
"-net", &hostfwd,
"-chardev", &chardev,
"-device", "pci-serial,chardev=gnc0",
"-kernel", &exe,
"-initrd", path,
"-append", &freq];
let app_port = hermit_env::app_port();
if app_port != "" {
port_str = format!("tcp:{}::{}", app_port, app_port);
args.push("-redir");
args.push(&port_str);
}
if hermit_env::use_kvm() != "0" {
args.push("-machine");
args.push("accel=kvm");
args.push("-cpu");
args.push("host");
}
if hermit_env::monitor() != "0" {
args.push("-monitor");
args.push(&monitor_str);
}
if hermit_env::should_debug() != "0" {
args.push("-s");
}
if hermit_env::capture_net() != "0" {
args.push("-net");
args.push("dump");
}
if hermit_env::verbose() != "0" {
println!("{:#?}", args);
}
debug!("Execute {} with args {:#?}", hermit_env::qemu_binary(), args);
let mut cmd = Command::new(hermit_env::qemu_binary());
cmd.args(args).stdout(Stdio::piped()).stderr(Stdio::piped());
Ok(cmd)
}
}
impl Drop for QEmu {
fn drop(&mut self) {
let mut id_str = String::new();
let mut file = File::open(&self.pid_file).unwrap();
file.read_to_string(&mut id_str);
id_str.pop();
let id = id_str.parse::<i32>().unwrap();
if id >= 0 {
unsafe { libc::kill(id, libc::SIGINT); }
}
utils::delete_tmp_file(&self.pid_file);
utils::delete_tmp_file(&self.tmp_file);
}
}

View file

@ -0,0 +1,151 @@
use std::net::TcpStream;
use std::env;
use std::mem::transmute;
use std::io::{Write, Read, Cursor};
use std::ffi::CString;
use std::process;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use dedicate::proto;
use dedicate::proto::Packet;
use libc;
const HERMIT_MAGIC: u32 = 0x7E317;
#[derive(Debug)]
pub enum Socket {
QEmu,
Multi(u8),
Connected { stream: TcpStream }
}
impl Socket {
pub fn new_qemu() -> Socket {
Socket::QEmu
}
pub fn new_multi(id: u8) -> Socket {
Socket::Multi(id)
}
pub fn connect(&self) -> Socket {
let mut stream = match *self {
Socket::QEmu => TcpStream::connect(("127.0.0.1", 0x494E)).unwrap(),
Socket::Multi(id) => TcpStream::connect((format!("127.0.0.{}", id).as_ref(), 0x494E)).unwrap(),
_ => panic!("")
};
debug!("Connected to {}", stream.peer_addr().unwrap());
let length: usize = 4 + env::args().skip(1).map(|x| 4+x.len()).sum::<usize>()+ 4 + env::vars().map(|(x,y)| 5 + x.len()+ y.len()).sum::<usize>();
let mut buf = Cursor::new(vec![0u8;length]);
buf.write_u32::<LittleEndian>(HERMIT_MAGIC);
// send all arguments (skip first)
buf.write_u32::<LittleEndian>(env::args().count() as u32 - 1);
for key in env::args().skip(1) {
buf.write_u32::<LittleEndian>(key.len() as u32);
buf.write(key.as_bytes());
}
// send the environment
buf.write_u32::<LittleEndian>(env::vars().count() as u32);
for (val,key) in env::vars() {
let tmp = format!("{}={}", val, key);
buf.write_u32::<LittleEndian>(tmp.len() as u32);
buf.write(tmp.as_bytes());
}
stream.write(buf.get_ref());
debug!("Transmitted environment and arguments with length {}", length);
Socket::Connected { stream: stream }
}
pub fn run(&mut self) {
debug!("Initializing protocol state machine");
let mut state = proto::State::Id;
let mut stream = match self {
&mut Socket::Connected { ref mut stream } => stream,
_ => return
};
let mut cur = Cursor::new(vec![]);
let mut buf = [0u8; 4096];
'main: loop {
debug!("Attempt read");
let nread = stream.read(&mut buf).unwrap();
let old_position = cur.position();
let end = cur.get_ref().len() as u64;
cur.set_position(end);
cur.write(&buf[0..nread]);
cur.set_position(old_position);
debug!("Got message with {} bytes: {:?}", nread, &buf[0 .. nread]);
let mut old_position = cur.position();
loop {
state = state.read_in(&mut cur);
if let proto::State::Finished(packet) = state {
unsafe {
match packet {
Packet::Exit { arg } => break 'main,
Packet::Write { fd, buf } => {
let buf_ret: [u8;8] = transmute(libc::write(fd as i32, buf.as_ptr() as *const libc::c_void, buf.len()).to_le());
if fd > 2 {
stream.write(&buf_ret);
}
},
Packet::Open { name, mode, flags } => {
let buf: [u8; 4] = transmute(libc::open(name.as_ptr(), flags as i32, mode as i32).to_le());
debug!("got {:?}", buf);
let written = stream.write(&buf).unwrap();
//let written = stream.write(&[0,0,0,1]).unwrap();
debug!("Written {}", written);
},
Packet::Close { fd } => {
let buf: [u8; 4] = transmute(libc::close(fd as i32).to_le());
stream.write(&buf);
},
Packet::Read { fd, len } => {
let mut tmp: Vec<u8> = vec![0; len as usize];
let got = libc::read(fd as i32, tmp.as_mut_ptr() as *mut libc::c_void, len as usize);
let buf: [u8; 8] = transmute(got.to_le());
debug!("Read size {:?}", buf);
stream.write(&buf);
if got > 0 {
stream.write(&tmp[..]);
}
},
Packet::LSeek { fd, offset, whence } => {
let buf: [u8; 8] = transmute(libc::lseek(fd as i32, offset, whence as i32).to_le());
stream.write(&buf);
}
};
}
state = proto::State::Id;
}
if cur.position() == old_position {
break;
} else {
old_position = cur.position();
}
}
}
}
}

View file

@ -0,0 +1,18 @@
use std::result;
use nix;
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
InternalError,
NotEnoughMemory,
InvalidFile,
FailedIOCTL(nix::Error),
KernelNotLoaded,
MissingFrequency,
MultiIsleFailed,
CannotCreateTmpFile(usize),
QEmu((String, String))
}

View file

@ -0,0 +1,116 @@
use std::env;
use std::fs::File;
use std::path::Path;
use std::io::{Write, Read, BufReader, BufRead};
use inotify::INotify;
use inotify::ffi::{IN_MODIFY, IN_CREATE};
use hermit_env;
use dedicate::qemu::QEmu;
use dedicate::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(|_| Error::InvalidFile)?;
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)
//debug!("HERMIT - isle log contains: {}", result);
//Ok(result == "TCP server is listening.")
}
pub fn wait_available(&self) -> Result<()> {
debug!("HERMIT - wait to be available");
let mut ino = INotify::init().unwrap();
match *self {
IsleKind::QEMU(_) => ino.add_watch(Path::new("/tmp"), IN_MODIFY | IN_CREATE).unwrap(),
IsleKind::MULTI(_) => ino.add_watch(Path::new("/sys/hermit"), IN_MODIFY | IN_CREATE).unwrap(),
IsleKind::UHYVE(_) => return Ok(())
};
loop {
let events = ino.wait_for_events().unwrap();
if self.is_available()? {
return Ok(());
}
}
}
pub fn stop(&self) -> Result<()> {
let mut cpus_file = File::create(format!("/sys/hermit/isle{}/cpus", self.get_num()))
.map_err(|_| Error::InvalidFile)?;
cpus_file.write("-1".as_bytes())
.map_err(|_| Error::InvalidFile);
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,46 @@
use std::env;
pub fn isle_kind() -> String {
env::var("HERMIT_ISLE").unwrap_or("qemu".into())
}
pub fn qemu_binary() -> String {
env::var("HERMIT_QEMU").unwrap_or("qemu-system-x86_64".into())
}
pub fn num_cpus() -> String {
env::var("HERMIT_CPUS").unwrap_or("1".into())
}
pub fn mem_size() -> String {
env::var("HERMIT_MEM").unwrap_or("2G".into())
}
pub fn use_kvm() -> String {
env::var("HERMIT_KVM").unwrap_or("1".into())
}
pub fn monitor() -> String {
env::var("HERMIT_MONITOR").unwrap_or("0".into())
}
pub fn should_debug() -> String {
env::var("HERMIT_DEBUG").unwrap_or("0".into())
}
pub fn capture_net() -> String {
env::var("HERMIT_CAPTURE_NET").unwrap_or("0".into())
}
pub fn verbose() -> String {
env::var("HERMIT_VERBOSE").unwrap_or("0".into())
}
pub fn port() -> String {
env::var("HERMIT_PORT").unwrap_or("18766".into())
}
pub fn app_port() -> String {
env::var("HERMIT_APP_PORT").unwrap_or("".into())
}

View file

@ -0,0 +1,50 @@
#![feature(untagged_unions)]
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate libc;
extern crate memmap;
extern crate elf;
extern crate errno;
extern crate inotify;
extern crate byteorder;
#[macro_use]
extern crate nix;
mod error;
mod utils;
mod hermit;
mod uhyve;
mod dedicate;
mod hermit_env;
use nix::sys::signal;
use std::{env, process};
extern fn exit(_:i32) {
panic!("Aborting ..");
}
fn main() {
env_logger::init().unwrap();
let sig_action = signal::SigAction::new(signal::SigHandler::Handler(exit), signal::SaFlags::empty(), signal::SigSet::empty());
unsafe {
signal::sigaction(signal::SIGINT, &sig_action).unwrap();
signal::sigaction(signal::SIGTERM, &sig_action).unwrap();
}
if let Some(path) = env::args().skip(1).next() {
let isle = hermit::IsleKind::new(&path).unwrap();
isle.wait_available();
isle.run();
}
}

View file

@ -0,0 +1,137 @@
//! This module wraps the structure of the GDT and helps to extract certain information about the
//! GDT. The Global Descriptor Table contains information about the memory structure used by the
//! X86 family.
use uhyve::kvm_header::kvm_segment;
/// Used segments in order
pub const BOOT_NULL: isize = 0;
pub const BOOT_CODE: isize = 1;
pub const BOOT_DATA: isize = 2;
pub const BOOT_MAX: usize = 3;
/// This struct breaks the access part of a GDT entry down into functions
pub struct AccessBits {
access: u8
}
impl AccessBits {
pub fn present(&self) -> u8 {
(self.access & 0b10000000) >> 7
}
pub fn privilege(&self) -> u8 {
(self.access & 0b01100000) >> 5
}
pub fn s(&self) -> u8 {
(self.access & 0b00010000) >> 4
}
pub fn kind(&self) -> u8 {
(self.access & 0b00001111)
}
#[inline(always)]
pub fn apply_to_kvm(&self, seg: &mut kvm_segment) {
seg.present = self.present();
seg.dpl = self.privilege();
seg.s = self.s();
seg.type_ = self.kind();
}
}
pub struct FlagBits {
flags: u8
}
impl FlagBits {
pub fn granularity(&self) -> u8 {
(self.flags & 0b00001000) >> 3
}
pub fn size(&self) -> u8 {
(self.flags & 0b00000100) >> 2
}
pub fn desc_x86_64(&self) -> u8 {
(self.flags & 0b00000010) >> 1
}
pub fn sz_x86_64(&self) -> u8 {
(self.flags & 0b00000001)
}
#[inline(always)]
pub fn apply_to_kvm(&self, seg: &mut kvm_segment) {
seg.g = self.granularity();
seg.db = self.size();
seg.l = self.desc_x86_64();
seg.avl = self.sz_x86_64();
}
}
/// This struct defines the arrangment one GDT entry in memory
pub struct Entry {
pub limit_l: u16,
pub offset_l: u16,
pub offset_m: u8,
pub access: u8,
pub flags_limit_h: u8,
pub offset_h: u8
}
impl Entry {
pub fn new(flags: u16, offset: u32, limit: u32) -> Entry {
Entry {
limit_l: limit as u16,
offset_l: offset as u16,
offset_m: (offset >> 16) as u8,
access: flags as u8,
flags_limit_h: (limit >> 16) as u8 & 0x0F | (flags >> 8) as u8 & 0xF0,
offset_h: (offset >> 24) as u8
}
}
// returns the maximum addressable unit
pub fn get_limit(&self) -> u32 {
((self.flags_limit_h & 0x0F) as u32) << 16 | (self.limit_l as u32)
}
// returns the offset of this segment
pub fn get_offset(&self) -> u32 {
(self.offset_h as u32) << 24 | (self.offset_m as u32) << 16 | (self.offset_l as u32)
}
// returns the access bits
pub fn get_access(&self) -> AccessBits {
AccessBits { access: self.access }
}
// return the flags bits
pub fn get_flags(&self) -> FlagBits {
FlagBits { flags: self.flags_limit_h & 0x0F }
}
// convert the struct to an unsigned integer
pub fn as_u64(&self) -> u64 {
(self.offset_h as u64) << 56 |
(self.flags_limit_h as u64) << 48 |
(self.access as u64) << 40 |
(self.offset_m as u64) << 32 |
(self.offset_l as u64) << 16 |
(self.limit_l as u64) << 0
}
#[inline(always)]
pub fn apply_to_kvm(&self, sel: isize, seg: &mut kvm_segment) {
seg.base = self.get_offset() as u64;
seg.limit = self.get_limit();
seg.selector = sel as u16 * 8;
self.get_access().apply_to_kvm(seg);
self.get_flags().apply_to_kvm(seg);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
//gdt.rs kvm_header.rs kvm.rs mod.rs vcpu.rs vm.rs
mod gdt;
mod kvm_header;
pub mod uhyve;
pub mod vcpu;
pub mod vm;
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::*;

View file

@ -0,0 +1,111 @@
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 };
const PORT_WRITE: u16 = 0x499;
const PORT_OPEN: u16 = 0x500;
const PORT_CLOSE: u16 = 0x501;
const PORT_READ: u16 = 0x502;
const PORT_EXIT: u16 = 0x503;
const PORT_LSEEK: u16 = 0x504;
pub struct Write {
fd: c_int,
buf: *const u8,
length: usize
}
pub struct Open {
name: *const u8,
flags: c_int,
mode: c_int,
ret: c_int
}
pub struct Close {
fd: c_int,
ret: c_int
}
pub struct Read {
fd: c_int,
buf: *mut u8,
len: usize,
ret: isize
}
pub struct LSeek {
fd: c_int,
offset: usize,
whence: c_int
}
pub enum Syscall {
Write(*mut Write),
Open(*mut Open),
Close(*mut Close),
Read(*mut Read),
LSeek(*mut LSeek),
Exit(*mut isize),
Other(*const kvm_run)
}
impl Syscall {
pub fn from_mem(mem: &[u8], guest_mem: &[u8]) -> Syscall {
unsafe {
let ref run = *(mem.as_ptr() as *const kvm_run);
if run.exit_reason != KVM_EXIT_IO {
return Syscall::Other(mem.as_ptr() as *const kvm_run);
}
let offset = mem.as_ptr().offset(run.__bindgen_anon_1.io.data_offset as isize) as isize;
match run.__bindgen_anon_1.io.port {
PORT_WRITE => { Syscall::Write(guest_mem.as_ptr().offset(offset) as *mut Write) },
PORT_READ => { Syscall::Read (guest_mem.as_ptr().offset(offset) as *mut Read) },
PORT_CLOSE => { Syscall::Close(guest_mem.as_ptr().offset(offset) as *mut Close) },
PORT_OPEN => { Syscall::Open (guest_mem.as_ptr().offset(offset) as *mut Open ) },
PORT_LSEEK => { Syscall::LSeek(guest_mem.as_ptr().offset(offset) as *mut LSeek) },
PORT_EXIT => { Syscall::Exit( guest_mem.as_ptr().offset(offset) as *mut isize) },
_ => { panic!("KVM: unhandled KVM_EXIT_IO at port 0x{}, direction {}", run.__bindgen_anon_1.io.port, run.__bindgen_anon_1.io.direction); }
}
}
}
pub unsafe fn run(&self, guest_mem: *mut u8) {
match *self {
Syscall::Write(obj) => {
//let Write { fd, buf, length } = *write;
(*obj).length = write((*obj).fd, guest_mem.offset((*obj).buf as isize) as *const c_void, (*obj).length) as usize;
},
Syscall::Read(obj) => {
(*obj).ret = read((*obj).fd, guest_mem.offset((*obj).buf as isize) as *mut c_void, (*obj).len) as isize;
},
Syscall::Exit(obj) => {
exit(*((guest_mem as *mut i32).offset((*obj) as isize)));
},
Syscall::Open(obj) => {
(*obj).ret = open((guest_mem as *const i8).offset((*obj).name as isize), (*obj).flags, (*obj).mode);
},
Syscall::Close(obj) => {
if (*obj).ret == 2 {
(*obj).ret = close((*obj).fd);
}
},
Syscall::LSeek(obj) => {
(*obj).offset = lseek((*obj).fd, (*obj).offset as i64, (*obj).whence) as usize;
},
Syscall::Other(id) => { match (*id).exit_reason {
KVM_EXIT_HLT => panic!("Guest has halted the CPU, this is considered as a normal exit."),
KVM_EXIT_MMIO =>panic!("KVM: unhandled KVM_EXIT_MMIO at 0x{}", (*id).__bindgen_anon_1.mmio.phys_addr ),
KVM_EXIT_FAIL_ENTRY => panic!("KVM: entry failure: hw_entry_failure_reason=0x{}", (*id).__bindgen_anon_1.fail_entry.hardware_entry_failure_reason),
KVM_EXIT_INTERNAL_ERROR => panic!("KVM: internal error exit: suberror = 0x{}", (*id).__bindgen_anon_1.internal.suberror),
KVM_EXIT_SHUTDOWN => panic!("KVM: receive shutdown command"),
_ => {
panic!("KVM: unhandled exit: exit_reason = 0x{}", (*id).exit_reason);
}
}}
}
}
}

View file

@ -0,0 +1,98 @@
//! This file contains the entry point to the Unikernel Hypervisor. The uhyve utilizes KVM to
//! create a Virtual Machine and load the kernel.
use std::ptr;
use std::fs::{File,OpenOptions};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use libc;
use uhyve::{Error, Result};
use uhyve::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, 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));
ioctl!(get_msr_index_list with iorw!(KVMIO, 0x02, mem::size_of::<kvm_msr_list>()));
ioctl!(check_extension with io!(KVMIO, 0x03));
ioctl!(get_vcpu_mmap_size with io!(KVMIO, 0x04));
ioctl!(get_supported_cpuid with iorw!(KVMIO, 0x05, mem::size_of::<kvm_cpuid2>()));
ioctl!(get_emulated_cpuid with iorw!(KVMIO, 0x09, mem::size_of::<kvm_cpuid2>()));
ioctl!(set_cpuid2 with iow!(KVMIO, 0x90, mem::size_of::<kvm_cpuid2>()));
ioctl!(set_memory_region with iorw!(KVMIO, 0x40,mem::size_of::<kvm_memory_region>()));
ioctl!(create_vcpu with io!(KVMIO, 0x41));
ioctl!(get_dirty_log with iow!(KVMIO, 0x42, mem::size_of::<kvm_dirty_log>()));
ioctl!(set_memory_alias with iow!(KVMIO, 0x43, mem::size_of::<kvm_memory_alias>()));
ioctl!(set_nr_mmu_pages with io!(KVMIO, 0x44));
ioctl!(get_nr_mmu_pages with io!(KVMIO, 0x45));
ioctl!(set_user_memory_region with iow!(KVMIO, 0x46, mem::size_of::<kvm_userspace_memory_region>()));
ioctl!(create_irqchip with io!(KVMIO, 0x60));
ioctl!(run with io!(KVMIO, 0x80));
ioctl!(get_regs with ior!(KVMIO, 0x81, mem::size_of::<kvm_regs>()));
ioctl!(set_regs with iow!(KVMIO, 0x82, mem::size_of::<kvm_regs>()));
ioctl!(get_sregs with ior!(KVMIO, 0x83, mem::size_of::<kvm_sregs>()));
ioctl!(set_sregs with iow!(KVMIO, 0x84, mem::size_of::<kvm_sregs>()));
}
/// KVM is freezed at version 12, so all others are invalid
#[derive(Debug)]
pub enum Version{
Version12,
Unsupported
}
/// 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 {
file: File
}
impl Uhyve {
// Connects to the KVM hypervisor, by opening the virtual device /dev/kvm
pub fn new() -> Uhyve {
let kvm_file = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_CLOEXEC)
.open("/dev/kvm").unwrap();
debug!("UHYVE - The connection to KVM was established.");
Uhyve { file: kvm_file }
}
// Acquires the KVM version to seperate ancient systems
pub fn version(&self) -> Result<Version> {
unsafe {
match ioctl::get_version(self.file.as_raw_fd(), ptr::null_mut()) {
Ok(12) => Ok(Version::Version12),
Ok(_) => Ok(Version::Unsupported),
Err(_) => Err(Error::InternalError)
}
}
}
// Creates a new virtual machine and forwards the new fd to an object
pub fn create_vm(&self, size: usize) -> Result<VirtualMachine> {
unsafe {
match ioctl::create_vm(self.file.as_raw_fd(), ptr::null_mut()) {
Ok(vm_fd) => VirtualMachine::new(self.file.as_raw_fd(), vm_fd, size),
Err(_) => Err(Error::InternalError)
}
}
}
}

View file

@ -0,0 +1,288 @@
use libc;
use libc::{c_void, c_int};
use std::{mem, ptr};
use std::fs::File;
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};
use uhyve::{Result, Error};
use uhyve::gdt;
use uhyve::proto;
pub const GUEST_OFFSET: usize = 0x0;
pub const CPUID_FUNC_PERFMON: usize = 0x0A;
pub const GUEST_PAGE_SIZE: usize = 0x200000;
// TODO configuration missing
pub const GUEST_SIZE: usize = 0x20000000;
pub const BOOT_GDT: usize = 0x1000;
pub const BOOT_INFO: usize = 0x2000;
pub const BOOT_PML4: usize = 0x10000;
pub const BOOT_PDPTE:usize = 0x11000;
pub const BOOT_PDE: usize = 0x12000;
/// Basic CPU control in CR0
pub const X86_CR0_PE: u64 = (1 << 0);
pub const X86_CR0_PG: u64 = (1 << 31);
/// Intel long mode page directory/table entries
pub const X86_CR4_PAE: u64 = (1 << 0);
/// Intel long mode page directory/table entries
pub const X86_PDPT_P: u64 = (1 << 0);
pub const X86_PDPT_RW: u64 = (1 << 1);
pub const X86_PDPT_PS: u64 = (1 << 7);
pub struct VirtualCPU {
vm_fd: libc::c_int,
pub vcpu_fd: libc::c_int,
id: u8,
run_mem: Mmap
}
impl VirtualCPU {
pub fn new(kvm_fd: libc::c_int, vm_fd: libc::c_int, id: u8, entry: u64, mem: &mut Mmap) -> Result<VirtualCPU> {
debug!("UHYVE - New virtual CPU with id {}", id);
// create a new VCPU and save the file descriptor
let fd = VirtualCPU::create_vcpu(vm_fd)?;
debug!("Got fd {}", fd);
let run_mem = unsafe {
Mmap::open_with_offset(&File::from_raw_fd(fd), Protection::ReadWrite, 0, VirtualCPU::get_mmap_size(kvm_fd)?)
.map_err(|x| panic!("{:?}", x) )//Error::InvalidFile)
}?;
let cpu = VirtualCPU {
vm_fd: vm_fd, vcpu_fd: fd, id: id, run_mem: run_mem
};
if id == 0 {
debug!("Setup the first processor");
let mut mem = unsafe {
mem.as_mut_slice()
};
cpu.setup_first(&mut mem)?;
}
debug!("Initialize the register of {}", id);
let mut regs = kvm_regs::empty();
regs.rip = entry;
regs.rflags = 0x2;
cpu.set_regs(regs)?;
cpu.setup_cpuid()?;
Ok(cpu)
}
pub fn create_vcpu(fd: c_int) -> Result<c_int> {
unsafe {
uhyve::ioctl::create_vcpu(fd, ptr::null_mut())
.map_err(|x| Error::FailedIOCTL(x))
}
}
pub fn get_sregs(&self) -> Result<kvm_sregs> {
let mut sregs = kvm_sregs::empty();
unsafe {
uhyve::ioctl::get_sregs(self.vcpu_fd, (&mut sregs) as *mut kvm_sregs as *mut u8)
.map_err(|x| Error::FailedIOCTL(x))?;
}
Ok(sregs)
}
pub fn set_sregs(&self, mut sregs: kvm_sregs) -> Result<()> {
unsafe {
uhyve::ioctl::set_sregs(self.vcpu_fd, (&mut sregs) as *mut kvm_sregs as *mut u8)
.map_err(|x| Error::FailedIOCTL(x))?;
}
Ok(())
}
pub fn set_regs(&self, mut regs: kvm_regs) -> Result<()> {
unsafe {
uhyve::ioctl::set_regs(self.vcpu_fd, (&mut regs) as *mut kvm_regs as *mut u8)
.map_err(|x| Error::FailedIOCTL(x))?;
}
Ok(())
}
pub fn get_supported_cpuid(&self) -> Result<kvm_cpuid2> {
let mut cpuid = kvm_cpuid2::empty();
unsafe {
uhyve::ioctl::get_supported_cpuid(self.vm_fd, (&mut cpuid) as *mut kvm_cpuid2 as *mut u8)
.map_err(|x| Error::FailedIOCTL(x))?;
}
Ok(cpuid)
}
pub fn set_cpuid2(&self, mut cpuid: kvm_cpuid2) -> Result<()> {
unsafe {
uhyve::ioctl::set_cpuid2(self.vm_fd, (&mut cpuid) as *mut kvm_cpuid2 as *mut u8)
.map_err(|x| Error::FailedIOCTL(x))?;
}
Ok(())
}
pub fn get_mmap_size(vcpu_fd: libc::c_int) -> Result<usize> {
unsafe {
uhyve::ioctl::get_vcpu_mmap_size(vcpu_fd, ptr::null_mut())
.map_err(|x| Error::FailedIOCTL(x)).map(|x| { debug!("MMAP size {}", x); x as usize })
}
}
pub fn single_run(&self, guest_mem: &mut [u8]) -> Result<i32> {
let ret = unsafe {
uhyve::ioctl::run(self.vcpu_fd, ptr::null_mut())
.map_err(|x| Error::FailedIOCTL(x))
}?;
if ret == -1 {
match errno().0 {
libc::EINTR => { return Ok(ret); },
libc::EFAULT => {
// TODO
panic!("translation fault!");
},
_ => {
panic!("KVM: ioctl KVM_RUN in vcpu_loop failed");
}
}
}
unsafe {
proto::Syscall::from_mem(self.run_mem.as_slice(), guest_mem).run(guest_mem.as_mut_ptr());
}
Ok(ret)
}
pub fn run(&self, guest_mem: &mut [u8]) -> Result<u32> {
loop {
self.single_run(guest_mem)?;
}
}
pub fn setup_first(&self, mem: &mut [u8]) -> Result<()> {
// This is a fatal fault, so we will abort here
if self.id != 0 {
panic!("Shouldn't be called by any other processor than the first!");
}
let mut sregs = self.get_sregs()?;
debug!("Setup GDT");
self.setup_system_gdt(&mut sregs, mem, 0)?;
debug!("Setup the page table");
self.setup_system_page_table(&mut sregs, mem)?;
debug!("Set the system to 64bit");
self.setup_system_64bit(&mut sregs)?;
self.set_sregs(sregs)?;
Ok(())
}
pub fn setup_system_gdt(&self, sregs: &mut kvm_sregs, mem: &mut [u8], offset: u64) -> Result<()> {
let (mut data_seg, mut code_seg) = (kvm_segment::empty(), kvm_segment::empty());
// create the GDT entries
let gdt_null = gdt::Entry::new(0, 0, 0);
let gdt_code = gdt::Entry::new(0xA09B, 0, 0xFFFFF);
let gdt_data = gdt::Entry::new(0xC093, 0, 0xFFFFF);
// apply the new GDTs to our guest memory
let ptr = mem[offset as usize..].as_ptr() as *mut u64;
unsafe {
*(ptr.offset(gdt::BOOT_NULL)) = gdt_null.as_u64();
*(ptr.offset(gdt::BOOT_CODE)) = gdt_code.as_u64();
*(ptr.offset(gdt::BOOT_DATA)) = gdt_code.as_u64();
}
gdt_code.apply_to_kvm(gdt::BOOT_CODE, &mut code_seg);
gdt_data.apply_to_kvm(gdt::BOOT_DATA, &mut data_seg);
sregs.gdt.base = offset;
sregs.gdt.limit = ((mem::size_of::<u64>() * gdt::BOOT_MAX) - 1) as u16;
sregs.cs = code_seg;
sregs.ds = data_seg;
sregs.es = data_seg;
sregs.fs = data_seg;
sregs.gs = data_seg;
sregs.ss = data_seg;
Ok(())
}
pub fn setup_system_page_table(&self, sregs: &mut kvm_sregs, mem: &mut [u8]) -> Result<()> {
let pml4 = mem[BOOT_PML4..].as_ptr() as *mut u64;
let pdpte = mem[BOOT_PDPTE..].as_ptr() as *mut u64;
let pde = mem[BOOT_PDE..].as_ptr() as *mut u64;
// TODO
//assert!((guest_size & (GUEST_PAGE_SIZE - 1)) == 0);
//assert!(guest_size <= (GUEST_PAGE_SIZE * 512));
unsafe {
libc::memset(pml4 as *mut c_void, 0x00, 4096);
libc::memset(pdpte as *mut c_void, 0x00, 4096);
libc::memset(pde as *mut c_void, 0x00, 4096);
*pml4 = (BOOT_PDPTE as u64) | (X86_PDPT_P | X86_PDPT_RW);
*pdpte = (BOOT_PDE as u64) | (X86_PDPT_P | X86_PDPT_RW);
for i in 0..(GUEST_SIZE/GUEST_PAGE_SIZE) {
*(pde.offset(i as isize)) = (i*GUEST_PAGE_SIZE) as u64 | (X86_PDPT_P | X86_PDPT_RW | X86_PDPT_PS);
}
}
sregs.cr3 = BOOT_PML4 as u64;
sregs.cr4 |= X86_CR4_PAE;
sregs.cr0 |= X86_CR0_PG;
Ok(())
}
pub fn setup_system_64bit(&self, sregs: &mut kvm_sregs) -> Result<()> {
sregs.cr0 |= X86_CR0_PE;
sregs.efer |= 1 << 8;
Ok(())
}
pub fn setup_cpuid(&self) -> Result<()> {
let mut kvm_cpuid = self.get_supported_cpuid()?;
for entry in kvm_cpuid.entries.iter_mut() {
match entry.function {
1 => {
entry.ecx |= 1u32 << 31; // propagate that we are running on a hypervisor
entry.edx |= 1u32 << 5; // enable msr support
},
0x0A => {
// disable it
entry.eax = 0x00;
},
_ => {}
}
}
self.set_cpuid2(kvm_cpuid)?;
Ok(())
}
}

View file

@ -0,0 +1,148 @@
//! By calling create_vm KVM returns a fd, this file wraps all relevant functions belonging to the
//! VM layer
use libc;
use std::ptr;
use std::fs::File;
use std::io::{Read, Cursor};
use memmap::{Mmap, Protection};
use elf;
use elf::types::{ELFCLASS64, OSABI, PT_LOAD};
use utils;
use uhyve;
use uhyve::kvm_header::{kvm_userspace_memory_region };
use uhyve::{Result, Error};
use uhyve::vcpu::VirtualCPU;
//use byteorder::ByteOrder;
// guest offset?
//pub const GUEST_OFFSET = 0;
pub struct VirtualMachine {
kvm_fd: libc::c_int,
vm_fd: libc::c_int,
mem: Mmap,
elf_entry: Option<u64>,
vcpus: Vec<VirtualCPU>,
size: usize
}
impl VirtualMachine {
pub fn new(kvm_fd: libc::c_int, fd: libc::c_int, size: usize) -> Result<VirtualMachine> {
debug!("UHYVE - New virtual machine with memory size {}", size);
// 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 })
}
/// Loads a kernel from path and initialite mem and elf_entry
pub fn load_kernel(&mut self, path: &str) -> Result<()> {
debug!("UHYVE - Load kernel from {}", path);
// open the file in read only
let file = Mmap::open_path(path, Protection::Read).map_err(|_| Error::InvalidFile)?;
// parse the header with ELF module
let file_efi = {
let mut data = unsafe { Cursor::new(file.as_slice()) };
elf::File::open_stream(&mut data).map_err(|_| Error::InvalidFile)
}?;
if file_efi.ehdr.class != ELFCLASS64 || file_efi.ehdr.osabi != OSABI(0x42) {
return Err(Error::InvalidFile);
}
self.elf_entry = Some(file_efi.ehdr.entry);
// acquire the slices of the user memory and kernel file
let vm_mem_length = self.mem.len() as u64;
let vm_mem = unsafe { self.mem.as_mut_slice() };
let kernel_file = unsafe { file.as_slice() };
for header in file_efi.phdrs {
if header.progtype != PT_LOAD {
continue;
}
let vm_start = header.paddr as usize;
let vm_end = vm_start + header.filesz as usize;
let kernel_start = header.offset as usize;
let kernel_end = kernel_start + header.filesz as usize;
debug!("Load segment with start addr {} and size {} to {}", header.paddr, header.filesz, header.offset);
vm_mem[vm_start..vm_end].copy_from_slice(&kernel_file[kernel_start..kernel_end]);
let ptr = vm_mem[vm_start..vm_end].as_mut_ptr();
unsafe {
*(ptr.offset(0x08) as *mut u64) = header.paddr; // physical start addr
*(ptr.offset(0x10) as *mut u64) = vm_mem_length; // physical size limit
*(ptr.offset(0x18) as *mut u32) = utils::cpufreq()?; // CPU frequency
*(ptr.offset(0x24) as *mut u32) = 1; // number of used CPUs
*(ptr.offset(0x30) as *mut u32) = 0; // apicid (?)
*(ptr.offset(0x38) as *mut u64) = header.filesz; //
*(ptr.offset(0x60) as *mut u32) = 1; // NUMA nodes
*(ptr.offset(0x94) as *mut u32) = 1; // announce uhyve
}
}
debug!("Kernel loaded");
Ok(())
}
/// Initialize the virtual machine
pub fn init(&mut self) -> Result<()> {
let start_ptr = unsafe { self.mem.as_slice().as_ptr() as u64 };
let kvm_region = kvm_userspace_memory_region {
slot: 0, guest_phys_addr: 0, flags: 0, memory_size: self.mem.len() as u64, userspace_addr: start_ptr
};
self.set_user_memory_region(kvm_region)?;
self.create_irqchip()?;
let vcpu = self.create_vcpu(0)?;
self.vcpus.push(vcpu);
Ok(())
}
pub fn set_user_memory_region(&self, mut region: kvm_userspace_memory_region) -> Result<()> {
unsafe {
let p: *mut kvm_userspace_memory_region = &mut region;
uhyve::ioctl::set_user_memory_region(self.vm_fd, p as *mut u8)
.map_err(|x| Error::FailedIOCTL(x)).map(|_| ())
}
}
pub fn create_irqchip(&self) -> Result<()> {
unsafe {
uhyve::ioctl::create_irqchip(self.vm_fd, ptr::null_mut())
.map_err(|x| Error::FailedIOCTL(x)).map(|_| ())
}
}
pub fn create_vcpu(&mut self, id: u8) -> Result<VirtualCPU> {
let entry = self.elf_entry.ok_or(Error::KernelNotLoaded)?;
let cpu = VirtualCPU::new(self.kvm_fd, self.vm_fd, id, entry, &mut self.mem)?;
Ok(cpu)
}
pub fn run(&mut self) -> Result<()> {
let mut guest_mem = unsafe { self.mem.as_mut_slice() };
for vcpu in &self.vcpus {
vcpu.run(&mut guest_mem)?;
}
Ok(())
}
}

View file

@ -0,0 +1,65 @@
use std::fs::File;
use std::os::unix::io::FromRawFd;
use std::ffi::CString;
use std::io::Read;
use error::*;
use libc;
/// Returns the cpu frequency
pub fn cpufreq() -> Result<u32> {
let mut content = String::new();
// If the file cpuinfo_max_freq exists, parse the content and return the frequency
if let Ok(mut file) = File::open("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq") {
file.read_to_string(&mut content).unwrap();
return content.trim().parse::<u32>().map_err(|_| Error::MissingFrequency);
}
// otherwise use the more acurate cpuinfo file and search for the right line
else if let Ok(mut file) = File::open("/proc/cpuinfo") {
file.read_to_string(&mut content).expect("Couldnt read!");
for line in content.lines() {
if line.starts_with("cpu MHz") {
return line.split(':').skip(1).next().unwrap().trim().parse::<f32>().map_err(|_| Error::MissingFrequency).map(|x| x as u32);
}
}
}
// ups shouldn't happened ..
Err(Error::MissingFrequency)
}
/// Creates a temporary file /tmp/<name>-XXXX, the X will be replaced by an arbitrary sequence
pub fn create_tmp_file(name: &str) -> Result<String> {
unsafe {
let c_str = CString::new(name).unwrap();
let raw = c_str.into_raw();
let res = libc::mkstemp(raw);
if res < 0 {
return Err(Error::CannotCreateTmpFile(res as usize));
}
let new_name = CString::from_raw(raw).into_string().unwrap();
debug!("UTILS - Created tmp file with name {}", new_name);
Ok(new_name)
}
}
/// Deletes a temporary file
pub fn delete_tmp_file(name: &str) -> Result<()> {
unsafe {
let c_str = CString::new(name).unwrap();
let res = libc::unlink(c_str.into_raw());
if res < 0 {
return Err(Error::InvalidFile);
} else {
return Ok(());
}
}
}