mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
acpi: x86: add support for ACPI Notify() forwarding
In order to replicate the ACPI notification from the host to the guest pass-through devices: 1) allocate GPE and eventfd per pci-vfio device 2) generate proper aml code for ACPI GPE handler. The example of generated aml: Scope (_GPE) { Method (_E00, 0, NotSerialized) // _Exx: Edge-Triggered GPE, xx=0x00-0xFF { Local0 = \_SB.PC00.PE08.NOTY Notify (\_SB.PC00.PE08, Local0) } } The eventfd is registered by host kernel via VFIO_DEVICE_SET_IRQS ioctl. Crosvm upon receiving early provided, per pci-vfio eventfd, stores the notification value and triggers GPE associated to pci-vfio device. Guest kernel upon handling GPE (thanks to generated aml [ad 2)], triggers Notify on required pass-through device and therefore replicates the ACPI Notification on the guest side [Accessing \_SB.PC00.PE08.NOTY pointed by VCFG opregion result with trap to crosvm, which returns previously stored notify value]. Kernel counterpart: CL:4538472 BUG=b:244205651 TEST=Observe replication of ACPI notification in the guest kernel Change-Id: Iaf66097acd84f3066f6ff70d382f83ecaeea9a00 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4538483 Commit-Queue: Grzegorz Jaszczyk <jaszczyk@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
parent
e70a8774a1
commit
298be81446
13 changed files with 303 additions and 32 deletions
|
@ -547,19 +547,20 @@ impl arch::LinuxArch for AArch64 {
|
|||
.into_iter()
|
||||
.map(|(dev, jail_orig)| (dev.into_pci_device().unwrap(), jail_orig))
|
||||
.collect();
|
||||
let (pci, pci_irqs, mut pid_debug_label_map, _amls) = arch::generate_pci_root(
|
||||
pci_devices,
|
||||
irq_chip.as_irq_chip_mut(),
|
||||
mmio_bus.clone(),
|
||||
io_bus.clone(),
|
||||
system_allocator,
|
||||
&mut vm,
|
||||
(devices::AARCH64_GIC_NR_SPIS - AARCH64_IRQ_BASE) as usize,
|
||||
None,
|
||||
#[cfg(feature = "swap")]
|
||||
swap_controller,
|
||||
)
|
||||
.map_err(Error::CreatePciRoot)?;
|
||||
let (pci, pci_irqs, mut pid_debug_label_map, _amls, _gpe_scope_amls) =
|
||||
arch::generate_pci_root(
|
||||
pci_devices,
|
||||
irq_chip.as_irq_chip_mut(),
|
||||
mmio_bus.clone(),
|
||||
io_bus.clone(),
|
||||
system_allocator,
|
||||
&mut vm,
|
||||
(devices::AARCH64_GIC_NR_SPIS - AARCH64_IRQ_BASE) as usize,
|
||||
None,
|
||||
#[cfg(feature = "swap")]
|
||||
swap_controller,
|
||||
)
|
||||
.map_err(Error::CreatePciRoot)?;
|
||||
|
||||
let pci_root = Arc::new(Mutex::new(pci));
|
||||
let pci_bus = Arc::new(Mutex::new(PciConfigMmio::new(pci_root.clone(), 8)));
|
||||
|
|
|
@ -39,6 +39,7 @@ use devices::BusDeviceObj;
|
|||
use devices::BusError;
|
||||
use devices::BusResumeDevice;
|
||||
use devices::FwCfgParameters;
|
||||
use devices::GpeScope;
|
||||
use devices::HotPlugBus;
|
||||
use devices::IrqChip;
|
||||
use devices::IrqEventSource;
|
||||
|
@ -957,6 +958,7 @@ pub fn generate_pci_root(
|
|||
Vec<(PciAddress, u32, PciInterruptPin)>,
|
||||
BTreeMap<u32, String>,
|
||||
BTreeMap<PciAddress, Vec<u8>>,
|
||||
BTreeMap<PciAddress, Vec<u8>>,
|
||||
),
|
||||
DeviceRegistrationError,
|
||||
> {
|
||||
|
@ -1081,6 +1083,7 @@ pub fn generate_pci_root(
|
|||
};
|
||||
|
||||
let mut amls = BTreeMap::new();
|
||||
let mut gpe_scope_amls = BTreeMap::new();
|
||||
for (dev_idx, dev_value) in devices {
|
||||
#[cfg(unix)]
|
||||
let (mut device, jail) = dev_value;
|
||||
|
@ -1113,6 +1116,7 @@ pub fn generate_pci_root(
|
|||
);
|
||||
}
|
||||
}
|
||||
let gpe_nr = device.set_gpe(resources);
|
||||
|
||||
#[cfg(unix)]
|
||||
let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
|
||||
|
@ -1148,9 +1152,24 @@ pub fn generate_pci_root(
|
|||
.insert(arced_dev.clone(), range.addr, range.size)
|
||||
.map_err(DeviceRegistrationError::MmioInsert)?;
|
||||
}
|
||||
|
||||
if let Some(gpe_nr) = gpe_nr {
|
||||
if let Some(acpi_path) = root.acpi_path(&address) {
|
||||
let mut gpe_aml = Vec::new();
|
||||
|
||||
GpeScope {}.cast_to_aml_bytes(
|
||||
&mut gpe_aml,
|
||||
gpe_nr,
|
||||
format!("\\{}", acpi_path).as_str(),
|
||||
);
|
||||
if !gpe_aml.is_empty() {
|
||||
gpe_scope_amls.insert(address, gpe_aml);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((root, pci_irqs, pid_labels, amls))
|
||||
Ok((root, pci_irqs, pid_labels, amls, gpe_scope_amls))
|
||||
}
|
||||
|
||||
/// Errors for image loading.
|
||||
|
|
|
@ -97,6 +97,7 @@ pub use self::pci::Ac97Dev;
|
|||
pub use self::pci::Ac97Parameters;
|
||||
pub use self::pci::BarRange;
|
||||
pub use self::pci::CrosvmDeviceId;
|
||||
pub use self::pci::GpeScope;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci::HotPluggable;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
|
|
|
@ -53,6 +53,7 @@ impl Aml for DeviceVcfgRegister {
|
|||
vec![
|
||||
aml::FieldEntry::Named(*b"PFPM", 32),
|
||||
aml::FieldEntry::Named(*b"PDSM", 32),
|
||||
aml::FieldEntry::Named(*b"NOTY", 32),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes(bytes);
|
||||
|
@ -531,3 +532,26 @@ impl Aml for PowerResourceMethod {
|
|||
.to_aml_bytes(aml);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GpeScope {}
|
||||
|
||||
impl GpeScope {
|
||||
pub fn cast_to_aml_bytes(&self, aml: &mut Vec<u8>, gpe_nr: u32, notification_path: &str) {
|
||||
aml::Scope::new(
|
||||
"_GPE".into(),
|
||||
vec![&aml::Method::new(
|
||||
format!("_E{:02X}", gpe_nr).as_str().into(),
|
||||
0,
|
||||
false,
|
||||
vec![
|
||||
&aml::Store::new(
|
||||
&aml::Local(0),
|
||||
&aml::Path::new(format!("{}.NOTY", notification_path).as_str()),
|
||||
),
|
||||
&aml::Notify::new(&aml::Path::new(notification_path), &aml::Local(0)),
|
||||
],
|
||||
)],
|
||||
)
|
||||
.to_aml_bytes(aml);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ pub use self::ac97::Ac97Dev;
|
|||
pub use self::ac97::Ac97Parameters;
|
||||
pub use self::acpi::DeviceVcfgRegister;
|
||||
pub use self::acpi::DsmMethod;
|
||||
pub use self::acpi::GpeScope;
|
||||
pub use self::acpi::PowerResourceMethod;
|
||||
#[cfg(unix)]
|
||||
pub use self::coiommu::CoIommuDev;
|
||||
|
@ -124,6 +125,7 @@ impl PciInterruptPin {
|
|||
// VCFG
|
||||
pub const PCI_VCFG_PM: usize = 0x0;
|
||||
pub const PCI_VCFG_DSM: usize = 0x1;
|
||||
pub const PCI_VCFG_NOTY: usize = 0x2;
|
||||
|
||||
pub const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
|
||||
pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36;
|
||||
|
|
|
@ -48,6 +48,15 @@ use crate::Suspendable;
|
|||
#[sorted]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Deactivation of ACPI notifications failed
|
||||
#[error("failed to disable ACPI notifications")]
|
||||
AcpiNotifyDeactivationFailed,
|
||||
/// Setup of ACPI notifications failed
|
||||
#[error("failed to enable ACPI notifications")]
|
||||
AcpiNotifySetupFailed,
|
||||
/// Simulating ACPI notifications hardware triggering failed
|
||||
#[error("failed to test ACPI notifications")]
|
||||
AcpiNotifyTestFailed,
|
||||
/// Added pci device's parent bus does not belong to this bus
|
||||
#[error("pci device {0}'s parent bus does not belong to bus {1}")]
|
||||
AddedDeviceBusNotExist(PciAddress, u8),
|
||||
|
@ -433,6 +442,10 @@ pub trait PciDevice: Send + Suspendable {
|
|||
(Vec::new(), None)
|
||||
}
|
||||
|
||||
fn set_gpe(&mut self, _resources: &mut SystemAllocator) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Invoked when the device is destroyed
|
||||
fn destroy_device(&mut self) {}
|
||||
|
||||
|
@ -766,6 +779,10 @@ impl<T: PciDevice + ?Sized> PciDevice for Box<T> {
|
|||
(**self).generate_acpi_methods()
|
||||
}
|
||||
|
||||
fn set_gpe(&mut self, resources: &mut SystemAllocator) -> Option<u32> {
|
||||
(**self).set_gpe(resources)
|
||||
}
|
||||
|
||||
fn destroy_device(&mut self) {
|
||||
(**self).destroy_device();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ use resources::AllocOptions;
|
|||
use resources::MmioType;
|
||||
use resources::SystemAllocator;
|
||||
use sync::Mutex;
|
||||
use vfio_sys::vfio::VFIO_PCI_ACPI_NTFY_IRQ_INDEX;
|
||||
use vfio_sys::*;
|
||||
use vm_control::api::VmMemoryClient;
|
||||
use vm_control::HotPlugDeviceInfo;
|
||||
|
@ -76,6 +77,7 @@ use crate::pci::PciClassCode;
|
|||
use crate::pci::PciId;
|
||||
use crate::pci::PciInterruptPin;
|
||||
use crate::pci::PCI_VCFG_DSM;
|
||||
use crate::pci::PCI_VCFG_NOTY;
|
||||
use crate::pci::PCI_VCFG_PM;
|
||||
use crate::pci::PCI_VENDOR_ID_INTEL;
|
||||
use crate::vfio::VfioDevice;
|
||||
|
@ -509,14 +511,18 @@ impl VfioPciWorker {
|
|||
&mut self,
|
||||
req_irq_evt: Event,
|
||||
wakeup_evt: Event,
|
||||
acpi_notify_evt: Event,
|
||||
kill_evt: Event,
|
||||
msix_evt: Vec<Event>,
|
||||
is_in_low_power: Arc<Mutex<bool>>,
|
||||
gpe: Option<u32>,
|
||||
notification_val: Arc<Mutex<Vec<u32>>>,
|
||||
) {
|
||||
#[derive(EventToken)]
|
||||
#[derive(EventToken, Debug)]
|
||||
enum Token {
|
||||
ReqIrq,
|
||||
WakeUp,
|
||||
AcpiNotifyEvent,
|
||||
Kill,
|
||||
MsixIrqi { index: usize },
|
||||
}
|
||||
|
@ -524,6 +530,7 @@ impl VfioPciWorker {
|
|||
let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
|
||||
(&req_irq_evt, Token::ReqIrq),
|
||||
(&wakeup_evt, Token::WakeUp),
|
||||
(&acpi_notify_evt, Token::AcpiNotifyEvent),
|
||||
(&kill_evt, Token::Kill),
|
||||
]) {
|
||||
Ok(pc) => pc,
|
||||
|
@ -596,6 +603,21 @@ impl VfioPciWorker {
|
|||
}
|
||||
}
|
||||
}
|
||||
Token::AcpiNotifyEvent => {
|
||||
if let Some(gpe) = gpe {
|
||||
if let Ok(val) = base::EventExt::read_count(&acpi_notify_evt) {
|
||||
notification_val.lock().push(val as u32);
|
||||
let request = VmRequest::Gpe(gpe);
|
||||
if self.vm_socket.send(&request).is_ok() {
|
||||
if let Err(e) = self.vm_socket.recv::<VmResponse>() {
|
||||
error!("{} failed to send GPE: {}", self.name.clone(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("{} failed to read acpi_notify_evt", self.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
Token::Kill => break 'wait,
|
||||
}
|
||||
}
|
||||
|
@ -641,6 +663,7 @@ pub struct VfioPciDevice {
|
|||
preferred_address: PciAddress,
|
||||
pci_address: Option<PciAddress>,
|
||||
interrupt_evt: Option<IrqLevelEvent>,
|
||||
acpi_notification_evt: Option<Event>,
|
||||
mmio_regions: Vec<PciBarConfiguration>,
|
||||
io_regions: Vec<PciBarConfiguration>,
|
||||
pm_cap: Option<Arc<Mutex<VfioPmCap>>>,
|
||||
|
@ -659,6 +682,8 @@ pub struct VfioPciDevice {
|
|||
vcfg_shm_mmap: Option<MemoryMapping>,
|
||||
mapped_mmio_bars: BTreeMap<PciBarIndex, (u64, Vec<VmMemoryRegionId>)>,
|
||||
activated: bool,
|
||||
acpi_notifier_val: Arc<Mutex<Vec<u32>>>,
|
||||
gpe: Option<u32>,
|
||||
}
|
||||
|
||||
impl VfioPciDevice {
|
||||
|
@ -806,6 +831,7 @@ impl VfioPciDevice {
|
|||
preferred_address,
|
||||
pci_address: None,
|
||||
interrupt_evt: None,
|
||||
acpi_notification_evt: None,
|
||||
mmio_regions: Vec::new(),
|
||||
io_regions: Vec::new(),
|
||||
pm_cap,
|
||||
|
@ -823,6 +849,8 @@ impl VfioPciDevice {
|
|||
vcfg_shm_mmap: None,
|
||||
mapped_mmio_bars: BTreeMap::new(),
|
||||
activated: false,
|
||||
acpi_notifier_val: Arc::new(Mutex::new(Vec::new())),
|
||||
gpe: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -843,6 +871,38 @@ impl VfioPciDevice {
|
|||
ret
|
||||
}
|
||||
|
||||
fn enable_acpi_notification(&mut self) -> Result<(), PciDeviceError> {
|
||||
if let Some(ref acpi_notification_evt) = self.acpi_notification_evt {
|
||||
return self
|
||||
.device
|
||||
.acpi_notification_evt_enable(acpi_notification_evt, VFIO_PCI_ACPI_NTFY_IRQ_INDEX)
|
||||
.map_err(|_| PciDeviceError::AcpiNotifySetupFailed);
|
||||
}
|
||||
Err(PciDeviceError::AcpiNotifySetupFailed)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn disable_acpi_notification(&mut self) -> Result<(), PciDeviceError> {
|
||||
if let Some(ref _acpi_notification_evt) = self.acpi_notification_evt {
|
||||
return self
|
||||
.device
|
||||
.acpi_notification_disable(VFIO_PCI_ACPI_NTFY_IRQ_INDEX)
|
||||
.map_err(|_| PciDeviceError::AcpiNotifyDeactivationFailed);
|
||||
}
|
||||
Err(PciDeviceError::AcpiNotifyDeactivationFailed)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn test_acpi_notification(&mut self, val: u32) -> Result<(), PciDeviceError> {
|
||||
if let Some(ref _acpi_notification_evt) = self.acpi_notification_evt {
|
||||
return self
|
||||
.device
|
||||
.acpi_notification_test(VFIO_PCI_ACPI_NTFY_IRQ_INDEX, val)
|
||||
.map_err(|_| PciDeviceError::AcpiNotifyTestFailed);
|
||||
}
|
||||
Err(PciDeviceError::AcpiNotifyTestFailed)
|
||||
}
|
||||
|
||||
fn enable_intx(&mut self) {
|
||||
if let Some(ref interrupt_evt) = self.interrupt_evt {
|
||||
if let Err(e) = self.device.irq_enable(
|
||||
|
@ -1223,6 +1283,24 @@ impl VfioPciDevice {
|
|||
};
|
||||
self.pm_evt = Some(self_pm_evt);
|
||||
|
||||
let (self_acpi_notify_evt, acpi_notify_evt) =
|
||||
match Event::new().and_then(|e| Ok((e.try_clone()?, e))) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"{} failed creating ACPI Event pair: {}",
|
||||
self.debug_label(),
|
||||
e
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.acpi_notification_evt = Some(self_acpi_notify_evt);
|
||||
|
||||
if let Err(e) = self.enable_acpi_notification() {
|
||||
error!("{}: {}", self.debug_label(), e);
|
||||
}
|
||||
|
||||
let mut msix_evt = Vec::new();
|
||||
if let Some(msix_cap) = &self.msix_cap {
|
||||
msix_evt = msix_cap.lock().clone_msix_evt();
|
||||
|
@ -1234,6 +1312,8 @@ impl VfioPciDevice {
|
|||
let pm_cap = self.pm_cap.clone();
|
||||
let msix_cap = self.msix_cap.clone();
|
||||
let is_in_low_power = self.is_in_low_power.clone();
|
||||
let gpe_nr = self.gpe;
|
||||
let notification_val = self.acpi_notifier_val.clone();
|
||||
self.worker_thread = Some(WorkerThread::start("vfio_pci", move |kill_evt| {
|
||||
let mut worker = VfioPciWorker {
|
||||
address,
|
||||
|
@ -1243,7 +1323,16 @@ impl VfioPciDevice {
|
|||
pm_cap,
|
||||
msix_cap,
|
||||
};
|
||||
worker.run(req_evt, pm_evt, kill_evt, msix_evt, is_in_low_power);
|
||||
worker.run(
|
||||
req_evt,
|
||||
pm_evt,
|
||||
acpi_notify_evt,
|
||||
kill_evt,
|
||||
msix_evt,
|
||||
is_in_low_power,
|
||||
gpe_nr,
|
||||
notification_val,
|
||||
);
|
||||
worker
|
||||
}));
|
||||
self.activated = true;
|
||||
|
@ -1865,6 +1954,16 @@ impl PciDevice for VfioPciDevice {
|
|||
}
|
||||
|
||||
fn read_virtual_config_register(&self, reg_idx: usize) -> u32 {
|
||||
if reg_idx == PCI_VCFG_NOTY {
|
||||
let mut q = self.acpi_notifier_val.lock();
|
||||
let mut val = 0;
|
||||
if !q.is_empty() {
|
||||
val = q.remove(0);
|
||||
}
|
||||
drop(q);
|
||||
return val;
|
||||
}
|
||||
|
||||
warn!(
|
||||
"{} read unsupported vcfg register {}",
|
||||
self.debug_label(),
|
||||
|
@ -2002,6 +2101,14 @@ impl PciDevice for VfioPciDevice {
|
|||
|
||||
(amls, shm)
|
||||
}
|
||||
|
||||
fn set_gpe(&mut self, resources: &mut SystemAllocator) -> Option<u32> {
|
||||
if let Some(gpe_nr) = resources.allocate_gpe() {
|
||||
base::debug!("set_gpe: gpe-nr {} addr {:?}", gpe_nr, self.pci_address);
|
||||
self.gpe = Some(gpe_nr);
|
||||
}
|
||||
self.gpe
|
||||
}
|
||||
}
|
||||
|
||||
impl Suspendable for VfioPciDevice {
|
||||
|
|
|
@ -44,6 +44,7 @@ use resources::Error as ResourcesError;
|
|||
use sync::Mutex;
|
||||
use thiserror::Error;
|
||||
use vfio_sys::vfio::vfio_acpi_dsm;
|
||||
use vfio_sys::vfio::VFIO_IRQ_SET_DATA_BOOL;
|
||||
use vfio_sys::*;
|
||||
use vm_memory::MemoryRegionInformation;
|
||||
use zerocopy::AsBytes;
|
||||
|
@ -96,6 +97,12 @@ pub enum VfioError {
|
|||
UnknownDeviceType(u32),
|
||||
#[error("failed to call vfio device's ACPI _DSM: {0}")]
|
||||
VfioAcpiDsm(Error),
|
||||
#[error("failed to disable vfio deviece's acpi notification: {0}")]
|
||||
VfioAcpiNotificationDisable(Error),
|
||||
#[error("failed to enable vfio deviece's acpi notification: {0}")]
|
||||
VfioAcpiNotificationEnable(Error),
|
||||
#[error("failed to test vfio deviece's acpi notification: {0}")]
|
||||
VfioAcpiNotificationTest(Error),
|
||||
#[error(
|
||||
"vfio API version doesn't match with VFIO_API_VERSION defined in vfio_sys/src/vfio.rs"
|
||||
)]
|
||||
|
@ -971,6 +978,78 @@ impl VfioDevice {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enable vfio device's ACPI notifications and associate EventFD with device.
|
||||
pub fn acpi_notification_evt_enable(
|
||||
&self,
|
||||
acpi_notification_eventfd: &Event,
|
||||
index: u32,
|
||||
) -> Result<()> {
|
||||
let u32_size = mem::size_of::<u32>();
|
||||
let count = 1;
|
||||
|
||||
let mut irq_set = vec_with_array_field::<vfio_irq_set, u32>(count);
|
||||
irq_set[0].argsz = (mem::size_of::<vfio_irq_set>() + count * u32_size) as u32;
|
||||
irq_set[0].flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set[0].index = index;
|
||||
irq_set[0].start = 0;
|
||||
irq_set[0].count = count as u32;
|
||||
|
||||
// It is safe as enough space is reserved through vec_with_array_field(u32)<count>.
|
||||
let data = unsafe { irq_set[0].data.as_mut_slice(count * u32_size) };
|
||||
data.copy_from_slice(&acpi_notification_eventfd.as_raw_descriptor().to_ne_bytes()[..]);
|
||||
|
||||
// Safe as we are the owner of self and irq_set which are valid value
|
||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||
if ret < 0 {
|
||||
Err(VfioError::VfioAcpiNotificationEnable(get_error()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable vfio device's ACPI notification and disconnect EventFd with device.
|
||||
pub fn acpi_notification_disable(&self, index: u32) -> Result<()> {
|
||||
let mut irq_set = vec_with_array_field::<vfio_irq_set, u32>(0);
|
||||
irq_set[0].argsz = mem::size_of::<vfio_irq_set>() as u32;
|
||||
irq_set[0].flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set[0].index = index;
|
||||
irq_set[0].start = 0;
|
||||
irq_set[0].count = 0;
|
||||
|
||||
// Safe as we are the owner of self and irq_set which are valid value
|
||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||
if ret < 0 {
|
||||
Err(VfioError::VfioAcpiNotificationDisable(get_error()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Test vfio device's ACPI notification by simulating hardware triggering.
|
||||
/// When the signaling mechanism is set, the VFIO_IRQ_SET_DATA_BOOL can be used with
|
||||
/// VFIO_IRQ_SET_ACTION_TRIGGER to perform kernel level interrupt loopback testing.
|
||||
pub fn acpi_notification_test(&self, index: u32, val: u32) -> Result<()> {
|
||||
let u32_size = mem::size_of::<u32>();
|
||||
let mut irq_set = vec_with_array_field::<vfio_irq_set, u32>(1);
|
||||
irq_set[0].argsz = (mem::size_of::<vfio_irq_set>() + u32_size) as u32;
|
||||
irq_set[0].flags = VFIO_IRQ_SET_DATA_BOOL | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set[0].index = index;
|
||||
irq_set[0].start = 0;
|
||||
irq_set[0].count = 1;
|
||||
|
||||
// It is safe as enough space is reserved through vec_with_array_field(u32)<count>.
|
||||
let data = unsafe { irq_set[0].data.as_mut_slice(u32_size) };
|
||||
data.copy_from_slice(&val.to_ne_bytes()[..]);
|
||||
|
||||
// Safe as we are the owner of self and irq_set which are valid value
|
||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||
if ret < 0 {
|
||||
Err(VfioError::VfioAcpiNotificationTest(get_error()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable vfio device's irq and associate Irqfd Event with device.
|
||||
/// When MSIx is enabled, multi vectors will be supported, and vectors starting from subindex to subindex +
|
||||
/// descriptors length will be assigned with irqfd in the descriptors array.
|
||||
|
|
|
@ -28,6 +28,13 @@ pub struct vfio_acpi_dsm {
|
|||
pub args: __IncompleteArrayField<u8>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct vfio_acpi_notify_eventfd {
|
||||
pub notify_eventfd: i32,
|
||||
pub reserved: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct vfio_region_info_with_cap {
|
||||
|
|
|
@ -19,6 +19,13 @@ pub struct vfio_acpi_dsm {
|
|||
pub args: __IncompleteArrayField<u8>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct vfio_acpi_notify_eventfd {
|
||||
pub notify_eventfd: i32,
|
||||
pub reserved: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct vfio_region_info_with_cap {
|
||||
|
@ -308,7 +315,8 @@ pub const VFIO_PCI_MSI_IRQ_INDEX: _bindgen_ty_2 = 1;
|
|||
pub const VFIO_PCI_MSIX_IRQ_INDEX: _bindgen_ty_2 = 2;
|
||||
pub const VFIO_PCI_ERR_IRQ_INDEX: _bindgen_ty_2 = 3;
|
||||
pub const VFIO_PCI_REQ_IRQ_INDEX: _bindgen_ty_2 = 4;
|
||||
pub const VFIO_PCI_NUM_IRQS: _bindgen_ty_2 = 5;
|
||||
pub const VFIO_PCI_ACPI_NTFY_IRQ_INDEX: _bindgen_ty_2 = 5;
|
||||
pub const VFIO_PCI_NUM_IRQS: _bindgen_ty_2 = 6;
|
||||
pub type _bindgen_ty_2 = ::std::os::raw::c_uint;
|
||||
pub const VFIO_CCW_CONFIG_REGION_INDEX: _bindgen_ty_3 = 0;
|
||||
pub const VFIO_CCW_NUM_REGIONS: _bindgen_ty_3 = 1;
|
||||
|
|
|
@ -178,6 +178,7 @@ const SSDT_REVISION: u8 = 2;
|
|||
pub fn create_customize_ssdt(
|
||||
pci_root: Arc<Mutex<PciRoot>>,
|
||||
amls: BTreeMap<PciAddress, Vec<u8>>,
|
||||
gpe_scope_amls: BTreeMap<PciAddress, Vec<u8>>,
|
||||
) -> Option<SDT> {
|
||||
if amls.is_empty() {
|
||||
return None;
|
||||
|
@ -198,6 +199,10 @@ pub fn create_customize_ssdt(
|
|||
}
|
||||
}
|
||||
|
||||
for children in gpe_scope_amls.values() {
|
||||
ssdt.append_slice(children);
|
||||
}
|
||||
|
||||
Some(ssdt)
|
||||
}
|
||||
|
||||
|
|
|
@ -746,19 +746,20 @@ impl arch::LinuxArch for X8664arch {
|
|||
.map(|(dev, jail_orig)| (dev.into_pci_device().unwrap(), jail_orig))
|
||||
.collect();
|
||||
|
||||
let (pci, pci_irqs, mut pid_debug_label_map, amls) = arch::generate_pci_root(
|
||||
pci_devices,
|
||||
irq_chip.as_irq_chip_mut(),
|
||||
mmio_bus.clone(),
|
||||
io_bus.clone(),
|
||||
system_allocator,
|
||||
&mut vm,
|
||||
4, // Share the four pin interrupts (INTx#)
|
||||
Some(pcie_vcfg_range.start),
|
||||
#[cfg(feature = "swap")]
|
||||
swap_controller,
|
||||
)
|
||||
.map_err(Error::CreatePciRoot)?;
|
||||
let (pci, pci_irqs, mut pid_debug_label_map, amls, gpe_scope_amls) =
|
||||
arch::generate_pci_root(
|
||||
pci_devices,
|
||||
irq_chip.as_irq_chip_mut(),
|
||||
mmio_bus.clone(),
|
||||
io_bus.clone(),
|
||||
system_allocator,
|
||||
&mut vm,
|
||||
4, // Share the four pin interrupts (INTx#)
|
||||
Some(pcie_vcfg_range.start),
|
||||
#[cfg(feature = "swap")]
|
||||
swap_controller,
|
||||
)
|
||||
.map_err(Error::CreatePciRoot)?;
|
||||
|
||||
let pci = Arc::new(Mutex::new(pci));
|
||||
pci.lock().enable_pcie_cfg_mmio(pcie_cfg_mmio_range.start);
|
||||
|
@ -896,7 +897,7 @@ impl arch::LinuxArch for X8664arch {
|
|||
)?;
|
||||
|
||||
// Create customized SSDT table
|
||||
let sdt = acpi::create_customize_ssdt(pci.clone(), amls);
|
||||
let sdt = acpi::create_customize_ssdt(pci.clone(), amls, gpe_scope_amls);
|
||||
if let Some(sdt) = sdt {
|
||||
acpi_dev_resource.sdts.push(sdt);
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ where
|
|||
|
||||
let devices = vec![];
|
||||
|
||||
let (pci, pci_irqs, _pid_debug_label_map, _amls) = arch::generate_pci_root(
|
||||
let (pci, pci_irqs, _pid_debug_label_map, _amls, _gpe_scope_amls) = arch::generate_pci_root(
|
||||
devices,
|
||||
&mut irq_chip,
|
||||
mmio_bus.clone(),
|
||||
|
|
Loading…
Reference in a new issue