1 use crate::{
2     address::{AccessSize, AddressSpace, GenericAddress, RawGenericAddress},
3     sdt::{ExtendedField, SdtHeader, Signature},
4     AcpiError,
5     AcpiTable,
6 };
7 use bit_field::BitField;
8 
9 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
10 pub enum PowerProfile {
11     Unspecified,
12     Desktop,
13     Mobile,
14     Workstation,
15     EnterpriseServer,
16     SohoServer,
17     AppliancePc,
18     PerformanceServer,
19     Tablet,
20     Reserved(u8),
21 }
22 
23 /// Represents the Fixed ACPI Description Table (FADT). This table contains various fixed hardware
24 /// details, such as the addresses of the hardware register blocks. It also contains a pointer to
25 /// the Differentiated Definition Block (DSDT).
26 ///
27 /// In cases where the FADT contains both a 32-bit and 64-bit field for the same address, we should
28 /// always prefer the 64-bit one. Only if it's zero or the CPU will not allow us to access that
29 /// address should the 32-bit one be used.
30 #[repr(C, packed)]
31 #[derive(Debug, Clone, Copy)]
32 pub struct Fadt {
33     header: SdtHeader,
34 
35     firmware_ctrl: u32,
36     dsdt_address: u32,
37 
38     // Used in acpi 1.0; compatibility only, should be zero
39     _reserved: u8,
40 
41     preferred_pm_profile: u8,
42     /// On systems with an i8259 PIC, this is the vector the System Control Interrupt (SCI) is wired to. On other systems, this is
43     /// the Global System Interrupt (GSI) number of the SCI.
44     ///
45     /// The SCI should be treated as a sharable, level, active-low interrupt.
46     pub sci_interrupt: u16,
47     /// The system port address of the SMI Command Port. This port should only be accessed from the boot processor.
48     /// A value of `0` indicates that System Management Mode is not supported.
49     ///
50     ///    - Writing the value in `acpi_enable` to this port will transfer control of the ACPI hardware registers
51     ///      from the firmware to the OS. You must synchronously wait for the transfer to complete, indicated by the
52     ///      setting of `SCI_EN`.
53     ///    - Writing the value in `acpi_disable` will relinquish ownership of the hardware registers to the
54     ///      firmware. This should only be done if you've previously acquired ownership. Before writing this value,
55     ///      the OS should mask all SCI interrupts and clear the `SCI_EN` bit.
56     ///    - Writing the value in `s4bios_req` requests that the firmware enter the S4 state through the S4BIOS
57     ///      feature. This is only supported if the `S4BIOS_F` flag in the FACS is set.
58     ///    - Writing the value in `pstate_control` yields control of the processor performance state to the OS.
59     ///      If this field is `0`, this feature is not supported.
60     ///    - Writing the value in `c_state_control` tells the firmware that the OS supports `_CST` AML objects and
61     ///      notifications of C State changes.
62     pub smi_cmd_port: u32,
63     pub acpi_enable: u8,
64     pub acpi_disable: u8,
65     pub s4bios_req: u8,
66     pub pstate_control: u8,
67     pm1a_event_block: u32,
68     pm1b_event_block: u32,
69     pm1a_control_block: u32,
70     pm1b_control_block: u32,
71     pm2_control_block: u32,
72     pm_timer_block: u32,
73     gpe0_block: u32,
74     gpe1_block: u32,
75     pm1_event_length: u8,
76     pm1_control_length: u8,
77     pm2_control_length: u8,
78     pm_timer_length: u8,
79     gpe0_block_length: u8,
80     gpe1_block_length: u8,
81     pub gpe1_base: u8,
82     pub c_state_control: u8,
83     /// The worst-case latency to enter and exit the C2 state, in microseconds. A value `>100` indicates that the
84     /// system does not support the C2 state.
85     pub worst_c2_latency: u16,
86     /// The worst-case latency to enter and exit the C3 state, in microseconds. A value `>1000` indicates that the
87     /// system does not support the C3 state.
88     pub worst_c3_latency: u16,
89     pub flush_size: u16,
90     pub flush_stride: u16,
91     pub duty_offset: u8,
92     pub duty_width: u8,
93     pub day_alarm: u8,
94     pub month_alarm: u8,
95     pub century: u8,
96     pub iapc_boot_arch: IaPcBootArchFlags,
97     _reserved2: u8, // must be 0
98     pub flags: FixedFeatureFlags,
99     reset_reg: RawGenericAddress,
100     pub reset_value: u8,
101     pub arm_boot_arch: ArmBootArchFlags,
102     fadt_minor_version: u8,
103     x_firmware_ctrl: ExtendedField<u64, 2>,
104     x_dsdt_address: ExtendedField<u64, 2>,
105     x_pm1a_event_block: ExtendedField<RawGenericAddress, 2>,
106     x_pm1b_event_block: ExtendedField<RawGenericAddress, 2>,
107     x_pm1a_control_block: ExtendedField<RawGenericAddress, 2>,
108     x_pm1b_control_block: ExtendedField<RawGenericAddress, 2>,
109     x_pm2_control_block: ExtendedField<RawGenericAddress, 2>,
110     x_pm_timer_block: ExtendedField<RawGenericAddress, 2>,
111     x_gpe0_block: ExtendedField<RawGenericAddress, 2>,
112     x_gpe1_block: ExtendedField<RawGenericAddress, 2>,
113     sleep_control_reg: ExtendedField<RawGenericAddress, 2>,
114     sleep_status_reg: ExtendedField<RawGenericAddress, 2>,
115     hypervisor_vendor_id: ExtendedField<u64, 2>,
116 }
117 
118 /// ### Safety: Implementation properly represents a valid FADT.
119 unsafe impl AcpiTable for Fadt {
120     const SIGNATURE: Signature = Signature::FADT;
121 
header(&self) -> &SdtHeader122     fn header(&self) -> &SdtHeader {
123         &self.header
124     }
125 }
126 
127 impl Fadt {
validate(&self) -> Result<(), AcpiError>128     pub fn validate(&self) -> Result<(), AcpiError> {
129         self.header.validate(crate::sdt::Signature::FADT)
130     }
131 
facs_address(&self) -> Result<usize, AcpiError>132     pub fn facs_address(&self) -> Result<usize, AcpiError> {
133         unsafe {
134             { self.x_firmware_ctrl }
135                 .access(self.header.revision)
136                 .filter(|&p| p != 0)
137                 .or(Some(self.firmware_ctrl as u64))
138                 .filter(|&p| p != 0)
139                 .map(|p| p as usize)
140                 .ok_or(AcpiError::InvalidFacsAddress)
141         }
142     }
143 
dsdt_address(&self) -> Result<usize, AcpiError>144     pub fn dsdt_address(&self) -> Result<usize, AcpiError> {
145         unsafe {
146             { self.x_dsdt_address }
147                 .access(self.header.revision)
148                 .filter(|&p| p != 0)
149                 .or(Some(self.dsdt_address as u64))
150                 .filter(|&p| p != 0)
151                 .map(|p| p as usize)
152                 .ok_or(AcpiError::InvalidDsdtAddress)
153         }
154     }
155 
power_profile(&self) -> PowerProfile156     pub fn power_profile(&self) -> PowerProfile {
157         match self.preferred_pm_profile {
158             0 => PowerProfile::Unspecified,
159             1 => PowerProfile::Desktop,
160             2 => PowerProfile::Mobile,
161             3 => PowerProfile::Workstation,
162             4 => PowerProfile::EnterpriseServer,
163             5 => PowerProfile::SohoServer,
164             6 => PowerProfile::AppliancePc,
165             7 => PowerProfile::PerformanceServer,
166             8 => PowerProfile::Tablet,
167             other => PowerProfile::Reserved(other),
168         }
169     }
170 
pm1a_event_block(&self) -> Result<GenericAddress, AcpiError>171     pub fn pm1a_event_block(&self) -> Result<GenericAddress, AcpiError> {
172         if let Some(raw) = unsafe { self.x_pm1a_event_block.access(self.header().revision) } {
173             if raw.address != 0x0 {
174                 return GenericAddress::from_raw(raw);
175             }
176         }
177 
178         Ok(GenericAddress {
179             address_space: AddressSpace::SystemIo,
180             bit_width: self.pm1_event_length * 8,
181             bit_offset: 0,
182             access_size: AccessSize::Undefined,
183             address: self.pm1a_event_block.into(),
184         })
185     }
186 
pm1b_event_block(&self) -> Result<Option<GenericAddress>, AcpiError>187     pub fn pm1b_event_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
188         if let Some(raw) = unsafe { self.x_pm1b_event_block.access(self.header().revision) } {
189             if raw.address != 0x0 {
190                 return Ok(Some(GenericAddress::from_raw(raw)?));
191             }
192         }
193 
194         if self.pm1b_event_block != 0 {
195             Ok(Some(GenericAddress {
196                 address_space: AddressSpace::SystemIo,
197                 bit_width: self.pm1_event_length * 8,
198                 bit_offset: 0,
199                 access_size: AccessSize::Undefined,
200                 address: self.pm1b_event_block.into(),
201             }))
202         } else {
203             Ok(None)
204         }
205     }
206 
pm1a_control_block(&self) -> Result<GenericAddress, AcpiError>207     pub fn pm1a_control_block(&self) -> Result<GenericAddress, AcpiError> {
208         if let Some(raw) = unsafe { self.x_pm1a_control_block.access(self.header().revision) } {
209             if raw.address != 0x0 {
210                 return GenericAddress::from_raw(raw);
211             }
212         }
213 
214         Ok(GenericAddress {
215             address_space: AddressSpace::SystemIo,
216             bit_width: self.pm1_control_length * 8,
217             bit_offset: 0,
218             access_size: AccessSize::Undefined,
219             address: self.pm1a_control_block.into(),
220         })
221     }
222 
pm1b_control_block(&self) -> Result<Option<GenericAddress>, AcpiError>223     pub fn pm1b_control_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
224         if let Some(raw) = unsafe { self.x_pm1b_control_block.access(self.header().revision) } {
225             if raw.address != 0x0 {
226                 return Ok(Some(GenericAddress::from_raw(raw)?));
227             }
228         }
229 
230         if self.pm1b_control_block != 0 {
231             Ok(Some(GenericAddress {
232                 address_space: AddressSpace::SystemIo,
233                 bit_width: self.pm1_control_length * 8,
234                 bit_offset: 0,
235                 access_size: AccessSize::Undefined,
236                 address: self.pm1b_control_block.into(),
237             }))
238         } else {
239             Ok(None)
240         }
241     }
242 
pm2_control_block(&self) -> Result<Option<GenericAddress>, AcpiError>243     pub fn pm2_control_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
244         if let Some(raw) = unsafe { self.x_pm2_control_block.access(self.header().revision) } {
245             if raw.address != 0x0 {
246                 return Ok(Some(GenericAddress::from_raw(raw)?));
247             }
248         }
249 
250         if self.pm2_control_block != 0 {
251             Ok(Some(GenericAddress {
252                 address_space: AddressSpace::SystemIo,
253                 bit_width: self.pm2_control_length * 8,
254                 bit_offset: 0,
255                 access_size: AccessSize::Undefined,
256                 address: self.pm2_control_block.into(),
257             }))
258         } else {
259             Ok(None)
260         }
261     }
262 
263     /// Attempts to parse the FADT's PWM timer blocks, first returning the extended block, and falling back to
264     /// parsing the legacy block into a `GenericAddress`.
pm_timer_block(&self) -> Result<Option<GenericAddress>, AcpiError>265     pub fn pm_timer_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
266         // ACPI spec indicates `PM_TMR_LEN` should be 4, or otherwise the PM_TMR is not supported.
267         if self.pm_timer_length != 4 {
268             return Ok(None);
269         }
270 
271         if let Some(raw) = unsafe { self.x_pm_timer_block.access(self.header().revision) } {
272             if raw.address != 0x0 {
273                 return Ok(Some(GenericAddress::from_raw(raw)?));
274             }
275         }
276 
277         if self.pm_timer_block != 0 {
278             Ok(Some(GenericAddress {
279                 address_space: AddressSpace::SystemIo,
280                 bit_width: 32,
281                 bit_offset: 0,
282                 access_size: AccessSize::Undefined,
283                 address: self.pm_timer_block.into(),
284             }))
285         } else {
286             Ok(None)
287         }
288     }
289 
gpe0_block(&self) -> Result<Option<GenericAddress>, AcpiError>290     pub fn gpe0_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
291         if let Some(raw) = unsafe { self.x_gpe0_block.access(self.header().revision) } {
292             if raw.address != 0x0 {
293                 return Ok(Some(GenericAddress::from_raw(raw)?));
294             }
295         }
296 
297         if self.gpe0_block != 0 {
298             Ok(Some(GenericAddress {
299                 address_space: AddressSpace::SystemIo,
300                 bit_width: self.gpe0_block_length * 8,
301                 bit_offset: 0,
302                 access_size: AccessSize::Undefined,
303                 address: self.gpe0_block.into(),
304             }))
305         } else {
306             Ok(None)
307         }
308     }
309 
gpe1_block(&self) -> Result<Option<GenericAddress>, AcpiError>310     pub fn gpe1_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
311         if let Some(raw) = unsafe { self.x_gpe1_block.access(self.header().revision) } {
312             if raw.address != 0x0 {
313                 return Ok(Some(GenericAddress::from_raw(raw)?));
314             }
315         }
316 
317         if self.gpe1_block != 0 {
318             Ok(Some(GenericAddress {
319                 address_space: AddressSpace::SystemIo,
320                 bit_width: self.gpe1_block_length * 8,
321                 bit_offset: 0,
322                 access_size: AccessSize::Undefined,
323                 address: self.gpe1_block.into(),
324             }))
325         } else {
326             Ok(None)
327         }
328     }
329 
reset_register(&self) -> Result<GenericAddress, AcpiError>330     pub fn reset_register(&self) -> Result<GenericAddress, AcpiError> {
331         GenericAddress::from_raw(self.reset_reg)
332     }
333 
sleep_control_register(&self) -> Result<Option<GenericAddress>, AcpiError>334     pub fn sleep_control_register(&self) -> Result<Option<GenericAddress>, AcpiError> {
335         if let Some(raw) = unsafe { self.sleep_control_reg.access(self.header().revision) } {
336             Ok(Some(GenericAddress::from_raw(raw)?))
337         } else {
338             Ok(None)
339         }
340     }
341 
sleep_status_register(&self) -> Result<Option<GenericAddress>, AcpiError>342     pub fn sleep_status_register(&self) -> Result<Option<GenericAddress>, AcpiError> {
343         if let Some(raw) = unsafe { self.sleep_status_reg.access(self.header().revision) } {
344             Ok(Some(GenericAddress::from_raw(raw)?))
345         } else {
346             Ok(None)
347         }
348     }
349 }
350 
351 #[derive(Clone, Copy, Debug)]
352 pub struct FixedFeatureFlags(u32);
353 
354 impl FixedFeatureFlags {
355     /// If true, an equivalent to the x86 [WBINVD](https://www.felixcloutier.com/x86/wbinvd) instruction is supported.
356     /// All caches will be flushed and invalidated upon completion of this instruction,
357     /// and memory coherency is properly maintained. The cache *SHALL* only contain what OSPM references or allows to be cached.
supports_equivalent_to_wbinvd(&self) -> bool358     pub fn supports_equivalent_to_wbinvd(&self) -> bool {
359         self.0.get_bit(0)
360     }
361 
362     /// If true, [WBINVD](https://www.felixcloutier.com/x86/wbinvd) properly flushes all caches and  memory coherency is maintained, but caches may not be invalidated.
wbinvd_flushes_all_caches(&self) -> bool363     pub fn wbinvd_flushes_all_caches(&self) -> bool {
364         self.0.get_bit(1)
365     }
366 
367     /// If true, all processors implement the C1 power state.
all_procs_support_c1_power_state(&self) -> bool368     pub fn all_procs_support_c1_power_state(&self) -> bool {
369         self.0.get_bit(2)
370     }
371 
372     /// If true, the C2 power state is configured to work on a uniprocessor and multiprocessor system.
c2_configured_for_mp_system(&self) -> bool373     pub fn c2_configured_for_mp_system(&self) -> bool {
374         self.0.get_bit(3)
375     }
376 
377     /// If true, the power button is handled as a control method device.
378     /// If false, the power button is handled as a fixed-feature programming model.
power_button_is_control_method(&self) -> bool379     pub fn power_button_is_control_method(&self) -> bool {
380         self.0.get_bit(4)
381     }
382 
383     /// If true, the sleep button is handled as a control method device.
384     /// If false, the sleep button is handled as a fixed-feature programming model.
sleep_button_is_control_method(&self) -> bool385     pub fn sleep_button_is_control_method(&self) -> bool {
386         self.0.get_bit(5)
387     }
388 
389     /// If true, the RTC wake status is not supported in fixed register space.
no_rtc_wake_in_fixed_register_space(&self) -> bool390     pub fn no_rtc_wake_in_fixed_register_space(&self) -> bool {
391         self.0.get_bit(6)
392     }
393 
394     /// If true, the RTC alarm function can wake the system from an S4 sleep state.
rtc_wakes_system_from_s4(&self) -> bool395     pub fn rtc_wakes_system_from_s4(&self) -> bool {
396         self.0.get_bit(7)
397     }
398 
399     /// If true, indicates that the PM timer is a 32-bit value.
400     /// If false, the PM timer is a 24-bit value and the remaining 8 bits are clear.
pm_timer_is_32_bit(&self) -> bool401     pub fn pm_timer_is_32_bit(&self) -> bool {
402         self.0.get_bit(8)
403     }
404 
405     /// If true, the system supports docking.
supports_docking(&self) -> bool406     pub fn supports_docking(&self) -> bool {
407         self.0.get_bit(9)
408     }
409 
410     /// If true, the system supports system reset via the reset_reg field of the FADT.
supports_system_reset_via_fadt(&self) -> bool411     pub fn supports_system_reset_via_fadt(&self) -> bool {
412         self.0.get_bit(10)
413     }
414 
415     /// If true, the system supports no expansion capabilities and the case is sealed.
case_is_sealed(&self) -> bool416     pub fn case_is_sealed(&self) -> bool {
417         self.0.get_bit(11)
418     }
419 
420     /// If true, the system cannot detect the monitor or keyboard/mouse devices.
system_is_headless(&self) -> bool421     pub fn system_is_headless(&self) -> bool {
422         self.0.get_bit(12)
423     }
424 
425     /// If true, OSPM must use a processor instruction after writing to the SLP_TYPx register.
use_instr_after_write_to_slp_typx(&self) -> bool426     pub fn use_instr_after_write_to_slp_typx(&self) -> bool {
427         self.0.get_bit(13)
428     }
429 
430     /// If set, the platform supports the `PCIEXP_WAKE_STS` and `PCIEXP_WAKE_EN` bits in the PM1 status and enable registers.
supports_pciexp_wake_in_pm1(&self) -> bool431     pub fn supports_pciexp_wake_in_pm1(&self) -> bool {
432         self.0.get_bit(14)
433     }
434 
435     /// If true, OSPM should use the ACPI power management timer or HPET for monotonically-decreasing timers.
use_pm_or_hpet_for_monotonically_decreasing_timers(&self) -> bool436     pub fn use_pm_or_hpet_for_monotonically_decreasing_timers(&self) -> bool {
437         self.0.get_bit(15)
438     }
439 
440     /// If true, the contents of the `RTC_STS` register are valid after wakeup from S4.
rtc_sts_is_valid_after_wakeup_from_s4(&self) -> bool441     pub fn rtc_sts_is_valid_after_wakeup_from_s4(&self) -> bool {
442         self.0.get_bit(16)
443     }
444 
445     /// If true, the platform supports OSPM leaving GPE wake events armed prior to an S5 transition.
ospm_may_leave_gpe_wake_events_armed_before_s5(&self) -> bool446     pub fn ospm_may_leave_gpe_wake_events_armed_before_s5(&self) -> bool {
447         self.0.get_bit(17)
448     }
449 
450     /// If true, all LAPICs must be configured using the cluster destination model when delivering interrupts in logical mode.
lapics_must_use_cluster_model_for_logical_mode(&self) -> bool451     pub fn lapics_must_use_cluster_model_for_logical_mode(&self) -> bool {
452         self.0.get_bit(18)
453     }
454 
455     /// If true, all LXAPICs must be configured using physical destination mode.
local_xapics_must_use_physical_destination_mode(&self) -> bool456     pub fn local_xapics_must_use_physical_destination_mode(&self) -> bool {
457         self.0.get_bit(19)
458     }
459 
460     /// If true, this system is a hardware-reduced ACPI platform, and software methods are used for fixed-feature functions defined in chapter 4 of the ACPI specification.
system_is_hw_reduced_acpi(&self) -> bool461     pub fn system_is_hw_reduced_acpi(&self) -> bool {
462         self.0.get_bit(20)
463     }
464 
465     /// If true, the system can achieve equal or better power savings in an S0 power state, making an S3 transition useless.
no_benefit_to_s3(&self) -> bool466     pub fn no_benefit_to_s3(&self) -> bool {
467         self.0.get_bit(21)
468     }
469 }
470 
471 #[derive(Clone, Copy, Debug)]
472 pub struct IaPcBootArchFlags(u16);
473 
474 impl IaPcBootArchFlags {
475     /// If true, legacy user-accessible devices are available on the LPC and/or ISA buses.
legacy_devices_are_accessible(&self) -> bool476     pub fn legacy_devices_are_accessible(&self) -> bool {
477         self.0.get_bit(0)
478     }
479 
480     /// If true, the motherboard exposes an IO port 60/64 keyboard controller, typically implemented as an 8042 microcontroller.
motherboard_implements_8042(&self) -> bool481     pub fn motherboard_implements_8042(&self) -> bool {
482         self.0.get_bit(1)
483     }
484 
485     /// If true, OSPM *must not* blindly probe VGA hardware.
486     /// VGA hardware is at MMIO addresses A0000h-BFFFFh and IO ports 3B0h-3BBh and 3C0h-3DFh.
dont_probe_vga(&self) -> bool487     pub fn dont_probe_vga(&self) -> bool {
488         self.0.get_bit(2)
489     }
490 
491     /// If true, OSPM *must not* enable message-signaled interrupts.
dont_enable_msi(&self) -> bool492     pub fn dont_enable_msi(&self) -> bool {
493         self.0.get_bit(3)
494     }
495 
496     /// If true, OSPM *must not* enable PCIe ASPM control.
dont_enable_pcie_aspm(&self) -> bool497     pub fn dont_enable_pcie_aspm(&self) -> bool {
498         self.0.get_bit(4)
499     }
500 
501     /// If true, OSPM *must not* use the RTC via its IO ports, either because it isn't implemented or is at other addresses;
502     /// instead, OSPM *MUST* use the time and alarm namespace device control method.
use_time_and_alarm_namespace_for_rtc(&self) -> bool503     pub fn use_time_and_alarm_namespace_for_rtc(&self) -> bool {
504         self.0.get_bit(5)
505     }
506 }
507 
508 #[derive(Clone, Copy, Debug)]
509 pub struct ArmBootArchFlags(u16);
510 
511 impl ArmBootArchFlags {
512     /// If true, the system implements PSCI.
implements_psci(&self) -> bool513     pub fn implements_psci(&self) -> bool {
514         self.0.get_bit(0)
515     }
516 
517     /// If true, OSPM must use HVC instead of SMC as the PSCI conduit.
use_hvc_as_psci_conduit(&self) -> bool518     pub fn use_hvc_as_psci_conduit(&self) -> bool {
519         self.0.get_bit(1)
520     }
521 }
522