lib.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. //! `aml_parser` is a pure-Rust AML (ACPI Machine Language) parser, used for parsing the DSDT and
  2. //! SSDT tables from ACPI. This crate can be used by kernels to gather information about the
  3. //! hardware, and invoke control methods (this is not yet supported) to query and change the state
  4. //! of devices in a hardware-independent way.
  5. //!
  6. //! ### Using the library
  7. //! To use the library, you will mostly interact with the `AmlContext` type. You should create an
  8. //! instance of this type using `AmlContext::new()`, and then pass it tables containing AML
  9. //! (probably from the `acpi` crate), which you've mapped into the virtual address space. This will
  10. //! parse the table, populating the namespace with objects encoded by the AML. After this, you may
  11. //! unmap the memory the table was mapped into - all the information needed will be extracted and
  12. //! allocated on the heap.
  13. //!
  14. //! You can then access specific objects by name like so: e.g.
  15. //! ```ignore
  16. //! let my_aml_value = aml_context.lookup(&AmlName::from_str("\\_SB.PCI0.S08._ADR").unwrap());
  17. //! ```
  18. //!
  19. //! ### About the parser
  20. //! The parser is written using a set of custom parser combinators - the code can be confusing on
  21. //! first reading, but provides an extensible and type-safe way to write parsers. For an easy
  22. //! introduction to parser combinators and the foundations used for this library, I suggest reading
  23. //! [Bodil's fantastic blog post](https://bodil.lol/parser-combinators/).
  24. //!
  25. //! The actual combinators can be found in `parser.rs`. Various tricks are used to provide a nice
  26. //! API and work around limitations in the type system, such as the concrete types like
  27. //! `MapWithContext`, and the `make_parser_concrete` hack macro.
  28. //!
  29. //! The actual parsers are then grouped into categories based loosely on the AML grammar sections in
  30. //! the ACPI spec. Most are written in terms of combinators, but some have to be written in a more
  31. //! imperitive style, either because they're clearer, or because we haven't yet found good
  32. //! combinator patterns to express the parse.
  33. #![no_std]
  34. #![feature(decl_macro, type_ascription, box_syntax)]
  35. extern crate alloc;
  36. #[cfg(test)]
  37. extern crate std;
  38. #[cfg(test)]
  39. mod test_utils;
  40. pub(crate) mod misc;
  41. pub(crate) mod name_object;
  42. pub(crate) mod namespace;
  43. pub(crate) mod opcode;
  44. pub(crate) mod parser;
  45. pub(crate) mod pkg_length;
  46. pub(crate) mod term_object;
  47. pub(crate) mod type1;
  48. pub(crate) mod type2;
  49. pub mod value;
  50. pub use crate::{
  51. namespace::{AmlHandle, AmlName, Namespace},
  52. value::AmlValue,
  53. };
  54. use alloc::string::String;
  55. use log::error;
  56. use misc::{ArgNum, LocalNum};
  57. use parser::Parser;
  58. use pkg_length::PkgLength;
  59. use value::Args;
  60. /// AML has a `RevisionOp` operator that returns the "AML interpreter revision". It's not clear
  61. /// what this is actually used for, but this is ours.
  62. pub const AML_INTERPRETER_REVISION: u64 = 0;
  63. #[derive(Debug)]
  64. pub struct AmlContext {
  65. pub namespace: Namespace,
  66. current_scope: AmlName,
  67. /*
  68. * AML local variables. These are used when we invoke a control method. A `None` value
  69. * represents a null AML object.
  70. */
  71. local_0: Option<AmlValue>,
  72. local_1: Option<AmlValue>,
  73. local_2: Option<AmlValue>,
  74. local_3: Option<AmlValue>,
  75. local_4: Option<AmlValue>,
  76. local_5: Option<AmlValue>,
  77. local_6: Option<AmlValue>,
  78. local_7: Option<AmlValue>,
  79. /// If we're currently invoking a control method, this stores the arguments that were passed to
  80. /// it. It's `None` if we aren't invoking a method.
  81. current_args: Option<Args>,
  82. }
  83. impl AmlContext {
  84. pub fn new() -> AmlContext {
  85. AmlContext {
  86. namespace: Namespace::new(),
  87. current_scope: AmlName::root(),
  88. local_0: None,
  89. local_1: None,
  90. local_2: None,
  91. local_3: None,
  92. local_4: None,
  93. local_5: None,
  94. local_6: None,
  95. local_7: None,
  96. current_args: None,
  97. }
  98. }
  99. pub fn parse_table(&mut self, stream: &[u8]) -> Result<(), AmlError> {
  100. if stream.len() == 0 {
  101. return Err(AmlError::UnexpectedEndOfStream);
  102. }
  103. let table_length = PkgLength::from_raw_length(stream, stream.len() as u32) as PkgLength;
  104. match term_object::term_list(table_length).parse(stream, self) {
  105. Ok(_) => Ok(()),
  106. Err((remaining, _context, err)) => {
  107. error!("Failed to parse AML stream. Err = {:?}", err);
  108. Err(err)
  109. }
  110. }
  111. }
  112. /// Invoke a method referred to by its path in the namespace, with the given arguments.
  113. pub fn invoke_method(&mut self, path: &AmlName, args: Args) -> Result<AmlValue, AmlError> {
  114. self.namespace.get_by_path(path)?.clone().invoke(self, args, path.clone())
  115. }
  116. pub(crate) fn current_arg(&self, arg: ArgNum) -> Result<&AmlValue, AmlError> {
  117. self.current_args.as_ref().ok_or(AmlError::InvalidArgumentAccess(0xff))?.arg(arg)
  118. }
  119. /// Get the current value of a local by its local number.
  120. ///
  121. /// ### Panics
  122. /// Panics if an invalid local number is passed (valid local numbers are `0..=7`)
  123. pub(crate) fn local(&self, local: LocalNum) -> Result<&AmlValue, AmlError> {
  124. match local {
  125. 0 => self.local_0.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  126. 1 => self.local_1.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  127. 2 => self.local_2.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  128. 3 => self.local_3.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  129. 4 => self.local_4.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  130. 5 => self.local_5.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  131. 6 => self.local_6.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  132. 7 => self.local_7.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  133. _ => panic!("Invalid local number: {}", local),
  134. }
  135. }
  136. }
  137. #[derive(Clone, Debug, PartialEq, Eq)]
  138. pub enum AmlError {
  139. /*
  140. * Errors produced parsing the AML stream.
  141. */
  142. UnexpectedEndOfStream,
  143. UnexpectedByte(u8),
  144. InvalidNameSeg([u8; 4]),
  145. InvalidFieldFlags,
  146. IncompatibleValueConversion,
  147. UnterminatedStringConstant,
  148. InvalidStringConstant,
  149. InvalidRegionSpace(u8),
  150. /// Emitted by a parser when it's clear that the stream doesn't encode the object parsed by
  151. /// that parser (e.g. the wrong opcode starts the stream). This is handled specially by some
  152. /// parsers such as `or` and `choice!`.
  153. WrongParser,
  154. /*
  155. * Errors produced manipulating AML names.
  156. */
  157. /// Produced when trying to normalize a path that does not point to a valid level of the
  158. /// namespace. E.g. `\_SB.^^PCI0` goes above the root of the namespace.
  159. InvalidNormalizedName(String),
  160. RootHasNoParent,
  161. /*
  162. * Errors produced working with the namespace.
  163. */
  164. /// Produced when a path is given that does not point to an object in the AML namespace.
  165. ObjectDoesNotExist(String),
  166. HandleDoesNotExist(AmlHandle),
  167. /// Produced when two values with the same name are added to the namespace.
  168. NameCollision(AmlName),
  169. /*
  170. * Errors produced executing control methods.
  171. */
  172. /// Produced when a method accesses an argument it does not have (e.g. a method that takes 2
  173. /// arguments accesses `Arg4`). The inner value is the number of the argument accessed. If any
  174. /// arguments are accessed when a method is not being executed, this error is produced with an
  175. /// argument number of `0xff`.
  176. InvalidArgumentAccess(ArgNum),
  177. InvalidLocalAccess(LocalNum),
  178. /// This is not a real error, but is used to propagate return values from within the deep
  179. /// parsing call-stack. It should only be emitted when parsing a `DefReturn`. We use the
  180. /// error system here because the way errors are propagated matches how we want to handle
  181. /// return values.
  182. Return(AmlValue),
  183. }