Skip to content

Commit b217e2f

Browse files
committed
acpi: add support for PCC table
The PCCT (Platform Communications Channel Table) is a generic mechanism for OSPM to communicate with an entity in the platform (e.g. a platform controller, or a Baseboard Management Controller (BMC)). The table provides access to a new namespace, to which Generic Address Structures (GAS) in other tables can refer to. Each entry in the PCCT is an addressable subspace, which provides the address and length of a shared memory region that can be used for communication with the platform. Subspaces may also have a doorbell register to notify the platform of certain events, as well as additional registers.
1 parent 5d37bb3 commit b217e2f

File tree

7 files changed

+894
-0
lines changed

7 files changed

+894
-0
lines changed

Diff for: acpi/src/address.rs

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub enum AddressSpace {
4545
Ipmi,
4646
GeneralIo,
4747
GenericSerialBus,
48+
/// Describes a subspace in the Platform Communications Channel Table ([PCCT](crate::pcct::Pcct)).
4849
PlatformCommunicationsChannel,
4950
FunctionalFixedHardware,
5051
OemDefined(u8),

Diff for: acpi/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub mod handler;
7070
pub mod hpet;
7171
pub mod madt;
7272
pub mod mcfg;
73+
pub mod pcct;
7374
pub mod rsdp;
7475
pub mod sdt;
7576
pub mod spcr;
@@ -139,6 +140,10 @@ pub enum AcpiError {
139140
InvalidMadt(MadtError),
140141
InvalidGenericAddress,
141142

143+
/// The signature for a PCC subspace shared memory region did not
144+
/// match.
145+
PccInvalidShmemSignature(u32),
146+
142147
AllocError,
143148
}
144149

Diff for: acpi/src/pcct/extended.rs

+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
use super::{PccCmdCompleteCheck, PccPlatformInterruptFlags, PccShmemHdr, PccSubspaceHeader, RawGenericAddress};
2+
use crate::{address::GenericAddress, AcpiResult};
3+
use bitflags::bitflags;
4+
5+
/// Extended PCC communication subspace.
6+
///
7+
/// The subspace might be a master or slave subspace, depending on
8+
/// its type (3 and 4 respectively). The type can be inspected in the
9+
/// subspace header or via this type's methods
10+
/// ([`is_master()`](Self::is_master) and [`is_slave()`](Self::is_slave)).
11+
///
12+
/// * Master subspaces are used by the OSPM to communicate with the
13+
/// platform.
14+
/// * Slave subspaces are used by the platform to send asynchronous
15+
/// notifications to the OSPM.
16+
///
17+
/// See section "14.1.6. Extended PCC subspaces (types 3 and 4)" of
18+
/// the ACPI spec for more information.
19+
#[repr(C, packed)]
20+
#[derive(Clone, Copy, Debug)]
21+
pub struct PccExtendedSubspace {
22+
/// PCC subspace header.
23+
pub header: PccSubspaceHeader,
24+
/// GSIV of the interrupt used for the PCC platform interrupt for
25+
/// this subspace.
26+
pub plat_interrupt: u32,
27+
/// Flags for the platform interrupt for this subspace.
28+
pub plat_interrupt_flags: PccPlatformInterruptFlags,
29+
_rsvd: u8,
30+
pub(super) base_address: u64,
31+
pub(super) mem_len: u32,
32+
pub(super) doorbell_reg: RawGenericAddress,
33+
pub(super) doorbell_preserve: u64,
34+
pub(super) doorbell_write: u64,
35+
/// Expected latency to process a command, in microseconds.
36+
pub nominal_latency: u32,
37+
/// The maximum number of periodic requests that the subspace
38+
/// channel can support, reported in commands per minute. 0
39+
/// indicates no limitation.
40+
pub max_periodic_access_rate: u32,
41+
/// The minimum amount of time that OSPM must wait after
42+
/// the completion of a command before issuing the next command,
43+
/// in microseconds.
44+
pub min_request_turnaround_time: u32,
45+
plat_interrupt_ack_reg: RawGenericAddress,
46+
plat_interrupt_ack_preserve: u64,
47+
plat_interrupt_ack_write: u64,
48+
_rsvd2: [u8; 8],
49+
cmd_complete_check_reg: RawGenericAddress,
50+
cmd_complete_check_mask: u64,
51+
cmd_complete_update_reg: RawGenericAddress,
52+
cmd_complete_update_preserve: u64,
53+
cmd_complete_update_write: u64,
54+
err_status_reg: RawGenericAddress,
55+
err_status_mask: u64,
56+
}
57+
58+
impl PccExtendedSubspace {
59+
/// Returns `true` if this is a master subspace.
60+
pub fn is_master(&self) -> bool {
61+
self.header.stype == 3
62+
}
63+
64+
/// Returns `true` if this is a slave subspace.
65+
pub fn is_slave(&self) -> bool {
66+
self.header.stype == 4
67+
}
68+
69+
/// Get information about the platform interrupt ACK mechanism.
70+
pub fn platform_interrupt_ack(&self) -> AcpiResult<PccPlatformInterruptAck> {
71+
let addr = GenericAddress::from_raw(self.plat_interrupt_ack_reg)?;
72+
Ok(PccPlatformInterruptAck {
73+
addr,
74+
preserve: self.plat_interrupt_ack_preserve,
75+
write: self.plat_interrupt_ack_write,
76+
})
77+
}
78+
79+
/// Get information about the command complete check register.
80+
pub fn cmd_complete_check(&self) -> AcpiResult<PccCmdCompleteCheck> {
81+
let addr = GenericAddress::from_raw(self.cmd_complete_check_reg)?;
82+
Ok(PccCmdCompleteCheck { addr, mask: self.cmd_complete_check_mask })
83+
}
84+
85+
/// Get information about the command complete update register.
86+
pub fn cmd_complete_update(&self) -> AcpiResult<PccCmdCompleteUpdate> {
87+
let addr = GenericAddress::from_raw(self.cmd_complete_update_reg)?;
88+
Ok(PccCmdCompleteUpdate {
89+
addr,
90+
preserve: self.cmd_complete_update_preserve,
91+
write: self.cmd_complete_update_write,
92+
})
93+
}
94+
95+
/// Get information about the error status register.
96+
pub fn error_status(&self) -> AcpiResult<PccErrorStatus> {
97+
let addr = GenericAddress::from_raw(self.err_status_reg)?;
98+
Ok(PccErrorStatus { addr, mask: self.err_status_mask })
99+
}
100+
}
101+
102+
/// Information about the command complete update register.
103+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104+
pub struct PccCmdCompleteUpdate {
105+
/// Location of the register.
106+
pub addr: GenericAddress,
107+
/// Mask of bits to preserve in the command complete update
108+
/// register, when updating command complete in this subspace.
109+
pub preserve: u64,
110+
/// Mask of bits to set in the command complete update register,
111+
/// when updating command complete in this subspace. For master
112+
/// subspaces the mask must indicate how to clear the command
113+
/// complete bit. For slave subspaces, the mask must indicate how
114+
/// to set the command complete bit.
115+
pub write: u64,
116+
}
117+
118+
/// Platform interrupt ACK information.
119+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
120+
pub struct PccPlatformInterruptAck {
121+
/// Location of the register.
122+
pub addr: GenericAddress,
123+
/// Bits to preserve when writing the register.
124+
pub preserve: u64,
125+
/// Bits to set when writing the register.
126+
pub write: u64,
127+
}
128+
129+
/// Information about the error status register.
130+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
131+
pub struct PccErrorStatus {
132+
/// Location of the register.
133+
pub addr: GenericAddress,
134+
/// The mask contained here can be combined through a logical AND
135+
/// with content of the Error status register to ascertain whether
136+
/// an error occurred in the transmission of the command through
137+
/// the subspace. The logical NOT of this mask is be used to clear
138+
/// the error. The inverted mask is combined through a logical AND
139+
/// with the content of the Error status register, and the result
140+
/// is written back into said register. This field is ignored for
141+
/// slave channels.
142+
pub mask: u64,
143+
}
144+
145+
/// Shared memory region header for [`PccExtendedSubspace`].
146+
///
147+
/// See section "14.3. Extended PCC Subspace Shared Memory Region" of
148+
/// the ACPI spec for more information.
149+
#[repr(C, packed)]
150+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151+
pub struct PccExtendedShmem {
152+
/// The PCC signature. The signature of a subspace is computed by
153+
/// a bitwise-or of the value `0x50434300` with the subspace ID.
154+
/// For example, subspace 3 has the signature `0x50434303`.
155+
pub signature: u32,
156+
/// Flags for doorbell behavior on this shared region.
157+
pub flags: PccExtendedShmemFlags,
158+
/// Length of payload being transmitted including command field.
159+
pub len: u32,
160+
/// Command being sent over the subspace.
161+
pub cmd: u32,
162+
}
163+
164+
impl PccShmemHdr for PccExtendedShmem {
165+
fn signature(&self) -> u32 {
166+
self.signature
167+
}
168+
}
169+
170+
bitflags! {
171+
/// Flags for [`PccExtendedShmem`].
172+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
173+
pub struct PccExtendedShmemFlags: u32 {
174+
/// **For master subspaces** this field indicates to the
175+
/// platform that it must generate an interrupt when the
176+
/// command has completed. - Setting this bit to 1 when
177+
/// sending a command, requests that completion of the command
178+
/// is signaled via the platform interrupt. - Setting it to 0
179+
/// when sending a command, requests that no interrupt is
180+
/// asserted when the command is completed.
181+
///
182+
/// **For slave subspaces**, if the doorbell field of the
183+
/// slave subspace is non zero, and this flag is set, the
184+
/// OSPM must access the doorbell once it has processed the
185+
/// notification. This bit is ignored by the platform if the
186+
/// [Platform Interrupt field](PcctFlags::INTERRUPT) of the
187+
/// [PCC flags](Pcct::flags) is set to zero.
188+
const NOTIFY_ON_COMPLETION = 1;
189+
}
190+
}
191+
192+
#[cfg(test)]
193+
mod test {
194+
use super::*;
195+
use core::mem::offset_of;
196+
197+
#[test]
198+
fn pcc_extended_subspace_offsets() {
199+
assert_eq!(offset_of!(PccExtendedSubspace, plat_interrupt), 2);
200+
assert_eq!(offset_of!(PccExtendedSubspace, plat_interrupt_flags), 6);
201+
assert_eq!(offset_of!(PccExtendedSubspace, base_address), 8);
202+
assert_eq!(offset_of!(PccExtendedSubspace, mem_len), 16);
203+
assert_eq!(offset_of!(PccExtendedSubspace, doorbell_reg), 20);
204+
assert_eq!(offset_of!(PccExtendedSubspace, doorbell_preserve), 32);
205+
assert_eq!(offset_of!(PccExtendedSubspace, doorbell_write), 40);
206+
assert_eq!(offset_of!(PccExtendedSubspace, nominal_latency), 48);
207+
assert_eq!(offset_of!(PccExtendedSubspace, max_periodic_access_rate), 52);
208+
assert_eq!(offset_of!(PccExtendedSubspace, min_request_turnaround_time), 56);
209+
assert_eq!(offset_of!(PccExtendedSubspace, plat_interrupt_ack_reg), 60);
210+
assert_eq!(offset_of!(PccExtendedSubspace, plat_interrupt_ack_preserve), 72);
211+
assert_eq!(offset_of!(PccExtendedSubspace, plat_interrupt_ack_write), 80);
212+
assert_eq!(offset_of!(PccExtendedSubspace, cmd_complete_check_reg), 96);
213+
assert_eq!(offset_of!(PccExtendedSubspace, cmd_complete_check_mask), 108);
214+
assert_eq!(offset_of!(PccExtendedSubspace, cmd_complete_update_reg), 116);
215+
assert_eq!(offset_of!(PccExtendedSubspace, cmd_complete_update_preserve), 128);
216+
assert_eq!(offset_of!(PccExtendedSubspace, cmd_complete_update_write), 136);
217+
assert_eq!(offset_of!(PccExtendedSubspace, err_status_reg), 144);
218+
assert_eq!(offset_of!(PccExtendedSubspace, err_status_mask), 156);
219+
}
220+
}

Diff for: acpi/src/pcct/generic.rs

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use super::{PccShmemHdr, PccSubspaceHeader, RawGenericAddress};
2+
use bitflags::bitflags;
3+
4+
/// Generic PCC communication subspace.
5+
///
6+
/// See section "14.1.3. Generic Communications Subspace Structure
7+
/// (type 0)" of the ACPI spec for more information.
8+
#[repr(C, packed)]
9+
#[derive(Clone, Copy, Debug)]
10+
pub struct PccGenericSubspace {
11+
/// PCC subspace header.
12+
pub header: PccSubspaceHeader,
13+
_rsvd: [u8; 6],
14+
pub(super) base_address: u64,
15+
pub(super) mem_len: u64,
16+
pub(super) doorbell_reg: RawGenericAddress,
17+
pub(super) doorbell_preserve: u64,
18+
pub(super) doorbell_write: u64,
19+
/// Expected latency to process a command, in microseconds.
20+
pub nominal_latency: u32,
21+
/// The maximum number of periodic requests that the subspace
22+
/// channel can support, reported in commands per minute. 0
23+
/// indicates no limitation.
24+
pub max_periodic_access_rate: u32,
25+
/// The minimum amount of time that OSPM must wait after
26+
/// the completion of a command before issuing the next command,
27+
/// in microseconds.
28+
pub min_request_turnaround_time: u16,
29+
}
30+
31+
/// Shared memory region header for [`PccGenericSubspace`].
32+
///
33+
/// See section "14.2. Generic Communications Channel Shared Memory
34+
/// Region" of the ACPI spec for more information.
35+
#[repr(C, packed)]
36+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37+
pub struct PccGenericShmem {
38+
/// The PCC signature. The signature of a subspace is computed b
39+
/// a bitwise-or of the value `0x50434300` with the subspace ID.
40+
/// For example, subspace 3 has the signature `0x50434303`.
41+
pub signature: u32,
42+
/// Commands for the platform to perform.
43+
pub cmd: PccGenericShmemCmd,
44+
/// Command processing status.
45+
pub status: PccGenericShmemStatus,
46+
}
47+
48+
impl PccShmemHdr for PccGenericShmem {
49+
fn signature(&self) -> u32 {
50+
self.signature
51+
}
52+
}
53+
54+
/// Command field for [`PccGenericShmem`].
55+
///
56+
/// For [`PccGenericSubspace`],
57+
/// [`PccHwReducedSubspace1`](super::PccHwReducedSubspace1) and
58+
/// [`PccHwReducedSubspace2`](super::PccHwReducedSubspace2), this
59+
/// 16-bit field is used to select one of the defined commands for
60+
/// the platform to perform. OSPM is esponsible for populating this
61+
/// field before each command invocation.
62+
///
63+
/// See section "14.2.1. Generic Communications Channel Command
64+
/// Field" of the ACPI spec for more information.
65+
#[repr(C, packed)]
66+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67+
pub struct PccGenericShmemCmd {
68+
/// Command code to execute. Command codes are application
69+
/// specific and defined by the consumer of this interface.
70+
pub cmd: u8,
71+
/// Additional bitfields for the command field.
72+
pub flags: PccShmemCmdFlags,
73+
}
74+
75+
bitflags! {
76+
/// Flags for [`PccGenericShmemCmd`].
77+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78+
pub struct PccShmemCmdFlags: u8 {
79+
/// If set, the platform should generate a Doorbell interrupt
80+
/// at the completion of this command. The interrupt is an
81+
/// SCI for a [`PccGenericSubspace`], or as described by
82+
/// the [Doorbell Interrupt field](PccHwReducedSubspace1::plat_interrupt)
83+
/// for [`PccHwReducedSubspace1`] and
84+
/// [`PccHwReducedSubspace2`]. If the
85+
/// [Doorbell bit](PcctFlags::PLATFORM_INTERRUPT) is not set
86+
/// in the [PCC global flags](Pcct::flags), this bit must be
87+
/// cleared.
88+
const NOTIFY_ON_COMPLETION = 1 << 7;
89+
}
90+
}
91+
92+
bitflags! {
93+
/// Status flags for [`PccGenericShmem`].
94+
///
95+
/// See section "14.2.2. Generic Communications Channel Status
96+
/// Field" of the ACPI spec for more information.
97+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98+
pub struct PccGenericShmemStatus: u16 {
99+
/// If set, the platform has completed processing the last
100+
/// command.
101+
const CMD_COMPLETE = 0b1;
102+
/// If set, the platform has issued a Platform Interrupt to
103+
/// this subspace. OSPM must check the
104+
/// [`CMD_COMPLETE`](Self::CMD_COMPLETE) and
105+
/// [`PLATFORM_NOTIFICATION`](Self::PLATFORM_NOTIFICATION)
106+
/// fields to determine the cause of the Interrupt.
107+
const PLATFORM_INTERRUPT = 1 << 1;
108+
/// If set, an error occurred executing the last command.
109+
const ERROR = 1 << 2;
110+
/// If set, indicates the platform is issuing an asynchronous
111+
/// notification to OSPM.
112+
const PLATFORM_NOTIFICATION = 1 << 3;
113+
}
114+
}
115+
#[cfg(test)]
116+
mod test {
117+
use super::*;
118+
use core::mem::offset_of;
119+
120+
#[test]
121+
fn pcc_generic_subspace_offsets() {
122+
assert_eq!(offset_of!(PccGenericSubspace, base_address), 8);
123+
assert_eq!(offset_of!(PccGenericSubspace, mem_len), 16);
124+
assert_eq!(offset_of!(PccGenericSubspace, doorbell_reg), 24);
125+
assert_eq!(offset_of!(PccGenericSubspace, doorbell_preserve), 36);
126+
assert_eq!(offset_of!(PccGenericSubspace, doorbell_write), 44);
127+
assert_eq!(offset_of!(PccGenericSubspace, nominal_latency), 52);
128+
assert_eq!(offset_of!(PccGenericSubspace, max_periodic_access_rate), 56);
129+
assert_eq!(offset_of!(PccGenericSubspace, min_request_turnaround_time), 60);
130+
}
131+
}

0 commit comments

Comments
 (0)