1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #![cfg_attr(windows, allow(dead_code))] 6 7 use zerocopy::FromBytes; 8 use zerocopy::Immutable; 9 use zerocopy::IntoBytes; 10 use zerocopy::KnownLayout; 11 12 use crate::pci::pci_configuration::PciCapConfig; 13 use crate::pci::pci_configuration::PciCapConfigWriteResult; 14 use crate::pci::pci_configuration::PciCapMapping; 15 use crate::pci::PciCapability; 16 use crate::pci::PciCapabilityID; 17 18 const PM_CAP_CONTROL_STATE_OFFSET: usize = 1; 19 pub const PM_CAP_LENGTH: usize = 8; 20 const PM_CAP_PME_SUPPORT_D0: u16 = 0x0800; 21 const PM_CAP_PME_SUPPORT_D3_HOT: u16 = 0x4000; 22 const PM_CAP_PME_SUPPORT_D3_COLD: u16 = 0x8000; 23 const PM_CAP_VERSION: u16 = 0x2; 24 const PM_PME_STATUS: u16 = 0x8000; 25 const PM_PME_ENABLE: u16 = 0x100; 26 const PM_NO_SOFT_RESET: u16 = 0x8; 27 const PM_POWER_STATE_MASK: u16 = 0x3; 28 const PM_POWER_STATE_D0: u16 = 0; 29 const PM_POWER_STATE_D3: u16 = 0x3; 30 31 #[derive(Eq, PartialEq)] 32 pub enum PciDevicePower { 33 D0 = 0, 34 D3 = 3, 35 Unsupported = 0xFF, 36 } 37 38 #[repr(C)] 39 #[derive(Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout)] 40 pub struct PciPmCap { 41 _cap_vndr: u8, 42 _cap_next: u8, 43 pm_cap: u16, 44 pm_control_status: u16, 45 padding: u16, 46 } 47 48 impl PciCapability for PciPmCap { bytes(&self) -> &[u8]49 fn bytes(&self) -> &[u8] { 50 self.as_bytes() 51 } 52 id(&self) -> PciCapabilityID53 fn id(&self) -> PciCapabilityID { 54 PciCapabilityID::PowerManagement 55 } 56 writable_bits(&self) -> Vec<u32>57 fn writable_bits(&self) -> Vec<u32> { 58 vec![0u32, 0x8103] 59 } 60 } 61 62 impl PciPmCap { new() -> Self63 pub fn new() -> Self { 64 PciPmCap { 65 _cap_vndr: 0, 66 _cap_next: 0, 67 pm_cap: Self::default_cap(), 68 pm_control_status: 0, 69 padding: 0, 70 } 71 } default_cap() -> u1672 pub fn default_cap() -> u16 { 73 let mut cap = PM_CAP_VERSION; 74 // We use ACPI GPEs for PMEs, which are x86_64 only. We should 75 // implement support for native PCIe PMEs to support non-ACPI platforms. 76 if cfg!(target_arch = "x86_64") { 77 cap |= PM_CAP_PME_SUPPORT_D0 | PM_CAP_PME_SUPPORT_D3_HOT | PM_CAP_PME_SUPPORT_D3_COLD 78 } 79 cap 80 } 81 } 82 83 pub struct PmConfig { 84 power_control_status: u16, 85 cap_mapping: Option<PciCapMapping>, 86 } 87 88 impl PmConfig { new(no_soft_reset: bool) -> Self89 pub fn new(no_soft_reset: bool) -> Self { 90 PmConfig { 91 power_control_status: if no_soft_reset { PM_NO_SOFT_RESET } else { 0 }, 92 cap_mapping: None, 93 } 94 } 95 read(&self, data: &mut u32)96 pub fn read(&self, data: &mut u32) { 97 *data = self.power_control_status as u32; 98 } 99 write(&mut self, offset: u64, data: &[u8])100 pub fn write(&mut self, offset: u64, data: &[u8]) { 101 if offset > 1 { 102 return; 103 } 104 105 if offset == 0 { 106 self.power_control_status &= !PM_POWER_STATE_MASK; 107 self.power_control_status |= data[0] as u16 & PM_POWER_STATE_MASK; 108 } 109 110 let write_data = if offset == 0 && (data.len() == 2 || data.len() == 4) { 111 Some((data[1] as u16) << 8) 112 } else if offset == 1 && data.len() == 1 { 113 Some((data[0] as u16) << 8) 114 } else { 115 None 116 }; 117 118 if let Some(write_data) = write_data { 119 if write_data & PM_PME_STATUS != 0 { 120 // clear PME_STATUS 121 self.power_control_status &= !PM_PME_STATUS; 122 } 123 124 if write_data & PM_PME_ENABLE != 0 { 125 self.power_control_status |= PM_PME_ENABLE; 126 } else { 127 self.power_control_status &= !PM_PME_ENABLE; 128 } 129 } 130 } 131 132 /// If device is in D3 and PME is enabled, set PME status, then device could 133 /// inject a pme interrupt into guest should_trigger_pme(&mut self) -> bool134 pub fn should_trigger_pme(&mut self) -> bool { 135 if self.power_control_status & PM_POWER_STATE_MASK == PM_POWER_STATE_D3 136 && self.power_control_status & PM_PME_ENABLE != 0 137 { 138 self.power_control_status |= PM_PME_STATUS; 139 if let Some(cap_mapping) = &mut self.cap_mapping { 140 cap_mapping.set_reg( 141 PM_CAP_CONTROL_STATE_OFFSET, 142 self.power_control_status as u32, 143 0xffff, 144 ); 145 } 146 return true; 147 } 148 149 false 150 } 151 152 /// Get device power status get_power_status(&self) -> PciDevicePower153 pub fn get_power_status(&self) -> PciDevicePower { 154 match self.power_control_status & PM_POWER_STATE_MASK { 155 PM_POWER_STATE_D0 => PciDevicePower::D0, 156 PM_POWER_STATE_D3 => PciDevicePower::D3, 157 _ => PciDevicePower::Unsupported, 158 } 159 } 160 } 161 162 pub struct PmStatusChange { 163 pub from: PciDevicePower, 164 pub to: PciDevicePower, 165 } 166 167 impl PciCapConfigWriteResult for PmStatusChange {} 168 169 const PM_CONFIG_READ_MASK: [u32; 2] = [0, 0xffff]; 170 171 impl PciCapConfig for PmConfig { read_mask(&self) -> &'static [u32]172 fn read_mask(&self) -> &'static [u32] { 173 &PM_CONFIG_READ_MASK 174 } 175 read_reg(&self, reg_idx: usize) -> u32176 fn read_reg(&self, reg_idx: usize) -> u32 { 177 let mut data = 0; 178 if reg_idx == PM_CAP_CONTROL_STATE_OFFSET { 179 self.read(&mut data); 180 } 181 data 182 } 183 write_reg( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> Option<Box<dyn PciCapConfigWriteResult>>184 fn write_reg( 185 &mut self, 186 reg_idx: usize, 187 offset: u64, 188 data: &[u8], 189 ) -> Option<Box<dyn PciCapConfigWriteResult>> { 190 if reg_idx == PM_CAP_CONTROL_STATE_OFFSET { 191 let from = self.get_power_status(); 192 self.write(offset, data); 193 let to = self.get_power_status(); 194 if from != to { 195 return Some(Box::new(PmStatusChange { from, to })); 196 } 197 } 198 None 199 } 200 set_cap_mapping(&mut self, mapping: PciCapMapping)201 fn set_cap_mapping(&mut self, mapping: PciCapMapping) { 202 self.cap_mapping = Some(mapping); 203 } 204 } 205