From 8f1f9d9e6b5ce0e20b8182a1ebb745fdd533c97d Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Mon, 25 May 2020 16:59:09 +0800 Subject: [PATCH] devices: Implement InterruptController on AArch64 This commit only implements the InterruptController crate on AArch64. The device specific part for GIC is to be added. Signed-off-by: Michael Zhao --- devices/src/gic.rs | 85 +++++++++++++++++++++++++++++ devices/src/interrupt_controller.rs | 3 + devices/src/lib.rs | 3 + vmm/src/device_manager.rs | 34 +++++++++++- 4 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 devices/src/gic.rs diff --git a/devices/src/gic.rs b/devices/src/gic.rs new file mode 100644 index 000000000..eb04ff189 --- /dev/null +++ b/devices/src/gic.rs @@ -0,0 +1,85 @@ +// Copyright 2020, ARM Limited. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause + +use super::interrupt_controller::{Error, InterruptController}; +use std::result; +use std::sync::Arc; +use vm_device::interrupt::{ + InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig, +}; +use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; + +type Result = result::Result; + +// Reserve 32 IRQs (GSI 32 ~ 64) for legacy device. +// GsiAllocator should allocate beyond this: from 64 on +pub const IRQ_LEGACY_COUNT: usize = 32; +pub const IRQ_SPI_OFFSET: usize = 32; + +// This Gic struct implements InterruptController to provide interrupt delivery service. +// The Gic source files in arch/ folder maintain the Aarch64 specific Gic device. +// The 2 Gic instances could be merged together. +// Leave this refactoring to future. Two options may be considered: +// 1. Move Gic*.rs from arch/ folder here. +// 2. Move this file and ioapic.rs to arch/, as they are architecture specific. +pub struct Gic { + interrupt_source_group: Arc>, +} + +impl Gic { + pub fn new( + _vcpu_count: u8, + interrupt_manager: Arc>, + ) -> Result { + let interrupt_source_group = interrupt_manager + .create_group(MsiIrqGroupConfig { + base: IRQ_SPI_OFFSET as InterruptIndex, + count: IRQ_LEGACY_COUNT as InterruptIndex, + }) + .map_err(Error::CreateInterruptSourceGroup)?; + + Ok(Gic { + interrupt_source_group, + }) + } +} + +impl InterruptController for Gic { + fn enable(&self) -> Result<()> { + &self + .interrupt_source_group + .enable() + .map_err(Error::EnableInterrupt)?; + Ok(()) + } + + // This should be called anytime an interrupt needs to be injected into the + // running guest. + fn service_irq(&mut self, irq: usize) -> Result<()> { + self.interrupt_source_group + .trigger(irq as InterruptIndex) + .map_err(Error::TriggerInterrupt)?; + + Ok(()) + } +} + +const GIC_SNAPSHOT_ID: &str = "gic"; +impl Snapshottable for Gic { + fn id(&self) -> String { + GIC_SNAPSHOT_ID.to_string() + } + + fn snapshot(&self) -> std::result::Result { + unimplemented!(); + } + + fn restore(&mut self, _snapshot: Snapshot) -> std::result::Result<(), MigratableError> { + unimplemented!(); + } +} + +impl Pausable for Gic {} +impl Transportable for Gic {} +impl Migratable for Gic {} diff --git a/devices/src/interrupt_controller.rs b/devices/src/interrupt_controller.rs index ad31a0f23..2b6f4a671 100644 --- a/devices/src/interrupt_controller.rs +++ b/devices/src/interrupt_controller.rs @@ -54,5 +54,8 @@ pub struct MsiMessage { // IOAPIC (X86) or GIC (Arm). pub trait InterruptController: Send { fn service_irq(&mut self, irq: usize) -> Result<()>; + #[cfg(target_arch = "aarch64")] + fn enable(&self) -> Result<()>; + #[cfg(target_arch = "x86_64")] fn end_of_interrupt(&mut self, vec: u8); } diff --git a/devices/src/lib.rs b/devices/src/lib.rs index d936f0fd0..329eaff70 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -31,7 +31,10 @@ use std::io; #[cfg(feature = "acpi")] mod acpi; mod bus; +#[cfg(target_arch = "aarch64")] +pub mod gic; pub mod interrupt_controller; +#[cfg(target_arch = "x86_64")] pub mod ioapic; pub mod legacy; diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index cc2d81181..3659545f2 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -26,8 +26,12 @@ use anyhow::anyhow; use arch::layout; #[cfg(target_arch = "x86_64")] use arch::layout::{APIC_START, IOAPIC_SIZE, IOAPIC_START}; +#[cfg(target_arch = "aarch64")] +use devices::gic; +#[cfg(target_arch = "x86_64")] +use devices::ioapic; use devices::{ - interrupt_controller, interrupt_controller::InterruptController, ioapic, BusDevice, + interrupt_controller, interrupt_controller::InterruptController, BusDevice, HotPlugNotificationFlags, }; use kvm_ioctls::*; @@ -81,6 +85,9 @@ const VFIO_DEVICE_NAME_PREFIX: &str = "_vfio"; #[cfg(target_arch = "x86_64")] const IOAPIC_DEVICE_NAME: &str = "_ioapic"; +#[cfg(target_arch = "aarch64")] +const GIC_DEVICE_NAME: &str = "_gic"; + const SERIAL_DEVICE_NAME_PREFIX: &str = "_serial"; const CONSOLE_DEVICE_NAME: &str = "_console"; @@ -630,7 +637,10 @@ pub struct DeviceManager { console: Arc, // Interrupt controller + #[cfg(target_arch = "x86_64")] interrupt_controller: Option>>, + #[cfg(target_arch = "aarch64")] + interrupt_controller: Option>>, // Things to be added to the commandline (i.e. for virtio-mmio) cmdline_additions: Vec, @@ -1011,7 +1021,27 @@ impl DeviceManager { fn add_interrupt_controller( &mut self, ) -> DeviceManagerResult>> { - unimplemented!(); + let id = String::from(GIC_DEVICE_NAME); + + let interrupt_controller: Arc> = Arc::new(Mutex::new( + gic::Gic::new( + self.config.lock().unwrap().cpus.boot_vcpus, + Arc::clone(&self.msi_interrupt_manager), + ) + .map_err(DeviceManagerError::CreateInterruptController)?, + )); + + self.interrupt_controller = Some(interrupt_controller.clone()); + + // Fill the device tree with a new node. In case of restore, we + // know there is nothing to do, so we can simply override the + // existing entry. + self.device_tree + .lock() + .unwrap() + .insert(id.clone(), device_node!(id, interrupt_controller)); + + Ok(interrupt_controller) } #[cfg(target_arch = "x86_64")]