Skip to content

Commit 020eb7b

Browse files
committed
Add mechanism to enter ACPI mode
1 parent 68c240a commit 020eb7b

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ pub enum AcpiError {
265265
InvalidMadt(MadtError),
266266
InvalidGenericAddress,
267267

268+
Timeout,
269+
268270
#[cfg(feature = "alloc")]
269271
Aml(aml::AmlError),
270272

src/platform/mod.rs

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
Handler,
1111
PowerProfile,
1212
address::GenericAddress,
13-
registers::{FixedRegisters, Pm1Event},
13+
registers::{FixedRegisters, Pm1ControlBit, Pm1Event},
1414
sdt::{
1515
Signature,
1616
fadt::Fadt,
@@ -27,7 +27,9 @@ pub struct AcpiPlatform<H: Handler, A: Allocator = Global> {
2727
pub tables: AcpiTables<H>,
2828
pub power_profile: PowerProfile,
2929
pub interrupt_model: InterruptModel<A>,
30-
/// The interrupt vector that the System Control Interrupt (SCI) is wired to.
30+
/// The interrupt vector that the System Control Interrupt (SCI) is wired to. On x86 systems with
31+
/// an 8259, this is the interrupt vector. On other systems, this is the GSI of the SCI
32+
/// interrupt. The interrupt should be treated as a shareable, level, active-low interrupt.
3133
pub sci_interrupt: u16,
3234
/// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
3335
/// interrupt model. That information is stored here, if present.
@@ -74,7 +76,68 @@ impl<H: Handler, A: Allocator + Clone> AcpiPlatform<H, A> {
7476
pm_timer,
7577
registers,
7678
})
79+
}
80+
81+
/// Initializes the event registers, masking all events to start.
82+
pub fn initialize_events(&self) -> Result<(), AcpiError> {
83+
/*
84+
* Disable all fixed events to start.
85+
*/
86+
self.registers.pm1_event_registers.set_event_enabled(Pm1Event::Timer, false)?;
87+
self.registers.pm1_event_registers.set_event_enabled(Pm1Event::GlobalLock, false)?;
88+
self.registers.pm1_event_registers.set_event_enabled(Pm1Event::PowerButton, false)?;
89+
self.registers.pm1_event_registers.set_event_enabled(Pm1Event::SleepButton, false)?;
90+
self.registers.pm1_event_registers.set_event_enabled(Pm1Event::Rtc, false)?;
91+
self.registers.pm1_event_registers.set_event_enabled(Pm1Event::PciEWake, false)?;
92+
self.registers.pm1_event_registers.set_event_enabled(Pm1Event::Wake, false)?;
93+
94+
// TODO: deal with GPEs
95+
96+
Ok(())
97+
}
7798

99+
pub fn read_mode(&self) -> Result<AcpiMode, AcpiError> {
100+
if self.registers.pm1_control_registers.read_bit(Pm1ControlBit::SciEnable)? {
101+
Ok(AcpiMode::Acpi)
102+
} else {
103+
Ok(AcpiMode::Legacy)
104+
}
105+
}
106+
107+
/// Move the platform into ACPI mode, if it is not already in it. This means platform power
108+
/// management events will be routed to the kernel via the SCI interrupt, instead of to the
109+
/// firmware's SMI handler.
110+
///
111+
/// ### Warning
112+
/// This can be a bad idea on real hardware if you are not able to handle platform events
113+
/// properly. Entering ACPI mode means you are responsible for dealing with events like the
114+
/// power button and thermal events instead of the firmware - if you do not handle these, it
115+
/// may be difficult to recover the platform. Hardware damage is unlikely as firmware usually
116+
/// has safeguards for critical events, but like with all things concerning firmware, you may
117+
/// not wish to rely on these.
118+
pub fn enter_acpi_mode(&self) -> Result<(), AcpiError> {
119+
if self.read_mode()? == AcpiMode::Acpi {
120+
return Ok(());
121+
}
122+
123+
let Some(fadt) = self.tables.find_table::<Fadt>() else { Err(AcpiError::TableNotFound(Signature::FADT))? };
124+
self.handler.write_io_u8(fadt.smi_cmd_port as u16, fadt.acpi_enable);
125+
126+
/*
127+
* We now have to spin and wait for the firmware to yield control. We'll wait up to 3
128+
* seconds.
129+
*/
130+
let mut spinning = 3 * 1000 * 1000; // Microseconds
131+
while spinning > 0 {
132+
if self.read_mode()? == AcpiMode::Acpi {
133+
return Ok(());
134+
}
135+
136+
spinning -= 100;
137+
self.handler.stall(100);
138+
}
139+
140+
Err(AcpiError::Timeout)
78141
}
79142

80143
/// Wake up all Application Processors (APs) using the Multiprocessor Wakeup Mailbox Mechanism.
@@ -192,3 +255,9 @@ impl PmTimer {
192255
}
193256
}
194257
}
258+
259+
#[derive(Clone, Copy, PartialEq, Debug)]
260+
pub enum AcpiMode {
261+
Legacy,
262+
Acpi,
263+
}

src/registers.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub struct Pm1ControlRegisterBlock<H: Handler> {
104104

105105
#[derive(Clone, Copy, PartialEq, Debug)]
106106
pub enum Pm1ControlBit {
107-
/// Selects whether the power management event produces an SCI or SMI interrupt. This is
107+
/// Determines whether the power management event produces an SCI or SMI interrupt. This is
108108
/// controlled by the firmware - OSPM should always preserve this bit.
109109
SciEnable = 0,
110110
/// When this bit is set, bus master requests can cause any processor in the C3 state to
@@ -124,6 +124,20 @@ impl<H> Pm1ControlRegisterBlock<H>
124124
where
125125
H: Handler,
126126
{
127+
pub fn read_bit(&self, bit: Pm1ControlBit) -> Result<bool, 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 pm1a = self.pm1a.read()?;
136+
let pm1b = if let Some(ref pm1b) = self.pm1b { pm1b.read()? } else { 0 };
137+
let pm1 = pm1a | pm1b;
138+
Ok(pm1.get_bit(control_bit))
139+
}
140+
127141
pub fn set_bit(&self, bit: Pm1ControlBit, set: bool) -> Result<(), AcpiError> {
128142
let control_bit = match bit {
129143
Pm1ControlBit::SciEnable => 0,

0 commit comments

Comments
 (0)