Browse Source

Parse name objects

Isaac Woods 5 years ago
parent
commit
b2f83748d9
5 changed files with 187 additions and 3 deletions
  1. 14 3
      aml_parser/src/lib.rs
  2. 149 0
      aml_parser/src/name_object.rs
  3. 1 0
      aml_parser/src/opcode.rs
  4. 22 0
      aml_parser/src/parser.rs
  5. 1 0
      aml_parser/src/value.rs

+ 14 - 3
aml_parser/src/lib.rs

@@ -1,5 +1,5 @@
 #![no_std]
-#![feature(alloc, decl_macro)]
+#![feature(decl_macro)]
 
 extern crate alloc;
 
@@ -9,16 +9,21 @@ extern crate std;
 #[cfg(test)]
 mod test_utils;
 
+pub(crate) mod name_object;
 pub(crate) mod opcode;
 pub(crate) mod parser;
 pub(crate) mod pkg_length;
+pub mod value;
 
+pub use crate::value::AmlValue;
 use alloc::{collections::BTreeMap, string::String};
+use log::{error, trace};
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum AmlError {
     UnexpectedEndOfStream,
     UnexpectedByte(u8),
+    InvalidNameSeg([u8; 4]),
 }
 
 pub struct AmlNamespace {
@@ -35,7 +40,13 @@ impl AmlNamespace {
             return Err(AmlError::UnexpectedEndOfStream);
         }
 
-        log::info!("stream length: {}, {:#x}", stream.len(), stream[0]);
-        return Err(AmlError::UnexpectedEndOfStream);
+        match term_object::parse_term_list(stream, self) {
+            Ok(_) => Ok(()),
+            Err((remaining, err)) => {
+                error!("Failed to parse AML stream. Err = {:?}", err);
+                trace!("Remaining AML: {:02x?}", remaining);
+                Err(err)
+            }
+        }
     }
 }

+ 149 - 0
aml_parser/src/name_object.rs

@@ -0,0 +1,149 @@
+use crate::{
+    opcode::{opcode, DUAL_NAME_PREFIX, MULTI_NAME_PREFIX, NULL_NAME, ROOT_CHAR},
+    parser::{choice, consume, n_of, take, Parser},
+    AmlError,
+};
+use alloc::string::String;
+use core::str;
+
+pub fn name_string<'a>() -> impl Parser<'a, String> {
+    /*
+     * NameString := <RootChar('\') NamePath> | <PrefixPath NamePath>
+     * PrefixPath := Nothing | <'^' PrefixPath>
+     */
+    // TODO: this does not yet parse <PrefixPath NamePath>
+    let root_name_string =
+        opcode(ROOT_CHAR).then(name_path()).map(|((), name_path)| String::from("\\") + &name_path);
+
+    choice!(root_name_string)
+}
+
+pub fn name_path<'a>() -> impl Parser<'a, String> {
+    /*
+     * NamePath := NullName | DualNamePath | MultiNamePath | NameSeg
+     */
+    choice!(
+        null_name(),
+        dual_name_path(),
+        multi_name_path(),
+        name_seg().map(|seg| String::from(seg.as_str()))
+    )
+}
+
+pub fn null_name<'a>() -> impl Parser<'a, String> {
+    /*
+     * NullName := 0x00
+     */
+    opcode(NULL_NAME).map(|_| String::from(""))
+}
+
+pub fn dual_name_path<'a>() -> impl Parser<'a, String> {
+    /*
+     * DualNamePath := 0x2e NameSeg NameSeg
+     */
+    opcode(DUAL_NAME_PREFIX)
+        .then(name_seg())
+        .then(name_seg())
+        .map(|(((), first), second)| String::from(first.as_str()) + second.as_str())
+}
+
+pub fn multi_name_path<'a>() -> impl Parser<'a, String> {
+    /*
+     * MultiNamePath := 0x2f ByteData{SegCount} NameSeg(SegCount)
+     */
+    move |input| {
+        let (new_input, ((), seg_count)) = opcode(MULTI_NAME_PREFIX).then(take()).parse(input)?;
+        match n_of(name_seg(), usize::from(seg_count)).parse(new_input) {
+            Ok((new_input, name_segs)) => Ok((
+                new_input,
+                name_segs.iter().fold(String::new(), |name, name_seg| name + name_seg.as_str()),
+            )),
+            // Correct returned input to the one we haven't touched
+            Err((_, err)) => Err((input, err)),
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct NameSeg([u8; 4]);
+
+impl NameSeg {
+    /// Turn a `NameSeg` into a `&str`. Returns it in a `ParseResult` so it's easy to use from
+    /// inside parsers.
+    pub fn as_str(&self) -> &str {
+        /*
+         * This is safe, because we always check that all the bytes are valid ASCII, so every
+         * `NameSeg` will be valid UTF8.
+         */
+        unsafe { str::from_utf8_unchecked(&self.0) }
+    }
+}
+
+pub fn name_seg<'a>() -> impl Parser<'a, NameSeg> {
+    /*
+     * NameSeg := <LeadNameChar NameChar NameChar NameChar>
+     */
+    // TODO: can we write this better?
+    move |input| {
+        let (input, char_1) = consume(is_lead_name_char).parse(input)?;
+        let (input, char_2) = consume(is_name_char).parse(input)?;
+        let (input, char_3) = consume(is_name_char).parse(input)?;
+        let (input, char_4) = consume(is_name_char).parse(input)?;
+        Ok((input, NameSeg([char_1, char_2, char_3, char_4])))
+    }
+}
+
+fn is_lead_name_char(byte: u8) -> bool {
+    (byte >= b'A' && byte <= b'Z') || byte == b'_'
+}
+
+fn is_digit_char(byte: u8) -> bool {
+    byte >= b'0' && byte <= b'9'
+}
+
+fn is_name_char(byte: u8) -> bool {
+    is_lead_name_char(byte) || is_digit_char(byte)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{parser::Parser, test_utils::*, AmlError};
+
+    #[test]
+    fn test_name_seg() {
+        check_ok!(
+            name_seg().parse(&[b'A', b'F', b'3', b'Z']),
+            NameSeg([b'A', b'F', b'3', b'Z']),
+            &[]
+        );
+        check_ok!(
+            name_seg().parse(&[b'A', b'F', b'3', b'Z', 0xff]),
+            NameSeg([b'A', b'F', b'3', b'Z']),
+            &[0xff]
+        );
+        check_err!(
+            name_seg().parse(&[0xff, b'E', b'A', b'7']),
+            AmlError::UnexpectedByte(0xff),
+            &[0xff, b'E', b'A', b'7']
+        );
+        check_err!(name_seg().parse(&[]), AmlError::UnexpectedEndOfStream, &[]);
+    }
+
+    #[test]
+    fn test_name_path() {
+        check_err!(name_path().parse(&[]), AmlError::UnexpectedEndOfStream, &[]);
+        check_ok!(name_path().parse(&[0x00]), String::from(""), &[]);
+        check_ok!(name_path().parse(&[0x00, 0x00]), String::from(""), &[0x00]);
+        check_err!(
+            name_path().parse(&[0x2e, b'A']),
+            AmlError::UnexpectedEndOfStream,
+            &[0x2e, 0x00]
+        );
+        check_ok!(
+            name_path().parse(&[0x2e, b'A', b'B', b'C', b'D', b'E', b'_', b'F', b'G']),
+            String::from("ABCDE_FG"),
+            &[]
+        );
+    }
+}

+ 1 - 0
aml_parser/src/opcode.rs

@@ -3,6 +3,7 @@ use crate::{parser::*, AmlError};
 pub const NULL_NAME: u8 = 0x00;
 pub const DUAL_NAME_PREFIX: u8 = 0x2E;
 pub const MULTI_NAME_PREFIX: u8 = 0x2F;
+pub const ROOT_CHAR: u8 = b'\\';
 
 pub const ZERO_OP: u8 = 0x00;
 pub const ONE_OP: u8 = 0x01;

+ 22 - 0
aml_parser/src/parser.rs

@@ -59,6 +59,28 @@ pub fn take_n<'a>(n: usize) -> impl Parser<'a, &'a [u8]> {
     }
 }
 
+// TODO: can we use const generics (e.g. [R; N]) to avoid allocating?
+pub fn n_of<'a, P, R>(parser: P, n: usize) -> impl Parser<'a, Vec<R>>
+where
+    P: Parser<'a, R>,
+{
+    move |input| {
+        let mut results = Vec::with_capacity(n);
+        let mut new_input = input;
+
+        for i in 0..n {
+            let (after_input, result) = match parser.parse(new_input) {
+                Ok((input, result)) => (input, result),
+                Err((_, err)) => return Err((input, err)),
+            };
+            results.push(result);
+            new_input = after_input;
+        }
+
+        Ok((new_input, results))
+    }
+}
+
 pub fn consume<'a, F>(condition: F) -> impl Parser<'a, u8>
 where
     F: Fn(u8) -> bool,

+ 1 - 0
aml_parser/src/value.rs

@@ -0,0 +1 @@
+pub enum AmlValue {}