Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions alioth/src/arch/x86_64/intr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use bitfield::bitfield;

bitfield! {
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct MsiAddrLo(u32);
impl Debug;
pub mode, set_mode : 2;
pub redirection, set_redirection : 3;
pub remappable, set_remappable : 4;
pub reserved, set_reserved : 11, 5;
pub dest_id, set_dest_id : 19, 12;
pub identifier, _: 31, 20;
}

bitfield! {
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct MsiAddrHi(u32);
impl Debug;
pub dest_id, set_dest_id : 31, 8;
}
11 changes: 11 additions & 0 deletions alioth/src/arch/x86_64/msr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use bitfield::bitfield;
use bitflags::bitflags;

// Intel Vol.4, Table 2-2.
Expand Down Expand Up @@ -47,3 +48,13 @@ bitflags! {
const FAST_STRINGS = 1 << 0;
}
}

bitfield! {
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct ApicBase(u64);
impl Debug;
pub bsp, set_bsp : 8;
pub x2apic, set_x2apic : 10;
pub xapic, set_xapic : 11;
pub base, set_base : 35, 12;
}
1 change: 1 addition & 0 deletions alioth/src/arch/x86_64/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

pub mod cpuid;
pub mod intr;
pub mod layout;
pub mod msr;
pub mod paging;
Expand Down
60 changes: 17 additions & 43 deletions alioth/src/hv/kvm/kvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ use crate::ffi;
use crate::hv::{Hypervisor, MemMapOption, Result, VmConfig, error};
#[cfg(target_arch = "aarch64")]
use crate::sys::kvm::KvmDevType;
#[cfg(target_arch = "x86_64")]
use crate::sys::kvm::kvm_get_supported_cpuid;
use crate::sys::kvm::{KVM_API_VERSION, kvm_get_api_version};
#[cfg(target_arch = "x86_64")]
use crate::sys::kvm::{KVM_MAX_CPUID_ENTRIES, KvmCpuid2, KvmCpuid2Flag, KvmCpuidEntry2};
use crate::sys::kvm::{KVM_API_VERSION, KvmCap, kvm_check_extension, kvm_get_api_version};

use self::vm::KvmVm;

Expand Down Expand Up @@ -80,16 +76,12 @@ pub enum KvmError {
MmapOption { option: MemMapOption },
#[snafu(display("Failed to mmap a VCPU fd"))]
MmapVcpuFd { error: std::io::Error },
#[snafu(display("Failed to check extension {ext}"))]
CheckExtension {
ext: &'static str,
error: std::io::Error,
},
#[snafu(display("Failed to enable capability {cap}"))]
EnableCap {
cap: &'static str,
error: std::io::Error,
},
#[snafu(display("Failed to check KVM capability"))]
CheckCap { error: std::io::Error },
#[snafu(display("KVM Capability {ext:?} not supported"))]
NotSupported { ext: KvmCap },
#[snafu(display("Failed to enable capability {cap:?}"))]
EnableCap { cap: KvmCap, error: std::io::Error },
#[snafu(display("Failed to create guest memfd"))]
GuestMemfd { error: std::io::Error },
#[cfg(target_arch = "aarch64")]
Expand Down Expand Up @@ -148,6 +140,15 @@ impl Kvm {
config,
})
}

pub fn check_extension(&self, id: KvmCap) -> Result<i32, KvmError> {
let ret = unsafe { kvm_check_extension(&self.fd, id) }.context(kvm_error::CheckCap)?;
if ret == 0 {
kvm_error::NotSupported { ext: id }.fail()
} else {
Ok(ret)
}
}
}

impl Hypervisor for Kvm {
Expand All @@ -159,33 +160,6 @@ impl Hypervisor for Kvm {

#[cfg(target_arch = "x86_64")]
fn get_supported_cpuids(&self) -> Result<HashMap<CpuidIn, CpuidResult>> {
let mut kvm_cpuid2 = KvmCpuid2 {
nent: KVM_MAX_CPUID_ENTRIES as u32,
padding: 0,
entries: [KvmCpuidEntry2::default(); KVM_MAX_CPUID_ENTRIES],
};
unsafe { kvm_get_supported_cpuid(&self.fd, &mut kvm_cpuid2) }.context(error::GuestCpuid)?;
let map_f = |e: &KvmCpuidEntry2| {
let in_ = CpuidIn {
func: e.function,
index: if e.flags.contains(KvmCpuid2Flag::SIGNIFCANT_INDEX) {
Some(e.index)
} else {
None
},
};
let out = CpuidResult {
eax: e.eax,
ebx: e.ebx,
ecx: e.ecx,
edx: e.edx,
};
(in_, out)
};
let cpuids = kvm_cpuid2.entries[0..kvm_cpuid2.nent as usize]
.iter()
.map(map_f)
.collect();
Ok(cpuids)
Kvm::get_supported_cpuids(self)
}
}
63 changes: 63 additions & 0 deletions alioth/src/hv/kvm/kvm_x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,69 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::arch::x86_64::CpuidResult;
use std::collections::HashMap;

use snafu::ResultExt;

use crate::arch::cpuid::CpuidIn;
use crate::hv::{Kvm, Result, error};
use crate::sys::kvm::{
KVM_CPUID_FEATURES, KVM_MAX_CPUID_ENTRIES, KvmCap, KvmCpuid2, KvmCpuid2Flag, KvmCpuidEntry2,
KvmCpuidFeature, KvmX2apicApiFlag, kvm_get_supported_cpuid,
};

impl Kvm {
pub fn get_supported_cpuids(&self) -> Result<HashMap<CpuidIn, CpuidResult>> {
let mut kvm_cpuid2 = KvmCpuid2 {
nent: KVM_MAX_CPUID_ENTRIES as u32,
padding: 0,
entries: [KvmCpuidEntry2::default(); KVM_MAX_CPUID_ENTRIES],
};
unsafe { kvm_get_supported_cpuid(&self.fd, &mut kvm_cpuid2) }.context(error::GuestCpuid)?;
let map_f = |e: &KvmCpuidEntry2| {
let in_ = CpuidIn {
func: e.function,
index: if e.flags.contains(KvmCpuid2Flag::SIGNIFCANT_INDEX) {
Some(e.index)
} else {
None
},
};
let out = CpuidResult {
eax: e.eax,
ebx: e.ebx,
ecx: e.ecx,
edx: e.edx,
};
(in_, out)
};
let mut cpuids: HashMap<_, _> = kvm_cpuid2
.entries
.iter()
.take(kvm_cpuid2.nent as usize)
.map(map_f)
.collect();

// Enable KVM_FEATURE_MSI_EXT_DEST_ID if KVM_CAP_X2APIC_API is supported
let ext = self.check_extension(KvmCap::X2APIC_API)?;
let flag = KvmX2apicApiFlag::from_bits_retain(ext as u64);
let x2apic_flags =
KvmX2apicApiFlag::USE_32BIT_IDS | KvmX2apicApiFlag::DISABLE_BROADCAST_QUIRK;
let leaf_features = CpuidIn {
func: KVM_CPUID_FEATURES,
index: None,
};
if let Some(entry) = cpuids.get_mut(&leaf_features)
&& flag.contains(x2apic_flags)
{
entry.eax |= KvmCpuidFeature::MSI_EXT_DEST_ID.bits();
}

Ok(cpuids)
}
}

#[cfg(test)]
#[path = "kvm_x86_64_test.rs"]
mod tests;
4 changes: 2 additions & 2 deletions alioth/src/hv/kvm/kvm_x86_64_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::hv::Hypervisor;
use crate::hv::kvm::{Kvm, KvmConfig};
use crate::sys::kvm::KVM_CPUID_SIGNATURE;

#[test]
#[cfg_attr(not(feature = "test-hv"), ignore)]
Expand All @@ -22,7 +22,7 @@ fn test_get_supported_cpuid() {
let mut kvm_cpuid_exist = false;
let supported_cpuids = kvm.get_supported_cpuids().unwrap();
for (in_, out) in &supported_cpuids {
if in_.func == 0x4000_0000
if in_.func == KVM_CPUID_SIGNATURE
&& out.ebx.to_le_bytes() == *b"KVMK"
&& out.ecx.to_le_bytes() == *b"VMKV"
&& out.edx.to_le_bytes() == *b"M\0\0\0"
Expand Down
5 changes: 2 additions & 3 deletions alioth/src/hv/kvm/vcpu/vcpu_aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,8 @@ impl KvmVcpu {
pub fn kvm_vcpu_init(&mut self, is_bsp: bool) -> Result<()> {
let mut arm_cpu_init =
unsafe { kvm_arm_preferred_target(&self.vm.fd) }.context(error::CreateVcpu)?;
if self.vm.check_extension(KvmCap::ARM_PSCI_0_2)? == 1 {
arm_cpu_init.features[0] |= KvmArmVcpuFeature::PSCI_0_2.bits();
}
self.vm.check_extension(KvmCap::ARM_PSCI_0_2)?;
arm_cpu_init.features[0] |= KvmArmVcpuFeature::PSCI_0_2.bits();
if !is_bsp {
arm_cpu_init.features[0] |= KvmArmVcpuFeature::POWER_OFF.bits();
}
Expand Down
49 changes: 25 additions & 24 deletions alioth/src/hv/kvm/vm/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,20 @@ use crate::sys::kvm::KVM_IRQCHIP_IOAPIC;
#[cfg(target_arch = "aarch64")]
use crate::sys::kvm::KvmMsiFlag;
use crate::sys::kvm::{
KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI, KvmCap, KvmEncRegion, KvmIoEventFd,
KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI, KvmCap, KvmEnableCap, KvmEncRegion, KvmIoEventFd,
KvmIoEventFdFlag, KvmIrqRouting, KvmIrqRoutingEntry, KvmIrqRoutingIrqchip, KvmIrqRoutingMsi,
KvmIrqfd, KvmIrqfdFlag, KvmMemFlag, KvmMemoryAttribute, KvmMemoryAttributes, KvmMsi,
KvmUserspaceMemoryRegion, KvmUserspaceMemoryRegion2, kvm_check_extension, kvm_create_vm,
kvm_get_vcpu_mmap_size, kvm_ioeventfd, kvm_irqfd, kvm_memory_encrypt_reg_region,
kvm_memory_encrypt_unreg_region, kvm_set_gsi_routing, kvm_set_memory_attributes,
kvm_set_user_memory_region, kvm_set_user_memory_region2, kvm_signal_msi,
kvm_enable_cap, kvm_get_vcpu_mmap_size, kvm_ioeventfd, kvm_irqfd,
kvm_memory_encrypt_reg_region, kvm_memory_encrypt_unreg_region, kvm_set_gsi_routing,
kvm_set_memory_attributes, kvm_set_user_memory_region, kvm_set_user_memory_region2,
kvm_signal_msi,
};

#[cfg(target_arch = "aarch64")]
use self::aarch64::VmArch;
use self::aarch64::{VmArch, translate_msi_addr};
#[cfg(target_arch = "x86_64")]
use self::x86_64::VmArch;
use self::x86_64::{VmArch, translate_msi_addr};

#[derive(Debug)]
pub struct VmInner {
Expand Down Expand Up @@ -101,9 +102,10 @@ impl VmInner {
{
entries[index].flags = KvmMsiFlag::VALID_DEVID;
}
let (lo, hi) = translate_msi_addr(entry.addr_lo, entry.addr_hi);
entries[index].routing.msi = KvmIrqRoutingMsi {
address_hi: entry.addr_hi,
address_lo: entry.addr_lo,
address_hi: hi,
address_lo: lo,
data: entry.data,
#[cfg(target_arch = "aarch64")]
devid: entry.devid,
Expand All @@ -122,16 +124,19 @@ impl VmInner {
Ok(())
}

pub fn check_extension(&self, id: KvmCap) -> Result<i32, Error> {
let ret = unsafe { kvm_check_extension(&self.fd, id) };
match ret {
Ok(num) => Ok(num),
Err(_) => error::Capability {
cap: "KVM_CAP_CHECK_EXTENSION_VM",
}
.fail(),
pub fn check_extension(&self, id: KvmCap) -> Result<i32, KvmError> {
let ret = unsafe { kvm_check_extension(&self.fd, id) }.context(kvm_error::CheckCap)?;
if ret == 0 {
kvm_error::NotSupported { ext: id }.fail()
} else {
Ok(ret)
}
}

pub fn enable_cap(&self, cap: &KvmEnableCap) -> Result<(), KvmError> {
unsafe { kvm_enable_cap(&self.fd, cap) }.context(kvm_error::EnableCap { cap: cap.cap })?;
Ok(())
}
}

impl Display for VmInner {
Expand Down Expand Up @@ -470,9 +475,10 @@ impl MsiSender for KvmMsiSender {
type IrqFd = KvmIrqFd;

fn send(&self, addr: u64, data: u32) -> Result<()> {
let (lo, hi) = translate_msi_addr(addr as u32, (addr >> 32) as u32);
let kvm_msi = KvmMsi {
address_lo: addr as u32,
address_hi: (addr >> 32) as u32,
address_lo: lo,
address_hi: hi,
data,
#[cfg(target_arch = "aarch64")]
devid: self.devid,
Expand Down Expand Up @@ -665,12 +671,7 @@ impl Vm for KvmVm {
if self.vm.pin_map.fetch_or(pin_flag, Ordering::AcqRel) & pin_flag == pin_flag {
return Err(std::io::ErrorKind::AlreadyExists.into()).context(error::CreateIrq { pin });
}
if self.vm.check_extension(KvmCap::IRQFD)? == 0 {
return error::Capability {
cap: "KVM_CAP_IRQFD",
}
.fail();
}
self.vm.check_extension(KvmCap::IRQFD)?;
let event_fd = ffi!(unsafe { eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK) })
.context(error::CreateIrq { pin })?;
let request = KvmIrqfd {
Expand Down
4 changes: 4 additions & 0 deletions alioth/src/hv/kvm/vm/vm_aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ use crate::sys::kvm::{
KvmVmType,
};

pub fn translate_msi_addr(addr_lo: u32, addr_hi: u32) -> (u32, u32) {
(addr_lo, addr_hi)
}

#[derive(Debug)]
pub struct KvmGicV2m;

Expand Down
Loading