crosvm/kvm: enable runtime detection of IOAPIC pins

Enable support for runtime verification of number
of irqchip kernel emulated inputs, up to 120 pins.

KVM implementation supporting extended input pins shall
report KVM_CHECK_EXTENSION/KVM_CAP_IOAPIC_NUM_PINS value.

BUG=b:179648314
TEST=On systems with 24/120 pin IOAPIC kvm emulation.

Change-Id: I80063216310e427d664e3eaca3aba27e8a972cde
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2893366
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Tomasz Jeznach <tjeznach@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Tomasz Jeznach 2021-04-28 16:39:45 -07:00 committed by Commit Bot
parent ac63740cf7
commit f622e504d3
8 changed files with 47 additions and 12 deletions

1
Cargo.lock generated
View file

@ -683,6 +683,7 @@ name = "net_util"
version = "0.1.0"
dependencies = [
"base",
"cros_async",
"data_model",
"libc",
"net_sys",

View file

@ -14,7 +14,7 @@ use crate::BusDevice;
use base::{error, warn, Error, Event, Result, Tube, TubeError};
use hypervisor::{
IoapicRedirectionTableEntry, IoapicState, MsiAddressMessage, MsiDataMessage, TriggerMode,
NUM_IOAPIC_PINS,
MAX_IOAPIC_PINS, NUM_IOAPIC_PINS,
};
use vm_control::{VmIrqRequest, VmIrqResponse};
@ -146,7 +146,7 @@ impl BusDevice for Ioapic {
impl Ioapic {
pub fn new(irq_tube: Tube, num_pins: usize) -> Result<Ioapic> {
let num_pins = num_pins.max(NUM_IOAPIC_PINS as usize);
let num_pins = num_pins.max(NUM_IOAPIC_PINS).min(MAX_IOAPIC_PINS);
let mut entry = IoapicRedirectionTableEntry::new();
entry.set_interrupt_mask(true);
Ok(Ioapic {

View file

@ -13,7 +13,7 @@ use base::FakeClock as Clock;
use hypervisor::kvm::{KvmVcpu, KvmVm};
use hypervisor::{
HypervisorCap, IoapicState, IrqRoute, IrqSource, IrqSourceChip, LapicState, MPState, PicSelect,
PicState, PitState, Vcpu, VcpuX86_64, Vm, VmX86_64, NUM_IOAPIC_PINS,
PicState, PitState, Vcpu, VcpuX86_64, Vm, VmX86_64,
};
use kvm_sys::*;
use resources::SystemAllocator;
@ -63,11 +63,12 @@ impl KvmKernelIrqChip {
pub fn new(vm: KvmVm, num_vcpus: usize) -> Result<KvmKernelIrqChip> {
vm.create_irq_chip()?;
vm.create_pit()?;
let ioapic_pins = vm.get_ioapic_num_pins()?;
Ok(KvmKernelIrqChip {
vm,
vcpus: Arc::new(Mutex::new((0..num_vcpus).map(|_| None).collect())),
routes: Arc::new(Mutex::new(kvm_default_irq_routing_table(NUM_IOAPIC_PINS))),
routes: Arc::new(Mutex::new(kvm_default_irq_routing_table(ioapic_pins))),
})
}
/// Attempt to create a shallow clone of this x86_64 KvmKernelIrqChip instance.
@ -192,7 +193,7 @@ impl KvmSplitIrqChip {
irq_tube: Tube,
ioapic_pins: Option<usize>,
) -> Result<Self> {
let ioapic_pins = ioapic_pins.unwrap_or(hypervisor::NUM_IOAPIC_PINS);
let ioapic_pins = ioapic_pins.unwrap_or(vm.get_ioapic_num_pins()?);
vm.enable_split_irqchip(ioapic_pins)?;
let pit_evt = Event::new()?;
let pit = Arc::new(Mutex::new(

View file

@ -20,6 +20,7 @@ use crate::{
ClockState, CpuId, CpuIdEntry, DebugRegs, DescriptorTable, DeviceKind, Fpu, HypervisorX86_64,
IoapicRedirectionTableEntry, IoapicState, IrqSourceChip, LapicState, PicSelect, PicState,
PitChannelState, PitState, Register, Regs, Segment, Sregs, VcpuX86_64, VmCap, VmX86_64,
MAX_IOAPIC_PINS, NUM_IOAPIC_PINS,
};
type KvmCpuId = kvm::CpuId;
@ -191,6 +192,17 @@ impl KvmVm {
}
}
/// Retrieves the KVM_IOAPIC_NUM_PINS value for emulated IO-APIC.
pub fn get_ioapic_num_pins(&self) -> Result<usize> {
// Safe because we know that our file is a KVM fd, and if the cap is invalid KVM assumes
// it's an unavailable extension and returns 0, producing default KVM_IOAPIC_NUM_PINS value.
match unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), KVM_CAP_IOAPIC_NUM_PINS as u64) }
{
ret if ret < 0 => errno_result(),
ret => Ok((ret as usize).max(NUM_IOAPIC_PINS).min(MAX_IOAPIC_PINS)),
}
}
/// Retrieves the state of IOAPIC by issuing KVM_GET_IRQCHIP ioctl.
///
/// Note that this call can only succeed after a call to `Vm::create_irq_chip`.
@ -760,7 +772,7 @@ impl From<&kvm_ioapic_state> for IoapicState {
ioregsel: item.ioregsel as u8,
ioapicid: item.id,
current_interrupt_level_bitmap: item.irr,
redirect_table: [IoapicRedirectionTableEntry::default(); 24],
redirect_table: [IoapicRedirectionTableEntry::default(); 120],
};
for (in_state, out_state) in item.redirtbl.iter().zip(state.redirect_table.iter_mut()) {
*out_state = in_state.into();
@ -1283,6 +1295,8 @@ mod tests {
#[test]
fn ioapic_state() {
let mut entry = IoapicRedirectionTableEntry::default();
let mut noredir = IoapicRedirectionTableEntry::default();
// default entry should be 0
assert_eq!(entry.get(0, 64), 0);
@ -1304,21 +1318,26 @@ mod tests {
assert_eq!(entry.get(0, 64), bit_repr);
let state = IoapicState {
let mut state = IoapicState {
base_address: 1,
ioregsel: 2,
ioapicid: 4,
current_interrupt_level_bitmap: 8,
redirect_table: [entry; 24],
redirect_table: [noredir; 120],
};
// Initialize first 24 (kvm_state limit) redirection entries
for i in 0..24 {
state.redirect_table[i] = entry;
}
let kvm_state = kvm_ioapic_state::from(&state);
assert_eq!(kvm_state.base_address, 1);
assert_eq!(kvm_state.ioregsel, 2);
assert_eq!(kvm_state.id, 4);
assert_eq!(kvm_state.irr, 8);
assert_eq!(kvm_state.pad, 0);
// check our entries
// check first 24 entries
for i in 0..24 {
assert_eq!(unsafe { kvm_state.redirtbl[i].bits }, bit_repr);
}

View file

@ -229,12 +229,15 @@ pub struct IoapicRedirectionTableEntry {
dest_id: BitField8,
}
/// Number of pins on the IOAPIC.
/// Number of pins on the standard KVM/IOAPIC.
pub const NUM_IOAPIC_PINS: usize = 24;
/// Maximum number of pins on the IOAPIC.
pub const MAX_IOAPIC_PINS: usize = 120;
/// Represents the state of the IOAPIC.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct IoapicState {
/// base_address is the memory base address for this IOAPIC. It cannot be changed.
pub base_address: u64,
@ -245,7 +248,13 @@ pub struct IoapicState {
/// current_interrupt_level_bitmap represents a bitmap of the state of all of the irq lines
pub current_interrupt_level_bitmap: u32,
/// redirect_table contains the irq settings for each irq line
pub redirect_table: [IoapicRedirectionTableEntry; 24],
pub redirect_table: [IoapicRedirectionTableEntry; 120],
}
impl Default for IoapicState {
fn default() -> IoapicState {
unsafe { std::mem::zeroed() }
}
}
#[repr(C)]

View file

@ -120,5 +120,6 @@ pub enum Cap {
S390UserSigp = KVM_CAP_S390_USER_SIGP,
ImmediateExit = KVM_CAP_IMMEDIATE_EXIT,
ArmPmuV3 = KVM_CAP_ARM_PMU_V3,
IoapicNumPins = KVM_CAP_IOAPIC_NUM_PINS,
ArmProtectedVm = KVM_CAP_ARM_PROTECTED_VM,
}

View file

@ -618,6 +618,8 @@ pub const KVM_CAP_ARM_PTRAUTH_GENERIC: u32 = 172;
pub const KVM_CAP_PMU_EVENT_FILTER: u32 = 173;
pub const KVM_CAP_ARM_IRQ_LINE_LAYOUT_2: u32 = 174;
pub const KVM_CAP_HYPERV_DIRECT_TLBFLUSH: u32 = 175;
// TODO(tjeznach): Remove this when reporting KVM_IOAPIC_NUM_PINS is no longer required.
pub const KVM_CAP_IOAPIC_NUM_PINS: u32 = 8191;
// TODO(qwandor): Update this once the pKVM patches are merged upstream with a stable capability ID.
pub const KVM_CAP_ARM_PROTECTED_VM: u32 = 0xffbadab1;
pub const KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE: u32 = 0;

View file

@ -568,6 +568,8 @@ pub const KVM_CAP_ARM_PTRAUTH_GENERIC: u32 = 172;
pub const KVM_CAP_PMU_EVENT_FILTER: u32 = 173;
pub const KVM_CAP_ARM_IRQ_LINE_LAYOUT_2: u32 = 174;
pub const KVM_CAP_HYPERV_DIRECT_TLBFLUSH: u32 = 175;
// TODO(tjeznach): Remove this when reporting KVM_IOAPIC_NUM_PINS is no longer required.
pub const KVM_CAP_IOAPIC_NUM_PINS: u32 = 8191;
// TODO(qwandor): Update this once the pKVM patches are merged upstream with a stable capability ID.
pub const KVM_CAP_ARM_PROTECTED_VM: u32 = 0xffbadab1;
pub const KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE: u32 = 0;