Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pci/pm.rs
5394 views
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 {
49
fn bytes(&self) -> &[u8] {
50
self.as_bytes()
51
}
52
53
fn id(&self) -> PciCapabilityID {
54
PciCapabilityID::PowerManagement
55
}
56
57
fn writable_bits(&self) -> Vec<u32> {
58
vec![0u32, 0x8103]
59
}
60
}
61
62
impl PciPmCap {
63
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
}
72
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 {
89
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
96
pub fn read(&self, data: &mut u32) {
97
*data = self.power_control_status as u32;
98
}
99
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
134
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
153
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 {
172
fn read_mask(&self) -> &'static [u32] {
173
&PM_CONFIG_READ_MASK
174
}
175
176
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
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
201
fn set_cap_mapping(&mut self, mapping: PciCapMapping) {
202
self.cap_mapping = Some(mapping);
203
}
204
}
205
206