Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/irqchip/kvm/aarch64.rs
5394 views
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::collections::BTreeMap;
6
use std::sync::Arc;
7
8
use aarch64_sys_reg::ICC_CTLR_EL1;
9
use anyhow::anyhow;
10
use anyhow::Context;
11
use base::errno_result;
12
use base::ioctl_with_ref;
13
use base::Result;
14
use base::SafeDescriptor;
15
use hypervisor::kvm::KvmVcpu;
16
use hypervisor::kvm::KvmVm;
17
use hypervisor::DeviceKind;
18
use hypervisor::IrqRoute;
19
use hypervisor::MPState;
20
use hypervisor::VcpuAArch64;
21
use hypervisor::Vm;
22
use kvm_sys::*;
23
use serde::Deserialize;
24
use serde::Serialize;
25
use snapshot::AnySnapshot;
26
use sync::Mutex;
27
28
use crate::icc_regs;
29
use crate::IrqChip;
30
use crate::IrqChipAArch64;
31
32
/// Default ARM routing table. AARCH64_GIC_NR_SPIS pins go to VGIC.
33
fn kvm_default_irq_routing_table() -> Vec<IrqRoute> {
34
let mut routes: Vec<IrqRoute> = Vec::new();
35
36
for i in 0..AARCH64_GIC_NR_SPIS {
37
routes.push(IrqRoute::gic_irq_route(i));
38
}
39
40
routes
41
}
42
43
/// IrqChip implementation where the entire IrqChip is emulated by KVM.
44
///
45
/// This implementation will use the KVM API to create and configure the in-kernel irqchip.
46
pub struct KvmKernelIrqChip {
47
pub(super) vm: KvmVm,
48
pub(super) vcpus: Arc<Mutex<Vec<Option<KvmVcpu>>>>,
49
vgic: SafeDescriptor,
50
vgic_its: Option<SafeDescriptor>,
51
device_kind: DeviceKind,
52
pub(super) routes: Arc<Mutex<Vec<IrqRoute>>>,
53
}
54
55
// These constants indicate the address space used by the ARM vGIC.
56
const AARCH64_GIC_DIST_SIZE: u64 = 0x10000;
57
const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000;
58
59
// These constants indicate the placement of the GIC registers in the physical
60
// address space.
61
const AARCH64_GIC_DIST_BASE: u64 = 0x40000000 - AARCH64_GIC_DIST_SIZE;
62
const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE;
63
const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000;
64
const AARCH64_GIC_ITS_BASE: u64 = 0x40000000;
65
66
// This is the minimum number of SPI interrupts aligned to 32 + 32 for the
67
// PPI (16) and GSI (16).
68
pub const AARCH64_GIC_NR_IRQS: u32 = 64;
69
// Number of SPIs (32), which is the NR_IRQS (64) minus the number of PPIs (16) and GSIs (16)
70
pub const AARCH64_GIC_NR_SPIS: u32 = 32;
71
72
impl KvmKernelIrqChip {
73
/// Construct a new KvmKernelIrqchip.
74
pub fn new(vm: KvmVm, num_vcpus: usize, allow_vgic_its: bool) -> Result<KvmKernelIrqChip> {
75
let cpu_if_addr: u64 = AARCH64_GIC_CPUI_BASE;
76
let dist_if_addr: u64 = AARCH64_GIC_DIST_BASE;
77
let redist_addr: u64 = dist_if_addr - (AARCH64_GIC_REDIST_SIZE * num_vcpus as u64);
78
let raw_cpu_if_addr = &cpu_if_addr as *const u64;
79
let raw_dist_if_addr = &dist_if_addr as *const u64;
80
let raw_redist_addr = &redist_addr as *const u64;
81
82
let cpu_if_attr = kvm_device_attr {
83
group: KVM_DEV_ARM_VGIC_GRP_ADDR,
84
attr: KVM_VGIC_V2_ADDR_TYPE_CPU as u64,
85
addr: raw_cpu_if_addr as u64,
86
flags: 0,
87
};
88
let redist_attr = kvm_device_attr {
89
group: KVM_DEV_ARM_VGIC_GRP_ADDR,
90
attr: KVM_VGIC_V3_ADDR_TYPE_REDIST as u64,
91
addr: raw_redist_addr as u64,
92
flags: 0,
93
};
94
let mut dist_attr = kvm_device_attr {
95
group: KVM_DEV_ARM_VGIC_GRP_ADDR,
96
addr: raw_dist_if_addr as u64,
97
attr: 0,
98
flags: 0,
99
};
100
101
let (vgic, device_kind, cpu_redist_attr, dist_attr_attr) =
102
match vm.create_device(DeviceKind::ArmVgicV3) {
103
Err(_) => (
104
vm.create_device(DeviceKind::ArmVgicV2)?,
105
DeviceKind::ArmVgicV2,
106
cpu_if_attr,
107
KVM_VGIC_V2_ADDR_TYPE_DIST as u64,
108
),
109
Ok(vgic) => (
110
vgic,
111
DeviceKind::ArmVgicV3,
112
redist_attr,
113
KVM_VGIC_V3_ADDR_TYPE_DIST as u64,
114
),
115
};
116
dist_attr.attr = dist_attr_attr;
117
118
// SAFETY:
119
// Safe because we allocated the struct that's being passed in
120
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR, &cpu_redist_attr) };
121
if ret != 0 {
122
return errno_result();
123
}
124
125
// SAFETY:
126
// Safe because we allocated the struct that's being passed in
127
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR, &dist_attr) };
128
if ret != 0 {
129
return errno_result();
130
}
131
132
// We need to tell the kernel how many irqs to support with this vgic
133
let nr_irqs: u32 = AARCH64_GIC_NR_IRQS;
134
let nr_irqs_ptr = &nr_irqs as *const u32;
135
let nr_irqs_attr = kvm_device_attr {
136
group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
137
attr: 0,
138
addr: nr_irqs_ptr as u64,
139
flags: 0,
140
};
141
// SAFETY:
142
// Safe because we allocated the struct that's being passed in
143
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR, &nr_irqs_attr) };
144
if ret != 0 {
145
return errno_result();
146
}
147
148
// Create an ITS if allowed and supported.
149
let vgic_its = 'its: {
150
if !allow_vgic_its || device_kind != DeviceKind::ArmVgicV3 {
151
break 'its None;
152
}
153
let vgic_its = match vm.create_device(DeviceKind::ArmVgicIts) {
154
Ok(x) => x,
155
Err(e) if e.errno() == libc::ENODEV => break 'its None, // unsupported
156
Err(e) => return Err(e),
157
};
158
// Set ITS base address.
159
let its_addr = AARCH64_GIC_ITS_BASE;
160
let its_addr_attr = kvm_device_attr {
161
group: KVM_DEV_ARM_VGIC_GRP_ADDR,
162
attr: KVM_VGIC_ITS_ADDR_TYPE.into(),
163
addr: &its_addr as *const u64 as u64,
164
flags: 0,
165
};
166
// SAFETY: Safe because we allocated the struct that's being passed in.
167
let ret = unsafe { ioctl_with_ref(&vgic_its, KVM_SET_DEVICE_ATTR, &its_addr_attr) };
168
if ret != 0 {
169
return errno_result();
170
}
171
Some(vgic_its)
172
};
173
174
Ok(KvmKernelIrqChip {
175
vm,
176
vcpus: Arc::new(Mutex::new((0..num_vcpus).map(|_| None).collect())),
177
vgic,
178
vgic_its,
179
device_kind,
180
routes: Arc::new(Mutex::new(kvm_default_irq_routing_table())),
181
})
182
}
183
184
/// Attempt to create a shallow clone of this aarch64 KvmKernelIrqChip instance.
185
pub(super) fn arch_try_clone(&self) -> Result<Self> {
186
Ok(KvmKernelIrqChip {
187
vm: self.vm.try_clone()?,
188
vcpus: self.vcpus.clone(),
189
vgic: self.vgic.try_clone()?,
190
vgic_its: self
191
.vgic_its
192
.as_ref()
193
.map(|fd| fd.try_clone())
194
.transpose()?,
195
device_kind: self.device_kind,
196
routes: self.routes.clone(),
197
})
198
}
199
}
200
201
impl IrqChipAArch64 for KvmKernelIrqChip {
202
fn try_box_clone(&self) -> Result<Box<dyn IrqChipAArch64>> {
203
Ok(Box::new(self.try_clone()?))
204
}
205
206
fn as_irq_chip(&self) -> &dyn IrqChip {
207
self
208
}
209
210
fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip {
211
self
212
}
213
214
fn get_vgic_version(&self) -> DeviceKind {
215
self.device_kind
216
}
217
218
fn has_vgic_its(&self) -> bool {
219
self.vgic_its.is_some()
220
}
221
222
fn snapshot(&self, _cpus_num: usize) -> anyhow::Result<AnySnapshot> {
223
if self.vgic_its.is_some() {
224
return Err(anyhow!("snapshot of vGIC ITS not supported yet"));
225
}
226
if self.device_kind == DeviceKind::ArmVgicV3 {
227
let save_gic_attr = kvm_device_attr {
228
group: KVM_DEV_ARM_VGIC_GRP_CTRL,
229
attr: KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64,
230
addr: 0,
231
flags: 0,
232
};
233
// SAFETY:
234
// Safe because we allocated the struct that's being passed in
235
// Safe because the device interrupts get stored in guest memory
236
let ret = unsafe { ioctl_with_ref(&self.vgic, KVM_SET_DEVICE_ATTR, &save_gic_attr) };
237
if ret != 0 {
238
return errno_result()
239
.context("ioctl KVM_SET_DEVICE_ATTR for save_gic_attr failed.")?;
240
}
241
let mut cpu_specific: BTreeMap<u64, CpuSpecificState> = BTreeMap::new();
242
let vcpus = self.vcpus.lock();
243
for vcpu in vcpus.iter().flatten() {
244
let mut cpu_sys_regs: BTreeMap<u16, u64> = BTreeMap::new();
245
let mut redist_regs: Vec<u32> = Vec::new();
246
let mpidr = mpidr_concat_aff(vcpu.get_mpidr()?);
247
// SAFETY:
248
// Safe because we are specifying CPU SYSREGS, which is 64 bits.
249
// https://docs.kernel.org/virt/kvm/devices/arm-vgic-v3.html
250
let ctlr = unsafe {
251
get_kvm_device_attr_u64(
252
&self.vgic,
253
KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
254
mpidr | ICC_CTLR_EL1.encoded() as u64,
255
)?
256
};
257
// The reported number of priority bits is missing 1, as per ARM documentation.
258
// See register ICC_CTLR_EL1 for more information. Add the missing 1.
259
let prio_bits = (((ctlr & 0x700) >> 8) + 1) as u8;
260
cpu_sys_regs.insert(ICC_CTLR_EL1.encoded(), ctlr);
261
get_cpu_vgic_regs(&self.vgic, &mut cpu_sys_regs, mpidr, prio_bits)?;
262
redist_regs.append(&mut get_redist_regs(&self.vgic, mpidr)?);
263
cpu_specific.insert(
264
mpidr,
265
CpuSpecificState {
266
cpu_sys_regs,
267
redist_regs,
268
mp_state: MPState::from(&vcpu.get_mp_state()?),
269
},
270
);
271
}
272
AnySnapshot::to_any(VgicSnapshot {
273
cpu_specific,
274
dist_regs: get_dist_regs(&self.vgic)?,
275
})
276
} else {
277
Err(anyhow!("Unsupported VGIC version for snapshot"))
278
}
279
}
280
281
fn restore(&mut self, data: AnySnapshot, _vcpus_num: usize) -> anyhow::Result<()> {
282
if self.device_kind == DeviceKind::ArmVgicV3 {
283
// SAVE_PENDING_TABLES operation wrote the pending tables into guest memory.
284
let deser: VgicSnapshot =
285
AnySnapshot::from_any(data).context("Failed to deserialize vgic data")?;
286
let vcpus = self.vcpus.lock();
287
for vcpu in vcpus.iter().flatten() {
288
let mpidr = mpidr_concat_aff(vcpu.get_mpidr()?);
289
let mpidr_data = deser
290
.cpu_specific
291
.get(&mpidr)
292
.with_context(|| format!("CPU with MPIDR {mpidr} does not exist"))?;
293
set_cpu_vgic_regs(&self.vgic, mpidr, &mpidr_data.cpu_sys_regs)?;
294
set_redist_regs(&self.vgic, mpidr, &mpidr_data.redist_regs)?;
295
vcpu.set_mp_state(&kvm_mp_state::from(&mpidr_data.mp_state))?;
296
}
297
set_dist_regs(&self.vgic, &deser.dist_regs)?;
298
Ok(())
299
} else {
300
Err(anyhow!("Unsupported VGIC version for restore"))
301
}
302
}
303
304
fn finalize(&self) -> Result<()> {
305
let init_gic_attr = kvm_device_attr {
306
group: KVM_DEV_ARM_VGIC_GRP_CTRL,
307
attr: KVM_DEV_ARM_VGIC_CTRL_INIT as u64,
308
addr: 0,
309
flags: 0,
310
};
311
312
// SAFETY:
313
// Safe because we allocated the struct that's being passed in
314
let ret = unsafe { ioctl_with_ref(&self.vgic, KVM_SET_DEVICE_ATTR, &init_gic_attr) };
315
if ret != 0 {
316
return errno_result();
317
}
318
if let Some(vgic_its) = &self.vgic_its {
319
// SAFETY:
320
// Safe because we allocated the struct that's being passed in
321
let ret = unsafe { ioctl_with_ref(vgic_its, KVM_SET_DEVICE_ATTR, &init_gic_attr) };
322
if ret != 0 {
323
return errno_result();
324
}
325
}
326
Ok(())
327
}
328
}
329
330
#[derive(Serialize, Deserialize)]
331
struct VgicSnapshot {
332
cpu_specific: BTreeMap<u64, CpuSpecificState>,
333
dist_regs: Vec<u32>,
334
}
335
336
#[derive(Serialize, Deserialize)]
337
struct CpuSpecificState {
338
// Key: Register ID.
339
cpu_sys_regs: BTreeMap<u16, u64>,
340
redist_regs: Vec<u32>,
341
mp_state: MPState,
342
}
343
344
// # Safety
345
// Unsafe if incorrect group or attr (offset) provided.
346
// The caller must ensure the group is 32 bits and attr is for a 32 bit value
347
unsafe fn get_kvm_device_attr_u32(
348
vgic: &SafeDescriptor,
349
group: u32,
350
attr: u64,
351
) -> anyhow::Result<u32> {
352
let mut val: u32 = 0;
353
let device_attr = kvm_device_attr {
354
group,
355
attr,
356
addr: (&mut val as *mut u32) as u64,
357
flags: 0,
358
};
359
// SAFETY:
360
// Unsafe if wrong group is provided, which could lead to trying to read from a 64 bit register
361
// to a 32 bit register.
362
// Unsafe if wrong attr (offset) is provided
363
let ret = unsafe { ioctl_with_ref(vgic, KVM_GET_DEVICE_ATTR, &device_attr) };
364
if ret != 0 {
365
errno_result().with_context(|| {
366
format!(
367
"ioctl KVM_GET_DEVICE_ATTR_u32 for attr:{} failed.",
368
device_attr.attr
369
)
370
})
371
} else {
372
Ok(val)
373
}
374
}
375
376
// # Safety
377
// Unsafe if incorrect group or attr (offset) provided.
378
// The caller must ensure the group is 32 bits and attr is for a 64 bit value
379
unsafe fn get_kvm_device_attr_u64(
380
vgic: &SafeDescriptor,
381
group: u32,
382
attr: u64,
383
) -> anyhow::Result<u64> {
384
let mut val: u64 = 0;
385
let device_attr = kvm_device_attr {
386
group,
387
attr,
388
addr: (&mut val as *mut u64) as u64,
389
flags: 0,
390
};
391
// SAFETY:
392
// Unsafe if wrong group is passed, which could lead to trying to read from a 32 bit register
393
// to a 64 bit register. The read will succeed but the attr (offset) would need to be correct,
394
// otherwise data could be skipped.
395
// Unsafe if wrong attr (offset) is provided
396
let ret = unsafe { ioctl_with_ref(vgic, KVM_GET_DEVICE_ATTR, &device_attr) };
397
if ret != 0 {
398
errno_result().with_context(|| {
399
format!(
400
"ioctl KVM_GET_DEVICE_ATTR_u64 for attr:{} failed.",
401
device_attr.attr
402
)
403
})
404
} else {
405
Ok(val)
406
}
407
}
408
409
// # Safety
410
// Unsafe if incorrect group or attr (offset) provided.
411
// The caller must ensure the group is 32 bits and attr is for a 32 bit value
412
unsafe fn set_kvm_device_attr_u32(
413
vgic: &SafeDescriptor,
414
group: u32,
415
attr: u64,
416
val: &u32,
417
) -> anyhow::Result<()> {
418
let device_attr = kvm_device_attr {
419
group,
420
attr,
421
addr: (val as *const u32) as u64,
422
flags: 0,
423
};
424
// SAFETY:
425
// Unsafe if the group provided is incorrect, 64 bits may be written to a 32 bit range
426
// Unsafe if the wrong offset is provided
427
let ret = unsafe { ioctl_with_ref(vgic, KVM_SET_DEVICE_ATTR, &device_attr) };
428
if ret != 0 {
429
errno_result().with_context(|| {
430
format!(
431
"ioctl KVM_SET_DEVICE_ATTR_u32 for attr:{} failed.",
432
device_attr.attr
433
)
434
})
435
} else {
436
Ok(())
437
}
438
}
439
440
// # Safety
441
// Unsafe if incorrect group or attr (offset) provided.
442
// The caller must ensure the group is 32 bits and attr is for a 64 bit value
443
unsafe fn set_kvm_device_attr_u64(
444
vgic: &SafeDescriptor,
445
group: u32,
446
attr: u64,
447
val: &u64,
448
) -> anyhow::Result<()> {
449
let device_attr = kvm_device_attr {
450
group,
451
attr,
452
addr: (val as *const u64) as u64,
453
flags: 0,
454
};
455
// SAFETY:
456
// Unsafe if the wrong group, 32 bits may be written to a 64 bit range,
457
// potentially overwriting other the higher 32 bits of a 64 bit register
458
// Unsafe if the wrong offset is provided
459
let ret = unsafe { ioctl_with_ref(vgic, KVM_SET_DEVICE_ATTR, &device_attr) };
460
if ret != 0 {
461
errno_result().with_context(|| {
462
format!(
463
"ioctl KVM_SET_DEVICE_ATTR_u64 for attr:{} failed.",
464
device_attr.attr
465
)
466
})
467
} else {
468
Ok(())
469
}
470
}
471
472
fn get_cpu_vgic_regs(
473
vgic: &SafeDescriptor,
474
vgic_data: &mut BTreeMap<u16, u64>,
475
mpidr: u64,
476
prio_bits: u8,
477
) -> anyhow::Result<()> {
478
for reg in icc_regs(prio_bits)? {
479
let offset = reg.encoded();
480
// SAFETY:
481
// Safe because we are specifying CPU SYSREGS, which is 64 bits.
482
let val = unsafe {
483
get_kvm_device_attr_u64(
484
vgic,
485
KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
486
mpidr | offset as u64,
487
)?
488
};
489
vgic_data.insert(offset, val);
490
}
491
Ok(())
492
}
493
494
fn get_redist_regs(vgic: &SafeDescriptor, mpidr: u64) -> anyhow::Result<Vec<u32>> {
495
let mut vgic_data: Vec<u32> = Vec::new();
496
for offset in (0..AARCH64_GIC_REDIST_SIZE).step_by(4) {
497
// SAFETY:
498
// Safe because we are specifying REDIST REGS, which is 32 bits.
499
let val = unsafe {
500
get_kvm_device_attr_u32(vgic, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, mpidr | offset)?
501
};
502
vgic_data.push(val);
503
}
504
Ok(vgic_data)
505
}
506
507
fn get_dist_regs(vgic: &SafeDescriptor) -> anyhow::Result<Vec<u32>> {
508
let mut vgic_data: Vec<u32> = Vec::new();
509
for offset in (0..AARCH64_GIC_DIST_SIZE).step_by(4) {
510
// SAFETY:
511
// Safe because we are specifying DIST REGS, which is 32 bits.
512
let val = unsafe { get_kvm_device_attr_u32(vgic, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, offset)? };
513
vgic_data.push(val);
514
}
515
Ok(vgic_data)
516
}
517
518
fn set_cpu_vgic_regs(
519
vgic: &SafeDescriptor,
520
mpidr: u64,
521
data: &BTreeMap<u16, u64>,
522
) -> anyhow::Result<()> {
523
for (offset, val) in data.iter() {
524
// SAFETY:
525
// Safe because we are specifying CPU SYSREGS, which is 64 bits.
526
unsafe {
527
set_kvm_device_attr_u64(
528
vgic,
529
KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
530
mpidr | *offset as u64,
531
val,
532
)?
533
};
534
}
535
Ok(())
536
}
537
538
fn set_redist_regs(vgic: &SafeDescriptor, mpidr: u64, data: &[u32]) -> anyhow::Result<()> {
539
let mut offset = 0;
540
for val in data.iter() {
541
// SAFETY:
542
// Safe because we are specifying REDIST REGS, which is 32 bits.
543
unsafe {
544
set_kvm_device_attr_u32(vgic, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, mpidr | offset, val)?
545
};
546
// Step by 4 for offset
547
offset += 4;
548
}
549
Ok(())
550
}
551
552
fn set_dist_regs(vgic: &SafeDescriptor, data: &[u32]) -> anyhow::Result<()> {
553
let mut offset = 0;
554
for val in data.iter() {
555
// SAFETY:
556
// Safe because we are specifying DIST REGS, which is 32 bits.
557
unsafe { set_kvm_device_attr_u32(vgic, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, offset, val)? };
558
// Step by 4 for offset
559
offset += 4;
560
}
561
Ok(())
562
}
563
564
fn mpidr_concat_aff(mpidr: u64) -> u64 {
565
// https://docs.kernel.org/virt/kvm/devices/arm-vgic-v3.html
566
// MPIDR described as Aff3 | Aff2 | Aff1 | Aff0. (32 bits).
567
// The structure of the returned value of MPIDR in Arm is a bit different:
568
// https://developer.arm.com/documentation/ddi0601/2024-12/AArch64-Registers/MPIDR-EL1--Multiprocessor-Affinity-Register
569
// RES0 | Aff3 | RES1 | U | RES0 | MT | Aff2 | Aff1 | Aff0
570
// 63-40| 39-32| 31 | 30| 29 | 24 | 23-16| 15-8 | 7-0
571
let mpidr_aff_concat: u32 = ((mpidr & 0xFF << 32) >> 8) as u32 | mpidr as u32 & 0x00FFFFFF;
572
((mpidr_aff_concat as u64) << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT)
573
& KVM_DEV_ARM_VGIC_V3_MPIDR_MASK as u64
574
}
575
576