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