xref: /cloud-hypervisor/devices/src/gic.rs (revision 80b2c98a68d4c68f372f849e8d26f7cae5867000)
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 std::result;
8 use std::sync::{Arc, Mutex};
9 
10 use anyhow::anyhow;
11 use arch::layout;
12 use hypervisor::arch::aarch64::gic::{Vgic, VgicConfig};
13 use hypervisor::{CpuState, GicState};
14 use vm_device::interrupt::{
15     InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
16     LegacyIrqSourceConfig, MsiIrqGroupConfig,
17 };
18 use vm_memory::address::Address;
19 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
20 use vmm_sys_util::eventfd::EventFd;
21 
22 type Result<T> = result::Result<T, Error>;
23 
24 // Reserve 32 IRQs for legacy devices.
25 pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
26 pub const IRQ_LEGACY_COUNT: usize = 32;
27 pub const GIC_SNAPSHOT_ID: &str = "gic-v3-its";
28 
29 // Gic (Generic Interrupt Controller) struct provides all the functionality of a
30 // GIC device. It wraps a hypervisor-emulated GIC device (Vgic) provided by the
31 // `hypervisor` crate.
32 // Gic struct also implements InterruptController to provide interrupt delivery
33 // service.
34 pub struct Gic {
35     interrupt_source_group: Arc<dyn InterruptSourceGroup>,
36     // The hypervisor agnostic virtual GIC
37     vgic: Option<Arc<Mutex<dyn Vgic>>>,
38 }
39 
40 impl Gic {
41     pub fn new(
42         vcpu_count: u8,
43         interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
44         vm: Arc<dyn hypervisor::Vm>,
45     ) -> Result<Gic> {
46         let interrupt_source_group = interrupt_manager
47             .create_group(MsiIrqGroupConfig {
48                 base: IRQ_LEGACY_BASE as InterruptIndex,
49                 count: IRQ_LEGACY_COUNT as InterruptIndex,
50             })
51             .map_err(Error::CreateInterruptSourceGroup)?;
52 
53         let vgic = vm
54             .create_vgic(Gic::create_default_config(vcpu_count as u64))
55             .map_err(Error::CreateGic)?;
56 
57         let gic = Gic {
58             interrupt_source_group,
59             vgic: Some(vgic),
60         };
61         gic.enable()?;
62 
63         Ok(gic)
64     }
65 
66     pub fn restore_vgic(
67         &mut self,
68         state: Option<GicState>,
69         saved_vcpu_states: &[CpuState],
70     ) -> Result<()> {
71         self.set_gicr_typers(saved_vcpu_states);
72         self.vgic
73             .clone()
74             .unwrap()
75             .lock()
76             .unwrap()
77             .set_state(&state.unwrap())
78             .map_err(Error::RestoreGic)
79     }
80 
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                     false,
101                 )
102                 .map_err(Error::EnableInterrupt)?;
103         }
104 
105         self.interrupt_source_group
106             .set_gsi()
107             .map_err(Error::EnableInterrupt)?;
108         Ok(())
109     }
110 
111     /// Default config implied by arch::layout
112     pub fn create_default_config(vcpu_count: u64) -> VgicConfig {
113         let redists_size = layout::GIC_V3_REDIST_SIZE * vcpu_count;
114         let redists_addr = layout::GIC_V3_DIST_START.raw_value() - redists_size;
115         VgicConfig {
116             vcpu_count,
117             dist_addr: layout::GIC_V3_DIST_START.raw_value(),
118             dist_size: layout::GIC_V3_DIST_SIZE,
119             redists_addr,
120             redists_size,
121             msi_addr: redists_addr - layout::GIC_V3_ITS_SIZE,
122             msi_size: layout::GIC_V3_ITS_SIZE,
123             nr_irqs: layout::IRQ_NUM,
124         }
125     }
126 
127     pub fn get_vgic(&mut self) -> Result<Arc<Mutex<dyn Vgic>>> {
128         Ok(self.vgic.clone().unwrap())
129     }
130 
131     pub fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
132         let vgic = self.vgic.as_ref().unwrap().clone();
133         vgic.lock().unwrap().set_gicr_typers(vcpu_states);
134     }
135 }
136 
137 impl InterruptController for Gic {
138     // This should be called anytime an interrupt needs to be injected into the
139     // running guest.
140     fn service_irq(&mut self, irq: usize) -> Result<()> {
141         self.interrupt_source_group
142             .trigger(irq as InterruptIndex)
143             .map_err(Error::TriggerInterrupt)?;
144 
145         Ok(())
146     }
147 
148     fn notifier(&self, irq: usize) -> Option<EventFd> {
149         self.interrupt_source_group.notifier(irq as InterruptIndex)
150     }
151 }
152 
153 impl Snapshottable for Gic {
154     fn id(&self) -> String {
155         GIC_SNAPSHOT_ID.to_string()
156     }
157 
158     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
159         let vgic = self.vgic.as_ref().unwrap().clone();
160         let state = vgic.lock().unwrap().state().unwrap();
161         Snapshot::new_from_state(&state)
162     }
163 }
164 
165 impl Pausable for Gic {
166     fn pause(&mut self) -> std::result::Result<(), MigratableError> {
167         // Flush tables to guest RAM
168         let vgic = self.vgic.as_ref().unwrap().clone();
169         vgic.lock().unwrap().save_data_tables().map_err(|e| {
170             MigratableError::Pause(anyhow!(
171                 "Could not save GICv3ITS GIC pending tables {:?}",
172                 e
173             ))
174         })?;
175         Ok(())
176     }
177 }
178 impl Transportable for Gic {}
179 impl Migratable for Gic {}
180