lib.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. //! `aml` 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 mod pci_routing;
  46. pub(crate) mod pkg_length;
  47. pub mod resource;
  48. pub(crate) mod term_object;
  49. pub(crate) mod type1;
  50. pub(crate) mod type2;
  51. pub mod value;
  52. pub use crate::{
  53. namespace::{AmlHandle, AmlName, Namespace},
  54. value::AmlValue,
  55. };
  56. use log::error;
  57. use misc::{ArgNum, LocalNum};
  58. use parser::Parser;
  59. use pkg_length::PkgLength;
  60. use term_object::term_list;
  61. use value::Args;
  62. /// AML has a `RevisionOp` operator that returns the "AML interpreter revision". It's not clear
  63. /// what this is actually used for, but this is ours.
  64. pub const AML_INTERPRETER_REVISION: u64 = 0;
  65. /// Describes how much debug information the parser should emit. Set the "maximum" expected verbosity in
  66. /// the context's `debug_verbosity` - everything will be printed that is less or equal in 'verbosity'.
  67. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
  68. pub enum DebugVerbosity {
  69. /// Print no debug information
  70. None,
  71. /// Print heads and tails when entering and leaving scopes of major objects, but not more minor ones.
  72. Scopes,
  73. /// Print heads and tails when entering and leaving scopes of all objects.
  74. AllScopes,
  75. /// Print heads and tails of all objects, and extra debug information as it's parsed.
  76. All,
  77. }
  78. #[derive(Debug)]
  79. pub struct AmlContext {
  80. pub namespace: Namespace,
  81. /*
  82. * AML local variables. These are used when we invoke a control method. A `None` value
  83. * represents a null AML object.
  84. */
  85. local_0: Option<AmlValue>,
  86. local_1: Option<AmlValue>,
  87. local_2: Option<AmlValue>,
  88. local_3: Option<AmlValue>,
  89. local_4: Option<AmlValue>,
  90. local_5: Option<AmlValue>,
  91. local_6: Option<AmlValue>,
  92. local_7: Option<AmlValue>,
  93. /// If we're currently invoking a control method, this stores the arguments that were passed to
  94. /// it. It's `None` if we aren't invoking a method.
  95. current_args: Option<Args>,
  96. /*
  97. * These track the state of the context while it's parsing an AML table.
  98. */
  99. current_scope: AmlName,
  100. debug_verbosity: DebugVerbosity,
  101. }
  102. impl AmlContext {
  103. pub fn new(debug_verbosity: DebugVerbosity) -> AmlContext {
  104. AmlContext {
  105. namespace: Namespace::new(),
  106. local_0: None,
  107. local_1: None,
  108. local_2: None,
  109. local_3: None,
  110. local_4: None,
  111. local_5: None,
  112. local_6: None,
  113. local_7: None,
  114. current_args: None,
  115. current_scope: AmlName::root(),
  116. debug_verbosity,
  117. }
  118. }
  119. pub fn parse_table(&mut self, stream: &[u8]) -> Result<(), AmlError> {
  120. if stream.len() == 0 {
  121. return Err(AmlError::UnexpectedEndOfStream);
  122. }
  123. let table_length = PkgLength::from_raw_length(stream, stream.len() as u32) as PkgLength;
  124. match term_object::term_list(table_length).parse(stream, self) {
  125. Ok(_) => Ok(()),
  126. Err((_, _, err)) => {
  127. error!("Failed to parse AML stream. Err = {:?}", err);
  128. Err(err)
  129. }
  130. }
  131. }
  132. /// Invoke a method referred to by its path in the namespace, with the given arguments.
  133. pub fn invoke_method(&mut self, path: &AmlName, args: Args) -> Result<AmlValue, AmlError> {
  134. if let AmlValue::Method { flags, code } = self.namespace.get_by_path(path)?.clone() {
  135. /*
  136. * First, set up the state we expect to enter the method with, but clearing local
  137. * variables to "null" and setting the arguments.
  138. */
  139. self.current_scope = path.clone();
  140. self.current_args = Some(args);
  141. self.local_0 = None;
  142. self.local_1 = None;
  143. self.local_2 = None;
  144. self.local_3 = None;
  145. self.local_4 = None;
  146. self.local_5 = None;
  147. self.local_6 = None;
  148. self.local_7 = None;
  149. log::trace!("Invoking method with {} arguments, code: {:x?}", flags.arg_count(), code);
  150. let return_value =
  151. match term_list(PkgLength::from_raw_length(&code, code.len() as u32)).parse(&code, self) {
  152. // If the method doesn't return a value, we implicitly return `0`
  153. Ok(_) => Ok(AmlValue::Integer(0)),
  154. Err((_, _, AmlError::Return(result))) => Ok(result),
  155. Err((_, _, err)) => {
  156. error!("Failed to execute control method: {:?}", err);
  157. Err(err)
  158. }
  159. };
  160. /*
  161. * Now clear the state.
  162. */
  163. self.current_args = None;
  164. self.local_0 = None;
  165. self.local_1 = None;
  166. self.local_2 = None;
  167. self.local_3 = None;
  168. self.local_4 = None;
  169. self.local_5 = None;
  170. self.local_6 = None;
  171. self.local_7 = None;
  172. return_value
  173. } else {
  174. Err(AmlError::IncompatibleValueConversion)
  175. }
  176. }
  177. pub(crate) fn current_arg(&self, arg: ArgNum) -> Result<&AmlValue, AmlError> {
  178. self.current_args.as_ref().ok_or(AmlError::InvalidArgumentAccess(0xff))?.arg(arg)
  179. }
  180. /// Get the current value of a local by its local number.
  181. ///
  182. /// ### Panics
  183. /// Panics if an invalid local number is passed (valid local numbers are `0..=7`)
  184. pub(crate) fn local(&self, local: LocalNum) -> Result<&AmlValue, AmlError> {
  185. match local {
  186. 0 => self.local_0.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  187. 1 => self.local_1.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  188. 2 => self.local_2.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  189. 3 => self.local_3.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  190. 4 => self.local_4.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  191. 5 => self.local_5.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  192. 6 => self.local_6.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  193. 7 => self.local_7.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
  194. _ => panic!("Invalid local number: {}", local),
  195. }
  196. }
  197. }
  198. #[derive(Clone, Debug, PartialEq, Eq)]
  199. pub enum AmlError {
  200. /*
  201. * Errors produced parsing the AML stream.
  202. */
  203. UnexpectedEndOfStream,
  204. UnexpectedByte(u8),
  205. InvalidNameSeg,
  206. InvalidFieldFlags,
  207. IncompatibleValueConversion,
  208. UnterminatedStringConstant,
  209. InvalidStringConstant,
  210. InvalidRegionSpace(u8),
  211. /// Emitted by a parser when it's clear that the stream doesn't encode the object parsed by
  212. /// that parser (e.g. the wrong opcode starts the stream). This is handled specially by some
  213. /// parsers such as `or` and `choice!`.
  214. WrongParser,
  215. /*
  216. * Errors produced manipulating AML names.
  217. */
  218. EmptyNamesAreInvalid,
  219. /// Produced when trying to normalize a path that does not point to a valid level of the
  220. /// namespace. E.g. `\_SB.^^PCI0` goes above the root of the namespace. The contained value is the name that
  221. /// normalization was attempted upon.
  222. InvalidNormalizedName(AmlName),
  223. RootHasNoParent,
  224. /*
  225. * Errors produced working with the namespace.
  226. */
  227. /// Produced when a sub-level or value is added to a level that has not yet been added to the namespace. The
  228. /// `AmlName` is the name of the entire sub-level/value.
  229. LevelDoesNotExist(AmlName),
  230. HandleDoesNotExist(AmlHandle),
  231. /// Produced when two values with the same name are added to the namespace.
  232. NameCollision(AmlName),
  233. /*
  234. * Errors produced executing control methods.
  235. */
  236. /// Produced when a method accesses an argument it does not have (e.g. a method that takes 2
  237. /// arguments accesses `Arg4`). The inner value is the number of the argument accessed. If any
  238. /// arguments are accessed when a method is not being executed, this error is produced with an
  239. /// argument number of `0xff`.
  240. InvalidArgumentAccess(ArgNum),
  241. InvalidLocalAccess(LocalNum),
  242. /// This is not a real error, but is used to propagate return values from within the deep
  243. /// parsing call-stack. It should only be emitted when parsing a `DefReturn`. We use the
  244. /// error system here because the way errors are propagated matches how we want to handle
  245. /// return values.
  246. Return(AmlValue),
  247. /*
  248. * Errors produced parsing the PCI routing tables (_PRT objects).
  249. */
  250. PrtInvalidAddress,
  251. PrtInvalidPin,
  252. PrtInvalidSource,
  253. PrtInvalidGsi,
  254. /// Produced when the PRT doesn't contain an entry for the requested address + pin
  255. PrtNoEntry,
  256. /*
  257. * Errors produced parsing Resource Descriptors.
  258. */
  259. ReservedResourceType,
  260. ResourceDescriptorTooShort,
  261. }