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