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