Skip to content

Commit 528e727

Browse files
committed
Add types for the PM1 event + control fixed hardware registers
1 parent c3fb274 commit 528e727

File tree

3 files changed

+182
-10
lines changed

3 files changed

+182
-10
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub mod address;
4646
pub mod aml;
4747
#[cfg(feature = "alloc")]
4848
pub mod platform;
49+
pub mod registers;
4950
pub mod rsdp;
5051
pub mod sdt;
5152

src/platform/mod.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,30 @@ use crate::{
1010
Handler,
1111
PowerProfile,
1212
address::GenericAddress,
13+
registers::{FixedRegisters, Pm1Event},
1314
sdt::{
1415
Signature,
1516
fadt::Fadt,
1617
madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
1718
},
1819
};
19-
use alloc::{alloc::Global, vec::Vec};
20+
use alloc::{alloc::Global, sync::Arc, vec::Vec};
2021
use core::{alloc::Allocator, mem, ptr};
2122

2223
/// `AcpiPlatform` is a higher-level view of the ACPI tables that makes it easier to perform common
2324
/// tasks with ACPI. It requires allocator support.
2425
pub struct AcpiPlatform<H: Handler, A: Allocator = Global> {
26+
pub handler: H,
2527
pub tables: AcpiTables<H>,
2628
pub power_profile: PowerProfile,
2729
pub interrupt_model: InterruptModel<A>,
30+
/// The interrupt vector that the System Control Interrupt (SCI) is wired to.
31+
pub sci_interrupt: u16,
2832
/// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
2933
/// interrupt model. That information is stored here, if present.
3034
pub processor_info: Option<ProcessorInfo<A>>,
3135
pub pm_timer: Option<PmTimer>,
36+
pub registers: Arc<FixedRegisters<H>>,
3237
}
3338

3439
unsafe impl<H, A> Send for AcpiPlatform<H, A>
@@ -57,8 +62,19 @@ impl<H: Handler, A: Allocator + Clone> AcpiPlatform<H, A> {
5762

5863
let (interrupt_model, processor_info) = InterruptModel::new_in(&tables, allocator)?;
5964
let pm_timer = PmTimer::new(&fadt)?;
65+
let registers = Arc::new(FixedRegisters::new(&fadt, handler.clone())?);
66+
67+
Ok(AcpiPlatform {
68+
handler: handler.clone(),
69+
tables,
70+
power_profile,
71+
interrupt_model,
72+
sci_interrupt: fadt.sci_interrupt,
73+
processor_info,
74+
pm_timer,
75+
registers,
76+
})
6077

61-
Ok(AcpiPlatform { tables, power_profile, interrupt_model, processor_info, pm_timer})
6278
}
6379

6480
/// Wake up all Application Processors (APs) using the Multiprocessor Wakeup Mailbox Mechanism.
@@ -71,17 +87,11 @@ impl<H: Handler, A: Allocator + Clone> AcpiPlatform<H, A> {
7187
/// # Safety
7288
/// An appropriate environment must exist for the AP to boot into at the given address, or the
7389
/// AP could fault or cause unexpected behaviour.
74-
pub unsafe fn wake_aps(
75-
&self,
76-
apic_id: u32,
77-
wakeup_vector: u64,
78-
timeout_loops: u64,
79-
handler: H,
80-
) -> Result<(), AcpiError> {
90+
pub unsafe fn wake_aps(&self, apic_id: u32, wakeup_vector: u64, timeout_loops: u64) -> Result<(), AcpiError> {
8191
let Some(madt) = self.tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };
8292
let mailbox_addr = madt.get().get_mpwk_mailbox_addr()?;
8393
let mut mpwk_mapping = unsafe {
84-
handler.map_physical_region::<MultiprocessorWakeupMailbox>(
94+
self.handler.map_physical_region::<MultiprocessorWakeupMailbox>(
8595
mailbox_addr as usize,
8696
mem::size_of::<MultiprocessorWakeupMailbox>(),
8797
)

src/registers.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use crate::{AcpiError, Handler, address::MappedGas, sdt::fadt::Fadt};
2+
use bit_field::BitField;
3+
4+
pub struct FixedRegisters<H: Handler> {
5+
pub pm1_event_registers: Pm1EventRegisterBlock<H>,
6+
pub pm1_control_registers: Pm1ControlRegisterBlock<H>,
7+
}
8+
9+
impl<H> FixedRegisters<H>
10+
where
11+
H: Handler,
12+
{
13+
pub fn new(fadt: &Fadt, handler: H) -> Result<FixedRegisters<H>, AcpiError> {
14+
let pm1_event_registers = {
15+
let pm1a = unsafe { MappedGas::map_gas(fadt.pm1a_event_block()?, &handler)? };
16+
let pm1b = match fadt.pm1b_event_block()? {
17+
Some(gas) => Some(unsafe { MappedGas::map_gas(gas, &handler)? }),
18+
None => None,
19+
};
20+
Pm1EventRegisterBlock { pm1_event_length: fadt.pm1_event_length as usize, pm1a, pm1b }
21+
};
22+
let pm1_control_registers = {
23+
let pm1a = unsafe { MappedGas::map_gas(fadt.pm1a_control_block()?, &handler)? };
24+
let pm1b = match fadt.pm1b_control_block()? {
25+
Some(gas) => Some(unsafe { MappedGas::map_gas(gas, &handler)? }),
26+
None => None,
27+
};
28+
Pm1ControlRegisterBlock { pm1a, pm1b }
29+
};
30+
31+
Ok(FixedRegisters { pm1_event_registers, pm1_control_registers })
32+
}
33+
}
34+
35+
/// The PM1 register grouping contains two register blocks that control fixed events. It is split
36+
/// into two to allow its functionality to be split between two hardware components. `PM1a` and
37+
/// `PM1b` are effectively mirrors of each other - reads are made from both of them and logically
38+
/// ORed, and writes are made to both of them.
39+
///
40+
/// The register grouping contains two registers - a `STS` status register that can be read to
41+
/// determine if an event has fired (and written to clear), and an `EN` enabling register to
42+
/// control whether an event should fire.
43+
pub struct Pm1EventRegisterBlock<H: Handler> {
44+
pm1_event_length: usize,
45+
pm1a: MappedGas<H>,
46+
pm1b: Option<MappedGas<H>>,
47+
}
48+
49+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
50+
#[repr(u8)]
51+
pub enum Pm1Event {
52+
Timer = 0,
53+
GlobalLock = 5,
54+
PowerButton = 8,
55+
SleepButton = 9,
56+
Rtc = 10,
57+
PciEWake = 14,
58+
Wake = 15,
59+
}
60+
61+
impl<H> Pm1EventRegisterBlock<H>
62+
where
63+
H: Handler,
64+
{
65+
pub fn set_event_enabled(&self, event: Pm1Event, enabled: bool) -> Result<(), AcpiError> {
66+
let enable_offset = self.pm1_event_length * 8 / 2;
67+
let event_bit = match event {
68+
Pm1Event::Timer => 0,
69+
Pm1Event::GlobalLock => 5,
70+
Pm1Event::PowerButton => 8,
71+
Pm1Event::SleepButton => 9,
72+
Pm1Event::Rtc => 10,
73+
Pm1Event::PciEWake => 14,
74+
Pm1Event::Wake => 15,
75+
};
76+
77+
let mut pm1a = self.pm1a.read()?;
78+
pm1a.set_bit(enable_offset + event_bit, enabled);
79+
self.pm1a.write(pm1a)?;
80+
81+
if let Some(pm1b) = &self.pm1b {
82+
let mut value = pm1b.read()?;
83+
value.set_bit(enable_offset + event_bit, enabled);
84+
pm1b.write(value)?;
85+
}
86+
87+
Ok(())
88+
}
89+
90+
pub fn read(&self) -> Result<u64, AcpiError> {
91+
let pm1_len = self.pm1_event_length * 8;
92+
93+
let pm1a = self.pm1a.read()?.get_bits(0..pm1_len);
94+
let pm1b = if let Some(pm1b) = &self.pm1b { pm1b.read()?.get_bits(0..pm1_len) } else { 0 };
95+
96+
Ok(pm1a | pm1b)
97+
}
98+
}
99+
100+
pub struct Pm1ControlRegisterBlock<H: Handler> {
101+
pm1a: MappedGas<H>,
102+
pm1b: Option<MappedGas<H>>,
103+
}
104+
105+
#[derive(Clone, Copy, PartialEq, Debug)]
106+
pub enum Pm1ControlBit {
107+
/// Selects whether the power management event produces an SCI or SMI interrupt. This is
108+
/// controlled by the firmware - OSPM should always preserve this bit.
109+
SciEnable = 0,
110+
/// When this bit is set, bus master requests can cause any processor in the C3 state to
111+
/// transistion to C0.
112+
BusMasterWake = 1,
113+
/// A write to this bit generates an SMI, passing control to the platform runtime firmware. It
114+
/// should be written when the global lock is released and the pending bit in the FACS is set.
115+
GlobalLockRelease = 2,
116+
/*
117+
* Bits 3..10 are reserved. Bits 10..13 are SLP_TYPx - this field is set separately and
118+
* contains the desired hardware sleep state the system enters when `SleepEnable` is set.
119+
*/
120+
SleepEnable = 13,
121+
}
122+
123+
impl<H> Pm1ControlRegisterBlock<H>
124+
where
125+
H: Handler,
126+
{
127+
pub fn set_bit(&self, bit: Pm1ControlBit, set: bool) -> Result<(), AcpiError> {
128+
let control_bit = match bit {
129+
Pm1ControlBit::SciEnable => 0,
130+
Pm1ControlBit::BusMasterWake => 1,
131+
Pm1ControlBit::GlobalLockRelease => 2,
132+
Pm1ControlBit::SleepEnable => 13,
133+
};
134+
135+
let mut pm1a = self.pm1a.read()?;
136+
pm1a.set_bit(control_bit, set);
137+
self.pm1a.write(pm1a)?;
138+
139+
if let Some(pm1b) = &self.pm1b {
140+
let mut value = pm1b.read()?;
141+
value.set_bit(control_bit, set);
142+
pm1b.write(value)?;
143+
}
144+
145+
Ok(())
146+
}
147+
148+
pub fn set_sleep_typ(&self, value: u8) -> Result<(), AcpiError> {
149+
let mut pm1a = self.pm1a.read()?;
150+
pm1a.set_bits(10..13, value as u64);
151+
self.pm1a.write(pm1a)?;
152+
153+
if let Some(pm1b) = &self.pm1b {
154+
let mut pm1b_value = pm1b.read()?;
155+
pm1b_value.set_bits(10..13, value as u64);
156+
pm1b.write(pm1b_value)?;
157+
}
158+
159+
Ok(())
160+
}
161+
}

0 commit comments

Comments
 (0)