arch: Refactor GIC code to seperate KVM specific code

Shrink GICDevice trait to contain hypervisor agnostic API's only, which
are used in generating FDT.
Move all KVM specific logic into KvmGICDevice trait.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
This commit is contained in:
Michael Zhao 2020-07-16 14:10:59 +08:00 committed by Samuel Ortiz
parent 6e8c8991d9
commit e3e771727a
7 changed files with 485 additions and 456 deletions

View File

@ -1,12 +1,8 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::gicv2::GICv2;
use super::gicv3::GICv3;
use super::gicv3_its::GICv3ITS;
use kvm_ioctls::DeviceFd; use kvm_ioctls::DeviceFd;
use std::sync::Arc; use std::result;
use std::{boxed::Box, result};
/// Errors thrown while setting up the GIC. /// Errors thrown while setting up the GIC.
#[derive(Debug)] #[derive(Debug)]
@ -14,31 +10,25 @@ pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller. /// Error while calling KVM ioctl for setting up the global interrupt controller.
CreateGIC(hypervisor::HypervisorVmError), CreateGIC(hypervisor::HypervisorVmError),
/// Error while setting device attributes for the GIC. /// Error while setting device attributes for the GIC.
SetDeviceAttribute(kvm_ioctls::Error), SetDeviceAttribute(hypervisor::kvm_ioctls::Error),
} }
type Result<T> = result::Result<T, Error>; type Result<T> = result::Result<T, Error>;
/// Trait for GIC devices. pub trait GICDevice {
pub trait GICDevice: Send + Sync {
/// Returns the file descriptor of the GIC device /// Returns the file descriptor of the GIC device
fn device_fd(&self) -> &DeviceFd; fn device_fd(&self) -> &DeviceFd;
/// Returns an array with GIC device properties
fn device_properties(&self) -> &[u64];
/// Returns the number of vCPUs this GIC handles
fn vcpu_count(&self) -> u64;
/// Returns the fdt compatibility property of the device /// Returns the fdt compatibility property of the device
fn fdt_compatibility(&self) -> &str; fn fdt_compatibility(&self) -> &str;
/// Returns the maint_irq fdt property of the device /// Returns the maint_irq fdt property of the device
fn fdt_maint_irq(&self) -> u32; fn fdt_maint_irq(&self) -> u32;
/// Returns the GIC version of the device /// Returns an array with GIC device properties
fn version() -> u32 fn device_properties(&self) -> &[u64];
where
Self: Sized; /// Returns the number of vCPUs this GIC handles
fn vcpu_count(&self) -> u64;
/// Returns whether the GIC device is MSI compatible or not /// Returns whether the GIC device is MSI compatible or not
fn msi_compatible(&self) -> bool { fn msi_compatible(&self) -> bool {
@ -54,124 +44,145 @@ pub trait GICDevice: Send + Sync {
fn msi_properties(&self) -> &[u64] { fn msi_properties(&self) -> &[u64] {
&[] &[]
} }
}
/// Create the GIC device object pub mod kvm {
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> use super::super::gicv2::kvm::KvmGICv2;
where use super::super::gicv3::kvm::KvmGICv3;
Self: Sized; use super::super::gicv3_its::kvm::KvmGICv3ITS;
use super::super::layout;
use super::GICDevice;
use super::Result;
use kvm_ioctls::DeviceFd;
use std::boxed::Box;
use std::sync::Arc;
/// Setup the device-specific attributes /// Trait for GIC devices.
fn init_device_attributes( pub trait KvmGICDevice: Send + Sync + GICDevice {
/// Returns the GIC version of the device
fn version() -> u32
where
Self: Sized;
/// Create the GIC device object
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice>
where
Self: Sized;
/// Setup the device-specific attributes
fn init_device_attributes(
vm: &Arc<dyn hypervisor::Vm>,
gic_device: &Box<dyn GICDevice>,
) -> Result<()>
where
Self: Sized;
/// Initialize a GIC device
fn init_device(vm: &Arc<dyn hypervisor::Vm>) -> Result<DeviceFd>
where
Self: Sized,
{
let mut gic_device = kvm_bindings::kvm_create_device {
type_: Self::version(),
fd: 0,
flags: 0,
};
vm.create_device(&mut gic_device)
.map_err(super::Error::CreateGIC)
}
/// Set a GIC device attribute
fn set_device_attribute(
fd: &DeviceFd,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()>
where
Self: Sized,
{
let attr = kvm_bindings::kvm_device_attr {
group: group,
attr: attr,
addr: addr,
flags: flags,
};
fd.set_device_attr(&attr)
.map_err(super::Error::SetDeviceAttribute)?;
Ok(())
}
/// Finalize the setup of a GIC device
fn finalize_device(gic_device: &Box<dyn GICDevice>) -> Result<()>
where
Self: Sized,
{
/* We need to tell the kernel how many irqs to support with this vgic.
* See the `layout` module for details.
*/
let nr_irqs: u32 = layout::IRQ_MAX - layout::IRQ_BASE + 1;
let nr_irqs_ptr = &nr_irqs as *const u32;
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0,
nr_irqs_ptr as u64,
0,
)?;
/* Finalize the GIC.
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
*/
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
/// Method to initialize the GIC device
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
where
Self: Sized,
{
let vgic_fd = Self::init_device(vm)?;
let device = Self::create_device(vgic_fd, vcpu_count);
Self::init_device_attributes(vm, &device)?;
Self::finalize_device(&device)?;
Ok(device)
}
}
/// Create a GIC device.
///
/// It will try to create by default a GICv3 device. If that fails it will try
/// to fall-back to a GICv2 device.
pub fn create_gic(
vm: &Arc<dyn hypervisor::Vm>, vm: &Arc<dyn hypervisor::Vm>,
gic_device: &Box<dyn GICDevice>, vcpu_count: u64,
) -> Result<()> its_required: bool,
where ) -> Result<Box<dyn GICDevice>> {
Self: Sized; if its_required {
KvmGICv3ITS::new(vm, vcpu_count)
/// Initialize a GIC device } else {
fn init_device(vm: &Arc<dyn hypervisor::Vm>) -> Result<DeviceFd> KvmGICv3ITS::new(vm, vcpu_count).or_else(|_| {
where debug!("Failed to create GICv3-ITS, will try GICv3 instead.");
Self: Sized, KvmGICv3::new(vm, vcpu_count).or_else(|_| {
{ debug!("Failed to create GICv3, will try GICv2 instead.");
let mut gic_device = kvm_bindings::kvm_create_device { KvmGICv2::new(vm, vcpu_count)
type_: Self::version(), })
fd: 0,
flags: 0,
};
vm.create_device(&mut gic_device).map_err(Error::CreateGIC)
}
/// Set a GIC device attribute
fn set_device_attribute(
fd: &DeviceFd,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()>
where
Self: Sized,
{
let attr = kvm_bindings::kvm_device_attr {
group: group,
attr: attr,
addr: addr,
flags: flags,
};
fd.set_device_attr(&attr)
.map_err(Error::SetDeviceAttribute)?;
Ok(())
}
/// Finalize the setup of a GIC device
fn finalize_device(gic_device: &Box<dyn GICDevice>) -> Result<()>
where
Self: Sized,
{
/* We need to tell the kernel how many irqs to support with this vgic.
* See the `layout` module for details.
*/
let nr_irqs: u32 = super::layout::IRQ_MAX - super::layout::IRQ_BASE + 1;
let nr_irqs_ptr = &nr_irqs as *const u32;
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0,
nr_irqs_ptr as u64,
0,
)?;
/* Finalize the GIC.
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
*/
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
/// Method to initialize the GIC device
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
where
Self: Sized,
{
let vgic_fd = Self::init_device(vm)?;
let device = Self::create_device(vgic_fd, vcpu_count);
Self::init_device_attributes(vm, &device)?;
Self::finalize_device(&device)?;
Ok(device)
}
}
/// Create a GIC device.
///
/// It will try to create by default a GICv3 device. If that fails it will try
/// to fall-back to a GICv2 device.
pub fn create_gic(
vm: &Arc<dyn hypervisor::Vm>,
vcpu_count: u64,
its_required: bool,
) -> Result<Box<dyn GICDevice>> {
if its_required {
GICv3ITS::new(vm, vcpu_count)
} else {
GICv3ITS::new(vm, vcpu_count).or_else(|_| {
debug!("Failed to create GICv3-ITS, will try GICv3 instead.");
GICv3::new(vm, vcpu_count).or_else(|_| {
debug!("Failed to create GICv3, will try GICv2 instead.");
GICv2::new(vm, vcpu_count)
}) })
}) }
} }
} }

View File

@ -1,116 +1,121 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::gic::{Error, GICDevice}; pub mod kvm {
use kvm_ioctls::DeviceFd; use super::super::gic::{Error, GICDevice};
use std::sync::Arc; use std::{boxed::Box, result};
use std::{boxed::Box, result}; type Result<T> = result::Result<T, Error>;
use super::super::gic::kvm::KvmGICDevice;
use super::super::layout;
use kvm_ioctls::DeviceFd;
use std::sync::Arc;
type Result<T> = result::Result<T, Error>; /// Represent a GIC v2 device
pub struct KvmGICv2 {
/// The file descriptor for the KVM device
fd: DeviceFd,
/// Represent a GIC v2 device /// GIC device properties, to be used for setting up the fdt entry
pub struct GICv2 { properties: [u64; 4],
/// The file descriptor for the KVM device
fd: DeviceFd,
/// GIC device properties, to be used for setting up the fdt entry /// Number of CPUs handled by the device
properties: [u64; 4], vcpu_count: u64,
/// Number of CPUs handled by the device
vcpu_count: u64,
}
impl GICv2 {
// Unfortunately bindgen omits defines that are based on other defines.
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000;
const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000;
// Device trees specific constants
const ARCH_GIC_V2_MAINT_IRQ: u32 = 8;
/// Get the address of the GICv2 distributor.
const fn get_dist_addr() -> u64 {
super::layout::MAPPED_IO_START - GICv2::KVM_VGIC_V2_DIST_SIZE
} }
/// Get the size of the GIC_v2 distributor. impl KvmGICv2 {
const fn get_dist_size() -> u64 { // Unfortunately bindgen omits defines that are based on other defines.
GICv2::KVM_VGIC_V2_DIST_SIZE // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000;
const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000;
// Device trees specific constants
const ARCH_GIC_V2_MAINT_IRQ: u32 = 8;
/// Get the address of the GICv2 distributor.
const fn get_dist_addr() -> u64 {
layout::MAPPED_IO_START - KvmGICv2::KVM_VGIC_V2_DIST_SIZE
}
/// Get the size of the GIC_v2 distributor.
const fn get_dist_size() -> u64 {
KvmGICv2::KVM_VGIC_V2_DIST_SIZE
}
/// Get the address of the GIC_v2 CPU.
const fn get_cpu_addr() -> u64 {
KvmGICv2::get_dist_addr() - KvmGICv2::KVM_VGIC_V2_CPU_SIZE
}
/// Get the size of the GIC_v2 CPU.
const fn get_cpu_size() -> u64 {
KvmGICv2::KVM_VGIC_V2_CPU_SIZE
}
} }
/// Get the address of the GIC_v2 CPU. impl GICDevice for KvmGICv2 {
const fn get_cpu_addr() -> u64 { fn device_fd(&self) -> &DeviceFd {
GICv2::get_dist_addr() - GICv2::KVM_VGIC_V2_CPU_SIZE &self.fd
}
fn device_properties(&self) -> &[u64] {
&self.properties
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-400"
}
fn fdt_maint_irq(&self) -> u32 {
KvmGICv2::ARCH_GIC_V2_MAINT_IRQ
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
} }
/// Get the size of the GIC_v2 CPU. impl KvmGICDevice for KvmGICv2 {
const fn get_cpu_size() -> u64 { fn version() -> u32 {
GICv2::KVM_VGIC_V2_CPU_SIZE kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2
} }
}
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
impl GICDevice for GICv2 { Box::new(KvmGICv2 {
fn version() -> u32 { fd: fd,
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2 properties: [
} KvmGICv2::get_dist_addr(),
KvmGICv2::get_dist_size(),
fn device_fd(&self) -> &DeviceFd { KvmGICv2::get_cpu_addr(),
&self.fd KvmGICv2::get_cpu_size(),
} ],
vcpu_count: vcpu_count,
fn device_properties(&self) -> &[u64] { })
&self.properties }
}
fn init_device_attributes(
fn vcpu_count(&self) -> u64 { _vm: &Arc<dyn hypervisor::Vm>,
self.vcpu_count gic_device: &Box<dyn GICDevice>,
} ) -> Result<()> {
/* Setting up the distributor attribute.
fn fdt_compatibility(&self) -> &str { We are placing the GIC below 1GB so we need to substract the size of the distributor. */
"arm,gic-400" Self::set_device_attribute(
} &gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
fn fdt_maint_irq(&self) -> u32 { u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST),
GICv2::ARCH_GIC_V2_MAINT_IRQ &KvmGICv2::get_dist_addr() as *const u64 as u64,
} 0,
)?;
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
Box::new(GICv2 { /* Setting up the CPU attribute. */
fd: fd, Self::set_device_attribute(
properties: [ &gic_device.device_fd(),
GICv2::get_dist_addr(), kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
GICv2::get_dist_size(), u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU),
GICv2::get_cpu_addr(), &KvmGICv2::get_cpu_addr() as *const u64 as u64,
GICv2::get_cpu_size(), 0,
], )?;
vcpu_count: vcpu_count,
}) Ok(())
} }
fn init_device_attributes(
_vm: &Arc<dyn hypervisor::Vm>,
gic_device: &Box<dyn GICDevice>,
) -> Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor. */
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST),
&GICv2::get_dist_addr() as *const u64 as u64,
0,
)?;
/* Setting up the CPU attribute. */
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU),
&GICv2::get_cpu_addr() as *const u64 as u64,
0,
)?;
Ok(())
} }
} }

View File

@ -1,119 +1,125 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::gic::{Error, GICDevice}; pub mod kvm {
use kvm_ioctls::DeviceFd; use super::super::gic::kvm::KvmGICDevice;
use std::sync::Arc; use super::super::gic::{Error, GICDevice};
use std::{boxed::Box, result}; use super::super::layout;
use kvm_ioctls::DeviceFd;
use std::sync::Arc;
use std::{boxed::Box, result};
type Result<T> = result::Result<T, Error>;
type Result<T> = result::Result<T, Error>; pub struct KvmGICv3 {
/// The file descriptor for the KVM device
fd: DeviceFd,
pub struct GICv3 { /// GIC device properties, to be used for setting up the fdt entry
/// The file descriptor for the KVM device properties: [u64; 4],
fd: DeviceFd,
/// GIC device properties, to be used for setting up the fdt entry /// Number of CPUs handled by the device
properties: [u64; 4], vcpu_count: u64,
/// Number of CPUs handled by the device
vcpu_count: u64,
}
impl GICv3 {
// Unfortunately bindgen omits defines that are based on other defines.
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
pub const SZ_64K: u64 = 0x0001_0000;
const KVM_VGIC_V3_DIST_SIZE: u64 = GICv3::SZ_64K;
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * GICv3::SZ_64K);
// Device trees specific constants
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
/// Get the address of the GIC distributor.
pub fn get_dist_addr() -> u64 {
super::layout::MAPPED_IO_START - GICv3::KVM_VGIC_V3_DIST_SIZE
} }
/// Get the size of the GIC distributor. impl KvmGICv3 {
pub fn get_dist_size() -> u64 { // Unfortunately bindgen omits defines that are based on other defines.
GICv3::KVM_VGIC_V3_DIST_SIZE // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
pub const SZ_64K: u64 = 0x0001_0000;
const KVM_VGIC_V3_DIST_SIZE: u64 = KvmGICv3::SZ_64K;
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * KvmGICv3::SZ_64K);
// Device trees specific constants
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
/// Get the address of the GIC distributor.
pub fn get_dist_addr() -> u64 {
layout::MAPPED_IO_START - KvmGICv3::KVM_VGIC_V3_DIST_SIZE
}
/// Get the size of the GIC distributor.
pub fn get_dist_size() -> u64 {
KvmGICv3::KVM_VGIC_V3_DIST_SIZE
}
/// Get the address of the GIC redistributors.
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
KvmGICv3::get_dist_addr() - KvmGICv3::get_redists_size(vcpu_count)
}
/// Get the size of the GIC redistributors.
pub fn get_redists_size(vcpu_count: u64) -> u64 {
vcpu_count * KvmGICv3::KVM_VGIC_V3_REDIST_SIZE
}
} }
/// Get the address of the GIC redistributors. impl GICDevice for KvmGICv3 {
pub fn get_redists_addr(vcpu_count: u64) -> u64 { fn device_fd(&self) -> &DeviceFd {
GICv3::get_dist_addr() - GICv3::get_redists_size(vcpu_count) &self.fd
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-v3"
}
fn fdt_maint_irq(&self) -> u32 {
KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
}
fn device_properties(&self) -> &[u64] {
&self.properties
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
} }
/// Get the size of the GIC redistributors. impl KvmGICDevice for KvmGICv3 {
pub fn get_redists_size(vcpu_count: u64) -> u64 { fn version() -> u32 {
vcpu_count * GICv3::KVM_VGIC_V3_REDIST_SIZE kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
} }
}
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
impl GICDevice for GICv3 { Box::new(KvmGICv3 {
fn version() -> u32 { fd: fd,
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3 properties: [
} KvmGICv3::get_dist_addr(),
KvmGICv3::get_dist_size(),
fn device_fd(&self) -> &DeviceFd { KvmGICv3::get_redists_addr(vcpu_count),
&self.fd KvmGICv3::get_redists_size(vcpu_count),
} ],
vcpu_count: vcpu_count,
fn device_properties(&self) -> &[u64] { })
&self.properties }
}
fn init_device_attributes(
fn vcpu_count(&self) -> u64 { _vm: &Arc<dyn hypervisor::Vm>,
self.vcpu_count gic_device: &Box<dyn GICDevice>,
} ) -> Result<()> {
/* Setting up the distributor attribute.
fn fdt_compatibility(&self) -> &str { We are placing the GIC below 1GB so we need to substract the size of the distributor.
"arm,gic-v3" */
} Self::set_device_attribute(
&gic_device.device_fd(),
fn fdt_maint_irq(&self) -> u32 { kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
GICv3::ARCH_GIC_V3_MAINT_IRQ u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
} &KvmGICv3::get_dist_addr() as *const u64 as u64,
0,
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> { )?;
Box::new(GICv3 {
fd: fd, /* Setting up the redistributors' attribute.
properties: [ We are calculating here the start of the redistributors address. We have one per CPU.
GICv3::get_dist_addr(), */
GICv3::get_dist_size(), Self::set_device_attribute(
GICv3::get_redists_addr(vcpu_count), &gic_device.device_fd(),
GICv3::get_redists_size(vcpu_count), kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
], u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
vcpu_count: vcpu_count, &KvmGICv3::get_redists_addr(u64::from(gic_device.vcpu_count())) as *const u64
}) as u64,
} 0,
)?;
fn init_device_attributes(
_vm: &Arc<dyn hypervisor::Vm>, Ok(())
gic_device: &Box<dyn GICDevice>, }
) -> Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor.
*/
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
&GICv3::get_dist_addr() as *const u64 as u64,
0,
)?;
/* Setting up the redistributors' attribute.
We are calculating here the start of the redistributors address. We have one per CPU.
*/
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
&GICv3::get_redists_addr(u64::from(gic_device.vcpu_count())) as *const u64 as u64,
0,
)?;
Ok(())
} }
} }

View File

@ -1,123 +1,130 @@
// Copyright 2020 ARM Limited // Copyright 2020 ARM Limited
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::gic::{Error, GICDevice}; pub mod kvm {
use super::gicv3::GICv3; use std::sync::Arc;
use kvm_ioctls::DeviceFd; use std::{boxed::Box, result};
use std::sync::Arc; type Result<T> = result::Result<T, Error>;
use std::{boxed::Box, result}; use super::super::gic::kvm::KvmGICDevice;
use super::super::gic::{Error, GICDevice};
use super::super::gicv3::kvm::KvmGICv3;
use kvm_ioctls::DeviceFd;
type Result<T> = result::Result<T, Error>; pub struct KvmGICv3ITS {
/// The file descriptor for the KVM device
fd: DeviceFd,
pub struct GICv3ITS { /// GIC device properties, to be used for setting up the fdt entry
/// The file descriptor for the KVM device gic_properties: [u64; 4],
fd: DeviceFd,
/// GIC device properties, to be used for setting up the fdt entry /// MSI device properties, to be used for setting up the fdt entry
gic_properties: [u64; 4], msi_properties: [u64; 2],
/// MSI device properties, to be used for setting up the fdt entry /// Number of CPUs handled by the device
msi_properties: [u64; 2], vcpu_count: u64,
/// Number of CPUs handled by the device
vcpu_count: u64,
}
impl GICv3ITS {
const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * GICv3::SZ_64K);
fn get_msi_size() -> u64 {
GICv3ITS::KVM_VGIC_V3_ITS_SIZE
} }
fn get_msi_addr(vcpu_count: u64) -> u64 { impl KvmGICv3ITS {
GICv3::get_redists_addr(vcpu_count) - GICv3ITS::get_msi_size() const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * KvmGICv3::SZ_64K);
}
} fn get_msi_size() -> u64 {
KvmGICv3ITS::KVM_VGIC_V3_ITS_SIZE
impl GICDevice for GICv3ITS { }
fn version() -> u32 {
GICv3::version() fn get_msi_addr(vcpu_count: u64) -> u64 {
} KvmGICv3::get_redists_addr(vcpu_count) - KvmGICv3ITS::get_msi_size()
}
fn device_fd(&self) -> &DeviceFd { }
&self.fd
} impl GICDevice for KvmGICv3ITS {
fn device_fd(&self) -> &DeviceFd {
fn device_properties(&self) -> &[u64] { &self.fd
&self.gic_properties }
}
fn fdt_compatibility(&self) -> &str {
fn msi_properties(&self) -> &[u64] { "arm,gic-v3"
&self.msi_properties }
}
fn msi_compatible(&self) -> bool {
fn vcpu_count(&self) -> u64 { true
self.vcpu_count }
}
fn msi_compatiblility(&self) -> &str {
fn fdt_compatibility(&self) -> &str { "arm,gic-v3-its"
"arm,gic-v3" }
}
fn fdt_maint_irq(&self) -> u32 {
fn msi_compatible(&self) -> bool { KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
true }
}
fn msi_properties(&self) -> &[u64] {
fn msi_compatiblility(&self) -> &str { &self.msi_properties
"arm,gic-v3-its" }
}
fn device_properties(&self) -> &[u64] {
fn fdt_maint_irq(&self) -> u32 { &self.gic_properties
GICv3::ARCH_GIC_V3_MAINT_IRQ }
}
fn vcpu_count(&self) -> u64 {
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> { self.vcpu_count
Box::new(GICv3ITS { }
fd: fd, }
gic_properties: [
GICv3::get_dist_addr(), impl KvmGICDevice for KvmGICv3ITS {
GICv3::get_dist_size(), fn version() -> u32 {
GICv3::get_redists_addr(vcpu_count), KvmGICv3::version()
GICv3::get_redists_size(vcpu_count), }
],
msi_properties: [GICv3ITS::get_msi_addr(vcpu_count), GICv3ITS::get_msi_size()], fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
vcpu_count: vcpu_count, Box::new(KvmGICv3ITS {
}) fd: fd,
} gic_properties: [
KvmGICv3::get_dist_addr(),
fn init_device_attributes( KvmGICv3::get_dist_size(),
vm: &Arc<dyn hypervisor::Vm>, KvmGICv3::get_redists_addr(vcpu_count),
gic_device: &Box<dyn GICDevice>, KvmGICv3::get_redists_size(vcpu_count),
) -> Result<()> { ],
GICv3::init_device_attributes(vm, gic_device)?; msi_properties: [
KvmGICv3ITS::get_msi_addr(vcpu_count),
let mut its_device = kvm_bindings::kvm_create_device { KvmGICv3ITS::get_msi_size(),
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS, ],
fd: 0, vcpu_count: vcpu_count,
flags: 0, })
}; }
let its_fd = vm fn init_device_attributes(
.create_device(&mut its_device) vm: &Arc<dyn hypervisor::Vm>,
.map_err(Error::CreateGIC)?; gic_device: &Box<dyn GICDevice>,
) -> Result<()> {
Self::set_device_attribute( KvmGICv3::init_device_attributes(vm, gic_device)?;
&its_fd,
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, let mut its_device = kvm_bindings::kvm_create_device {
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
&GICv3ITS::get_msi_addr(u64::from(gic_device.vcpu_count())) as *const u64 as u64, fd: 0,
0, flags: 0,
)?; };
Self::set_device_attribute( let its_fd = vm
&its_fd, .create_device(&mut its_device)
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, .map_err(Error::CreateGIC)?;
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0, Self::set_device_attribute(
0, &its_fd,
)?; kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
Ok(()) &KvmGICv3ITS::get_msi_addr(u64::from(gic_device.vcpu_count())) as *const u64 as u64,
0,
)?;
Self::set_device_attribute(
&its_fd,
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
} }
} }

View File

@ -148,8 +148,8 @@ pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug>(
// If pci_space_address is present, it means PCI devices are used ("pci" feature enabled). // If pci_space_address is present, it means PCI devices are used ("pci" feature enabled).
// Then GITv3-ITS is required for MSI messaging. // Then GITv3-ITS is required for MSI messaging.
// Otherwise ("mmio" feature enabled), any version of GIC is OK. // Otherwise ("mmio" feature enabled), any version of GIC is OK.
let gic_device = let gic_device = gic::kvm::create_gic(vm, vcpu_count, pci_space_address.is_some())
gic::create_gic(vm, vcpu_count, pci_space_address.is_some()).map_err(Error::SetupGIC)?; .map_err(Error::SetupGIC)?;
fdt::create_fdt( fdt::create_fdt(
guest_mem, guest_mem,

View File

@ -395,7 +395,7 @@ pub mod kvm {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use arch::aarch64::gic::create_gic; use arch::aarch64::gic::kvm::create_gic;
#[test] #[test]
fn test_create_gic() { fn test_create_gic() {

View File

@ -1493,7 +1493,7 @@ mod tests {
mod tests { mod tests {
use super::*; use super::*;
use arch::aarch64::fdt::create_fdt; use arch::aarch64::fdt::create_fdt;
use arch::aarch64::gic::create_gic; use arch::aarch64::gic::kvm::create_gic;
use arch::aarch64::{layout, DeviceInfoForFDT}; use arch::aarch64::{layout, DeviceInfoForFDT};
use arch::DeviceType; use arch::DeviceType;
use vm_memory::{GuestAddress, GuestMemoryMmap}; use vm_memory::{GuestAddress, GuestMemoryMmap};