rsdp.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. use super::AcpiError;
  2. use core::{mem, str};
  3. /// The first structure found in ACPI. It just tells us where the RSDT is.
  4. ///
  5. /// On BIOS systems, it is either found in the first 1KB of the Extended Bios Data Area, or between
  6. /// 0x000E0000 and 0x000FFFFF. The signature is always on a 16 byte boundary. On (U)EFI, it may not
  7. /// be located in these locations, and so an address should be found in the EFI_SYSTEM_TABLE instead.
  8. ///
  9. /// The recommended way of locating the RSDP is to let the bootloader do it - Multiboot2 can pass a
  10. /// tag with the physical address of it. If this is not possible, a manual scan can be done.
  11. ///
  12. /// If `revision > 0`, (the hardware ACPI version is Version 2.0 or greater), the RSDP contains
  13. /// some new fields. For ACPI Version 1.0, these fields are not valid and should not be accessed.
  14. /// For ACPI Version 2.0+, `xsdt_address` should be used (truncated to `u32` on x86) instead of
  15. /// `rsdt_address`.
  16. #[repr(C, packed)]
  17. pub(crate) struct Rsdp {
  18. signature: [u8; 8],
  19. checksum: u8,
  20. oem_id: [u8; 6],
  21. revision: u8,
  22. rsdt_address: u32,
  23. /*
  24. * These fields are only valid for ACPI Version 2.0 and greater
  25. */
  26. length: u32,
  27. xsdt_address: u64,
  28. ext_checksum: u8,
  29. reserved: [u8; 3],
  30. }
  31. impl Rsdp {
  32. /// Checks that:
  33. /// 1) The signature is correct
  34. /// 2) The checksum is correct
  35. /// 3) For Version 2.0+, that the extension checksum is correct
  36. pub(crate) fn validate(&self) -> Result<(), AcpiError> {
  37. // Check the signature
  38. if &self.signature != b"RSD PTR " {
  39. return Err(AcpiError::RsdpIncorrectSignature);
  40. }
  41. // Check the OEM id is valid UTF8 (allows use of unwrap)
  42. if str::from_utf8(&self.oem_id).is_err() {
  43. return Err(AcpiError::RsdpInvalidOemId);
  44. }
  45. /*
  46. * `self.length` doesn't exist on ACPI version 1.0, so we mustn't rely on it. Instead,
  47. * check for version 1.0 and use a hard-coded length instead.
  48. */
  49. let length = if self.revision > 0 {
  50. // For Version 2.0+, check ALL the fields
  51. mem::size_of::<Self>()
  52. } else {
  53. // For Version 1, only check fields up to v1 length only
  54. mem::size_of::<[u8; 8]>()
  55. + mem::size_of::<u8>()
  56. + mem::size_of::<[u8; 6]>()
  57. + mem::size_of::<u8>()
  58. + mem::size_of::<u32>()
  59. };
  60. let self_ptr = self as *const Rsdp as *const u8;
  61. let mut sum: u8 = 0;
  62. for i in 0..length {
  63. sum = sum.wrapping_add(unsafe { *(self_ptr.offset(i as isize)) });
  64. }
  65. if sum != 0 {
  66. return Err(AcpiError::RsdpInvalidChecksum);
  67. }
  68. Ok(())
  69. }
  70. pub(crate) fn oem_id<'a>(&'a self) -> &'a str {
  71. str::from_utf8(&self.oem_id).unwrap()
  72. }
  73. pub(crate) fn revision(&self) -> u8 {
  74. self.revision
  75. }
  76. pub(crate) fn rsdt_address(&self) -> u32 {
  77. self.rsdt_address
  78. }
  79. pub(crate) fn xsdt_address(&self) -> u64 {
  80. assert!(
  81. self.revision > 0,
  82. "Tried to read extended RSDP field with ACPI Version 1.0"
  83. );
  84. self.xsdt_address
  85. }
  86. }
  87. #[cfg(test)]
  88. mod tests {
  89. use rsdp::Rsdp;
  90. impl Rsdp {
  91. /// Create a test RSDP. Checksums are passed as `Option<u8>`; if `None` is passed, the
  92. /// correct checksum is calculated and used.
  93. pub fn make_testcase(
  94. signature: [u8; 8],
  95. checksum: Option<u8>,
  96. oem_id: [u8; 6],
  97. revision: u8,
  98. rsdt_address: u32,
  99. length: u32,
  100. xsdt_address: u64,
  101. ext_checksum: Option<u8>,
  102. reserved: [u8; 3],
  103. ) -> Rsdp {
  104. let checksum = checksum.unwrap_or(
  105. ((0isize
  106. - signature.iter().map(|&b| isize::from(b)).sum::<isize>()
  107. - oem_id.iter().map(|&b| isize::from(b)).sum::<isize>()
  108. - revision as isize
  109. - rsdt_address as isize)
  110. & 0b1111_1111) as u8,
  111. );
  112. let ext_checksum = ext_checksum.unwrap_or(
  113. ((0isize
  114. - length as isize
  115. - xsdt_address as isize
  116. - reserved.iter().map(|&b| isize::from(b)).sum::<isize>())
  117. & 0b1111_1111) as u8,
  118. );
  119. Rsdp {
  120. signature,
  121. checksum,
  122. oem_id,
  123. revision,
  124. rsdt_address,
  125. length,
  126. xsdt_address,
  127. ext_checksum,
  128. reserved,
  129. }
  130. }
  131. }
  132. }