xref: /cloud-hypervisor/hypervisor/src/kvm/aarch64/gic/mod.rs (revision 906580ee92cd9ccda1afd771261b166e387ea6bf)
1 // Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 
5 mod dist_regs;
6 mod icc_regs;
7 mod redist_regs;
8 
9 use std::any::Any;
10 
11 use dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
12 use icc_regs::{get_icc_regs, set_icc_regs};
13 use kvm_ioctls::DeviceFd;
14 use redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
15 use serde::{Deserialize, Serialize};
16 
17 use crate::arch::aarch64::gic::{Error, Result, Vgic, VgicConfig};
18 use crate::device::HypervisorDeviceError;
19 use crate::kvm::KvmVm;
20 use crate::{CpuState, Vm};
21 
22 const GITS_CTLR: u32 = 0x0000;
23 const GITS_IIDR: u32 = 0x0004;
24 const GITS_CBASER: u32 = 0x0080;
25 const GITS_CWRITER: u32 = 0x0088;
26 const GITS_CREADR: u32 = 0x0090;
27 const GITS_BASER: u32 = 0x0100;
28 
29 fn gicv3_its_attr_set(its_device: &DeviceFd, group: u32, attr: u32, val: u64) -> Result<()> {
30     let gicv3_its_attr = kvm_bindings::kvm_device_attr {
31         group,
32         attr: attr as u64,
33         addr: &val as *const u64 as u64,
34         flags: 0,
35     };
36 
37     its_device
38         .set_device_attr(&gicv3_its_attr)
39         .map_err(|e| Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into())))
40 }
41 
42 fn gicv3_its_attr_get(its_device: &DeviceFd, group: u32, attr: u32) -> Result<u64> {
43     let mut val = 0;
44 
45     let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
46         group,
47         attr: attr as u64,
48         addr: &mut val as *mut u64 as u64,
49         flags: 0,
50     };
51 
52     // SAFETY: gicv3_its_attr.addr is safe to write to.
53     unsafe { its_device.get_device_attr(&mut gicv3_its_attr) }.map_err(|e| {
54         Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
55     })?;
56 
57     Ok(val)
58 }
59 
60 /// Function that saves/restores ITS tables into guest RAM.
61 ///
62 /// The tables get flushed to guest RAM whenever the VM gets stopped.
63 pub fn gicv3_its_tables_access(its_device: &DeviceFd, save: bool) -> Result<()> {
64     let attr = if save {
65         u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES)
66     } else {
67         u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES)
68     };
69 
70     let init_gic_attr = kvm_bindings::kvm_device_attr {
71         group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
72         attr,
73         addr: 0,
74         flags: 0,
75     };
76     its_device
77         .set_device_attr(&init_gic_attr)
78         .map_err(|e| Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into())))
79 }
80 
81 pub struct KvmGicV3Its {
82     /// The KVM device for the GicV3
83     device: DeviceFd,
84 
85     /// The KVM device for the Its device
86     its_device: Option<DeviceFd>,
87 
88     /// Vector holding values of GICR_TYPER for each vCPU
89     gicr_typers: Vec<u64>,
90 
91     /// GIC distributor address
92     dist_addr: u64,
93 
94     /// GIC distributor size
95     dist_size: u64,
96 
97     /// GIC distributors address
98     redists_addr: u64,
99 
100     /// GIC distributors size
101     redists_size: u64,
102 
103     /// GIC MSI address
104     msi_addr: u64,
105 
106     /// GIC MSI size
107     msi_size: u64,
108 
109     /// Number of CPUs handled by the device
110     vcpu_count: u64,
111 }
112 
113 #[derive(Clone, Default, Serialize, Deserialize)]
114 pub struct Gicv3ItsState {
115     dist: Vec<u32>,
116     rdist: Vec<u32>,
117     icc: Vec<u32>,
118     // special register that enables interrupts and affinity routing
119     gicd_ctlr: u32,
120     its_ctlr: u64,
121     its_iidr: u64,
122     its_cbaser: u64,
123     its_cwriter: u64,
124     its_creadr: u64,
125     its_baser: [u64; 8],
126 }
127 
128 impl KvmGicV3Its {
129     /// Device trees specific constants
130     pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
131 
132     /// Returns the GIC version of the device
133     fn version() -> u32 {
134         kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
135     }
136 
137     /// Setup the device-specific attributes
138     fn init_device_attributes(&mut self, vm: &KvmVm, nr_irqs: u32) -> Result<()> {
139         // GicV3 part attributes
140         /* Setting up the distributor attribute. */
141         Self::set_device_attribute(
142             &self.device,
143             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
144             u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
145             &self.dist_addr as *const u64 as u64,
146             0,
147         )?;
148 
149         /* Setting up the redistributors' attribute. */
150         Self::set_device_attribute(
151             &self.device,
152             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
153             u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
154             &self.redists_addr as *const u64 as u64,
155             0,
156         )?;
157 
158         // ITS part attributes
159         let mut its_device = kvm_bindings::kvm_create_device {
160             type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
161             fd: 0,
162             flags: 0,
163         };
164 
165         let its_fd = vm
166             .create_device(&mut its_device)
167             .map_err(Error::CreateGic)?;
168 
169         // We know vm is KvmVm
170         let its_fd = its_fd.to_kvm().unwrap();
171 
172         Self::set_device_attribute(
173             &its_fd,
174             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
175             u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
176             &self.msi_addr as *const u64 as u64,
177             0,
178         )?;
179 
180         Self::set_device_attribute(
181             &its_fd,
182             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
183             u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
184             0,
185             0,
186         )?;
187 
188         self.its_device = Some(its_fd);
189 
190         /* We need to tell the kernel how many irqs to support with this vgic.
191          * See the `layout` module for details.
192          */
193         let nr_irqs_ptr = &nr_irqs as *const u32;
194         Self::set_device_attribute(
195             &self.device,
196             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
197             0,
198             nr_irqs_ptr as u64,
199             0,
200         )?;
201 
202         /* Finalize the GIC.
203          * See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
204          */
205         Self::set_device_attribute(
206             &self.device,
207             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
208             u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
209             0,
210             0,
211         )
212     }
213 
214     /// Create a KVM Vgic device
215     fn create_device(vm: &KvmVm) -> Result<DeviceFd> {
216         let mut gic_device = kvm_bindings::kvm_create_device {
217             type_: Self::version(),
218             fd: 0,
219             flags: 0,
220         };
221 
222         let device_fd = vm
223             .create_device(&mut gic_device)
224             .map_err(Error::CreateGic)?;
225 
226         // We know for sure this is a KVM fd
227         Ok(device_fd.to_kvm().unwrap())
228     }
229 
230     /// Set a GIC device attribute
231     fn set_device_attribute(
232         device: &DeviceFd,
233         group: u32,
234         attr: u64,
235         addr: u64,
236         flags: u32,
237     ) -> Result<()> {
238         let attr = kvm_bindings::kvm_device_attr {
239             flags,
240             group,
241             attr,
242             addr,
243         };
244         device.set_device_attr(&attr).map_err(|e| {
245             Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
246         })
247     }
248 
249     /// Method to initialize the GIC device
250     pub fn new(vm: &dyn Vm, config: VgicConfig) -> Result<KvmGicV3Its> {
251         // This is inside KVM module
252         let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
253 
254         let vgic = Self::create_device(vm)?;
255 
256         let mut gic_device = KvmGicV3Its {
257             device: vgic,
258             its_device: None,
259             gicr_typers: vec![0; config.vcpu_count.try_into().unwrap()],
260             dist_addr: config.dist_addr,
261             dist_size: config.dist_size,
262             redists_addr: config.redists_addr,
263             redists_size: config.redists_size,
264             msi_addr: config.msi_addr,
265             msi_size: config.msi_size,
266             vcpu_count: config.vcpu_count,
267         };
268 
269         gic_device.init_device_attributes(vm, config.nr_irqs)?;
270 
271         Ok(gic_device)
272     }
273 }
274 
275 impl Vgic for KvmGicV3Its {
276     fn fdt_compatibility(&self) -> &str {
277         "arm,gic-v3"
278     }
279 
280     fn msi_compatible(&self) -> bool {
281         true
282     }
283 
284     fn msi_compatibility(&self) -> &str {
285         "arm,gic-v3-its"
286     }
287 
288     fn fdt_maint_irq(&self) -> u32 {
289         KvmGicV3Its::ARCH_GIC_V3_MAINT_IRQ
290     }
291 
292     fn vcpu_count(&self) -> u64 {
293         self.vcpu_count
294     }
295 
296     fn device_properties(&self) -> [u64; 4] {
297         [
298             self.dist_addr,
299             self.dist_size,
300             self.redists_addr,
301             self.redists_size,
302         ]
303     }
304 
305     fn msi_properties(&self) -> [u64; 2] {
306         [self.msi_addr, self.msi_size]
307     }
308 
309     fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) {
310         let gicr_typers = construct_gicr_typers(vcpu_states);
311         self.gicr_typers = gicr_typers;
312     }
313 
314     fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
315         self
316     }
317 
318     /// Save the state of GICv3ITS.
319     fn state(&self) -> Result<Gicv3ItsState> {
320         let gicr_typers = self.gicr_typers.clone();
321 
322         let gicd_ctlr = read_ctlr(&self.device)?;
323 
324         let dist_state = get_dist_regs(&self.device)?;
325 
326         let rdist_state = get_redist_regs(&self.device, &gicr_typers)?;
327 
328         let icc_state = get_icc_regs(&self.device, &gicr_typers)?;
329 
330         let mut its_baser_state: [u64; 8] = [0; 8];
331         for i in 0..8 {
332             its_baser_state[i as usize] = gicv3_its_attr_get(
333                 self.its_device.as_ref().unwrap(),
334                 kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
335                 GITS_BASER + i * 8,
336             )?;
337         }
338 
339         let its_ctlr_state = gicv3_its_attr_get(
340             self.its_device.as_ref().unwrap(),
341             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
342             GITS_CTLR,
343         )?;
344 
345         let its_cbaser_state = gicv3_its_attr_get(
346             self.its_device.as_ref().unwrap(),
347             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
348             GITS_CBASER,
349         )?;
350 
351         let its_creadr_state = gicv3_its_attr_get(
352             self.its_device.as_ref().unwrap(),
353             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
354             GITS_CREADR,
355         )?;
356 
357         let its_cwriter_state = gicv3_its_attr_get(
358             self.its_device.as_ref().unwrap(),
359             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
360             GITS_CWRITER,
361         )?;
362 
363         let its_iidr_state = gicv3_its_attr_get(
364             self.its_device.as_ref().unwrap(),
365             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
366             GITS_IIDR,
367         )?;
368 
369         Ok(Gicv3ItsState {
370             dist: dist_state,
371             rdist: rdist_state,
372             icc: icc_state,
373             gicd_ctlr,
374             its_ctlr: its_ctlr_state,
375             its_iidr: its_iidr_state,
376             its_cbaser: its_cbaser_state,
377             its_cwriter: its_cwriter_state,
378             its_creadr: its_creadr_state,
379             its_baser: its_baser_state,
380         })
381     }
382 
383     /// Restore the state of GICv3ITS.
384     fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
385         let gicr_typers = self.gicr_typers.clone();
386 
387         write_ctlr(&self.device, state.gicd_ctlr)?;
388 
389         set_dist_regs(&self.device, &state.dist)?;
390 
391         set_redist_regs(&self.device, &gicr_typers, &state.rdist)?;
392 
393         set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
394 
395         //Restore GICv3ITS registers
396         gicv3_its_attr_set(
397             self.its_device.as_ref().unwrap(),
398             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
399             GITS_IIDR,
400             state.its_iidr,
401         )?;
402 
403         gicv3_its_attr_set(
404             self.its_device.as_ref().unwrap(),
405             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
406             GITS_CBASER,
407             state.its_cbaser,
408         )?;
409 
410         gicv3_its_attr_set(
411             self.its_device.as_ref().unwrap(),
412             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
413             GITS_CREADR,
414             state.its_creadr,
415         )?;
416 
417         gicv3_its_attr_set(
418             self.its_device.as_ref().unwrap(),
419             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
420             GITS_CWRITER,
421             state.its_cwriter,
422         )?;
423 
424         for i in 0..8 {
425             gicv3_its_attr_set(
426                 self.its_device.as_ref().unwrap(),
427                 kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
428                 GITS_BASER + i * 8,
429                 state.its_baser[i as usize],
430             )?;
431         }
432 
433         // Restore ITS tables
434         gicv3_its_tables_access(self.its_device.as_ref().unwrap(), false)?;
435 
436         gicv3_its_attr_set(
437             self.its_device.as_ref().unwrap(),
438             kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
439             GITS_CTLR,
440             state.its_ctlr,
441         )
442     }
443 
444     /// Saves GIC internal data tables into RAM, including:
445     /// - RDIST pending tables
446     /// - ITS tables into guest RAM.
447     fn save_data_tables(&self) -> Result<()> {
448         // Flash RDIST pending tables
449         let init_gic_attr = kvm_bindings::kvm_device_attr {
450             group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
451             attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
452             addr: 0,
453             flags: 0,
454         };
455         self.device.set_device_attr(&init_gic_attr).map_err(|e| {
456             Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
457         })?;
458         // Flush ITS tables to guest RAM.
459         gicv3_its_tables_access(self.its_device.as_ref().unwrap(), true)
460     }
461 }
462 
463 #[cfg(test)]
464 mod tests {
465     use crate::aarch64::gic::{
466         get_dist_regs, get_icc_regs, get_redist_regs, set_dist_regs, set_icc_regs, set_redist_regs,
467     };
468     use crate::arch::aarch64::gic::VgicConfig;
469     use crate::kvm::KvmGicV3Its;
470 
471     fn create_test_vgic_config() -> VgicConfig {
472         VgicConfig {
473             vcpu_count: 1,
474             dist_addr: 0x0900_0000 - 0x01_0000,
475             dist_size: 0x01_0000,
476             // dist_addr - redists_size
477             redists_addr: 0x0900_0000 - 0x01_0000 - 0x02_0000,
478             redists_size: 0x02_0000,
479             // redists_addr - msi_size
480             msi_addr: 0x0900_0000 - 0x01_0000 - 0x02_0000 - 0x02_0000,
481             msi_size: 0x02_0000,
482             nr_irqs: 256,
483         }
484     }
485 
486     #[test]
487     fn test_create_gic() {
488         let hv = crate::new().unwrap();
489         let vm = hv.create_vm().unwrap();
490 
491         KvmGicV3Its::new(&*vm, create_test_vgic_config()).unwrap();
492     }
493 
494     #[test]
495     fn test_get_set_dist_regs() {
496         let hv = crate::new().unwrap();
497         let vm = hv.create_vm().unwrap();
498         let _ = vm.create_vcpu(0, None).unwrap();
499         let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
500 
501         let state = get_dist_regs(&gic.device).unwrap();
502         assert_eq!(state.len(), 568);
503 
504         set_dist_regs(&gic.device, &state).unwrap();
505     }
506 
507     #[test]
508     fn test_get_set_redist_regs() {
509         let hv = crate::new().unwrap();
510         let vm = hv.create_vm().unwrap();
511         let _ = vm.create_vcpu(0, None).unwrap();
512         let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
513 
514         let gicr_typer = vec![123];
515         let state = get_redist_regs(&gic.device, &gicr_typer).unwrap();
516         println!("{}", state.len());
517         assert!(state.len() == 24);
518 
519         set_redist_regs(&gic.device, &gicr_typer, &state).unwrap();
520     }
521 
522     #[test]
523     fn test_get_set_icc_regs() {
524         let hv = crate::new().unwrap();
525         let vm = hv.create_vm().unwrap();
526         let _ = vm.create_vcpu(0, None).unwrap();
527         let gic = KvmGicV3Its::new(&*vm, create_test_vgic_config()).expect("Cannot create gic");
528 
529         let gicr_typer = vec![123];
530         let state = get_icc_regs(&gic.device, &gicr_typer).unwrap();
531         println!("{}", state.len());
532         assert!(state.len() == 9);
533 
534         set_icc_regs(&gic.device, &gicr_typer, &state).unwrap();
535     }
536 
537     #[test]
538     fn test_save_data_tables() {
539         let hv = crate::new().unwrap();
540         let vm = hv.create_vm().unwrap();
541         let _ = vm.create_vcpu(0, None).unwrap();
542         let gic = vm
543             .create_vgic(create_test_vgic_config())
544             .expect("Cannot create gic");
545 
546         gic.lock().unwrap().save_data_tables().unwrap();
547     }
548 }
549