• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 base::errno_result;
8 use base::ioctl_with_ref;
9 use base::Result;
10 use base::SafeDescriptor;
11 use hypervisor::kvm::KvmVcpu;
12 use hypervisor::kvm::KvmVm;
13 use hypervisor::DeviceKind;
14 use hypervisor::IrqRoute;
15 use hypervisor::Vm;
16 use kvm_sys::*;
17 use sync::Mutex;
18 
19 use crate::IrqChip;
20 use crate::IrqChipAArch64;
21 
22 /// Default ARM routing table.  AARCH64_GIC_NR_SPIS pins go to VGIC.
kvm_default_irq_routing_table() -> Vec<IrqRoute>23 fn kvm_default_irq_routing_table() -> Vec<IrqRoute> {
24     let mut routes: Vec<IrqRoute> = Vec::new();
25 
26     for i in 0..AARCH64_GIC_NR_SPIS {
27         routes.push(IrqRoute::gic_irq_route(i));
28     }
29 
30     routes
31 }
32 
33 /// IrqChip implementation where the entire IrqChip is emulated by KVM.
34 ///
35 /// This implementation will use the KVM API to create and configure the in-kernel irqchip.
36 pub struct KvmKernelIrqChip {
37     pub(super) vm: KvmVm,
38     pub(super) vcpus: Arc<Mutex<Vec<Option<KvmVcpu>>>>,
39     vgic: SafeDescriptor,
40     device_kind: DeviceKind,
41     pub(super) routes: Arc<Mutex<Vec<IrqRoute>>>,
42 }
43 
44 // These constants indicate the address space used by the ARM vGIC.
45 const AARCH64_GIC_DIST_SIZE: u64 = 0x10000;
46 const AARCH64_GIC_CPUI_SIZE: u64 = 0x20000;
47 
48 // These constants indicate the placement of the GIC registers in the physical
49 // address space.
50 const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE;
51 const AARCH64_GIC_CPUI_BASE: u64 = AARCH64_GIC_DIST_BASE - AARCH64_GIC_CPUI_SIZE;
52 const AARCH64_GIC_REDIST_SIZE: u64 = 0x20000;
53 
54 // This is the minimum number of SPI interrupts aligned to 32 + 32 for the
55 // PPI (16) and GSI (16).
56 pub const AARCH64_GIC_NR_IRQS: u32 = 64;
57 // Number of SPIs (32), which is the NR_IRQS (64) minus the number of PPIs (16) and GSIs (16)
58 pub const AARCH64_GIC_NR_SPIS: u32 = 32;
59 
60 const AARCH64_AXI_BASE: u64 = 0x40000000;
61 
62 impl KvmKernelIrqChip {
63     /// Construct a new KvmKernelIrqchip.
new(vm: KvmVm, num_vcpus: usize) -> Result<KvmKernelIrqChip>64     pub fn new(vm: KvmVm, num_vcpus: usize) -> Result<KvmKernelIrqChip> {
65         let cpu_if_addr: u64 = AARCH64_GIC_CPUI_BASE;
66         let dist_if_addr: u64 = AARCH64_GIC_DIST_BASE;
67         let redist_addr: u64 = dist_if_addr - (AARCH64_GIC_REDIST_SIZE * num_vcpus as u64);
68         let raw_cpu_if_addr = &cpu_if_addr as *const u64;
69         let raw_dist_if_addr = &dist_if_addr as *const u64;
70         let raw_redist_addr = &redist_addr as *const u64;
71 
72         let cpu_if_attr = kvm_device_attr {
73             group: KVM_DEV_ARM_VGIC_GRP_ADDR,
74             attr: KVM_VGIC_V2_ADDR_TYPE_CPU as u64,
75             addr: raw_cpu_if_addr as u64,
76             flags: 0,
77         };
78         let redist_attr = kvm_device_attr {
79             group: KVM_DEV_ARM_VGIC_GRP_ADDR,
80             attr: KVM_VGIC_V3_ADDR_TYPE_REDIST as u64,
81             addr: raw_redist_addr as u64,
82             flags: 0,
83         };
84         let mut dist_attr = kvm_device_attr {
85             group: KVM_DEV_ARM_VGIC_GRP_ADDR,
86             addr: raw_dist_if_addr as u64,
87             attr: 0,
88             flags: 0,
89         };
90 
91         let (vgic, device_kind, cpu_redist_attr, dist_attr_attr) =
92             match vm.create_device(DeviceKind::ArmVgicV3) {
93                 Err(_) => (
94                     vm.create_device(DeviceKind::ArmVgicV2)?,
95                     DeviceKind::ArmVgicV2,
96                     cpu_if_attr,
97                     KVM_VGIC_V2_ADDR_TYPE_DIST as u64,
98                 ),
99                 Ok(vgic) => (
100                     vgic,
101                     DeviceKind::ArmVgicV3,
102                     redist_attr,
103                     KVM_VGIC_V3_ADDR_TYPE_DIST as u64,
104                 ),
105             };
106         dist_attr.attr = dist_attr_attr;
107 
108         // SAFETY:
109         // Safe because we allocated the struct that's being passed in
110         let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &cpu_redist_attr) };
111         if ret != 0 {
112             return errno_result();
113         }
114 
115         // SAFETY:
116         // Safe because we allocated the struct that's being passed in
117         let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &dist_attr) };
118         if ret != 0 {
119             return errno_result();
120         }
121 
122         // We need to tell the kernel how many irqs to support with this vgic
123         let nr_irqs: u32 = AARCH64_GIC_NR_IRQS;
124         let nr_irqs_ptr = &nr_irqs as *const u32;
125         let nr_irqs_attr = kvm_device_attr {
126             group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
127             attr: 0,
128             addr: nr_irqs_ptr as u64,
129             flags: 0,
130         };
131         // SAFETY:
132         // Safe because we allocated the struct that's being passed in
133         let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &nr_irqs_attr) };
134         if ret != 0 {
135             return errno_result();
136         }
137 
138         Ok(KvmKernelIrqChip {
139             vm,
140             vcpus: Arc::new(Mutex::new((0..num_vcpus).map(|_| None).collect())),
141             vgic,
142             device_kind,
143             routes: Arc::new(Mutex::new(kvm_default_irq_routing_table())),
144         })
145     }
146 
147     /// Attempt to create a shallow clone of this aarch64 KvmKernelIrqChip instance.
arch_try_clone(&self) -> Result<Self>148     pub(super) fn arch_try_clone(&self) -> Result<Self> {
149         Ok(KvmKernelIrqChip {
150             vm: self.vm.try_clone()?,
151             vcpus: self.vcpus.clone(),
152             vgic: self.vgic.try_clone()?,
153             device_kind: self.device_kind,
154             routes: self.routes.clone(),
155         })
156     }
157 }
158 
159 impl IrqChipAArch64 for KvmKernelIrqChip {
try_box_clone(&self) -> Result<Box<dyn IrqChipAArch64>>160     fn try_box_clone(&self) -> Result<Box<dyn IrqChipAArch64>> {
161         Ok(Box::new(self.try_clone()?))
162     }
163 
as_irq_chip(&self) -> &dyn IrqChip164     fn as_irq_chip(&self) -> &dyn IrqChip {
165         self
166     }
167 
as_irq_chip_mut(&mut self) -> &mut dyn IrqChip168     fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip {
169         self
170     }
171 
get_vgic_version(&self) -> DeviceKind172     fn get_vgic_version(&self) -> DeviceKind {
173         self.device_kind
174     }
175 
finalize(&self) -> Result<()>176     fn finalize(&self) -> Result<()> {
177         let init_gic_attr = kvm_device_attr {
178             group: KVM_DEV_ARM_VGIC_GRP_CTRL,
179             attr: KVM_DEV_ARM_VGIC_CTRL_INIT as u64,
180             addr: 0,
181             flags: 0,
182         };
183 
184         // SAFETY:
185         // Safe because we allocated the struct that's being passed in
186         let ret = unsafe { ioctl_with_ref(&self.vgic, KVM_SET_DEVICE_ATTR(), &init_gic_attr) };
187         if ret != 0 {
188             return errno_result();
189         }
190         Ok(())
191     }
192 }
193