mirror of
https://github.com/hermitcore/libhermit.git
synced 2025-03-09 00:00:03 +01:00
First steps towards MP supported
This commit is contained in:
parent
cee53b2967
commit
4521e6ae07
7 changed files with 173 additions and 48 deletions
|
@ -58,5 +58,8 @@ pub enum NameIOCTL {
|
|||
GetRegs,
|
||||
SetRegs,
|
||||
GetSRegs,
|
||||
SetSRegs
|
||||
SetSRegs,
|
||||
SetTssIdentity,
|
||||
SetTssAddr,
|
||||
SetMPState
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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<VirtualCPU> {
|
||||
pub fn new(kvm_fd: libc::c_int, vm_fd: libc::c_int, id: u8, entry: u64, mem: &mut Mmap, mboot: *mut u8) -> Result<VirtualCPU> {
|
||||
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<i32> {
|
||||
pub fn single_run(obj: &VirtualCPU) -> Result<i32> {
|
||||
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<u32> {
|
||||
loop {
|
||||
self.single_run(guest_mem)?;
|
||||
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);
|
||||
|
||||
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 {}
|
||||
|
|
|
@ -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<elf::types::FileHeader>,
|
||||
klog: Option<*const i8>,
|
||||
vcpus: Vec<VirtualCPU>,
|
||||
size: usize
|
||||
mboot: Option<*mut u8>,
|
||||
vcpus: Vec<VirtualCPU>
|
||||
}
|
||||
|
||||
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);
|
||||
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<VirtualCPU> {
|
||||
pub fn check_extension(&self, mut extension: u32) -> Result<bool> {
|
||||
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<String> {
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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)]
|
||||
|
|
Loading…
Add table
Reference in a new issue