mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-11-04 19:11:11 +00:00
vmm: Implement ACPI hotplug/unplug handling for PCI segments
For the bus scanning the GED AML code now calls into a PSCN method that scans all buses. This approach was chosen since it handles the case correctly where one GED interrupt is services for two hotplugs on distinct segments. The PCIU and PCID field values are now determined by the PSEG field that is uses to select which segment those values should be used for. Similarly _EJ0 will notify based on the value of _SEG. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
49f19e061b
commit
7a4606f800
@ -151,7 +151,7 @@ impl Aml for AcpiGedDevice {
|
||||
&aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
|
||||
&aml::If::new(
|
||||
&aml::Equal::new(&aml::Local(1), &4usize),
|
||||
vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])],
|
||||
vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])],
|
||||
),
|
||||
&aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
|
||||
&aml::If::new(
|
||||
|
@ -84,7 +84,7 @@ use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vfio_ioctls::{VfioContainer, VfioDevice};
|
||||
use virtio_devices::transport::VirtioPciDevice;
|
||||
use virtio_devices::transport::VirtioTransport;
|
||||
@ -883,6 +883,8 @@ pub struct DeviceManager {
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
acpi_address: GuestAddress,
|
||||
#[cfg(feature = "acpi")]
|
||||
selected_segment: usize,
|
||||
|
||||
// Possible handle to the virtio-balloon device
|
||||
virtio_mem_devices: Vec<Arc<Mutex<virtio_devices::Mem>>>,
|
||||
@ -983,6 +985,8 @@ impl DeviceManager {
|
||||
.map_err(DeviceManagerError::EventFd)?,
|
||||
#[cfg(feature = "acpi")]
|
||||
acpi_address,
|
||||
#[cfg(feature = "acpi")]
|
||||
selected_segment: 0,
|
||||
serial_pty: None,
|
||||
serial_manager: None,
|
||||
console_pty: None,
|
||||
@ -3472,6 +3476,11 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
pub fn eject_device(&mut self, pci_segment_id: u16, device_id: u8) -> DeviceManagerResult<()> {
|
||||
info!(
|
||||
"Ejecting device_id = {} on segment_id={}",
|
||||
device_id, pci_segment_id
|
||||
);
|
||||
|
||||
// Convert the device ID into the corresponding b/d/f.
|
||||
let pci_device_bdf = (device_id as u32) << 3;
|
||||
|
||||
@ -3803,6 +3812,19 @@ impl Aml for DeviceManager {
|
||||
use arch::aarch64::DeviceInfoForFdt;
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
let mut pci_scan_methods = Vec::new();
|
||||
for i in 0..self.pci_segments.len() {
|
||||
pci_scan_methods.push(aml::MethodCall::new(
|
||||
format!("\\_SB_.PCI{:X}.PCNT", i).as_str().into(),
|
||||
vec![],
|
||||
));
|
||||
}
|
||||
let mut pci_scan_inner: Vec<&dyn Aml> = Vec::new();
|
||||
for method in &pci_scan_methods {
|
||||
pci_scan_inner.push(method)
|
||||
}
|
||||
|
||||
// PCI hotplug controller
|
||||
bytes.extend_from_slice(
|
||||
&aml::Device::new(
|
||||
@ -3836,15 +3858,18 @@ impl Aml for DeviceManager {
|
||||
aml::FieldEntry::Named(*b"PCIU", 32),
|
||||
aml::FieldEntry::Named(*b"PCID", 32),
|
||||
aml::FieldEntry::Named(*b"B0EJ", 32),
|
||||
aml::FieldEntry::Named(*b"PSEG", 32),
|
||||
],
|
||||
),
|
||||
&aml::Method::new(
|
||||
"PCEJ".into(),
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
vec![
|
||||
// Take lock defined above
|
||||
&aml::Acquire::new("BLCK".into(), 0xffff),
|
||||
// Choose the current segment
|
||||
&aml::Store::new(&aml::Path::new("PSEG"), &aml::Arg(1)),
|
||||
// Write PCI bus number (in first argument) to I/O port via field
|
||||
&aml::ShiftLeft::new(&aml::Path::new("B0EJ"), &aml::ONE, &aml::Arg(0)),
|
||||
// Release lock
|
||||
@ -3853,6 +3878,7 @@ impl Aml for DeviceManager {
|
||||
&aml::Return::new(&aml::ZERO),
|
||||
],
|
||||
),
|
||||
&aml::Method::new("PSCN".into(), 0, true, pci_scan_inner),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes(),
|
||||
@ -4083,28 +4109,46 @@ impl Migratable for DeviceManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
const PCIU_FIELD_OFFSET: u64 = 0;
|
||||
#[cfg(feature = "acpi")]
|
||||
const PCID_FIELD_OFFSET: u64 = 4;
|
||||
#[cfg(feature = "acpi")]
|
||||
const B0EJ_FIELD_OFFSET: u64 = 8;
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
const PSEG_FIELD_OFFSET: u64 = 12;
|
||||
#[cfg(feature = "acpi")]
|
||||
const PCIU_FIELD_SIZE: usize = 4;
|
||||
#[cfg(feature = "acpi")]
|
||||
const PCID_FIELD_SIZE: usize = 4;
|
||||
#[cfg(feature = "acpi")]
|
||||
const B0EJ_FIELD_SIZE: usize = 4;
|
||||
#[cfg(feature = "acpi")]
|
||||
const PSEG_FIELD_SIZE: usize = 4;
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
impl BusDevice for DeviceManager {
|
||||
fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
match offset {
|
||||
PCIU_FIELD_OFFSET => {
|
||||
assert!(data.len() == PCIU_FIELD_SIZE);
|
||||
data.copy_from_slice(&self.pci_segments[0].pci_devices_up.to_le_bytes());
|
||||
data.copy_from_slice(
|
||||
&self.pci_segments[self.selected_segment]
|
||||
.pci_devices_up
|
||||
.to_le_bytes(),
|
||||
);
|
||||
// Clear the PCIU bitmap
|
||||
self.pci_segments[0].pci_devices_up = 0;
|
||||
self.pci_segments[self.selected_segment].pci_devices_up = 0;
|
||||
}
|
||||
PCID_FIELD_OFFSET => {
|
||||
assert!(data.len() == PCID_FIELD_SIZE);
|
||||
data.copy_from_slice(&self.pci_segments[0].pci_devices_down.to_le_bytes());
|
||||
data.copy_from_slice(
|
||||
&self.pci_segments[self.selected_segment]
|
||||
.pci_devices_down
|
||||
.to_le_bytes(),
|
||||
);
|
||||
// Clear the PCID bitmap
|
||||
self.pci_segments[0].pci_devices_down = 0;
|
||||
self.pci_segments[self.selected_segment].pci_devices_down = 0;
|
||||
}
|
||||
B0EJ_FIELD_OFFSET => {
|
||||
assert!(data.len() == B0EJ_FIELD_SIZE);
|
||||
@ -4112,6 +4156,10 @@ impl BusDevice for DeviceManager {
|
||||
// taken care of right away during a write access.
|
||||
data.fill(0);
|
||||
}
|
||||
PSEG_FIELD_OFFSET => {
|
||||
assert_eq!(data.len(), PSEG_FIELD_SIZE);
|
||||
data.copy_from_slice(&(self.selected_segment as u32).to_le_bytes());
|
||||
}
|
||||
_ => error!(
|
||||
"Accessing unknown location at base 0x{:x}, offset 0x{:x}",
|
||||
base, offset
|
||||
@ -4124,7 +4172,7 @@ impl BusDevice for DeviceManager {
|
||||
)
|
||||
}
|
||||
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<std::sync::Barrier>> {
|
||||
match offset {
|
||||
B0EJ_FIELD_OFFSET => {
|
||||
assert!(data.len() == B0EJ_FIELD_SIZE);
|
||||
@ -4134,12 +4182,27 @@ impl BusDevice for DeviceManager {
|
||||
|
||||
while slot_bitmap > 0 {
|
||||
let slot_id = slot_bitmap.trailing_zeros();
|
||||
if let Err(e) = self.eject_device(0, slot_id as u8) {
|
||||
if let Err(e) = self.eject_device(self.selected_segment as u16, slot_id as u8) {
|
||||
error!("Failed ejecting device {}: {:?}", slot_id, e);
|
||||
}
|
||||
slot_bitmap &= !(1 << slot_id);
|
||||
}
|
||||
}
|
||||
PSEG_FIELD_OFFSET => {
|
||||
assert_eq!(data.len(), PSEG_FIELD_SIZE);
|
||||
let mut data_array: [u8; 4] = [0, 0, 0, 0];
|
||||
data_array.copy_from_slice(data);
|
||||
let selected_segment = u32::from_le_bytes(data_array) as usize;
|
||||
if selected_segment >= self.pci_segments.len() {
|
||||
error!(
|
||||
"Segment selection out of range: {} >= {}",
|
||||
selected_segment,
|
||||
self.pci_segments.len()
|
||||
);
|
||||
return None;
|
||||
}
|
||||
self.selected_segment = selected_segment;
|
||||
}
|
||||
_ => error!(
|
||||
"Accessing unknown location at base 0x{:x}, offset 0x{:x}",
|
||||
base, offset
|
||||
|
@ -189,7 +189,7 @@ impl Aml for PciDevSlot {
|
||||
true,
|
||||
vec![&aml::MethodCall::new(
|
||||
"\\_SB_.PHPR.PCEJ".into(),
|
||||
vec![&aml::Path::new("_SUN")],
|
||||
vec![&aml::Path::new("_SUN"), &aml::Path::new("_SEG")],
|
||||
)],
|
||||
),
|
||||
],
|
||||
@ -245,6 +245,8 @@ impl Aml for PciDevSlotMethods {
|
||||
0,
|
||||
true,
|
||||
vec![
|
||||
&aml::Acquire::new("\\_SB_.PHPR.BLCK".into(), 0xffff),
|
||||
&aml::Store::new(&aml::Path::new("\\_SB_.PHPR.PSEG"), &aml::Path::new("_SEG")),
|
||||
&aml::MethodCall::new(
|
||||
"DVNT".into(),
|
||||
vec![&aml::Path::new("\\_SB_.PHPR.PCIU"), &aml::ONE],
|
||||
@ -253,6 +255,7 @@ impl Aml for PciDevSlotMethods {
|
||||
"DVNT".into(),
|
||||
vec![&aml::Path::new("\\_SB_.PHPR.PCID"), &3usize],
|
||||
),
|
||||
&aml::Release::new("\\_SB_.PHPR.BLCK".into()),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes(),
|
||||
|
Loading…
Reference in New Issue
Block a user