Skip to content

Commit 763a80c

Browse files
bchaliosacatangiu
authored andcommitted
GIC: Adding support for GICv2
We refactor the GIC-related code, to define a GICDevice Trait and then create a GICv3 object that implements this Trait. This way, it is more straightforward to introduce an implementations for different GIC versions. Moreover, we implement the Trait for GICv2 interrupt controllers. At the moment, the creation of the GICDevice tries to create a version 3 device. If that fails, we fallback to GICv2. Signed-off-by: Babis Chalios <[email protected]>
1 parent aa0e373 commit 763a80c

File tree

7 files changed

+405
-121
lines changed

7 files changed

+405
-121
lines changed

arch/src/aarch64/fdt.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{io, result};
1414

1515
use super::super::DeviceType;
1616
use super::get_fdt_addr;
17-
use super::gic::{get_dist_addr, get_dist_size, get_redists_addr, get_redists_size};
17+
use super::gic::GICDevice;
1818
use super::layout::FDT_MAX_SIZE;
1919
use aarch64::fdt::Error::CstringFDTTransform;
2020
use memory_model::{GuestAddress, GuestMemory, GuestMemoryError};
@@ -26,8 +26,6 @@ const CLOCK_PHANDLE: u32 = 2;
2626
// Read the documentation specified when appending the root node to the FDT.
2727
const ADDRESS_CELLS: u32 = 0x2;
2828
const SIZE_CELLS: u32 = 0x2;
29-
// Taken from qemu.
30-
const ARCH_GIC_MAINT_IRQ: u32 = 9;
3129

3230
// As per kvm tool and
3331
// https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.txt
@@ -90,6 +88,7 @@ pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug>(
9088
num_cpus: u32,
9189
cmdline: &CStr,
9290
device_info: Option<&HashMap<(DeviceType, String), T>>,
91+
gic_device: &Box<dyn GICDevice>,
9392
) -> Result<(Vec<u8>)> {
9493
// Alocate stuff necessary for the holding the blob.
9594
let mut fdt = vec![0; FDT_MAX_SIZE];
@@ -113,7 +112,7 @@ pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug>(
113112
create_cpu_nodes(&mut fdt, num_cpus)?;
114113
create_memory_node(&mut fdt, guest_mem)?;
115114
create_chosen_node(&mut fdt, cmdline)?;
116-
create_gic_node(&mut fdt, u64::from(num_cpus))?;
115+
create_gic_node(&mut fdt, gic_device)?;
117116
create_timer_node(&mut fdt)?;
118117
create_clock_node(&mut fdt)?;
119118
create_psci_node(&mut fdt)?;
@@ -350,18 +349,11 @@ fn create_chosen_node(fdt: &mut Vec<u8>, cmdline: &CStr) -> Result<()> {
350349
Ok(())
351350
}
352351

353-
fn create_gic_node(fdt: &mut Vec<u8>, vcpu_count: u64) -> Result<()> {
354-
// Look at https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic-v3.yaml
355-
// for understanding this.
356-
let gic_reg_prop = generate_prop64(&[
357-
get_dist_addr(),
358-
get_dist_size(),
359-
get_redists_addr(vcpu_count),
360-
get_redists_size(vcpu_count),
361-
]);
352+
fn create_gic_node(fdt: &mut Vec<u8>, gic_device: &Box<dyn GICDevice>) -> Result<()> {
353+
let gic_reg_prop = generate_prop64(gic_device.device_properties());
362354

363355
append_begin_node(fdt, "intc")?;
364-
append_property_string(fdt, "compatible", "arm,gic-v3")?;
356+
append_property_string(fdt, "compatible", gic_device.fdt_compatibility())?;
365357
append_property_null(fdt, "interrupt-controller")?;
366358
// "interrupt-cells" field specifies the number of cells needed to encode an
367359
// interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description
@@ -372,7 +364,11 @@ fn create_gic_node(fdt: &mut Vec<u8>, vcpu_count: u64) -> Result<()> {
372364
append_property_u32(fdt, "#address-cells", 2)?;
373365
append_property_u32(fdt, "#size-cells", 2)?;
374366
append_property_null(fdt, "ranges")?;
375-
let gic_intr = [GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, IRQ_TYPE_LEVEL_HI];
367+
let gic_intr = [
368+
GIC_FDT_IRQ_TYPE_PPI,
369+
gic_device.fdt_maint_irq(),
370+
IRQ_TYPE_LEVEL_HI,
371+
];
376372
let gic_intr_prop = generate_prop32(&gic_intr);
377373

378374
append_property(fdt, "interrupts", &gic_intr_prop)?;
@@ -516,7 +512,9 @@ fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug>(
516512
#[cfg(test)]
517513
mod tests {
518514
use super::*;
515+
use aarch64::gic::create_gic;
519516
use aarch64::{arch_memory_regions, layout};
517+
use kvm_ioctls::Kvm;
520518

521519
const LEN: u64 = 4096;
522520

@@ -574,11 +572,15 @@ mod tests {
574572
.iter()
575573
.cloned()
576574
.collect();
575+
let kvm = Kvm::new().unwrap();
576+
let vm = kvm.create_vm().unwrap();
577+
let gic = create_gic(&vm, 1).unwrap();
577578
assert!(create_fdt(
578579
&mem,
579580
1,
580581
&CString::new("console=tty0").unwrap(),
581582
Some(&dev_info),
583+
&gic,
582584
)
583585
.is_ok())
584586
}
@@ -587,11 +589,15 @@ mod tests {
587589
fn test_create_fdt() {
588590
let regions = arch_memory_regions(layout::FDT_MAX_SIZE + 0x1000);
589591
let mem = GuestMemory::new(&regions).expect("Cannot initialize memory");
592+
let kvm = Kvm::new().unwrap();
593+
let vm = kvm.create_vm().unwrap();
594+
let gic = create_gic(&vm, 1).unwrap();
590595
let mut dtb = create_fdt(
591596
&mem,
592597
1,
593598
&CString::new("console=tty0").unwrap(),
594599
None::<&std::collections::HashMap<(DeviceType, std::string::String), MMIODeviceInfo>>,
600+
&gic,
595601
)
596602
.unwrap();
597603

arch/src/aarch64/gic.rs

Lines changed: 124 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use std::{io, result};
4+
use std::{boxed::Box, io, result};
55

66
use kvm_ioctls::{DeviceFd, VmFd};
77

8-
// Unfortunately bindgen omits defines that are based on other defines.
9-
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
10-
const SZ_64K: u64 = 0x0001_0000;
11-
const KVM_VGIC_V3_DIST_SIZE: u64 = SZ_64K;
12-
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * SZ_64K);
8+
use super::gicv2::GICv2;
9+
use super::gicv3::GICv3;
1310

1411
/// Errors thrown while setting up the GIC.
1512
#[derive(Debug)]
@@ -21,104 +18,130 @@ pub enum Error {
2118
}
2219
type Result<T> = result::Result<T, Error>;
2320

24-
/// Create a GICv3 device.
25-
///
26-
/// Logic from this function is based on virt/kvm/arm/vgic/vgic-kvm-device.c from linux kernel.
27-
pub fn create_gicv3(vm: &VmFd, vcpu_count: u8) -> Result<DeviceFd> {
28-
/* We are creating a V3 GIC.
29-
As per https://static.docs.arm.com/dai0492/b/GICv3_Software_Overview_Official_Release_B.pdf,
30-
section 3.5 Programmers' model, the register interface of a GICv3 interrupt controller is split
31-
into three groups: distributor, redistributor, CPU.
32-
As per Figure 9 from same section, there is 1 Distributor and multiple redistributors (one per
33-
each CPU).
34-
*/
35-
let mut gic_device = kvm_bindings::kvm_create_device {
36-
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
37-
fd: 0,
38-
flags: 0,
39-
};
40-
41-
let vgic_fd = vm
42-
.create_device(&mut gic_device)
43-
.map_err(Error::CreateGIC)?;
44-
45-
/* Setting up the distributor attribute.
46-
We are placing the GIC below 1GB so we need to substract the size of the distributor.
47-
*/
48-
let dist_attr = kvm_bindings::kvm_device_attr {
49-
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
50-
attr: u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
51-
addr: &get_dist_addr() as *const u64 as u64,
52-
flags: 0,
53-
};
54-
vgic_fd
55-
.set_device_attr(&dist_attr)
56-
.map_err(Error::SetDeviceAttribute)?;
57-
58-
/* Setting up the redistributors' attribute.
59-
We are calculating here the start of the redistributors address. We have one per CPU.
60-
*/
61-
let redists_attr = kvm_bindings::kvm_device_attr {
62-
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
63-
attr: u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
64-
addr: &get_redists_addr(u64::from(vcpu_count)) as *const u64 as u64,
65-
flags: 0,
66-
};
67-
vgic_fd
68-
.set_device_attr(&redists_attr)
69-
.map_err(Error::SetDeviceAttribute)?;
70-
71-
/* We need to tell the kernel how many irqs to support with this vgic.
72-
See the `layout` module for details.
73-
*/
74-
let nr_irqs: u32 = super::layout::IRQ_MAX - super::layout::IRQ_BASE + 1;
75-
let nr_irqs_ptr = &nr_irqs as *const u32;
76-
let nr_irqs_attr = kvm_bindings::kvm_device_attr {
77-
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
78-
attr: 0,
79-
addr: nr_irqs_ptr as u64,
80-
flags: 0,
81-
};
82-
vgic_fd
83-
.set_device_attr(&nr_irqs_attr)
84-
.map_err(Error::SetDeviceAttribute)?;
85-
86-
/* Finalize the GIC.
87-
See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
88-
*/
89-
let init_gic_attr = kvm_bindings::kvm_device_attr {
90-
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
91-
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
92-
addr: 0,
93-
flags: 0,
94-
};
95-
vgic_fd
96-
.set_device_attr(&init_gic_attr)
97-
.map_err(Error::SetDeviceAttribute)?;
98-
99-
Ok(vgic_fd)
100-
}
21+
/// Trait for GIC devices.
22+
pub trait GICDevice {
23+
/// Returns the file descriptor of the GIC device
24+
fn device_fd(&self) -> &DeviceFd;
25+
26+
/// Returns an array with GIC device properties
27+
fn device_properties(&self) -> &[u64];
28+
29+
/// Returns the number of vCPUs this GIC handles
30+
fn vcpu_count(&self) -> u64;
31+
32+
/// Returns the fdt compatibility property of the device
33+
fn fdt_compatibility(&self) -> &str;
34+
35+
/// Returns the maint_irq fdt property of the device
36+
fn fdt_maint_irq(&self) -> u32;
37+
38+
/// Returns the GIC version of the device
39+
fn version() -> u32
40+
where
41+
Self: Sized;
42+
43+
/// Create the GIC device object
44+
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice>
45+
where
46+
Self: Sized;
47+
48+
/// Setup the device-specific attributes
49+
fn init_device_attributes(gic_device: &Box<dyn GICDevice>) -> Result<()>
50+
where
51+
Self: Sized;
52+
53+
/// Initialize a GIC device
54+
fn init_device(vm: &VmFd) -> Result<DeviceFd>
55+
where
56+
Self: Sized,
57+
{
58+
let mut gic_device = kvm_bindings::kvm_create_device {
59+
type_: Self::version(),
60+
fd: 0,
61+
flags: 0,
62+
};
63+
64+
vm.create_device(&mut gic_device).map_err(Error::CreateGIC)
65+
}
10166

102-
// Auxiliary functions for getting addresses and size of where the distributor and redistributor
103-
// are placed.
104-
/// Get the address of the GIC distributor.
105-
pub fn get_dist_addr() -> u64 {
106-
super::layout::MAPPED_IO_START - KVM_VGIC_V3_DIST_SIZE
107-
}
67+
/// Set a GIC device attribute
68+
fn set_device_attribute(
69+
fd: &DeviceFd,
70+
group: u32,
71+
attr: u64,
72+
addr: u64,
73+
flags: u32,
74+
) -> Result<()>
75+
where
76+
Self: Sized,
77+
{
78+
let attr = kvm_bindings::kvm_device_attr {
79+
group: group,
80+
attr: attr,
81+
addr: addr,
82+
flags: flags,
83+
};
84+
fd.set_device_attr(&attr)
85+
.map_err(Error::SetDeviceAttribute)?;
86+
87+
Ok(())
88+
}
10889

109-
/// Get the size of the GIC distributor.
110-
pub fn get_dist_size() -> u64 {
111-
KVM_VGIC_V3_DIST_SIZE
112-
}
90+
/// Finalize the setup of a GIC device
91+
fn finalize_device(gic_device: &Box<dyn GICDevice>) -> Result<()>
92+
where
93+
Self: Sized,
94+
{
95+
/* We need to tell the kernel how many irqs to support with this vgic.
96+
* See the `layout` module for details.
97+
*/
98+
let nr_irqs: u32 = super::layout::IRQ_MAX - super::layout::IRQ_BASE + 1;
99+
let nr_irqs_ptr = &nr_irqs as *const u32;
100+
Self::set_device_attribute(
101+
gic_device.device_fd(),
102+
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
103+
0,
104+
nr_irqs_ptr as u64,
105+
0,
106+
)?;
107+
108+
/* Finalize the GIC.
109+
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
110+
*/
111+
Self::set_device_attribute(
112+
gic_device.device_fd(),
113+
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
114+
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
115+
0,
116+
0,
117+
)?;
118+
119+
Ok(())
120+
}
113121

114-
/// Get the address of the GIC redistributors.
115-
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
116-
get_dist_addr() - get_redists_size(vcpu_count)
122+
/// Method to initialize the GIC device
123+
fn new(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
124+
where
125+
Self: Sized,
126+
{
127+
let vgic_fd = Self::init_device(vm)?;
128+
129+
let device = Self::create_device(vgic_fd, vcpu_count);
130+
131+
Self::init_device_attributes(&device)?;
132+
133+
Self::finalize_device(&device)?;
134+
135+
Ok(device)
136+
}
117137
}
118138

119-
/// Get the size of the GIC redistributors.
120-
pub fn get_redists_size(vcpu_count: u64) -> u64 {
121-
vcpu_count * KVM_VGIC_V3_REDIST_SIZE
139+
/// Create a GIC device.
140+
///
141+
/// It will try to create by default a GICv3 device. If that fails it will try
142+
/// to fall-back to a GICv2 device.
143+
pub fn create_gic(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
144+
GICv3::new(vm, vcpu_count).or_else(|_| GICv2::new(vm, vcpu_count))
122145
}
123146

124147
#[cfg(test)]
@@ -128,9 +151,9 @@ mod tests {
128151
use kvm_ioctls::Kvm;
129152

130153
#[test]
131-
fn test_create_gicv3() {
154+
fn test_create_gic() {
132155
let kvm = Kvm::new().unwrap();
133156
let vm = kvm.create_vm().unwrap();
134-
assert!(create_gicv3(&vm, 1).is_ok());
157+
assert!(create_gic(&vm, 1).is_ok());
135158
}
136159
}

0 commit comments

Comments
 (0)