• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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