rsdp.rs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. use super::AcpiError;
  2. use core::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. // Check the fields present in all versions against `checksum`
  46. let mut sum: usize = 0;
  47. sum += self
  48. .signature
  49. .iter()
  50. .map(|&b| usize::from(b))
  51. .sum::<usize>();
  52. sum += self.checksum as usize;
  53. sum += self.oem_id.iter().map(|&b| usize::from(b)).sum::<usize>();
  54. sum += self.revision as usize;
  55. sum += self.rsdt_address as usize;
  56. // Check that the lowest byte is 0
  57. if sum & 0b1111_1111 != 0 {
  58. return Err(AcpiError::RsdpInvalidChecksum);
  59. }
  60. // For Version 2.0+, check the extension checksum too
  61. if self.revision > 0 {
  62. let mut sum: usize = 0;
  63. sum += self.length as usize;
  64. sum += self.xsdt_address as usize;
  65. sum += self.ext_checksum as usize;
  66. sum += self.reserved.iter().map(|&b| usize::from(b)).sum::<usize>();
  67. if sum & 0b1111_1111 != 0 {
  68. return Err(AcpiError::RsdpInvalidChecksum);
  69. }
  70. }
  71. Ok(())
  72. }
  73. pub(crate) fn oem_id<'a>(&'a self) -> &'a str {
  74. str::from_utf8(&self.oem_id).unwrap()
  75. }
  76. pub(crate) fn revision(&self) -> u8 {
  77. self.revision
  78. }
  79. pub(crate) fn rsdt_address(&self) -> u32 {
  80. self.rsdt_address
  81. }
  82. pub(crate) fn xsdt_address(&self) -> u64 {
  83. assert!(
  84. self.revision > 0,
  85. "Tried to read extended RSDP field with ACPI Version 1.0"
  86. );
  87. self.xsdt_address
  88. }
  89. }
  90. #[cfg(test)]
  91. mod tests {
  92. use rsdp::Rsdp;
  93. impl Rsdp {
  94. /// Create a test RSDP. Checksums are passed as `Option<u8>`; if `None` is passed, the
  95. /// correct checksum is calculated and used.
  96. pub fn make_testcase(
  97. signature: [u8; 8],
  98. checksum: Option<u8>,
  99. oem_id: [u8; 6],
  100. revision: u8,
  101. rsdt_address: u32,
  102. length: u32,
  103. xsdt_address: u64,
  104. ext_checksum: Option<u8>,
  105. reserved: [u8; 3],
  106. ) -> Rsdp {
  107. let checksum = checksum.unwrap_or(
  108. ((0isize
  109. - signature.iter().map(|&b| isize::from(b)).sum::<isize>()
  110. - oem_id.iter().map(|&b| isize::from(b)).sum::<isize>()
  111. - revision as isize
  112. - rsdt_address as isize)
  113. & 0b1111_1111) as u8,
  114. );
  115. let ext_checksum = ext_checksum.unwrap_or(
  116. ((0isize
  117. - length as isize
  118. - xsdt_address as isize
  119. - reserved.iter().map(|&b| isize::from(b)).sum::<isize>())
  120. & 0b1111_1111) as u8,
  121. );
  122. Rsdp {
  123. signature,
  124. checksum,
  125. oem_id,
  126. revision,
  127. rsdt_address,
  128. length,
  129. xsdt_address,
  130. ext_checksum,
  131. reserved,
  132. }
  133. }
  134. }
  135. }