@@ -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+ }
0 commit comments