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

Support memory and ncores conf, Extend MP support

This commit is contained in:
bytesnake 2017-06-09 13:36:10 +02:00
parent 4521e6ae07
commit e7c08c271c
8 changed files with 123 additions and 72 deletions

View file

@ -14,7 +14,9 @@ pub enum Error {
MultiIsleFailed,
CannotCreateTmpFile(usize),
QEmu((String, String)),
MissingBinary
MissingBinary,
Protocol(String),
ParseMemory
}
impl fmt::Debug for Error {
@ -33,7 +35,9 @@ impl fmt::Debug for Error {
Error::MultiIsleFailed => write!(f, "The Multi isle was selected on a system without supported, please load the kernel driver."),
Error::CannotCreateTmpFile(_) => write!(f, "Couldn't create a tmp file in /tmp."),
Error::QEmu((_, ref stderr)) => write!(f, "The qemu binary has encountered an error: {}", stderr),
Error::MissingBinary => write!(f, "Please specify a binary.")
Error::MissingBinary => write!(f, "Please specify a binary."),
Error::Protocol(ref err) => write!(f, "{}", err),
Error::ParseMemory => write!(f, "Couldn't parse the guest memory size from the environment")
}
}
}

View file

@ -1,4 +1,5 @@
use std::env;
use super::utils;
pub fn isle_kind() -> String {
env::var("HERMIT_ISLE").unwrap_or("qemu".into())
@ -12,10 +13,18 @@ pub fn num_cpus() -> String {
env::var("HERMIT_CPUS").unwrap_or("1".into())
}
pub fn num_cpus_parsed() -> u32 {
env::var("HERMIT_CPUS").map(|x| x.parse().unwrap_or(1)).unwrap_or(1)
}
pub fn mem_size() -> String {
env::var("HERMIT_MEM").unwrap_or("2G".into())
}
pub fn mem_size_parsed() -> usize {
env::var("HERMIT_MEM").map(|x| utils::parse_mem(&x).unwrap_or(2*1000*1000)).unwrap_or(2*1000*1000)
}
pub fn use_kvm() -> String {
env::var("HERMIT_KVM").unwrap_or("1".into())
}

View file

@ -73,6 +73,7 @@ pub trait Isle {
ino.add_watch(Path::new(&log_path), watch_mask::MODIFY | watch_mask::CREATE).unwrap();
let mut buffer = [0; 1024];
loop {
if let Some(_) = ino.read_events(&mut buffer).unwrap().next() {

View file

@ -2,6 +2,7 @@ use libc::{write, read, lseek, exit, open, close, c_int, c_void};
use super::kvm_header::{kvm_run, KVM_EXIT_IO, KVM_EXIT_HLT, KVM_EXIT_MMIO,KVM_EXIT_FAIL_ENTRY, KVM_EXIT_INTERNAL_ERROR, KVM_EXIT_SHUTDOWN };
use std::ffi::CStr;
use super::{Error, Result};
const PORT_WRITE: u16 = 0x499;
const PORT_OPEN: u16 = 0x500;
@ -57,6 +58,11 @@ pub enum Syscall {
Other(*const kvm_run)
}
pub enum Return {
Continue,
Exit(i32)
}
impl Syscall {
pub fn from_mem(mem: *const u8, guest_mem: *const u8) -> Syscall {
unsafe {
@ -84,7 +90,7 @@ impl Syscall {
}
pub unsafe fn run(&self, guest_mem: *mut u8) {
pub unsafe fn run(&self, guest_mem: *mut u8) -> Result<Return> {
debug!("{:?}", *self);
match *self {
@ -95,7 +101,7 @@ impl Syscall {
(*obj).ret = read((*obj).fd, guest_mem.offset((*obj).buf) as *mut c_void, (*obj).len);
},
Syscall::Exit(obj) => {
exit(*((guest_mem as *mut i32).offset((*obj) as isize)));
return Ok(Return::Exit(*((guest_mem as *mut i32).offset((*obj) as isize))));
},
Syscall::Open(obj) => {
(*obj).ret = open(guest_mem.offset((*obj).name) as *const i8, (*obj).flags, (*obj).mode);
@ -108,16 +114,22 @@ impl Syscall {
Syscall::LSeek(obj) => {
(*obj).offset = lseek((*obj).fd, (*obj).offset as i64, (*obj).whence);
},
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);
}
}}
Syscall::Other(id) => {
let err = match (*id).exit_reason {
KVM_EXIT_HLT => format!("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 => format!("KVM: receive shutdown command"),
_ => {
panic!("KVM: unhandled exit: exit_reason = 0x{}", (*id).exit_reason)
}
};
return Err(Error::Protocol(err));
}
}
return Ok(Return::Continue);
}
}

View file

@ -10,6 +10,7 @@ use libc;
use hermit::Isle;
use super::{Error, Result, NameIOCTL};
use super::vm::VirtualMachine;
use hermit::hermit_env;
/// 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
@ -92,10 +93,10 @@ impl KVM {
}
// Creates a new virtual machine and forwards the new fd to an object
pub fn create_vm(&self, size: usize) -> Result<VirtualMachine> {
pub fn create_vm(&self, size: usize,num_cpus: u32) -> 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),
Ok(vm_fd) => VirtualMachine::new(self.file.as_raw_fd(), vm_fd, size,num_cpus),
Err(_) => Err(Error::InternalError)
}
}
@ -112,7 +113,7 @@ pub struct Uhyve {
impl Uhyve {
pub fn new(path: &str) -> Result<Uhyve> {
let kvm = KVM::new();
let mut vm = kvm.create_vm(0x20000000)?;
let mut vm = kvm.create_vm(hermit_env::mem_size_parsed(), hermit_env::num_cpus_parsed())?;
vm.load_kernel(path)?;
vm.init()?;
@ -141,7 +142,7 @@ impl Isle for Uhyve {
}
fn run(&mut self) -> Result<()> {
self.vm.run();
self.vm.run()?;
Ok(())
}

View file

@ -8,6 +8,7 @@ use std::thread;
use std::thread::current;
use std::ptr::Unique;
use std::sync::Arc;
use std::thread::JoinHandle;
use memmap::{Mmap, Protection};
use errno::errno;
@ -59,7 +60,7 @@ impl 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)?;
let fd = VirtualCPU::create_vcpu(vm_fd, id as u32)?;
debug!("Got fd {}", fd);
@ -87,32 +88,27 @@ impl VirtualCPU {
}
debug!("Initialize the register of {} with start address {:?}", id, entry);
debug!("Set the CPUID");
cpu.setup_cpuid()?;
let mut regs = kvm_regs::empty();
regs.rip = entry;
regs.rflags = 0x2;
regs.rax = 2;
regs.rbx = 2;
debug!("Initialize the register of {} with start address {:?}", id, entry);
cpu.set_regs(regs)?;
debug!("Set the CPUID");
cpu.setup_cpuid()?;
unsafe {
while volatile_load(mboot.offset(0x20)) < id {}
volatile_store(mboot.offset(0x30), id);
}
Ok(cpu)
}
pub fn create_vcpu(fd: c_int) -> Result<c_int> {
pub fn create_vcpu(fd: c_int, mut id: u32) -> Result<c_int> {
let a = (&mut id) as *mut u32 as *mut u8;
unsafe {
uhyve::ioctl::create_vcpu(fd, ptr::null_mut())
uhyve::ioctl::create_vcpu(fd, id as *mut u8)
.map_err(|_| Error::IOCTL(NameIOCTL::CreateVcpu))
}
}
@ -178,17 +174,17 @@ impl VirtualCPU {
}
}
pub fn single_run(obj: &VirtualCPU) -> Result<i32> {
pub fn single_run(obj: &VirtualCPU) -> Result<proto::Return> {
let ret = unsafe {
uhyve::ioctl::run(obj.vcpu_fd, ptr::null_mut())
.map_err(|x| Error::IOCTL(NameIOCTL::Run))
}?;
debug!("Run with {}", ret);
debug!("Single Run CPU {}", obj.id);
if ret == -1 {
match errno().0 {
libc::EINTR => { return Ok(ret); },
libc::EINTR => { return Ok(proto::Return::Continue); },
libc::EFAULT => {
// TODO
panic!("translation fault!");
@ -200,32 +196,35 @@ impl VirtualCPU {
}
unsafe {
proto::Syscall::from_mem(obj.run_mem.ptr(), obj.guest_mem).run(obj.guest_mem);
proto::Syscall::from_mem(obj.run_mem.ptr(), obj.guest_mem).run(obj.guest_mem)
}
Ok(ret)
}
pub fn run(self) -> Result<u32> {
if self.id == 0 {
let wrapped = Arc::new(self);
loop {
VirtualCPU::single_run(&wrapped)?;
}
} else {
let wrapped = Arc::new(self);
pub fn run(self) -> JoinHandle<Result<i32>> {
debug!("Run CPU {}", self.id);
let wrapped = Arc::new(self);
let child = thread::spawn(move || {
loop {
VirtualCPU::single_run(&wrapped);
let child = thread::spawn(move || {
unsafe {
while volatile_load(wrapped.mboot.offset(0x20)) < wrapped.id {
thread::yield_now();
debug!("{} - {}", wrapped.id, volatile_load(wrapped.mboot.offset(0x20)));
}
});
child.join();
}
volatile_store(wrapped.mboot.offset(0x30), wrapped.id);
}
Ok(0)
loop {
match VirtualCPU::single_run(&wrapped) {
Ok(proto::Return::Exit(code)) => return Ok(code),
Err(err) => return Err(err),
_ => {}
}
}
});
child
}
pub fn setup_first(&self, mem: &mut [u8]) -> Result<()> {

View file

@ -10,6 +10,7 @@ use elf;
use elf::types::{ELFCLASS64, OSABI, PT_LOAD};
use std::ffi::CStr;
use hermit::hermit_env;
use hermit::utils;
use hermit::uhyve;
use super::kvm_header::{kvm_userspace_memory_region, KVM_CAP_SYNC_MMU, KVM_32BIT_GAP_START, KVM_32BIT_GAP_SIZE};
@ -27,11 +28,12 @@ pub struct VirtualMachine {
elf_header: Option<elf::types::FileHeader>,
klog: Option<*const i8>,
mboot: Option<*mut u8>,
vcpus: Vec<VirtualCPU>
vcpus: Vec<VirtualCPU>,
num_cpus: u32
}
impl VirtualMachine {
pub fn new(kvm_fd: libc::c_int, fd: libc::c_int, size: usize) -> Result<VirtualMachine> {
pub fn new(kvm_fd: libc::c_int, fd: libc::c_int, size: usize, num_cpus: u32) -> Result<VirtualMachine> {
debug!("New virtual machine with memory size {}", size);
// create a new memory region to map the memory of our guest
@ -46,7 +48,7 @@ impl VirtualMachine {
unsafe { libc::mprotect((mem.mut_ptr() as *mut libc::c_void).offset(KVM_32BIT_GAP_START as isize), KVM_32BIT_GAP_START, libc::PROT_NONE); }
}
Ok(VirtualMachine { kvm_fd: kvm_fd, vm_fd: fd, mem: mem, elf_header: None, klog: None, vcpus: Vec::new(), mboot: None })
Ok(VirtualMachine { kvm_fd: kvm_fd, vm_fd: fd, mem: mem, elf_header: None, klog: None, vcpus: Vec::new(), mboot: None, num_cpus: num_cpus})
}
/// Loads a kernel from path and initialite mem and elf_entry
@ -147,7 +149,11 @@ impl VirtualMachine {
self.create_irqchip()?;
self.create_vcpu(0)
for i in 0..self.num_cpus {
self.create_vcpu(i as u8)?;
}
Ok(())
}
pub fn set_user_memory_region(&self, mut region: kvm_userspace_memory_region) -> Result<()> {
@ -192,15 +198,6 @@ impl VirtualMachine {
let id = id as usize;
/*
let len = self.vcpus.len();
if len < id+1 {
debug!("Reserve one!");
self.vcpus.reserve(id - len + 1);
}
self.vcpus[id] = cpu;
*/
self.vcpus.insert(id, cpu);
Ok(())
@ -215,15 +212,24 @@ impl VirtualMachine {
pub fn run(&mut self) -> Result<()> {
let mut guest_mem = unsafe { self.mem.as_mut_slice() };
unsafe { *(self.mboot.unwrap().offset(0x24) as *mut u32) = self.num_cpus; }
let mut handles = vec![];
loop {
if let Some(vcpu) = self.vcpus.pop() {
vcpu.run()?;
handles.push(vcpu.run());
} else {
break;
}
}
for handle in handles {
let code = handle.join().unwrap()?;
debug!("Exited with {}", code);
}
Ok(())
}
@ -234,7 +240,9 @@ impl VirtualMachine {
impl Drop for VirtualMachine {
fn drop(&mut self) {
debug!("Drop the Virtual Machine");
if let Ok(output) = self.output() {
debug!("-------- Output --------");
debug!("{}", output);
}
}

View file

@ -31,6 +31,23 @@ pub fn cpufreq() -> Result<u32> {
Err(Error::MissingFrequency)
}
pub fn parse_mem(mem: &str) -> Result<usize> {
let (num, postfix): (String, String) = mem.chars().partition(|&x| x.is_numeric());
let num = num.parse::<usize>().map_err(|_| Error::ParseMemory)?;
let factor = match postfix.as_str() {
"E" | "e" => 1 << 60,
"P" | "p" => 1 << 50,
"T" | "t" => 1 << 40,
"G" | "g" => 1 << 30,
"M" | "m" => 1 << 20,
"K" | "k" => 1 << 10,
_ => return Err(Error::ParseMemory)
};
Ok(num*factor)
}
/// 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 {