From e8c330e2208f8998a6cba510ff5585c87ac4a2f3 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 4 Dec 2024 15:52:19 +0800 Subject: [PATCH] devices: Introduce RISC-V AIA interrupt device Introduce definitions, implementations and error variants of RISC-V AIA (Advance Interrupt Architecture) interrupt controller. Signed-off-by: Ruoqing He --- devices/src/aia.rs | 144 ++++++++++++++++++++++++++++ devices/src/interrupt_controller.rs | 11 ++- devices/src/lib.rs | 2 + 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 devices/src/aia.rs diff --git a/devices/src/aia.rs b/devices/src/aia.rs new file mode 100644 index 000000000..83ed1585f --- /dev/null +++ b/devices/src/aia.rs @@ -0,0 +1,144 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. +// Copyright 2020, ARM Limited. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause + +use super::interrupt_controller::{Error, InterruptController}; +extern crate arch; +use std::result; +use std::sync::{Arc, Mutex}; + +use arch::layout; +use hypervisor::arch::riscv64::aia::{Vaia, VaiaConfig}; +use hypervisor::{AiaState, CpuState}; +use vm_device::interrupt::{ + InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup, + LegacyIrqSourceConfig, MsiIrqGroupConfig, +}; +use vm_memory::address::Address; +use vm_migration::{Migratable, Pausable, Snapshottable, Transportable}; +use vmm_sys_util::eventfd::EventFd; + +type Result = result::Result; + +// Reserve 32 IRQs for legacy devices. +pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize; +pub const IRQ_LEGACY_COUNT: usize = 32; +// TODO: AIA snapshotting is not yet completed. +pub const _AIA_SNAPSHOT_ID: &str = ""; + +// Aia (Advance Interrupt Architecture) struct provides all the functionality of a +// AIA device. It wraps a hypervisor-emulated AIA device (Vaia) provided by the +// `hypervisor` crate. +// Aia struct also implements InterruptController to provide interrupt delivery +// service. +pub struct Aia { + interrupt_source_group: Arc, + // The hypervisor agnostic virtual AIA + vaia: Arc>, +} + +impl Aia { + pub fn new( + vcpu_count: u8, + interrupt_manager: Arc>, + vm: Arc, + ) -> Result { + let interrupt_source_group = interrupt_manager + .create_group(MsiIrqGroupConfig { + base: IRQ_LEGACY_BASE as InterruptIndex, + count: IRQ_LEGACY_COUNT as InterruptIndex, + }) + .map_err(Error::CreateInterruptSourceGroup)?; + + let vaia = vm + .create_vaia(Aia::create_default_config(vcpu_count as u64)) + .map_err(Error::CreateAia)?; + + let aia = Aia { + interrupt_source_group, + vaia, + }; + aia.enable()?; + + Ok(aia) + } + + pub fn restore_vaia( + &mut self, + state: Option, + _saved_vcpu_states: &[CpuState], + ) -> Result<()> { + self.vaia + .clone() + .lock() + .unwrap() + .set_state(&state.unwrap()) + .map_err(Error::RestoreAia) + } + + fn enable(&self) -> Result<()> { + // Set irqfd for legacy interrupts + self.interrupt_source_group + .enable() + .map_err(Error::EnableInterrupt)?; + + // Set irq_routing for legacy interrupts. + // irqchip: Hardcode to 0 as we support only 1 APLIC + // pin: Use irq number as pin + for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) { + let config = LegacyIrqSourceConfig { + irqchip: 0, + pin: (i - IRQ_LEGACY_BASE) as u32, + }; + self.interrupt_source_group + .update( + i as InterruptIndex, + InterruptSourceConfig::LegacyIrq(config), + false, + false, + ) + .map_err(Error::EnableInterrupt)?; + } + + self.interrupt_source_group + .set_gsi() + .map_err(Error::EnableInterrupt)?; + Ok(()) + } + + /// Default config implied by arch::layout + pub fn create_default_config(vcpu_count: u64) -> VaiaConfig { + VaiaConfig { + vcpu_count: vcpu_count as u32, + aplic_addr: layout::APLIC_START.raw_value(), + imsic_addr: layout::IMSIC_START.raw_value(), + nr_irqs: layout::IRQ_NUM, + } + } + + pub fn get_vaia(&mut self) -> Result>> { + Ok(self.vaia.clone()) + } +} + +impl InterruptController for Aia { + // 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(()) + } + + fn notifier(&self, irq: usize) -> Option { + self.interrupt_source_group.notifier(irq as InterruptIndex) + } +} + +impl Snapshottable for Aia {} +impl Pausable for Aia {} +impl Transportable for Aia {} +impl Migratable for Aia {} diff --git a/devices/src/interrupt_controller.rs b/devices/src/interrupt_controller.rs index f2deeb9de..92fcf3ba0 100644 --- a/devices/src/interrupt_controller.rs +++ b/devices/src/interrupt_controller.rs @@ -1,3 +1,4 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. // Copyright 2020, ARM Limited. // // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause @@ -41,6 +42,14 @@ pub enum Error { /// Failed restoring GIC device. #[error("Failed restoring GIC device: {0}")] RestoreGic(hypervisor::arch::aarch64::gic::Error), + #[cfg(target_arch = "riscv64")] + /// Failed creating AIA device. + #[error("Failed creating AIA device: {0}")] + CreateAia(hypervisor::HypervisorVmError), + #[cfg(target_arch = "riscv64")] + /// Failed restoring AIA device. + #[error("Failed restoring AIA device: {0}")] + RestoreAia(hypervisor::arch::riscv64::aia::Error), } type Result = result::Result; @@ -67,7 +76,7 @@ pub struct MsiMessage { // Introduce trait InterruptController to uniform the interrupt // service provided for devices. // Device manager uses this trait without caring whether it is a -// IOAPIC (X86) or GIC (Arm). +// IOAPIC (X86), GIC (Arm) or AIA (RISC-V). pub trait InterruptController: Send { fn service_irq(&mut self, irq: usize) -> Result<()>; #[cfg(target_arch = "x86_64")] diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 4a63fbf50..5778a1e65 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -15,6 +15,8 @@ extern crate event_monitor; extern crate log; pub mod acpi; +#[cfg(target_arch = "riscv64")] +pub mod aia; #[cfg(target_arch = "x86_64")] pub mod debug_console; #[cfg(target_arch = "aarch64")]