Skip to content

Commit 507cbce

Browse files
committed
arm: support MSI-X on ARM
Expose MSI properties for GIC via the FDT. Signed-off-by: Babis Chalios <[email protected]>
1 parent a0e1523 commit 507cbce

File tree

4 files changed

+105
-19
lines changed

4 files changed

+105
-19
lines changed

src/vmm/src/arch/aarch64/fdt.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ use crate::device_manager::mmio::MMIODeviceInfo;
2222
use crate::device_manager::pci_mngr::PciDevices;
2323
use crate::devices::acpi::vmgenid::{VMGENID_MEM_SIZE, VmGenId};
2424
use crate::initrd::InitrdConfig;
25+
use crate::logger::info;
2526
use crate::vstate::memory::{Address, GuestMemory, GuestMemoryMmap};
2627

2728
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
2829
const GIC_PHANDLE: u32 = 1;
2930
// This is a value for uniquely identifying the FDT node containing the clock definition.
3031
const CLOCK_PHANDLE: u32 = 2;
32+
// This is a value for uniquely identifying the FDT node declaring the MSI controller.
33+
const MSI_PHANDLE: u32 = 3;
3134
// You may be wondering why this big value?
3235
// This phandle is used to uniquely identify the FDT nodes containing cache information. Each cpu
3336
// can have a variable number of caches, some of these caches may be shared with other cpus.
@@ -302,6 +305,17 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &GICDevice) -> Result<(), Fd
302305
];
303306

304307
fdt.property_array_u32("interrupts", &gic_intr)?;
308+
309+
if let Some(msi_properties) = gic_device.msi_properties() {
310+
info!("msi_properties: {msi_properties:#?}");
311+
let msic_node = fdt.begin_node("msic")?;
312+
fdt.property_string("compatible", "arm,gic-v3-its")?;
313+
fdt.property_null("msi-controller")?;
314+
fdt.property_u32("phandle", MSI_PHANDLE)?;
315+
fdt.property_array_u64("reg", msi_properties)?;
316+
fdt.end_node(msic_node)?;
317+
}
318+
305319
fdt.end_node(interrupt)?;
306320

307321
Ok(())
@@ -471,6 +485,21 @@ fn create_pci_nodes(fdt: &mut FdtWriter, pci_devices: &PciDevices) -> Result<(),
471485
(MEM_64BIT_DEVICES_SIZE >> 32) as u32, // Range size
472486
((MEM_64BIT_DEVICES_SIZE & 0xffff_ffff) >> 32) as u32,
473487
];
488+
489+
// See kernel document Documentation/devicetree/bindings/pci/pci-msi.txt
490+
let msi_map = [
491+
// rid-base: A single cell describing the first RID matched by the entry.
492+
0x0,
493+
// msi-controller: A single phandle to an MSI controller.
494+
MSI_PHANDLE,
495+
// msi-base: An msi-specifier describing the msi-specifier produced for the
496+
// first RID matched by the entry.
497+
segment.id as u32,
498+
// length: A single cell describing how many consecutive RIDs are matched
499+
// following the rid-base.
500+
0x100,
501+
];
502+
474503
let pci_node = fdt.begin_node(&pci_node_name)?;
475504

476505
fdt.property_string("compatible", "pci-host-ecam-generic")?;
@@ -491,6 +520,9 @@ fn create_pci_nodes(fdt: &mut FdtWriter, pci_devices: &PciDevices) -> Result<(),
491520
fdt.property_null("interrupt-map")?;
492521
fdt.property_null("interrupt-map-mask")?;
493522
fdt.property_null("dma-coherent")?;
523+
fdt.property_array_u32("msi-map", &msi_map)?;
524+
fdt.property_u32("msi-parent", MSI_PHANDLE)?;
525+
494526
Ok(fdt.end_node(pci_node)?)
495527
}
496528

src/vmm/src/arch/aarch64/gic/gicv2/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ impl GICv2 {
6868
GICv2::get_cpu_addr(),
6969
GICv2::get_cpu_size(),
7070
],
71+
msi_properties: None,
7172
vcpu_count,
73+
its_device: None,
7274
})
7375
}
7476

src/vmm/src/arch/aarch64/gic/gicv3/mod.rs

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl GICv3 {
2424
const SZ_64K: u64 = 0x0001_0000;
2525
const KVM_VGIC_V3_DIST_SIZE: u64 = GICv3::SZ_64K;
2626
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * GICv3::SZ_64K);
27+
const GIC_V3_ITS_SIZE: u64 = 0x2_0000;
2728

2829
// Device trees specific constants
2930
const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
@@ -48,6 +49,16 @@ impl GICv3 {
4849
vcpu_count * GICv3::KVM_VGIC_V3_REDIST_SIZE
4950
}
5051

52+
/// Get the MSI address
53+
fn get_msi_address(vcpu_count: u64) -> u64 {
54+
Self::get_redists_addr(vcpu_count) - GICv3::GIC_V3_ITS_SIZE
55+
}
56+
57+
/// Get the MSI size
58+
const fn get_msi_size() -> u64 {
59+
GICv3::GIC_V3_ITS_SIZE
60+
}
61+
5162
pub const VERSION: u32 = kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3;
5263

5364
pub fn fdt_compatibility(&self) -> &str {
@@ -59,17 +70,41 @@ impl GICv3 {
5970
}
6071

6172
/// Create the GIC device object
62-
pub fn create_device(fd: DeviceFd, vcpu_count: u64) -> Self {
63-
GICv3(super::GIC {
64-
fd,
73+
pub fn create_device(vm: &VmFd, vcpu_count: u64) -> Result<Self, GicError> {
74+
// Create the GIC device
75+
let mut gic_device = kvm_bindings::kvm_create_device {
76+
type_: Self::VERSION,
77+
fd: 0,
78+
flags: 0,
79+
};
80+
81+
let gic_fd = vm
82+
.create_device(&mut gic_device)
83+
.map_err(GicError::CreateGIC)?;
84+
85+
// ITS part attributes
86+
let mut its_device = kvm_bindings::kvm_create_device {
87+
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
88+
fd: 0,
89+
flags: 0,
90+
};
91+
92+
let its_fd = vm
93+
.create_device(&mut its_device)
94+
.map_err(GicError::CreateGIC)?;
95+
96+
Ok(GICv3(super::GIC {
97+
fd: gic_fd,
6598
properties: [
6699
GICv3::get_dist_addr(),
67100
GICv3::get_dist_size(),
68101
GICv3::get_redists_addr(vcpu_count),
69102
GICv3::get_redists_size(vcpu_count),
70103
],
104+
msi_properties: Some([GICv3::get_msi_address(vcpu_count), GICv3::get_msi_size()]),
71105
vcpu_count,
72-
})
106+
its_device: Some(its_fd),
107+
}))
73108
}
74109

75110
pub fn save_device(&self, mpidrs: &[u64]) -> Result<GicState, GicError> {
@@ -82,7 +117,7 @@ impl GICv3 {
82117

83118
pub fn init_device_attributes(gic_device: &Self) -> Result<(), GicError> {
84119
// Setting up the distributor attribute.
85-
// We are placing the GIC below 1GB so we need to substract the size of the distributor.
120+
// We are placing the GIC below 1GB so we need to subtract the size of the distributor.
86121
Self::set_device_attribute(
87122
gic_device.device_fd(),
88123
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
@@ -101,26 +136,29 @@ impl GICv3 {
101136
0,
102137
)?;
103138

104-
Ok(())
105-
}
139+
// Setting up the ITS attributes
140+
Self::set_device_attribute(
141+
gic_device.its_device.as_ref().unwrap(),
142+
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
143+
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
144+
Self::get_msi_address(gic_device.vcpu_count()) as *const u64 as u64,
145+
0,
146+
)?;
106147

107-
/// Initialize a GIC device
108-
pub fn init_device(vm: &VmFd) -> Result<DeviceFd, GicError> {
109-
let mut gic_device = kvm_bindings::kvm_create_device {
110-
type_: Self::VERSION,
111-
fd: 0,
112-
flags: 0,
113-
};
148+
Self::set_device_attribute(
149+
gic_device.its_device.as_ref().unwrap(),
150+
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
151+
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
152+
0,
153+
0,
154+
)?;
114155

115-
vm.create_device(&mut gic_device)
116-
.map_err(GicError::CreateGIC)
156+
Ok(())
117157
}
118158

119159
/// Method to initialize the GIC device
120160
pub fn create(vm: &VmFd, vcpu_count: u64) -> Result<Self, GicError> {
121-
let vgic_fd = Self::init_device(vm)?;
122-
123-
let device = Self::create_device(vgic_fd, vcpu_count);
161+
let device = Self::create_device(vm, vcpu_count)?;
124162

125163
Self::init_device_attributes(&device)?;
126164

src/vmm/src/arch/aarch64/gic/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@ pub struct GIC {
2121
/// GIC device properties, to be used for setting up the fdt entry
2222
properties: [u64; 4],
2323

24+
/// MSI properties of the GIC device
25+
msi_properties: Option<[u64; 2]>,
26+
2427
/// Number of CPUs handled by the device
2528
vcpu_count: u64,
29+
30+
/// ITS device
31+
its_device: Option<DeviceFd>,
2632
}
2733
impl GIC {
2834
/// Returns the file descriptor of the GIC device
@@ -88,6 +94,14 @@ impl GICDevice {
8894
}
8995
}
9096

97+
/// Returns an array with MSI properties if GIC supports it
98+
pub fn msi_properties(&self) -> Option<&[u64; 2]> {
99+
match self {
100+
Self::V2(x) => x.msi_properties.as_ref(),
101+
Self::V3(x) => x.msi_properties.as_ref(),
102+
}
103+
}
104+
91105
/// Returns the number of vCPUs this GIC handles
92106
pub fn vcpu_count(&self) -> u64 {
93107
match self {

0 commit comments

Comments
 (0)