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:
parent
47f5261b94
commit
5226d4a19a
20 changed files with 7564 additions and 0 deletions
1
tools/hermit_proxy/.gitignore
vendored
Normal file
1
tools/hermit_proxy/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
target
|
216
tools/hermit_proxy/Cargo.lock
generated
Normal file
216
tools/hermit_proxy/Cargo.lock
generated
Normal 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"
|
15
tools/hermit_proxy/Cargo.toml
Normal file
15
tools/hermit_proxy/Cargo.toml
Normal 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"
|
4
tools/hermit_proxy/src/dedicate/mod.rs
Normal file
4
tools/hermit_proxy/src/dedicate/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod qemu;
|
||||
pub mod multi;
|
||||
pub mod proto;
|
||||
pub mod socket;
|
48
tools/hermit_proxy/src/dedicate/multi.rs
Normal file
48
tools/hermit_proxy/src/dedicate/multi.rs
Normal 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();
|
||||
}
|
||||
}
|
139
tools/hermit_proxy/src/dedicate/proto.rs
Normal file
139
tools/hermit_proxy/src/dedicate/proto.rs
Normal 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 }
|
||||
}
|
||||
}
|
||||
}
|
134
tools/hermit_proxy/src/dedicate/qemu.rs
Normal file
134
tools/hermit_proxy/src/dedicate/qemu.rs
Normal 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);
|
||||
}
|
||||
}
|
151
tools/hermit_proxy/src/dedicate/socket.rs
Normal file
151
tools/hermit_proxy/src/dedicate/socket.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
tools/hermit_proxy/src/error.rs
Normal file
18
tools/hermit_proxy/src/error.rs
Normal 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))
|
||||
|
||||
}
|
116
tools/hermit_proxy/src/hermit.rs
Normal file
116
tools/hermit_proxy/src/hermit.rs
Normal 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(())
|
||||
}
|
||||
}
|
46
tools/hermit_proxy/src/hermit_env.rs
Normal file
46
tools/hermit_proxy/src/hermit_env.rs
Normal 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())
|
||||
}
|
||||
|
50
tools/hermit_proxy/src/main.rs
Normal file
50
tools/hermit_proxy/src/main.rs
Normal 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();
|
||||
}
|
||||
}
|
137
tools/hermit_proxy/src/uhyve/gdt.rs
Normal file
137
tools/hermit_proxy/src/uhyve/gdt.rs
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
5767
tools/hermit_proxy/src/uhyve/kvm_header.rs
Normal file
5767
tools/hermit_proxy/src/uhyve/kvm_header.rs
Normal file
File diff suppressed because it is too large
Load diff
12
tools/hermit_proxy/src/uhyve/mod.rs
Normal file
12
tools/hermit_proxy/src/uhyve/mod.rs
Normal 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::*;
|
111
tools/hermit_proxy/src/uhyve/proto.rs
Normal file
111
tools/hermit_proxy/src/uhyve/proto.rs
Normal 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);
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
98
tools/hermit_proxy/src/uhyve/uhyve.rs
Normal file
98
tools/hermit_proxy/src/uhyve/uhyve.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
288
tools/hermit_proxy/src/uhyve/vcpu.rs
Normal file
288
tools/hermit_proxy/src/uhyve/vcpu.rs
Normal 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(())
|
||||
}
|
||||
}
|
148
tools/hermit_proxy/src/uhyve/vm.rs
Normal file
148
tools/hermit_proxy/src/uhyve/vm.rs
Normal 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(())
|
||||
}
|
||||
}
|
65
tools/hermit_proxy/src/utils.rs
Normal file
65
tools/hermit_proxy/src/utils.rs
Normal 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(());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue