xref: /cloud-hypervisor/devices/src/gic.rs (revision f7f2f25a574b1b2dba22c094fc8226d404157d15)
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