1 // Copyright 2020, ARM Limited. 2 // 3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 4 5 use super::interrupt_controller::{Error, InterruptController}; 6 extern crate arch; 7 use arch::aarch64::gic::GicDevice; 8 use std::result; 9 use std::sync::{Arc, Mutex}; 10 use vm_device::interrupt::{ 11 InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup, 12 LegacyIrqSourceConfig, MsiIrqGroupConfig, 13 }; 14 use vmm_sys_util::eventfd::EventFd; 15 16 type Result<T> = result::Result<T, Error>; 17 18 // Reserve 32 IRQs for legacy device. 19 pub const IRQ_LEGACY_BASE: usize = arch::layout::IRQ_BASE as usize; 20 pub const IRQ_LEGACY_COUNT: usize = 32; 21 22 // This Gic struct implements InterruptController to provide interrupt delivery service. 23 // The Gic source files in arch/ folder maintain the Aarch64 specific Gic device. 24 // The 2 Gic instances could be merged together. 25 // Leave this refactoring to future. Two options may be considered: 26 // 1. Move Gic*.rs from arch/ folder here. 27 // 2. Move this file and ioapic.rs to arch/, as they are architecture specific. 28 pub struct Gic { 29 interrupt_source_group: Arc<dyn InterruptSourceGroup>, 30 gic_device: Option<Arc<Mutex<Box<dyn GicDevice>>>>, 31 } 32 33 impl Gic { 34 pub fn new( 35 _vcpu_count: u8, 36 interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>, 37 ) -> Result<Gic> { 38 let interrupt_source_group = interrupt_manager 39 .create_group(MsiIrqGroupConfig { 40 base: IRQ_LEGACY_BASE as InterruptIndex, 41 count: IRQ_LEGACY_COUNT as InterruptIndex, 42 }) 43 .map_err(Error::CreateInterruptSourceGroup)?; 44 45 Ok(Gic { 46 interrupt_source_group, 47 gic_device: None, 48 }) 49 } 50 51 pub fn set_gic_device(&mut self, gic_device: Arc<Mutex<Box<dyn GicDevice>>>) { 52 self.gic_device = Some(gic_device); 53 } 54 55 pub fn get_gic_device(&self) -> Option<&Arc<Mutex<Box<dyn GicDevice>>>> { 56 self.gic_device.as_ref() 57 } 58 } 59 60 impl InterruptController for Gic { 61 fn enable(&self) -> Result<()> { 62 // Set irqfd for legacy interrupts 63 self.interrupt_source_group 64 .enable() 65 .map_err(Error::EnableInterrupt)?; 66 67 // Set irq_routing for legacy interrupts. 68 // irqchip: Hardcode to 0 as we support only 1 GIC 69 // pin: Use irq number as pin 70 for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) { 71 let config = LegacyIrqSourceConfig { 72 irqchip: 0, 73 pin: (i - IRQ_LEGACY_BASE) as u32, 74 }; 75 self.interrupt_source_group 76 .update( 77 i as InterruptIndex, 78 InterruptSourceConfig::LegacyIrq(config), 79 ) 80 .map_err(Error::EnableInterrupt)?; 81 } 82 Ok(()) 83 } 84 85 // This should be called anytime an interrupt needs to be injected into the 86 // running guest. 87 fn service_irq(&mut self, irq: usize) -> Result<()> { 88 self.interrupt_source_group 89 .trigger(irq as InterruptIndex) 90 .map_err(Error::TriggerInterrupt)?; 91 92 Ok(()) 93 } 94 95 fn notifier(&self, irq: usize) -> Option<EventFd> { 96 self.interrupt_source_group.notifier(irq as InterruptIndex) 97 } 98 } 99