Skip to content

Commit

Permalink
Merge pull request #794 from hermit-os/no_global_kvm
Browse files Browse the repository at this point in the history
WIP: Introduced VirtualizationInterface trait to get rid of KVM global state
  • Loading branch information
jounathaen authored Nov 19, 2024
2 parents 3309524 + 3b6c8b7 commit c0a8bac
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 169 deletions.
4 changes: 2 additions & 2 deletions benches/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use byte_unit::Byte;
use criterion::{criterion_group, Criterion};
use uhyvelib::{
params::Params,
vm::{UhyveVm, VcpuDefault},
vm::{DefaultBackend, UhyveVm},
};

pub fn load_vm_hello_world(c: &mut Criterion) {
Expand All @@ -14,7 +14,7 @@ pub fn load_vm_hello_world(c: &mut Criterion) {
..Default::default()
};

let mut vm = UhyveVm::<VcpuDefault>::new(path, params).expect("Unable to create VM");
let mut vm = UhyveVm::<DefaultBackend>::new(path, params).expect("Unable to create VM");

c.bench_function("vm::load_kernel(hello world)", |b| {
b.iter(|| vm.load_kernel().unwrap())
Expand Down
9 changes: 6 additions & 3 deletions src/linux/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,23 @@ use super::HypervisorError;
use crate::{
arch::x86_64::{registers::debug::HwBreakpoints, virt_to_phys},
consts::BOOT_PML4,
linux::{x86_64::kvm_cpu::KvmCpu, KickSignal},
linux::{
x86_64::kvm_cpu::{KvmCpu, KvmVm},
KickSignal,
},
vcpu::{VcpuStopReason, VirtualCPU},
vm::UhyveVm,
};

pub struct GdbUhyve {
vm: Arc<UhyveVm<KvmCpu>>,
vm: Arc<UhyveVm<KvmVm>>,
vcpu: KvmCpu,
hw_breakpoints: HwBreakpoints,
sw_breakpoints: SwBreakpoints,
}

impl GdbUhyve {
pub fn new(vm: Arc<UhyveVm<KvmCpu>>, vcpu: KvmCpu) -> Self {
pub fn new(vm: Arc<UhyveVm<KvmVm>>, vcpu: KvmCpu) -> Self {
Self {
vm,
vcpu,
Expand Down
15 changes: 9 additions & 6 deletions src/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ use nix::sys::{
use crate::{
linux::{
gdb::{GdbUhyve, UhyveGdbEventLoop},
x86_64::kvm_cpu::KvmCpu,
x86_64::kvm_cpu::KvmVm,
},
vcpu::VirtualCPU,
vm::UhyveVm,
vm::{UhyveVm, VirtualizationBackend},
};

static KVM: LazyLock<Kvm> = LazyLock::new(|| Kvm::new().unwrap());
Expand Down Expand Up @@ -67,7 +67,7 @@ impl KickSignal {
}
}

impl UhyveVm<KvmCpu> {
impl UhyveVm<KvmVm> {
/// Runs the VM.
///
/// Blocks until the VM has finished execution.
Expand Down Expand Up @@ -96,6 +96,11 @@ impl UhyveVm<KvmCpu> {
.as_ref()
.and_then(|core_ids| core_ids.get(cpu_id as usize).copied());

let mut cpu = parent_vm
.virt_backend
.new_cpu(cpu_id, parent_vm.clone())
.unwrap();

thread::spawn(move || {
debug!("Create thread for CPU {}", cpu_id);
match local_cpu_affinity {
Expand All @@ -106,8 +111,6 @@ impl UhyveVm<KvmCpu> {
None => debug!("No affinity specified, not binding thread"),
}

let mut cpu = KvmCpu::new(cpu_id, parent_vm.clone()).unwrap();

thread::sleep(std::time::Duration::from_millis(cpu_id as u64 * 50));

// jump into the VM and execute code of the guest
Expand Down Expand Up @@ -163,7 +166,7 @@ impl UhyveVm<KvmCpu> {
}

let this = Arc::new(self);
let cpu = KvmCpu::new(cpu_id, this.clone()).unwrap();
let cpu = this.virt_backend.new_cpu(cpu_id, this.clone()).unwrap();

let connection = wait_for_gdb_connection(this.gdb_port.unwrap()).unwrap();
let debugger = GdbStub::new(connection);
Expand Down
183 changes: 93 additions & 90 deletions src/linux/x86_64/kvm_cpu.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::{Arc, Mutex};
use std::sync::Arc;

use kvm_bindings::*;
use kvm_ioctls::{VcpuExit, VcpuFd, VmFd};
Expand All @@ -11,9 +11,10 @@ use crate::{
hypercall,
linux::KVM,
mem::MmapMemory,
params::Params,
vcpu::{VcpuStopReason, VirtualCPU},
virtio::*,
vm::UhyveVm,
vm::{UhyveVm, VirtualizationBackend},
HypervisorError, HypervisorResult,
};

Expand All @@ -28,95 +29,113 @@ const KVM_32BIT_MAX_MEM_SIZE: usize = 1 << 32;
const KVM_32BIT_GAP_SIZE: usize = 768 << 20;
const KVM_32BIT_GAP_START: usize = KVM_32BIT_MAX_MEM_SIZE - KVM_32BIT_GAP_SIZE;

static KVM_ACCESS: Mutex<Option<VmFd>> = Mutex::new(None);
pub struct KvmVm {
vm_fd: VmFd,
}
impl VirtualizationBackend for KvmVm {
type VCPU = KvmCpu;
fn new_cpu(&self, id: u32, parent_vm: Arc<UhyveVm<KvmVm>>) -> HypervisorResult<KvmCpu> {
let vcpu = self.vm_fd.create_vcpu(id as u64)?;
let mut kvcpu = KvmCpu {
id,
vcpu,
parent_vm: parent_vm.clone(),
pci_addr: None,
};
kvcpu.init(parent_vm.get_entry_point(), parent_vm.stack_address(), id)?;

pub fn initialize_kvm(mem: &MmapMemory, use_pit: bool) -> HypervisorResult<()> {
let sz = std::cmp::min(mem.memory_size, KVM_32BIT_GAP_START);
Ok(kvcpu)
}

let kvm_mem = kvm_userspace_memory_region {
slot: 0,
flags: mem.flags,
memory_size: sz as u64,
guest_phys_addr: mem.guest_address.as_u64(),
userspace_addr: mem.host_address as u64,
};
fn new(mem: &MmapMemory, params: &Params) -> HypervisorResult<Self> {
let vm = KVM.create_vm().unwrap();

// TODO: make vm a global struct in linux blah
let vm = KVM.create_vm()?;
unsafe { vm.set_user_memory_region(kvm_mem) }?;
let sz = std::cmp::min(mem.memory_size, KVM_32BIT_GAP_START);

if mem.memory_size > KVM_32BIT_GAP_START + KVM_32BIT_GAP_SIZE {
let kvm_mem = kvm_userspace_memory_region {
slot: 1,
slot: 0,
flags: mem.flags,
memory_size: (mem.memory_size - KVM_32BIT_GAP_START - KVM_32BIT_GAP_SIZE) as u64,
guest_phys_addr: mem.guest_address.as_u64()
+ (KVM_32BIT_GAP_START + KVM_32BIT_GAP_SIZE) as u64,
userspace_addr: (mem.host_address as usize + KVM_32BIT_GAP_START + KVM_32BIT_GAP_SIZE)
as u64,
memory_size: sz as u64,
guest_phys_addr: mem.guest_address.as_u64(),
userspace_addr: mem.host_address as u64,
};

unsafe { vm.set_user_memory_region(kvm_mem) }?;
}

debug!("Initialize interrupt controller");
if mem.memory_size > KVM_32BIT_GAP_START + KVM_32BIT_GAP_SIZE {
let kvm_mem = kvm_userspace_memory_region {
slot: 1,
flags: mem.flags,
memory_size: (mem.memory_size - KVM_32BIT_GAP_START - KVM_32BIT_GAP_SIZE) as u64,
guest_phys_addr: mem.guest_address.as_u64()
+ (KVM_32BIT_GAP_START + KVM_32BIT_GAP_SIZE) as u64,
userspace_addr: (mem.host_address as usize
+ KVM_32BIT_GAP_START
+ KVM_32BIT_GAP_SIZE) as u64,
};

unsafe { vm.set_user_memory_region(kvm_mem) }?;
}

// create basic interrupt controller
vm.create_irq_chip()?;
debug!("Initialize interrupt controller");

if use_pit {
vm.create_pit2(kvm_pit_config::default()).unwrap();
}
// create basic interrupt controller
vm.create_irq_chip()?;

if params.pit {
vm.create_pit2(kvm_pit_config::default()).unwrap();
}

// enable x2APIC support
let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_X2APIC_API,
flags: 0,
..Default::default()
};
cap.args[0] =
(KVM_X2APIC_API_USE_32BIT_IDS | KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK).into();
vm.enable_cap(&cap)
.expect("Unable to enable x2apic support");

// currently, we support only system, which provides the
// cpu feature TSC_DEADLINE
let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_TSC_DEADLINE_TIMER,
..Default::default()
};
cap.args[0] = 0;
vm.enable_cap(&cap)
.expect_err("Processor feature `tsc deadline` isn't supported!");

let cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_IRQFD,
..Default::default()
};
vm.enable_cap(&cap)
.expect_err("The support of KVM_CAP_IRQFD is currently required");

let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_X86_DISABLE_EXITS,
flags: 0,
..Default::default()
};
cap.args[0] =
(KVM_X86_DISABLE_EXITS_PAUSE | KVM_X86_DISABLE_EXITS_MWAIT | KVM_X86_DISABLE_EXITS_HLT)
.into();
vm.enable_cap(&cap)
.expect("Unable to disable exists due pause instructions");

let evtfd = EventFd::new(0).unwrap();
vm.register_irqfd(&evtfd, UHYVE_IRQ_NET)?;

// enable x2APIC support
let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_X2APIC_API,
flags: 0,
..Default::default()
};
cap.args[0] = (KVM_X2APIC_API_USE_32BIT_IDS | KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK).into();
vm.enable_cap(&cap)
.expect("Unable to enable x2apic support");

// currently, we support only system, which provides the
// cpu feature TSC_DEADLINE
let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_TSC_DEADLINE_TIMER,
..Default::default()
};
cap.args[0] = 0;
vm.enable_cap(&cap)
.expect_err("Processor feature `tsc deadline` isn't supported!");

let cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_IRQFD,
..Default::default()
};
vm.enable_cap(&cap)
.expect_err("The support of KVM_CAP_IRQFD is currently required");

let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap {
cap: KVM_CAP_X86_DISABLE_EXITS,
flags: 0,
..Default::default()
};
cap.args[0] =
(KVM_X86_DISABLE_EXITS_PAUSE | KVM_X86_DISABLE_EXITS_MWAIT | KVM_X86_DISABLE_EXITS_HLT)
.into();
vm.enable_cap(&cap)
.expect("Unable to disable exists due pause instructions");

let evtfd = EventFd::new(0).unwrap();
vm.register_irqfd(&evtfd, UHYVE_IRQ_NET)?;

*KVM_ACCESS.lock().unwrap() = Some(vm);
Ok(())
Ok(Self { vm_fd: vm })
}
}

pub struct KvmCpu {
id: u32,
vcpu: VcpuFd,
parent_vm: Arc<UhyveVm<Self>>,
parent_vm: Arc<UhyveVm<KvmVm>>,
pci_addr: Option<u32>,
}

Expand Down Expand Up @@ -322,23 +341,7 @@ impl KvmCpu {
}

impl VirtualCPU for KvmCpu {
fn new(id: u32, parent_vm: Arc<UhyveVm<KvmCpu>>) -> HypervisorResult<KvmCpu> {
let vcpu = KVM_ACCESS
.lock()
.unwrap()
.as_mut()
.expect("KVM is not initialized yet")
.create_vcpu(id as u64)?;
let mut kvcpu = KvmCpu {
id,
vcpu,
parent_vm: parent_vm.clone(),
pci_addr: None,
};
kvcpu.init(parent_vm.get_entry_point(), parent_vm.stack_address(), id)?;

Ok(kvcpu)
}
type VirtIf = KvmVm;

fn r#continue(&mut self) -> HypervisorResult<VcpuStopReason> {
loop {
Expand Down
46 changes: 32 additions & 14 deletions src/macos/aarch64/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use std::sync::Arc;

use log::debug;
use uhyve_interface::{GuestPhysAddr, Hypercall};
use xhypervisor::{self, Register, SystemRegister, VirtualCpuExitReason};
use xhypervisor::{
self, create_vm, map_mem, MemPerm, Register, SystemRegister, VirtualCpuExitReason,
};

use crate::{
aarch64::{
Expand All @@ -14,15 +16,41 @@ use crate::{
},
consts::*,
hypercall::{self, copy_argv, copy_env},
mem::MmapMemory,
params::Params,
vcpu::{VcpuStopReason, VirtualCPU},
vm::UhyveVm,
vm::{UhyveVm, VirtualizationBackend},
HypervisorResult,
};

pub struct XhyveVm {}
impl VirtualizationBackend for XhyveVm {
type VCPU = XhyveCpu;
fn new_cpu(&self, id: u32, parent_vm: Arc<UhyveVm<XhyveVm>>) -> HypervisorResult<XhyveCpu> {
let mut vcpu = XhyveCpu {
id,
parent_vm: parent_vm.clone(),
vcpu: xhypervisor::VirtualCpu::new().unwrap(),
};
vcpu.init(parent_vm.get_entry_point(), parent_vm.stack_address(), id)?;

Ok(vcpu)
}

fn new(mem: &MmapMemory, _params: &Params) -> HypervisorResult<Self> {
debug!("Create VM...");
create_vm()?;

debug!("Map guest memory...");
map_mem(unsafe { mem.as_slice_mut() }, 0, MemPerm::ExecAndWrite)?;
Ok(Self {})
}
}

pub struct XhyveCpu {
id: u32,
vcpu: xhypervisor::VirtualCpu,
parent_vm: Arc<UhyveVm<Self>>,
parent_vm: Arc<UhyveVm<XhyveVm>>,
}

impl XhyveCpu {
Expand Down Expand Up @@ -136,17 +164,7 @@ impl XhyveCpu {
}

impl VirtualCPU for XhyveCpu {
fn new(id: u32, parent_vm: Arc<UhyveVm<Self>>) -> HypervisorResult<Self> {
let mut vcpu = XhyveCpu {
id,
parent_vm: parent_vm.clone(),
vcpu: xhypervisor::VirtualCpu::new().unwrap(),
};
vcpu.init(parent_vm.get_entry_point(), parent_vm.stack_address(), id)?;

Ok(vcpu)
}

type VirtIf = XhyveVm;
fn r#continue(&mut self) -> HypervisorResult<VcpuStopReason> {
loop {
self.vcpu.run()?;
Expand Down
Loading

0 comments on commit c0a8bac

Please sign in to comment.