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