1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
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 libc::{EINVAL, ENOMEM, ENOSYS, ENXIO};
6
7 use base::{
8 errno_result, error, ioctl_with_mut_ref, ioctl_with_ref, Error, MemoryMappingBuilder, Result,
9 };
10 use kvm_sys::*;
11 use vm_memory::GuestAddress;
12
13 use super::{KvmCap, KvmVcpu, KvmVm};
14 use crate::{
15 ClockState, DeviceKind, Hypervisor, IrqSourceChip, PsciVersion, VcpuAArch64, VcpuFeature, Vm,
16 VmAArch64, VmCap,
17 };
18
19 impl KvmVm {
20 /// Checks if a particular `VmCap` is available, or returns None if arch-independent
21 /// Vm.check_capability() should handle the check.
check_capability_arch(&self, _c: VmCap) -> Option<bool>22 pub fn check_capability_arch(&self, _c: VmCap) -> Option<bool> {
23 None
24 }
25
26 /// Returns the params to pass to KVM_CREATE_DEVICE for a `kind` device on this arch, or None to
27 /// let the arch-independent `KvmVm::create_device` handle it.
get_device_params_arch(&self, kind: DeviceKind) -> Option<kvm_create_device>28 pub fn get_device_params_arch(&self, kind: DeviceKind) -> Option<kvm_create_device> {
29 match kind {
30 DeviceKind::ArmVgicV2 => Some(kvm_create_device {
31 type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2,
32 fd: 0,
33 flags: 0,
34 }),
35 DeviceKind::ArmVgicV3 => Some(kvm_create_device {
36 type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
37 fd: 0,
38 flags: 0,
39 }),
40 _ => None,
41 }
42 }
43
44 /// Arch-specific implementation of `Vm::get_pvclock`. Always returns an error on AArch64.
get_pvclock_arch(&self) -> Result<ClockState>45 pub fn get_pvclock_arch(&self) -> Result<ClockState> {
46 Err(Error::new(ENXIO))
47 }
48
49 /// Arch-specific implementation of `Vm::set_pvclock`. Always returns an error on AArch64.
set_pvclock_arch(&self, _state: &ClockState) -> Result<()>50 pub fn set_pvclock_arch(&self, _state: &ClockState) -> Result<()> {
51 Err(Error::new(ENXIO))
52 }
53
get_protected_vm_info(&self) -> Result<KvmProtectedVmInfo>54 fn get_protected_vm_info(&self) -> Result<KvmProtectedVmInfo> {
55 let mut info = KvmProtectedVmInfo {
56 firmware_size: 0,
57 reserved: [0; 7],
58 };
59 // Safe because we allocated the struct and we know the kernel won't write beyond the end of
60 // the struct or keep a pointer to it.
61 unsafe {
62 self.enable_raw_capability(
63 KvmCap::ArmProtectedVm,
64 KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO,
65 &[&mut info as *mut KvmProtectedVmInfo as u64, 0, 0, 0],
66 )
67 }?;
68 Ok(info)
69 }
70 }
71
72 #[repr(C)]
73 struct KvmProtectedVmInfo {
74 firmware_size: u64,
75 reserved: [u64; 7],
76 }
77
78 impl VmAArch64 for KvmVm {
get_hypervisor(&self) -> &dyn Hypervisor79 fn get_hypervisor(&self) -> &dyn Hypervisor {
80 &self.kvm
81 }
82
enable_protected_vm(&mut self, fw_addr: GuestAddress, fw_max_size: u64) -> Result<()>83 fn enable_protected_vm(&mut self, fw_addr: GuestAddress, fw_max_size: u64) -> Result<()> {
84 if !self.check_capability(VmCap::Protected) {
85 return Err(Error::new(ENOSYS));
86 }
87 let info = self.get_protected_vm_info()?;
88 let memslot = if info.firmware_size == 0 {
89 u64::MAX
90 } else {
91 if info.firmware_size > fw_max_size {
92 return Err(Error::new(ENOMEM));
93 }
94 let mem = MemoryMappingBuilder::new(info.firmware_size as usize)
95 .build()
96 .map_err(|_| Error::new(EINVAL))?;
97 self.add_memory_region(fw_addr, Box::new(mem), false, false)? as u64
98 };
99 // Safe because none of the args are pointers.
100 unsafe {
101 self.enable_raw_capability(
102 KvmCap::ArmProtectedVm,
103 KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE,
104 &[memslot, 0, 0, 0],
105 )
106 }
107 }
108
create_vcpu(&self, id: usize) -> Result<Box<dyn VcpuAArch64>>109 fn create_vcpu(&self, id: usize) -> Result<Box<dyn VcpuAArch64>> {
110 // create_vcpu is declared separately in VmAArch64 and VmX86, so it can return VcpuAArch64
111 // or VcpuX86. But both use the same implementation in KvmVm::create_vcpu.
112 Ok(Box::new(KvmVm::create_vcpu(self, id)?))
113 }
114 }
115
116 impl KvmVcpu {
117 /// Arch-specific implementation of `Vcpu::pvclock_ctrl`. Always returns an error on AArch64.
pvclock_ctrl_arch(&self) -> Result<()>118 pub fn pvclock_ctrl_arch(&self) -> Result<()> {
119 Err(Error::new(ENXIO))
120 }
121 }
122
123 impl VcpuAArch64 for KvmVcpu {
init(&self, features: &[VcpuFeature]) -> Result<()>124 fn init(&self, features: &[VcpuFeature]) -> Result<()> {
125 let mut kvi = kvm_vcpu_init {
126 target: KVM_ARM_TARGET_GENERIC_V8,
127 features: [0; 7],
128 };
129 // Safe because we allocated the struct and we know the kernel will write exactly the size
130 // of the struct.
131 let ret = unsafe { ioctl_with_mut_ref(&self.vm, KVM_ARM_PREFERRED_TARGET(), &mut kvi) };
132 if ret != 0 {
133 return errno_result();
134 }
135
136 for f in features {
137 let shift = match f {
138 VcpuFeature::PsciV0_2 => KVM_ARM_VCPU_PSCI_0_2,
139 VcpuFeature::PmuV3 => KVM_ARM_VCPU_PMU_V3,
140 VcpuFeature::PowerOff => KVM_ARM_VCPU_POWER_OFF,
141 };
142 kvi.features[0] |= 1 << shift;
143 }
144
145 // Safe because we allocated the struct and we know the kernel will read exactly the size of
146 // the struct.
147 let ret = unsafe { ioctl_with_ref(self, KVM_ARM_VCPU_INIT(), &kvi) };
148 if ret == 0 {
149 Ok(())
150 } else {
151 errno_result()
152 }
153 }
154
init_pmu(&self, irq: u64) -> Result<()>155 fn init_pmu(&self, irq: u64) -> Result<()> {
156 let irq_addr = &irq as *const u64;
157
158 // The in-kernel PMU virtualization is initialized by setting the irq
159 // with KVM_ARM_VCPU_PMU_V3_IRQ and then by KVM_ARM_VCPU_PMU_V3_INIT.
160
161 let irq_attr = kvm_device_attr {
162 group: KVM_ARM_VCPU_PMU_V3_CTRL,
163 attr: KVM_ARM_VCPU_PMU_V3_IRQ as u64,
164 addr: irq_addr as u64,
165 flags: 0,
166 };
167 // Safe because we allocated the struct and we know the kernel will read exactly the size of
168 // the struct.
169 let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_HAS_DEVICE_ATTR(), &irq_attr) };
170 if ret < 0 {
171 return errno_result();
172 }
173
174 // Safe because we allocated the struct and we know the kernel will read exactly the size of
175 // the struct.
176 let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_SET_DEVICE_ATTR(), &irq_attr) };
177 if ret < 0 {
178 return errno_result();
179 }
180
181 let init_attr = kvm_device_attr {
182 group: KVM_ARM_VCPU_PMU_V3_CTRL,
183 attr: KVM_ARM_VCPU_PMU_V3_INIT as u64,
184 addr: 0,
185 flags: 0,
186 };
187 // Safe because we allocated the struct and we know the kernel will read exactly the size of
188 // the struct.
189 let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_SET_DEVICE_ATTR(), &init_attr) };
190 if ret < 0 {
191 return errno_result();
192 }
193
194 Ok(())
195 }
196
set_one_reg(&self, reg_id: u64, data: u64) -> Result<()>197 fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()> {
198 let data_ref = &data as *const u64;
199 let onereg = kvm_one_reg {
200 id: reg_id,
201 addr: data_ref as u64,
202 };
203 // Safe because we allocated the struct and we know the kernel will read exactly the size of
204 // the struct.
205 let ret = unsafe { ioctl_with_ref(self, KVM_SET_ONE_REG(), &onereg) };
206 if ret == 0 {
207 Ok(())
208 } else {
209 errno_result()
210 }
211 }
212
get_one_reg(&self, reg_id: u64) -> Result<u64>213 fn get_one_reg(&self, reg_id: u64) -> Result<u64> {
214 let val: u64 = 0;
215 let mut onereg = kvm_one_reg {
216 id: reg_id,
217 addr: (&val as *const u64) as u64,
218 };
219
220 // Safe because we allocated the struct and we know the kernel will read exactly the size of
221 // the struct.
222 let ret = unsafe { ioctl_with_ref(self, KVM_GET_ONE_REG(), &mut onereg) };
223 if ret == 0 {
224 Ok(val)
225 } else {
226 return errno_result();
227 }
228 }
229
get_psci_version(&self) -> Result<PsciVersion>230 fn get_psci_version(&self) -> Result<PsciVersion> {
231 // The definition of KVM_REG_ARM_PSCI_VERSION is in arch/arm64/include/uapi/asm/kvm.h.
232 const KVM_REG_ARM_PSCI_VERSION: u64 =
233 KVM_REG_ARM64 | (KVM_REG_SIZE_U64 as u64) | (KVM_REG_ARM_FW as u64);
234
235 match self.get_one_reg(KVM_REG_ARM_PSCI_VERSION) {
236 Ok(v) => {
237 let major = (v >> PSCI_VERSION_MAJOR_SHIFT) as u32;
238 let minor = (v as u32) & PSCI_VERSION_MINOR_MASK;
239 Ok(PsciVersion { major, minor })
240 }
241 Err(_) => {
242 // When `KVM_REG_ARM_PSCI_VERSION` is not supported, we can return PSCI 0.2, as vCPU
243 // has been initialized with `KVM_ARM_VCPU_PSCI_0_2` successfully.
244 Ok(PsciVersion { major: 0, minor: 2 })
245 }
246 }
247 }
248 }
249
250 // This function translates an IrqSrouceChip to the kvm u32 equivalent. It has a different
251 // implementation between x86_64 and aarch64 because the irqchip KVM constants are not defined on
252 // all architectures.
chip_to_kvm_chip(chip: IrqSourceChip) -> u32253 pub(super) fn chip_to_kvm_chip(chip: IrqSourceChip) -> u32 {
254 match chip {
255 // ARM does not have a constant for this, but the default routing
256 // setup seems to set this to 0
257 IrqSourceChip::Gic => 0,
258 _ => {
259 error!("Invalid IrqChipSource for ARM {:?}", chip);
260 0
261 }
262 }
263 }
264
265 #[cfg(test)]
266 mod tests {
267 use super::super::Kvm;
268 use super::*;
269 use crate::{IrqRoute, IrqSource, IrqSourceChip};
270 use vm_memory::{GuestAddress, GuestMemory};
271
272 #[test]
set_gsi_routing()273 fn set_gsi_routing() {
274 let kvm = Kvm::new().unwrap();
275 let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
276 let vm = KvmVm::new(&kvm, gm).unwrap();
277 vm.create_irq_chip().unwrap();
278 vm.set_gsi_routing(&[]).unwrap();
279 vm.set_gsi_routing(&[IrqRoute {
280 gsi: 1,
281 source: IrqSource::Irqchip {
282 chip: IrqSourceChip::Gic,
283 pin: 3,
284 },
285 }])
286 .unwrap();
287 vm.set_gsi_routing(&[IrqRoute {
288 gsi: 1,
289 source: IrqSource::Msi {
290 address: 0xf000000,
291 data: 0xa0,
292 },
293 }])
294 .unwrap();
295 vm.set_gsi_routing(&[
296 IrqRoute {
297 gsi: 1,
298 source: IrqSource::Irqchip {
299 chip: IrqSourceChip::Gic,
300 pin: 3,
301 },
302 },
303 IrqRoute {
304 gsi: 2,
305 source: IrqSource::Msi {
306 address: 0xf000000,
307 data: 0xa0,
308 },
309 },
310 ])
311 .unwrap();
312 }
313 }
314