blob: ddcad4bc21881ed6d1dc48b34307dc2201b43a7e [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::BTreeMap;
use std::str::FromStr;
use std::sync::Arc;
use sync::Mutex;
use crate::bus::{HostHotPlugKey, HotPlugBus};
use crate::pci::pci_configuration::PciCapabilityID;
use crate::pci::{MsiConfig, PciAddress, PciCapability, PciDeviceError};
use crate::pci::pcie::pci_bridge::PciBridgeBusRange;
use crate::pci::pcie::pcie_device::{PciPmcCap, PcieCap, PcieDevice, PmcConfig};
use crate::pci::pcie::pcie_host::PcieHostPort;
use crate::pci::pcie::*;
use anyhow::{anyhow, Result};
use base::warn;
use data_model::DataInit;
use resources::{Alloc, SystemAllocator};
use vm_control::GpeNotify;
// reserve 8MB memory window
const PCIE_RP_BR_MEM_SIZE: u64 = 0x80_0000;
// reserve 64MB prefetch window
const PCIE_RP_BR_PREF_MEM_SIZE: u64 = 0x400_0000;
const PCIE_RP_DID: u16 = 0x3420;
pub struct PcieRootPort {
pcie_cap_reg_idx: Option<usize>,
msi_config: Option<Arc<Mutex<MsiConfig>>>,
pmc_config: PmcConfig,
pmc_cap_reg_idx: Option<usize>,
pci_address: Option<PciAddress>,
slot_control: Option<u16>,
slot_status: u16,
root_control: u16,
root_status: u32,
hp_interrupt_pending: bool,
pme_pending_request_id: Option<PciAddress>,
bus_range: PciBridgeBusRange,
downstream_devices: BTreeMap<PciAddress, HostHotPlugKey>,
hotplug_out_begin: bool,
removed_downstream: Vec<PciAddress>,
removed_downstream_valid: bool,
pcie_host: Option<PcieHostPort>,
prepare_hotplug: bool,
}
impl PcieRootPort {
/// Constructs a new PCIE root port
pub fn new(secondary_bus_num: u8, slot_implemented: bool) -> Self {
let bus_range = PciBridgeBusRange {
primary: 0,
secondary: secondary_bus_num,
subordinate: secondary_bus_num,
};
PcieRootPort {
pcie_cap_reg_idx: None,
msi_config: None,
pmc_config: PmcConfig::new(),
pmc_cap_reg_idx: None,
pci_address: None,
slot_control: if slot_implemented {
Some(PCIE_SLTCTL_PIC_OFF | PCIE_SLTCTL_AIC_OFF)
} else {
None
},
slot_status: 0,
root_control: 0,
root_status: 0,
hp_interrupt_pending: false,
pme_pending_request_id: None,
bus_range,
downstream_devices: BTreeMap::new(),
hotplug_out_begin: false,
removed_downstream: Vec::new(),
removed_downstream_valid: false,
pcie_host: None,
prepare_hotplug: false,
}
}
/// Constructs a new PCIE root port which associated with the host physical pcie RP
pub fn new_from_host(pcie_host: PcieHostPort, slot_implemented: bool) -> Result<Self> {
let bus_range = pcie_host.get_bus_range();
// if physical pcie root port isn't on bus 0, ignore this physical pcie root port.
if bus_range.primary != 0 {
return Err(anyhow!(
"physical pcie RP isn't on bus 0: {}",
bus_range.primary
));
}
Ok(PcieRootPort {
pcie_cap_reg_idx: None,
msi_config: None,
pmc_config: PmcConfig::new(),
pmc_cap_reg_idx: None,
pci_address: None,
slot_control: if slot_implemented {
Some(PCIE_SLTCTL_PIC_OFF | PCIE_SLTCTL_AIC_OFF)
} else {
None
},
slot_status: 0,
root_control: 0,
root_status: 0,
hp_interrupt_pending: false,
pme_pending_request_id: None,
bus_range,
downstream_devices: BTreeMap::new(),
hotplug_out_begin: false,
removed_downstream: Vec::new(),
removed_downstream_valid: false,
pcie_host: Some(pcie_host),
prepare_hotplug: false,
})
}
fn get_slot_control(&self) -> u16 {
if let Some(slot_control) = self.slot_control {
return slot_control;
}
0
}
fn read_pcie_cap(&self, offset: usize, data: &mut u32) {
if offset == PCIE_SLTCTL_OFFSET {
*data = ((self.slot_status as u32) << 16) | (self.get_slot_control() as u32);
} else if offset == PCIE_ROOTCTL_OFFSET {
*data = self.root_control as u32;
} else if offset == PCIE_ROOTSTA_OFFSET {
*data = self.root_status;
}
}
fn write_pcie_cap(&mut self, offset: usize, data: &[u8]) {
self.removed_downstream_valid = false;
match offset {
PCIE_SLTCTL_OFFSET => {
let value = match u16::from_slice(data) {
Some(&v) => v,
None => {
warn!("write SLTCTL isn't word, len: {}", data.len());
return;
}
};
// if slot is populated, power indicator is off,
// it will detach devices
let old_control = self.get_slot_control();
match self.slot_control.as_mut() {
Some(v) => *v = value,
None => return,
}
if (self.slot_status & PCIE_SLTSTA_PDS != 0)
&& (value & PCIE_SLTCTL_PIC_OFF == PCIE_SLTCTL_PIC_OFF)
&& (old_control & PCIE_SLTCTL_PIC_OFF != PCIE_SLTCTL_PIC_OFF)
{
self.removed_downstream_valid = true;
self.slot_status &= !PCIE_SLTSTA_PDS;
self.slot_status |= PCIE_SLTSTA_PDC;
self.trigger_hp_interrupt();
}
if old_control != value {
// send Command completed events
self.slot_status |= PCIE_SLTSTA_CC;
self.trigger_cc_interrupt();
}
}
PCIE_SLTSTA_OFFSET => {
if self.slot_control.is_none() {
return;
}
let value = match u16::from_slice(data) {
Some(v) => *v,
None => {
warn!("write SLTSTA isn't word, len: {}", data.len());
return;
}
};
if value & PCIE_SLTSTA_ABP != 0 {
self.slot_status &= !PCIE_SLTSTA_ABP;
}
if value & PCIE_SLTSTA_PFD != 0 {
self.slot_status &= !PCIE_SLTSTA_PFD;
}
if value & PCIE_SLTSTA_PDC != 0 {
self.slot_status &= !PCIE_SLTSTA_PDC;
}
if value & PCIE_SLTSTA_CC != 0 {
self.slot_status &= !PCIE_SLTSTA_CC;
}
if value & PCIE_SLTSTA_DLLSC != 0 {
self.slot_status &= !PCIE_SLTSTA_DLLSC;
}
}
PCIE_ROOTCTL_OFFSET => match u16::from_slice(data) {
Some(v) => self.root_control = *v,
None => warn!("write root control isn't word, len: {}", data.len()),
},
PCIE_ROOTSTA_OFFSET => match u32::from_slice(data) {
Some(v) => {
if *v & PCIE_ROOTSTA_PME_STATUS != 0 {
if let Some(request_id) = self.pme_pending_request_id {
self.root_status &= !PCIE_ROOTSTA_PME_PENDING;
let req_id = ((request_id.bus as u32) << 8)
| ((request_id.dev as u32) << 3)
| (request_id.func as u32);
self.root_status &= !PCIE_ROOTSTA_PME_REQ_ID_MASK;
self.root_status |= req_id;
self.root_status |= PCIE_ROOTSTA_PME_STATUS;
self.pme_pending_request_id = None;
self.trigger_pme_interrupt();
} else {
self.root_status &= !PCIE_ROOTSTA_PME_STATUS;
if self.hp_interrupt_pending {
self.hp_interrupt_pending = false;
self.trigger_hp_interrupt();
}
}
}
}
None => warn!("write root status isn't dword, len: {}", data.len()),
},
_ => (),
}
}
fn trigger_interrupt(&self) {
if let Some(msi_config) = &self.msi_config {
let msi_config = msi_config.lock();
if msi_config.is_msi_enabled() {
msi_config.trigger()
}
}
}
fn trigger_cc_interrupt(&self) {
if (self.get_slot_control() & PCIE_SLTCTL_CCIE) != 0
&& (self.slot_status & PCIE_SLTSTA_CC) != 0
{
self.trigger_interrupt()
}
}
fn trigger_hp_interrupt(&self) {
let slot_control = self.get_slot_control();
if (slot_control & PCIE_SLTCTL_HPIE) != 0
&& (self.slot_status & slot_control & (PCIE_SLTCTL_ABPE | PCIE_SLTCTL_PDCE)) != 0
{
self.trigger_interrupt()
}
}
fn trigger_pme_interrupt(&self) {
if (self.root_control & PCIE_ROOTCTL_PME_ENABLE) != 0
&& (self.root_status & PCIE_ROOTSTA_PME_STATUS) != 0
{
self.trigger_interrupt()
}
}
fn inject_pme(&mut self) {
if (self.root_status & PCIE_ROOTSTA_PME_STATUS) != 0 {
self.root_status |= PCIE_ROOTSTA_PME_PENDING;
self.pme_pending_request_id = self.pci_address;
} else {
let request_id = self.pci_address.unwrap();
let req_id = ((request_id.bus as u32) << 8)
| ((request_id.dev as u32) << 3)
| (request_id.func as u32);
self.root_status &= !PCIE_ROOTSTA_PME_REQ_ID_MASK;
self.root_status |= req_id;
self.pme_pending_request_id = None;
self.root_status |= PCIE_ROOTSTA_PME_STATUS;
self.trigger_pme_interrupt();
}
}
// when RP is D3, HP interrupt is disabled by pcie driver, so inject a PME to wakeup
// RP first, then inject HP interrupt.
fn trigger_hp_or_pme_interrupt(&mut self) {
if self.pmc_config.should_trigger_pme() {
self.hp_interrupt_pending = true;
self.inject_pme();
} else {
self.trigger_hp_interrupt();
}
}
}
impl PcieDevice for PcieRootPort {
fn get_device_id(&self) -> u16 {
match &self.pcie_host {
Some(host) => host.read_device_id(),
None => PCIE_RP_DID,
}
}
fn debug_label(&self) -> String {
match &self.pcie_host {
Some(host) => host.host_name(),
None => "PcieRootPort".to_string(),
}
}
fn allocate_address(
&mut self,
resources: &mut SystemAllocator,
) -> std::result::Result<PciAddress, PciDeviceError> {
if self.pci_address.is_none() {
match &self.pcie_host {
Some(host) => {
let address = PciAddress::from_str(&host.host_name())
.map_err(|e| PciDeviceError::PciAddressParseFailure(host.host_name(), e))?;
if resources.reserve_pci(
Alloc::PciBar {
bus: address.bus,
dev: address.dev,
func: address.func,
bar: 0,
},
host.host_name(),
) {
self.pci_address = Some(address);
} else {
self.pci_address = None;
}
}
None => match resources.allocate_pci(self.bus_range.primary, self.debug_label()) {
Some(Alloc::PciBar {
bus,
dev,
func,
bar: _,
}) => self.pci_address = Some(PciAddress { bus, dev, func }),
_ => self.pci_address = None,
},
}
}
self.pci_address.ok_or(PciDeviceError::PciAllocationFailed)
}
fn clone_interrupt(&mut self, msi_config: Arc<Mutex<MsiConfig>>) {
self.msi_config = Some(msi_config);
}
fn get_caps(&self) -> Vec<Box<dyn PciCapability>> {
vec![
Box::new(PcieCap::new(
PcieDevicePortType::RootPort,
self.slot_control.is_some(),
0,
)),
Box::new(PciPmcCap::new()),
]
}
fn set_capability_reg_idx(&mut self, id: PciCapabilityID, reg_idx: usize) {
match id {
PciCapabilityID::PciExpress => self.pcie_cap_reg_idx = Some(reg_idx),
PciCapabilityID::PowerManagement => self.pmc_cap_reg_idx = Some(reg_idx),
_ => (),
}
}
fn read_config(&self, reg_idx: usize, data: &mut u32) {
if let Some(pcie_cap_reg_idx) = self.pcie_cap_reg_idx {
if reg_idx >= pcie_cap_reg_idx && reg_idx < pcie_cap_reg_idx + (PCIE_CAP_LEN / 4) {
let offset = (reg_idx - pcie_cap_reg_idx) * 4;
self.read_pcie_cap(offset, data);
}
}
if let Some(pmc_cap_reg_idx) = self.pmc_cap_reg_idx {
if reg_idx == pmc_cap_reg_idx + PMC_CAP_CONTROL_STATE_OFFSET {
self.pmc_config.read(data);
}
}
if let Some(host) = &self.pcie_host {
// pcie host may override some config registers
host.read_config(reg_idx, data);
}
}
fn write_config(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
if let Some(pcie_cap_reg_idx) = self.pcie_cap_reg_idx {
if reg_idx >= pcie_cap_reg_idx && reg_idx < pcie_cap_reg_idx + (PCIE_CAP_LEN / 4) {
let delta = ((reg_idx - pcie_cap_reg_idx) * 4) + offset as usize;
self.write_pcie_cap(delta, data);
}
}
if let Some(pmc_cap_reg_idx) = self.pmc_cap_reg_idx {
if reg_idx == pmc_cap_reg_idx + PMC_CAP_CONTROL_STATE_OFFSET {
let old_status = self.pmc_config.get_power_status();
self.pmc_config.write(offset, data);
let new_status = self.pmc_config.get_power_status();
if old_status == PciDevicePower::D3
&& new_status == PciDevicePower::D0
&& self.prepare_hotplug
{
if let Some(host) = self.pcie_host.as_mut() {
host.hotplug_probe();
self.prepare_hotplug = false;
}
}
}
}
if let Some(host) = self.pcie_host.as_mut() {
// device may write data to host, or do something at specific register write
host.write_config(reg_idx, offset, data);
}
}
fn get_bus_range(&self) -> Option<PciBridgeBusRange> {
Some(self.bus_range)
}
fn get_removed_devices(&self) -> Vec<PciAddress> {
if self.removed_downstream_valid {
self.removed_downstream.clone()
} else {
Vec::new()
}
}
fn hotplug_implemented(&self) -> bool {
self.slot_control.is_some()
}
fn get_bridge_window_size(&self) -> (u64, u64) {
if let Some(host) = &self.pcie_host {
host.get_bridge_window_size()
} else {
(PCIE_RP_BR_MEM_SIZE, PCIE_RP_BR_PREF_MEM_SIZE)
}
}
}
impl HotPlugBus for PcieRootPort {
fn hot_plug(&mut self, addr: PciAddress) {
if self.downstream_devices.get(&addr).is_none() {
return;
}
self.slot_status = self.slot_status | PCIE_SLTSTA_PDS | PCIE_SLTSTA_PDC | PCIE_SLTSTA_ABP;
self.trigger_hp_or_pme_interrupt();
}
fn hot_unplug(&mut self, addr: PciAddress) {
if self.downstream_devices.remove(&addr).is_none() {
return;
}
if !self.hotplug_out_begin {
self.removed_downstream.clear();
self.removed_downstream.push(addr);
// All the remaine devices will be removed also in this hotplug out interrupt
for (guest_pci_addr, _) in self.downstream_devices.iter() {
self.removed_downstream.push(*guest_pci_addr);
}
self.slot_status = self.slot_status | PCIE_SLTSTA_PDC | PCIE_SLTSTA_ABP;
self.trigger_hp_or_pme_interrupt();
if let Some(host) = self.pcie_host.as_mut() {
host.hot_unplug();
}
}
self.hotplug_out_begin = true;
}
fn is_match(&self, host_addr: PciAddress) -> Option<u8> {
let _ = self.slot_control?;
if (host_addr.bus >= self.bus_range.secondary
&& host_addr.bus <= self.bus_range.subordinate)
|| self.pcie_host.is_none()
{
Some(self.bus_range.secondary)
} else {
None
}
}
fn add_hotplug_device(&mut self, host_key: HostHotPlugKey, guest_addr: PciAddress) {
if self.slot_control.is_none() {
return;
}
// Begin the next round hotplug in process
if self.hotplug_out_begin {
self.hotplug_out_begin = false;
self.downstream_devices.clear();
self.removed_downstream.clear();
}
self.downstream_devices.insert(guest_addr, host_key);
}
fn get_hotplug_device(&self, host_key: HostHotPlugKey) -> Option<PciAddress> {
for (guest_address, host_info) in self.downstream_devices.iter() {
match host_info {
HostHotPlugKey::Vfio { host_addr } => {
let saved_addr = *host_addr;
match host_key {
HostHotPlugKey::Vfio { host_addr } => {
if host_addr == saved_addr {
return Some(*guest_address);
}
}
}
}
}
}
None
}
}
impl GpeNotify for PcieRootPort {
fn notify(&mut self) {
if self.slot_control.is_none() {
return;
}
if self.pcie_host.is_some() {
self.prepare_hotplug = true;
}
if self.pmc_config.should_trigger_pme() {
self.inject_pme();
}
}
}