浏览代码

Parse DefBuffer

Isaac Woods 5 年之前
父节点
当前提交
14c11842a1
共有 4 个文件被更改,包括 68 次插入2 次删除
  1. 1 0
      aml_parser/src/lib.rs
  2. 14 0
      aml_parser/src/parser.rs
  3. 27 2
      aml_parser/src/term_object.rs
  4. 26 0
      aml_parser/src/value.rs

+ 1 - 0
aml_parser/src/lib.rs

@@ -33,6 +33,7 @@ pub enum AmlError {
     UnexpectedByte(u8),
     InvalidNameSeg([u8; 4]),
     InvalidFieldFlags,
+    IncompatibleValueConversion,
     UnterminatedStringConstant,
     InvalidStringConstant,
 }

+ 14 - 0
aml_parser/src/parser.rs

@@ -410,6 +410,20 @@ pub macro choice {
     }
 }
 
+/// This encapsulates an unfortunate hack we sometimes need to use, where the type checker gets
+/// caught in an infinite loop of parser types. This occurs when an object can indirectly contain
+/// itself, and so the parser type will contain its own type. This works by breaking the cycle of
+/// `impl Parser` chains that build up, by effectively creating a "concrete" closure type.
+///
+/// You can try using this hack if you are writing a parser and end up with an error of the form:
+/// `error[E0275]: overflow evaluating the requirement 'impl Parser<{a type}>'
+///     help: consider adding a a '#![recursion_limit="128"] attribute to your crate`
+/// Note: Increasing the recursion limit will not fix the issue, as the cycle will just continue
+/// until you either hit the new recursion limit or `rustc` overflows its stack.
+pub macro make_parser_concrete($parser: expr) {
+    |input, context| ($parser).parse(input, context)
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;

+ 27 - 2
aml_parser/src/term_object.rs

@@ -5,6 +5,7 @@ use crate::{
         choice,
         comment_scope,
         comment_scope_verbose,
+        make_parser_concrete,
         take,
         take_to_end_of_pkglength,
         take_u16,
@@ -18,7 +19,8 @@ use crate::{
     AmlContext,
     AmlError,
 };
-use alloc::string::String;
+use alloc::{string::String, vec::Vec};
+use core::str;
 use log::trace;
 
 /// `TermList`s are usually found within explicit-length objects (so they have a `PkgLength`
@@ -313,6 +315,28 @@ where
         .discard_result()
 }
 
+pub fn def_buffer<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
+where
+    'c: 'a,
+{
+    /*
+     * DefBuffer := 0x11 PkgLength BufferSize ByteList
+     * BufferSize := TermArg => Integer
+     *
+     * XXX: The spec says that zero-length buffers (e.g. the PkgLength is 0) are illegal, but
+     * we've encountered them in QEMU-generated tables, so we return an empty buffer in these
+     * cases.
+     */
+    opcode(opcode::DEF_BUFFER_OP)
+        .then(comment_scope(
+            "DefBuffer",
+            pkg_length().then(term_arg()).feed(|(pkg_length, buffer_size)| {
+                take_to_end_of_pkglength(pkg_length)
+                    .map(move |bytes| Ok((bytes.to_vec(), buffer_size.as_integer()?)))
+            }),
+        ))
+        .map(|((), (bytes, buffer_size))| Ok(AmlValue::Buffer { bytes, size: buffer_size }))
+}
 pub fn term_arg<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
 where
     'c: 'a,
@@ -410,7 +434,8 @@ where
         choice!(
             ext_opcode(opcode::EXT_REVISION_OP)
                 .map(|_| Ok(AmlValue::Integer(crate::AML_INTERPRETER_REVISION))),
-            const_parser //TODO: parse DefBuffer here too
+            const_parser,
+            make_parser_concrete!(def_buffer())
         ),
     )
 }

+ 26 - 0
aml_parser/src/value.rs

@@ -96,4 +96,30 @@ pub enum AmlValue {
     Integer(u64),
     String(String),
     Field { flags: FieldFlags, offset: u64, length: u64 },
+impl AmlValue {
+    pub fn as_integer(&self) -> Result<u64, AmlError> {
+        match self {
+            AmlValue::Integer(value) => Ok(*value),
+
+            AmlValue::Buffer { size, ref bytes } => {
+                /*
+                 * "The first 8 bytes of the buffer are converted to an integer, taking the first
+                 * byte as the least significant byte of the integer. A zero-length buffer is
+                 * illegal." - §19.6.140
+                 *
+                 * XXX: We return `0` for zero-length buffers because they literally occur in
+                 * the reference implementation.
+                 */
+                let bytes = if bytes.len() > 8 { &bytes[0..8] } else { bytes };
+
+                Ok(bytes.iter().rev().fold(0: u64, |mut i, &popped| {
+                    i <<= 8;
+                    i += popped as u64;
+                    i
+                }))
+            }
+
+            _ => Err(AmlError::IncompatibleValueConversion),
+        }
+    }
 }