@@ -10,7 +10,7 @@ use crate::{
10
10
Handler ,
11
11
PowerProfile ,
12
12
address:: GenericAddress ,
13
- registers:: { FixedRegisters , Pm1Event } ,
13
+ registers:: { FixedRegisters , Pm1ControlBit , Pm1Event } ,
14
14
sdt:: {
15
15
Signature ,
16
16
fadt:: Fadt ,
@@ -27,7 +27,9 @@ pub struct AcpiPlatform<H: Handler, A: Allocator = Global> {
27
27
pub tables : AcpiTables < H > ,
28
28
pub power_profile : PowerProfile ,
29
29
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.
31
33
pub sci_interrupt : u16 ,
32
34
/// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
33
35
/// interrupt model. That information is stored here, if present.
@@ -74,7 +76,68 @@ impl<H: Handler, A: Allocator + Clone> AcpiPlatform<H, A> {
74
76
pm_timer,
75
77
registers,
76
78
} )
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
+ }
77
98
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 )
78
141
}
79
142
80
143
/// Wake up all Application Processors (APs) using the Multiprocessor Wakeup Mailbox Mechanism.
@@ -192,3 +255,9 @@ impl PmTimer {
192
255
}
193
256
}
194
257
}
258
+
259
+ #[ derive( Clone , Copy , PartialEq , Debug ) ]
260
+ pub enum AcpiMode {
261
+ Legacy ,
262
+ Acpi ,
263
+ }
0 commit comments