xref: /cloud-hypervisor/devices/src/gic.rs (revision b440cb7d2330770cd415b63544a371d4caa2db3a)
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 anyhow::anyhow;
8 use arch::layout;
9 use hypervisor::{arch::aarch64::gic::Vgic, CpuState};
10 use std::result;
11 use std::sync::{Arc, Mutex};
12 use vm_device::interrupt::{
13     InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
14     LegacyIrqSourceConfig, MsiIrqGroupConfig,
15 };
16 use vm_memory::Address;
17 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
18 use vmm_sys_util::eventfd::EventFd;
19 
20 type Result<T> = result::Result<T, Error>;
21 
22 // Reserve 32 IRQs for legacy devices.
23 pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
24 pub const IRQ_LEGACY_COUNT: usize = 32;
25 
26 // Gic (Generic Interupt Controller) struct provides all the functionality of a
27 // GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
28 // `hypervisor` crate.
29 // Gic struct also implements InterruptController to provide interrupt delivery
30 // service.
31 pub struct Gic {
32     interrupt_source_group: Arc<dyn InterruptSourceGroup>,
33     // The hypervisor agnostic virtual GIC
34     vgic: Option<Arc<Mutex<dyn Vgic>>>,
35 }
36 
37 impl Gic {
38     pub fn new(
39         _vcpu_count: u8,
40         interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
41     ) -> Result<Gic> {
42         let interrupt_source_group = interrupt_manager
43             .create_group(MsiIrqGroupConfig {
44                 base: IRQ_LEGACY_BASE as InterruptIndex,
45                 count: IRQ_LEGACY_COUNT as InterruptIndex,
46             })
47             .map_err(Error::CreateInterruptSourceGroup)?;
48 
49         Ok(Gic {
50             interrupt_source_group,
51             vgic: None,
52         })
53     }
54 
55     pub fn create_vgic(
56         &mut self,
57         vm: &Arc<dyn hypervisor::Vm>,
58         vcpu_count: u64,
59     ) -> Result<Arc<Mutex<dyn Vgic>>> {
60         let vgic = vm
61             .create_vgic(
62                 vcpu_count,
63                 layout::GIC_V3_DIST_START.raw_value(),
64                 layout::GIC_V3_DIST_SIZE,
65                 layout::GIC_V3_REDIST_SIZE,
66                 layout::GIC_V3_ITS_SIZE,
67                 layout::IRQ_NUM,
68             )
69             .map_err(Error::CreateGic)?;
70         self.vgic = Some(vgic.clone());
71         Ok(vgic.clone())
72     }
73 
74     pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
75         let vgic = self.vgic.as_ref().unwrap().clone();
76         vgic.lock().unwrap().set_gicr_typers(vcpu_states);
77     }
78 }
79 
80 impl InterruptController for Gic {
81     fn enable(&self) -> Result<()> {
82         // Set irqfd for legacy interrupts
83         self.interrupt_source_group
84             .enable()
85             .map_err(Error::EnableInterrupt)?;
86 
87         // Set irq_routing for legacy interrupts.
88         //   irqchip: Hardcode to 0 as we support only 1 GIC
89         //   pin: Use irq number as pin
90         for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) {
91             let config = LegacyIrqSourceConfig {
92                 irqchip: 0,
93                 pin: (i - IRQ_LEGACY_BASE) as u32,
94             };
95             self.interrupt_source_group
96                 .update(
97                     i as InterruptIndex,
98                     InterruptSourceConfig::LegacyIrq(config),
99                     false,
100                 )
101                 .map_err(Error::EnableInterrupt)?;
102         }
103         Ok(())
104     }
105 
106     // This should be called anytime an interrupt needs to be injected into the
107     // running guest.
108     fn service_irq(&mut self, irq: usize) -> Result<()> {
109         self.interrupt_source_group
110             .trigger(irq as InterruptIndex)
111             .map_err(Error::TriggerInterrupt)?;
112 
113         Ok(())
114     }
115 
116     fn notifier(&self, irq: usize) -> Option<EventFd> {
117         self.interrupt_source_group.notifier(irq as InterruptIndex)
118     }
119 }
120 
121 pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its";
122 impl Snapshottable for Gic {
123     fn id(&self) -> String {
124         GIC_V3_ITS_SNAPSHOT_ID.to_string()
125     }
126 
127     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
128         let vgic = self.vgic.as_ref().unwrap().clone();
129         let state = vgic.lock().unwrap().state().unwrap();
130         Snapshot::new_from_state(&self.id(), &state)
131     }
132 
133     fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
134         let vgic = self.vgic.as_ref().unwrap().clone();
135         vgic.lock()
136             .unwrap()
137             .set_state(&snapshot.to_state(&self.id())?)
138             .map_err(|e| {
139                 MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e))
140             })?;
141         Ok(())
142     }
143 }
144 
145 impl Pausable for Gic {
146     fn pause(&mut self) -> std::result::Result<(), MigratableError> {
147         // Flush tables to guest RAM
148         let vgic = self.vgic.as_ref().unwrap().clone();
149         vgic.lock().unwrap().save_data_tables().map_err(|e| {
150             MigratableError::Pause(anyhow!(
151                 "Could not save GICv3ITS GIC pending tables {:?}",
152                 e
153             ))
154         })?;
155         Ok(())
156     }
157 }
158 impl Transportable for Gic {}
159 impl Migratable for Gic {}
160