diff --git a/tools/hermit_proxy/src/hermit/error.rs b/tools/hermit_proxy/src/hermit/error.rs index dc347ffe8..ad4f8c279 100644 --- a/tools/hermit_proxy/src/hermit/error.rs +++ b/tools/hermit_proxy/src/hermit/error.rs @@ -58,5 +58,8 @@ pub enum NameIOCTL { GetRegs, SetRegs, GetSRegs, - SetSRegs + SetSRegs, + SetTssIdentity, + SetTssAddr, + SetMPState } diff --git a/tools/hermit_proxy/src/hermit/uhyve/kvm_header.rs b/tools/hermit_proxy/src/hermit/uhyve/kvm_header.rs index 49fa3c508..645ef8c9d 100644 --- a/tools/hermit_proxy/src/hermit/uhyve/kvm_header.rs +++ b/tools/hermit_proxy/src/hermit/uhyve/kvm_header.rs @@ -220,6 +220,9 @@ pub const KVMIO: ::std::os::raw::c_uint = 174; pub const KVM_VM_S390_UCONTROL: ::std::os::raw::c_uint = 1; pub const KVM_VM_PPC_HV: ::std::os::raw::c_uint = 1; pub const KVM_VM_PPC_PR: ::std::os::raw::c_uint = 2; +pub const KVM_32BIT_MAX_MEM_SIZE: usize = 1 << 32; +pub const KVM_32BIT_GAP_SIZE: usize = 768 << 20; +pub const KVM_32BIT_GAP_START: usize = (KVM_32BIT_MAX_MEM_SIZE - KVM_32BIT_GAP_SIZE) as usize; pub const KVM_S390_SIE_PAGE_OFFSET: ::std::os::raw::c_uint = 1; pub const KVM_CAP_IRQCHIP: ::std::os::raw::c_uint = 0; pub const KVM_CAP_HLT: ::std::os::raw::c_uint = 1; diff --git a/tools/hermit_proxy/src/hermit/uhyve/proto.rs b/tools/hermit_proxy/src/hermit/uhyve/proto.rs index c738937fe..e1d3fd346 100644 --- a/tools/hermit_proxy/src/hermit/uhyve/proto.rs +++ b/tools/hermit_proxy/src/hermit/uhyve/proto.rs @@ -58,26 +58,26 @@ pub enum Syscall { } impl Syscall { - pub fn from_mem(mem: &[u8], guest_mem: &[u8]) -> Syscall { + pub fn from_mem(mem: *const u8, guest_mem: *const u8) -> Syscall { unsafe { - let ref run = *(mem.as_ptr() as *const kvm_run); + let ref run = *(mem as *const kvm_run); debug!("Exit reason {}", run.exit_reason); if run.exit_reason != KVM_EXIT_IO { - return Syscall::Other(mem.as_ptr() as *const kvm_run); + return Syscall::Other(mem as *const kvm_run); } - let offset = *((mem.as_ptr().offset(run.__bindgen_anon_1.io.data_offset as isize) as *const isize)); + let offset = *((mem.offset(run.__bindgen_anon_1.io.data_offset as isize) as *const 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) }, + PORT_WRITE => { Syscall::Write(guest_mem.offset(offset) as *mut Write) }, + PORT_READ => { Syscall::Read (guest_mem.offset(offset) as *mut Read) }, + PORT_CLOSE => { Syscall::Close(guest_mem.offset(offset) as *mut Close) }, + PORT_OPEN => { Syscall::Open (guest_mem.offset(offset) as *mut Open ) }, + PORT_LSEEK => { Syscall::LSeek(guest_mem.offset(offset) as *mut LSeek) }, + PORT_EXIT => { Syscall::Exit( guest_mem.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); } } } diff --git a/tools/hermit_proxy/src/hermit/uhyve/uhyve.rs b/tools/hermit_proxy/src/hermit/uhyve/uhyve.rs index da39d1cb2..0ada68f2f 100644 --- a/tools/hermit_proxy/src/hermit/uhyve/uhyve.rs +++ b/tools/hermit_proxy/src/hermit/uhyve/uhyve.rs @@ -16,12 +16,11 @@ use super::vm::VirtualMachine; /// ioctl! macro and need to be wrapped further to provide a safe interface. pub mod ioctl { use std::mem; - use hermit::uhyve::kvm_header::{KVMIO, kvm_msr_list, kvm_cpuid2_header, kvm_memory_region, kvm_dirty_log, kvm_memory_alias, kvm_userspace_memory_region, kvm_regs,kvm_sregs}; + use hermit::uhyve::kvm_header::{KVMIO, kvm_msr_list, kvm_cpuid2_header, kvm_memory_region, kvm_dirty_log, kvm_memory_alias, kvm_userspace_memory_region, kvm_regs,kvm_sregs, kvm_enable_cap, kvm_mp_state}; ioctl!(get_version with io!(KVMIO, 0x00)); ioctl!(create_vm with io!(KVMIO, 0x01)); ioctl!(read get_msr_index_list with KVMIO, 0x02; kvm_msr_list); - ioctl!(check_extension with io!(KVMIO, 0x03)); ioctl!(get_vcpu_mmap_size with io!(KVMIO, 0x04)); ioctl!(readwrite get_supported_cpuid with KVMIO, 0x05; kvm_cpuid2_header); @@ -45,6 +44,12 @@ pub mod ioctl { ioctl!(read get_sregs with KVMIO, 0x83; kvm_sregs); ioctl!(write set_sregs with KVMIO, 0x84; kvm_sregs); + ioctl!(check_extension with io!(KVMIO, 0x03)); + ioctl!(set_tss_addr with io!(KVMIO, 0x47)); + ioctl!(write set_identity_map_addr with KVMIO, 0x48; u64); + ioctl!(write enable_cap with KVMIO, 0xa3; kvm_enable_cap); + + ioctl!(write set_mp_state with KVMIO, 0x99; kvm_mp_state); } /// KVM is freezed at version 12, so all others are invalid @@ -70,7 +75,7 @@ impl KVM { .custom_flags(libc::O_CLOEXEC) .open("/dev/kvm").unwrap(); - debug!("UHYVE - The connection to KVM was established."); + debug!("Connection to KVM is established."); KVM { file: kvm_file } } diff --git a/tools/hermit_proxy/src/hermit/uhyve/vcpu.rs b/tools/hermit_proxy/src/hermit/uhyve/vcpu.rs index 77cf9359a..a1396b81b 100644 --- a/tools/hermit_proxy/src/hermit/uhyve/vcpu.rs +++ b/tools/hermit_proxy/src/hermit/uhyve/vcpu.rs @@ -3,11 +3,17 @@ use libc::{c_void, c_int}; use std::{mem, ptr}; use std::fs::File; use std::os::unix::io::FromRawFd; +use std::intrinsics::{volatile_load,volatile_store}; +use std::thread; +use std::thread::current; +use std::ptr::Unique; +use std::sync::Arc; + use memmap::{Mmap, Protection}; use errno::errno; use hermit::uhyve; -use super::kvm_header::{kvm_sregs, kvm_regs, kvm_segment, kvm_cpuid2,kvm_cpuid2_header}; +use super::kvm_header::{kvm_sregs, kvm_regs, kvm_segment, kvm_cpuid2,kvm_cpuid2_header, KVM_MP_STATE_RUNNABLE, kvm_mp_state}; use super::{Result, Error, NameIOCTL}; use super::gdt; use super::proto; @@ -43,11 +49,13 @@ pub struct VirtualCPU { pub vcpu_fd: libc::c_int, id: u8, file: File, - run_mem: Mmap + run_mem: Mmap, + mboot:*mut u8, + guest_mem: *mut u8 } impl VirtualCPU { - pub fn new(kvm_fd: libc::c_int, vm_fd: libc::c_int, id: u8, entry: u64, mem: &mut Mmap) -> Result { + pub fn new(kvm_fd: libc::c_int, vm_fd: libc::c_int, id: u8, entry: u64, mem: &mut Mmap, mboot: *mut u8) -> Result { debug!("UHYVE - New virtual CPU with id {}", id); // create a new VCPU and save the file descriptor @@ -61,11 +69,13 @@ impl VirtualCPU { Mmap::open_with_offset(&file, Protection::ReadWrite, 0, VirtualCPU::get_mmap_size(kvm_fd)?) .map_err(|x| panic!("{:?}", x) )//Error::InvalidFile) }?; - + let cpu = VirtualCPU { - kvm_fd: kvm_fd, vm_fd: vm_fd, vcpu_fd: fd, id: id, file: file, run_mem: run_mem + kvm_fd: kvm_fd, vm_fd: vm_fd, vcpu_fd: fd, id: id, file: file, run_mem: run_mem, mboot: mboot, guest_mem: mem.mut_ptr() }; + cpu.set_mp_state(kvm_mp_state { mp_state: KVM_MP_STATE_RUNNABLE })?; + if id == 0 { debug!("Setup the first processor"); @@ -91,6 +101,12 @@ impl VirtualCPU { cpu.setup_cpuid()?; + unsafe { + while volatile_load(mboot.offset(0x20)) < id {} + + volatile_store(mboot.offset(0x30), id); + } + Ok(cpu) } @@ -155,10 +171,16 @@ impl VirtualCPU { .map_err(|x| Error::IOCTL(NameIOCTL::GetVCPUMMAPSize)).map(|x| { x as usize}) } } + + pub fn set_mp_state(&self, mp_state: kvm_mp_state) -> Result<()> { + unsafe { + uhyve::ioctl::set_mp_state(self.vcpu_fd, (&mp_state) as *const kvm_mp_state).map_err(|x| Error::IOCTL(NameIOCTL::SetMPState)).map(|_| ()) + } + } - pub fn single_run(&self, guest_mem: &mut [u8]) -> Result { + pub fn single_run(obj: &VirtualCPU) -> Result { let ret = unsafe { - uhyve::ioctl::run(self.vcpu_fd, ptr::null_mut()) + uhyve::ioctl::run(obj.vcpu_fd, ptr::null_mut()) .map_err(|x| Error::IOCTL(NameIOCTL::Run)) }?; @@ -178,16 +200,32 @@ impl VirtualCPU { } unsafe { - proto::Syscall::from_mem(self.run_mem.as_slice(), guest_mem).run(guest_mem.as_mut_ptr()); + proto::Syscall::from_mem(obj.run_mem.ptr(), obj.guest_mem).run(obj.guest_mem); } Ok(ret) } - pub fn run(&self, guest_mem: &mut [u8]) -> Result { - loop { - self.single_run(guest_mem)?; + pub fn run(self) -> Result { + + if self.id == 0 { + let wrapped = Arc::new(self); + loop { + VirtualCPU::single_run(&wrapped)?; + } + } else { + let wrapped = Arc::new(self); + + let child = thread::spawn(move || { + loop { + VirtualCPU::single_run(&wrapped); + } + }); + + child.join(); } + + Ok(0) } pub fn setup_first(&self, mem: &mut [u8]) -> Result<()> { @@ -299,3 +337,6 @@ impl VirtualCPU { Ok(()) } } + +unsafe impl Sync for VirtualCPU { } +unsafe impl Send for VirtualCPU {} diff --git a/tools/hermit_proxy/src/hermit/uhyve/vm.rs b/tools/hermit_proxy/src/hermit/uhyve/vm.rs index 1656bdade..7726cc11b 100644 --- a/tools/hermit_proxy/src/hermit/uhyve/vm.rs +++ b/tools/hermit_proxy/src/hermit/uhyve/vm.rs @@ -12,7 +12,7 @@ use std::ffi::CStr; use hermit::utils; use hermit::uhyve; -use super::kvm_header::{kvm_userspace_memory_region }; +use super::kvm_header::{kvm_userspace_memory_region, KVM_CAP_SYNC_MMU, KVM_32BIT_GAP_START, KVM_32BIT_GAP_SIZE}; use super::{Result, Error, NameIOCTL}; use super::vcpu::VirtualCPU; @@ -26,32 +26,43 @@ pub struct VirtualMachine { mem: Mmap, elf_header: Option, klog: Option<*const i8>, - vcpus: Vec, - size: usize + mboot: Option<*mut u8>, + vcpus: Vec } impl VirtualMachine { pub fn new(kvm_fd: libc::c_int, fd: libc::c_int, size: usize) -> Result { - debug!("UHYVE - New virtual machine with memory size {}", size); + debug!("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_header: None, klog: None, vcpus: Vec::new(), size: size }) + let mut mem; + if size < KVM_32BIT_GAP_START { + mem = Mmap::anonymous(size, Protection::ReadWrite) + .map_err(|_| Error::NotEnoughMemory)?; + } else { + mem = Mmap::anonymous(size + KVM_32BIT_GAP_START, Protection::ReadWrite) + .map_err(|_| Error::NotEnoughMemory)?; + + 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 }) } /// 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); - + debug!("Load kernel from {}", path); + // open the file in read only - let file = Mmap::open_path(path, Protection::Read).map_err(|_| Error::InvalidFile(path.into()))?; + let file = Mmap::open_path(path, Protection::Read) + .map_err(|_| Error::InvalidFile(path.into()))?; // 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(path.into())) + elf::File::open_stream(&mut data) + .map_err(|_| Error::InvalidFile(path.into())) }?; if file_efi.ehdr.class != ELFCLASS64 || file_efi.ehdr.osabi != OSABI(0x42) { @@ -97,6 +108,7 @@ impl VirtualMachine { *(ptr.offset(0x94) as *mut u32) = 1; // announce uhyve self.klog = Some(vm_mem.as_ptr().offset(header.paddr as isize + 0x5000) as *const i8); + self.mboot = Some(vm_mem.as_mut_ptr().offset(header.paddr as isize) as *mut u8); } } @@ -107,18 +119,35 @@ impl VirtualMachine { /// Initialize the virtual machine pub fn init(&mut self) -> Result<()> { + let mut identity_base: u64 = 0xfffbc000; + if let Ok(true) = self.check_extension(KVM_CAP_SYNC_MMU) { + identity_base = 0xfeffc000; + + self.set_tss_identity(identity_base)?; + } + + self.set_tss_addr(identity_base+0x1000)?; + 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 + let mut kvm_region = kvm_userspace_memory_region { + slot: 0, guest_phys_addr: 0, flags: 0, memory_size: self.mem_size() as u64, userspace_addr: start_ptr }; - self.set_user_memory_region(kvm_region)?; + if self.mem_size() <= KVM_32BIT_GAP_START { + self.set_user_memory_region(kvm_region)?; + } else { + kvm_region.memory_size = KVM_32BIT_GAP_START as u64; + self.set_user_memory_region(kvm_region)?; + + kvm_region.slot = 1; + kvm_region.guest_phys_addr = (KVM_32BIT_GAP_START+KVM_32BIT_GAP_SIZE) as u64; + kvm_region.memory_size = (self.mem_size() - KVM_32BIT_GAP_SIZE - KVM_32BIT_GAP_START) as u64; + self.set_user_memory_region(kvm_region)?; + } + self.create_irqchip()?; - let vcpu = self.create_vcpu(0)?; - self.vcpus.push(vcpu); - - Ok(()) + self.create_vcpu(0) } pub fn set_user_memory_region(&self, mut region: kvm_userspace_memory_region) -> Result<()> { @@ -135,12 +164,46 @@ impl VirtualMachine { } } - pub fn create_vcpu(&mut self, id: u8) -> Result { + pub fn check_extension(&self, mut extension: u32) -> Result { + unsafe { + uhyve::ioctl::check_extension(self.vm_fd, (&mut extension) as *mut u32 as *mut u8) + .map_err(|x| Error::IOCTL(NameIOCTL::CheckExtension)).map(|x| x == 1) + } + } + + pub fn set_tss_identity(&self, identity_base: u64) -> Result<()> { + unsafe { + uhyve::ioctl::set_identity_map_addr(self.kvm_fd, (&identity_base) as *const u64) + .map_err(|x| Error::IOCTL(NameIOCTL::SetTssIdentity)).map(|_| ()) + } + } + + pub fn set_tss_addr(&self, mut identity_base: u64) -> Result<()> { + unsafe { + uhyve::ioctl::set_tss_addr(self.vm_fd, identity_base as *mut u8) + .map_err(|x| Error::IOCTL(NameIOCTL::SetTssAddr)).map(|_| ()) + } + } + + pub fn create_vcpu(&mut self, id: u8) -> Result<()> { let entry = self.elf_header.ok_or(Error::KernelNotLoaded)?.entry; - let cpu = VirtualCPU::new(self.kvm_fd, self.vm_fd, id, entry, &mut self.mem)?; + let cpu = VirtualCPU::new(self.kvm_fd, self.vm_fd, id, entry, &mut self.mem, self.mboot.unwrap())?; - Ok(cpu) + 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(()) } pub fn output(&self) -> Result { @@ -153,12 +216,20 @@ impl VirtualMachine { 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)?; + loop { + if let Some(vcpu) = self.vcpus.pop() { + vcpu.run()?; + } else { + break; + } } Ok(()) } + + pub fn mem_size(&self) -> usize { + self.mem.len() + } } impl Drop for VirtualMachine { diff --git a/tools/hermit_proxy/src/main.rs b/tools/hermit_proxy/src/main.rs index 28e6bd4a0..217fcb06d 100644 --- a/tools/hermit_proxy/src/main.rs +++ b/tools/hermit_proxy/src/main.rs @@ -1,4 +1,6 @@ #![feature(untagged_unions)] +#![feature(core_intrinsics)] +#![feature(unique)] #![allow(dead_code)] #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)]