xref: /aosp_15_r20/external/crosvm/devices/src/irqchip/kvm/aarch64.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::sync::Arc;
6 
7 use anyhow::Context;
8 use base::errno_result;
9 use base::ioctl_with_ref;
10 use base::Result;
11 use base::SafeDescriptor;
12 use hypervisor::kvm::KvmVcpu;
13 use hypervisor::kvm::KvmVm;
14 use hypervisor::DeviceKind;
15 use hypervisor::IrqRoute;
16 use hypervisor::Vm;
17 use kvm_sys::*;
18 use sync::Mutex;
19 
20 use crate::IrqChip;
21 use crate::IrqChipAArch64;
22 
23 /// Default ARM routing table.  AARCH64_GIC_NR_SPIS pins go to VGIC.
kvm_default_irq_routing_table() -> Vec<IrqRoute>24 fn kvm_default_irq_routing_table() -> Vec<IrqRoute> {
25     let mut routes: Vec<IrqRoute> = Vec::new();
26 
27     for i in 0..AARCH64_GIC_NR_SPIS {
28         routes.push(IrqRoute::gic_irq_route(i));
29     }
30 
31     routes
32 }
33 
34 /// IrqChip implementation where the entire IrqChip is emulated by KVM.
35 ///
36 /// This implementation will use the KVM API to create and configure the in-kernel irqchip.
37 pub struct KvmKernelIrqChip {
38     pub(super) vm: KvmVm,
39     pub(super) vcpus: Arc<Mutex<Vec<Option<KvmVcpu>>>>,
40     vgic: SafeDescriptor,
41     device_kind: DeviceKind,
42     pub(super) routes: Arc<Mutex<Vec<IrqRoute>>>,
43 }
44 
45 // These constants indicate the address space used by the ARM vGIC.
46 const AARCH64_GIC_DIST_SIZE: u64 = 0x10000;
47 const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000;
48 
49 // These constants indicate the placement of the GIC registers in the physical
50 // address space.
51 const AARCH64_GIC_DIST_BASE: u64 = 0x40000000 - AARCH64_GIC_DIST_SIZE;
52 const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE;
53 const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000;
54 
55 // This is the minimum number of SPI interrupts aligned to 32 + 32 for the
56 // PPI (16) and GSI (16).
57 pub const AARCH64_GIC_NR_IRQS: u32 = 64;
58 // Number of SPIs (32), which is the NR_IRQS (64) minus the number of PPIs (16) and GSIs (16)
59 pub const AARCH64_GIC_NR_SPIS: u32 = 32;
60 
61 impl KvmKernelIrqChip {
62     /// Construct a new KvmKernelIrqchip.
new(vm: KvmVm, num_vcpus: usize) -> Result<KvmKernelIrqChip>63     pub fn new(vm: KvmVm, num_vcpus: usize) -> Result<KvmKernelIrqChip> {
64         let cpu_if_addr: u64 = AARCH64_GIC_CPUI_BASE;
65         let dist_if_addr: u64 = AARCH64_GIC_DIST_BASE;
66         let redist_addr: u64 = dist_if_addr - (AARCH64_GIC_REDIST_SIZE * num_vcpus as u64);
67         let raw_cpu_if_addr = &cpu_if_addr as *const u64;
68         let raw_dist_if_addr = &dist_if_addr as *const u64;
69         let raw_redist_addr = &redist_addr as *const u64;
70 
71         let cpu_if_attr = kvm_device_attr {
72             group: KVM_DEV_ARM_VGIC_GRP_ADDR,
73             attr: KVM_VGIC_V2_ADDR_TYPE_CPU as u64,
74             addr: raw_cpu_if_addr as u64,
75             flags: 0,
76         };
77         let redist_attr = kvm_device_attr {
78             group: KVM_DEV_ARM_VGIC_GRP_ADDR,
79             attr: KVM_VGIC_V3_ADDR_TYPE_REDIST as u64,
80             addr: raw_redist_addr as u64,
81             flags: 0,
82         };
83         let mut dist_attr = kvm_device_attr {
84             group: KVM_DEV_ARM_VGIC_GRP_ADDR,
85             addr: raw_dist_if_addr as u64,
86             attr: 0,
87             flags: 0,
88         };
89 
90         let (vgic, device_kind, cpu_redist_attr, dist_attr_attr) =
91             match vm.create_device(DeviceKind::ArmVgicV3) {
92                 Err(_) => (
93                     vm.create_device(DeviceKind::ArmVgicV2)?,
94                     DeviceKind::ArmVgicV2,
95                     cpu_if_attr,
96                     KVM_VGIC_V2_ADDR_TYPE_DIST as u64,
97                 ),
98                 Ok(vgic) => (
99                     vgic,
100                     DeviceKind::ArmVgicV3,
101                     redist_attr,
102                     KVM_VGIC_V3_ADDR_TYPE_DIST as u64,
103                 ),
104             };
105         dist_attr.attr = dist_attr_attr;
106 
107         // SAFETY:
108         // Safe because we allocated the struct that's being passed in
109         let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR, &cpu_redist_attr) };
110         if ret != 0 {
111             return errno_result();
112         }
113 
114         // SAFETY:
115         // Safe because we allocated the struct that's being passed in
116         let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR, &dist_attr) };
117         if ret != 0 {
118             return errno_result();
119         }
120 
121         // We need to tell the kernel how many irqs to support with this vgic
122         let nr_irqs: u32 = AARCH64_GIC_NR_IRQS;
123         let nr_irqs_ptr = &nr_irqs as *const u32;
124         let nr_irqs_attr = kvm_device_attr {
125             group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
126             attr: 0,
127             addr: nr_irqs_ptr as u64,
128             flags: 0,
129         };
130         // SAFETY:
131         // Safe because we allocated the struct that's being passed in
132         let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR, &nr_irqs_attr) };
133         if ret != 0 {
134             return errno_result();
135         }
136 
137         Ok(KvmKernelIrqChip {
138             vm,
139             vcpus: Arc::new(Mutex::new((0..num_vcpus).map(|_| None).collect())),
140             vgic,
141             device_kind,
142             routes: Arc::new(Mutex::new(kvm_default_irq_routing_table())),
143         })
144     }
145 
146     /// Attempt to create a shallow clone of this aarch64 KvmKernelIrqChip instance.
arch_try_clone(&self) -> Result<Self>147     pub(super) fn arch_try_clone(&self) -> Result<Self> {
148         Ok(KvmKernelIrqChip {
149             vm: self.vm.try_clone()?,
150             vcpus: self.vcpus.clone(),
151             vgic: self.vgic.try_clone()?,
152             device_kind: self.device_kind,
153             routes: self.routes.clone(),
154         })
155     }
156 }
157 
158 impl IrqChipAArch64 for KvmKernelIrqChip {
try_box_clone(&self) -> Result<Box<dyn IrqChipAArch64>>159     fn try_box_clone(&self) -> Result<Box<dyn IrqChipAArch64>> {
160         Ok(Box::new(self.try_clone()?))
161     }
162 
as_irq_chip(&self) -> &dyn IrqChip163     fn as_irq_chip(&self) -> &dyn IrqChip {
164         self
165     }
166 
as_irq_chip_mut(&mut self) -> &mut dyn IrqChip167     fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip {
168         self
169     }
170 
get_vgic_version(&self) -> DeviceKind171     fn get_vgic_version(&self) -> DeviceKind {
172         self.device_kind
173     }
174 
snapshot(&self, _cpus_num: usize) -> anyhow::Result<serde_json::Value>175     fn snapshot(&self, _cpus_num: usize) -> anyhow::Result<serde_json::Value> {
176         if self.device_kind == DeviceKind::ArmVgicV3 {
177             let save_gic_attr = kvm_device_attr {
178                 group: KVM_DEV_ARM_VGIC_GRP_CTRL,
179                 attr: KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64,
180                 addr: 0,
181                 flags: 0,
182             };
183             // SAFETY:
184             // Safe because we allocated the struct that's being passed in
185             // Safe because the device interrupts get stored in guest memory
186             let ret = unsafe { ioctl_with_ref(&self.vgic, KVM_SET_DEVICE_ATTR, &save_gic_attr) };
187             if ret != 0 {
188                 return errno_result()
189                     .context("ioctl KVM_SET_DEVICE_ATTR for save_gic_attr failed.")?;
190             }
191         }
192         Ok(serde_json::Value::Null)
193     }
194 
restore(&mut self, _data: serde_json::Value, _vcpus_num: usize) -> anyhow::Result<()>195     fn restore(&mut self, _data: serde_json::Value, _vcpus_num: usize) -> anyhow::Result<()> {
196         // SAVE_PENDING_TABLES operation wrote the pending tables into guest memory.
197         // Assumption is that no work is necessary on restore of IrqChip.
198         Ok(())
199     }
200 
finalize(&self) -> Result<()>201     fn finalize(&self) -> Result<()> {
202         let init_gic_attr = kvm_device_attr {
203             group: KVM_DEV_ARM_VGIC_GRP_CTRL,
204             attr: KVM_DEV_ARM_VGIC_CTRL_INIT as u64,
205             addr: 0,
206             flags: 0,
207         };
208 
209         // SAFETY:
210         // Safe because we allocated the struct that's being passed in
211         let ret = unsafe { ioctl_with_ref(&self.vgic, KVM_SET_DEVICE_ATTR, &init_gic_attr) };
212         if ret != 0 {
213             return errno_result();
214         }
215         Ok(())
216     }
217 }
218