1
0
Fork 0
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:
bytesnake 2017-06-02 15:30:48 +02:00
parent cee53b2967
commit 4521e6ae07
7 changed files with 173 additions and 48 deletions

View file

@ -58,5 +58,8 @@ pub enum NameIOCTL {
GetRegs,
SetRegs,
GetSRegs,
SetSRegs
SetSRegs,
SetTssIdentity,
SetTssAddr,
SetMPState
}

View file

@ -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;

View file

@ -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); }
}
}

View file

@ -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 }
}

View 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 {}

View file

@ -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 {

View file

@ -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)]