fadt.rs 18 KB

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