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 PM_CAP_VERSION 73 | PM_CAP_PME_SUPPORT_D0 74 | PM_CAP_PME_SUPPORT_D3_HOT 75 | PM_CAP_PME_SUPPORT_D3_COLD 76 } 77 } 78 79 pub struct PmConfig { 80 power_control_status: u16, 81 cap_mapping: Option<PciCapMapping>, 82 } 83 84 impl PmConfig { new(no_soft_reset: bool) -> Self85 pub fn new(no_soft_reset: bool) -> Self { 86 PmConfig { 87 power_control_status: if no_soft_reset { PM_NO_SOFT_RESET } else { 0 }, 88 cap_mapping: None, 89 } 90 } 91 read(&self, data: &mut u32)92 pub fn read(&self, data: &mut u32) { 93 *data = self.power_control_status as u32; 94 } 95 write(&mut self, offset: u64, data: &[u8])96 pub fn write(&mut self, offset: u64, data: &[u8]) { 97 if offset > 1 { 98 return; 99 } 100 101 if offset == 0 { 102 self.power_control_status &= !PM_POWER_STATE_MASK; 103 self.power_control_status |= data[0] as u16 & PM_POWER_STATE_MASK; 104 } 105 106 let write_data = if offset == 0 && (data.len() == 2 || data.len() == 4) { 107 Some((data[1] as u16) << 8) 108 } else if offset == 1 && data.len() == 1 { 109 Some((data[0] as u16) << 8) 110 } else { 111 None 112 }; 113 114 if let Some(write_data) = write_data { 115 if write_data & PM_PME_STATUS != 0 { 116 // clear PME_STATUS 117 self.power_control_status &= !PM_PME_STATUS; 118 } 119 120 if write_data & PM_PME_ENABLE != 0 { 121 self.power_control_status |= PM_PME_ENABLE; 122 } else { 123 self.power_control_status &= !PM_PME_ENABLE; 124 } 125 } 126 } 127 128 /// If device is in D3 and PME is enabled, set PME status, then device could 129 /// inject a pme interrupt into guest should_trigger_pme(&mut self) -> bool130 pub fn should_trigger_pme(&mut self) -> bool { 131 if self.power_control_status & PM_POWER_STATE_MASK == PM_POWER_STATE_D3 132 && self.power_control_status & PM_PME_ENABLE != 0 133 { 134 self.power_control_status |= PM_PME_STATUS; 135 if let Some(cap_mapping) = &mut self.cap_mapping { 136 cap_mapping.set_reg( 137 PM_CAP_CONTROL_STATE_OFFSET, 138 self.power_control_status as u32, 139 0xffff, 140 ); 141 } 142 return true; 143 } 144 145 false 146 } 147 148 /// Get device power status get_power_status(&self) -> PciDevicePower149 pub fn get_power_status(&self) -> PciDevicePower { 150 match self.power_control_status & PM_POWER_STATE_MASK { 151 PM_POWER_STATE_D0 => PciDevicePower::D0, 152 PM_POWER_STATE_D3 => PciDevicePower::D3, 153 _ => PciDevicePower::Unsupported, 154 } 155 } 156 } 157 158 pub struct PmStatusChange { 159 pub from: PciDevicePower, 160 pub to: PciDevicePower, 161 } 162 163 impl PciCapConfigWriteResult for PmStatusChange {} 164 165 const PM_CONFIG_READ_MASK: [u32; 2] = [0, 0xffff]; 166 167 impl PciCapConfig for PmConfig { read_mask(&self) -> &'static [u32]168 fn read_mask(&self) -> &'static [u32] { 169 &PM_CONFIG_READ_MASK 170 } 171 read_reg(&self, reg_idx: usize) -> u32172 fn read_reg(&self, reg_idx: usize) -> u32 { 173 let mut data = 0; 174 if reg_idx == PM_CAP_CONTROL_STATE_OFFSET { 175 self.read(&mut data); 176 } 177 data 178 } 179 write_reg( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> Option<Box<dyn PciCapConfigWriteResult>>180 fn write_reg( 181 &mut self, 182 reg_idx: usize, 183 offset: u64, 184 data: &[u8], 185 ) -> Option<Box<dyn PciCapConfigWriteResult>> { 186 if reg_idx == PM_CAP_CONTROL_STATE_OFFSET { 187 let from = self.get_power_status(); 188 self.write(offset, data); 189 let to = self.get_power_status(); 190 if from != to { 191 return Some(Box::new(PmStatusChange { from, to })); 192 } 193 } 194 None 195 } 196 set_cap_mapping(&mut self, mapping: PciCapMapping)197 fn set_cap_mapping(&mut self, mapping: PciCapMapping) { 198 self.cap_mapping = Some(mapping); 199 } 200 } 201