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