Browse Source

Parse DefOpRegions

Isaac Woods 6 years ago
parent
commit
a1dff4bf8b
4 changed files with 232 additions and 21 deletions
  1. 11 0
      src/aml/opcodes.rs
  2. 201 20
      src/aml/parser.rs
  3. 19 1
      src/aml/value.rs
  4. 1 0
      src/lib.rs

+ 11 - 0
src/aml/opcodes.rs

@@ -2,6 +2,17 @@ pub const NULL_NAME: u8 = 0x00;
 pub const DUAL_NAME_PREFIX: u8 = 0x2E;
 pub const MULTI_NAME_PREFIX: u8 = 0x2F;
 
+pub const ZERO_OP: u8 = 0x00;
+pub const ONE_OP: u8 = 0x01;
+pub const ONES_OP: u8 = 0xff;
+pub const BYTE_CONST: u8 = 0x0a;
+pub const WORD_CONST: u8 = 0x0b;
+pub const DWORD_CONST: u8 = 0x0c;
+pub const STRING_PREFIX: u8 = 0x0d;
+pub const QWORD_CONST: u8 = 0x0e;
+
 pub const SCOPE_OP: u8 = 0x10;
+pub const EXT_OP_REGION_OP: u8 = 0x80;
+pub const EXT_REVISION_OP: u8 = 0x30;
 
 pub const EXT_OPCODE_PREFIX: u8 = 0x5b;

+ 201 - 20
src/aml/parser.rs

@@ -3,7 +3,9 @@ use alloc::String;
 use bit_field::BitField;
 use core::str;
 use {Acpi, AcpiHandler};
+use super::value::{RegionSpace, AmlValue};
 
+#[derive(Clone)]
 pub struct AmlStream<'a> {
     data: &'a [u8],
     offset: u32, // TODO: PkgLength can't be longer than u32, but can a whole AML stream?
@@ -28,6 +30,30 @@ impl<'a> AmlStream<'a> {
         Ok(byte)
     }
 
+    pub fn next_u16(&mut self) -> Result<u16, AmlError> {
+        let first_byte = self.next()?;
+        let second_byte = self.next()?;
+        Ok(first_byte as u16 + ((second_byte as u16) << 8))
+    }
+
+    pub fn next_u32(&mut self) -> Result<u32, AmlError> {
+        let first_byte = self.next()?;
+        let second_byte = self.next()?;
+        let third_byte = self.next()?;
+        Ok(first_byte as u32 + ((second_byte as u32) << 8)
+                             + ((third_byte as u32) << 16))
+    }
+
+    pub fn next_u64(&mut self) -> Result<u64, AmlError> {
+        let first_byte = self.next()?;
+        let second_byte = self.next()?;
+        let third_byte = self.next()?;
+        let forth_byte = self.next()?;
+        Ok(first_byte as u64 + ((second_byte as u64) << 8)
+                             + ((third_byte as u64) << 16)
+                             + ((forth_byte as u64) << 24))
+    }
+
     pub fn len(&self) -> u32 {
         self.data.len() as u32
     }
@@ -68,7 +94,7 @@ where
         parser.parse_term_list(end_offset)
     }
 
-    fn consume<F>(&mut self, predicate: F) -> Result<u8, AmlError>
+    fn consume_byte<F>(&mut self, predicate: F) -> Result<u8, AmlError>
     where
         F: Fn(u8) -> bool,
     {
@@ -80,23 +106,70 @@ where
         }
     }
 
+    fn match_byte<F>(&mut self, predicate: F) -> Result<bool, AmlError>
+    where
+        F: Fn(u8) -> bool,
+    {
+        Ok(predicate(self.stream.peek()?))
+    }
+
     fn consume_opcode(&mut self, opcode: u8) -> Result<(), AmlError> {
-        self.consume(matches_char(opcode))?;
+        self.consume_byte(matches_byte(opcode))?;
         Ok(())
     }
 
     fn consume_ext_opcode(&mut self, ext_opcode: u8) -> Result<(), AmlError> {
-        self.consume(matches_char(opcodes::EXT_OPCODE_PREFIX))?;
-        self.consume(matches_char(ext_opcode))?;
+        self.consume_byte(matches_byte(opcodes::EXT_OPCODE_PREFIX))?;
+        self.consume_byte(matches_byte(ext_opcode))?;
         Ok(())
     }
 
+    /// See if the next byte in the stream is the specified opcode, but without advancing the
+    /// stream or producing an error if the byte doesn't match.
+    fn match_opcode(&mut self, opcode: u8) -> Result<bool, AmlError> {
+        self.match_byte(matches_byte(opcode))
+    }
+
+    fn match_ext_opcode(&mut self, ext_opcode: u8) -> Result<bool, AmlError> {
+        if !self.match_byte(matches_byte(opcodes::EXT_OPCODE_PREFIX))? {
+            return Ok(false);
+        }
+
+        if self.match_byte(matches_byte(ext_opcode))? {
+            return Ok(false);
+        }
+
+        Ok(true)
+    }
+
+    /// Try to parse the next part of the stream with the given parsing function. This returns any
+    /// `AmlError` as an `Err`, except `AmlError::UnexpectedByte`, to which it will return
+    /// `Ok(None)`. A successful parse gives `Ok(Some(...))`. On failure, this also reverts any
+    /// changes made to the stream, so it's as if the parsing function never run.
+    fn try_parse<T, F>(&mut self, parsing_function: F) -> Result<Option<T>, AmlError>
+    where
+        F: Fn(&mut Self) -> Result<T, AmlError>,
+    {
+        let stream = self.stream.clone();
+
+        match parsing_function(self) {
+            Ok(result) => Ok(Some(result)),
+
+            Err(AmlError::UnexpectedByte(_)) => {
+                self.stream = stream;
+                Ok(None)
+            }
+
+            Err(error) => Err(error),
+        }
+    }
+
     fn parse_term_list(&mut self, end_offset: u32) -> Result<(), AmlError> {
         /*
          * TermList := Nothing | <TermObj TermList>
          *
-         * TermLists are always within an explicit-length structure, so we can just parse to the
-         * end of the PkgLength. TODO: check this is true
+         * Because TermLists don't have PkgLengths, we pass the offset to stop at from whatever
+         * explicit-length object we were parsing before.
          */
 
         /*
@@ -114,32 +187,140 @@ where
         /*
          * TermObj := NameSpaceModifierObj | NamedObj | Type1Opcode | Type2Opcode
          * NameSpaceModifierObj := DefAlias | DefName | DefScope
+         * NamedObj := DefBankField | DefCreateBitField | DefCreateByteField | DefCreateDWordField |
+         *             DefCreateField | DefCreateQWordField | DefCreateWordField | DefDataRegion |
+         *             DefExternal | DefOpRegion | DefPowerRes | DefProcessor | DefThermalZone
          */
-        match self.stream.peek()? {
-            opcodes::SCOPE_OP => {
-                self.parse_scope_op()?;
-            }
+        if self.match_opcode(opcodes::SCOPE_OP)? {
+            return self.parse_def_scope();
+        }
 
-            byte => return Err(AmlError::UnexpectedByte(byte)),
+        if self.match_ext_opcode(opcodes::EXT_OP_REGION_OP)? {
+            return self.parse_def_op_region();
         }
 
-        Ok(())
+        Err(AmlError::UnexpectedByte(self.stream.peek()?))
     }
 
-    fn parse_scope_op(&mut self) -> Result<(), AmlError> {
-        trace!("Parsing scope op");
+    fn parse_def_scope(&mut self) -> Result<(), AmlError> {
         /*
          * DefScope := 0x10 PkgLength NameString TermList
          */
+        trace!("Parsing scope op");
         self.consume_opcode(opcodes::SCOPE_OP)?;
         let scope_end_offset = self.parse_pkg_length()?;
-        // let pkg_length_handle = self.stream.start_explicit_length_parse(pkg_length);
 
         let name_string = self.parse_name_string()?;
         let term_list = self.parse_term_list(scope_end_offset)?;
         Ok(())
     }
 
+    fn parse_def_op_region(&mut self) -> Result<(), AmlError> {
+        /*
+         * DefOpRegion := ExtOpPrefix 0x80 NameString RegionSpace RegionOffset RegionLen
+         * RegionSpace := ByteData (where 0x00      = SystemMemory
+         *                                0x01      = SystemIO
+         *                                0x02      = PciConfig
+         *                                0x03      = EmbeddedControl
+         *                                0x04      = SMBus
+         *                                0x05      = SystemCMOS
+         *                                0x06      = PciBarTarget
+         *                                0x07      = IPMI
+         *                                0x08      = GeneralPurposeIO
+         *                                0x09      = GenericSerialBus
+         *                                0x80-0xff = OEM Defined)
+         * ByteData := 0x00 - 0xff
+         * RegionOffset := TermArg => Integer
+         * RegionLen := TermArg => Integer
+         */
+        trace!("Parsing def op region");
+        self.consume_ext_opcode(opcodes::EXT_OPCODE_PREFIX);
+
+        let name = self.parse_name_string()?;
+        info!("name: {}", name);
+        let region_space = match self.stream.next()? {
+            0x00 => RegionSpace::SystemMemory,
+            0x01 => RegionSpace::SystemIo,
+            0x02 => RegionSpace::PciConfig,
+            0x03 => RegionSpace::EmbeddedControl,
+            0x04 => RegionSpace::SMBus,
+            0x05 => RegionSpace::SystemCmos,
+            0x06 => RegionSpace::PciBarTarget,
+            0x07 => RegionSpace::IPMI,
+            0x08 => RegionSpace::GeneralPurposeIo,
+            0x09 => RegionSpace::GenericSerialBus,
+            space @ 0x80..0xff => RegionSpace::OemDefined(space),
+            byte => return Err(AmlError::UnexpectedByte(byte)),
+        };
+        info!("region space: {:?}", region_space);
+        let region_offset = self.parse_term_arg()?;
+        info!("region offset: {:?}", region_offset);
+        let region_len = self.parse_term_arg()?;
+        info!("region len: {:?}", region_len);
+
+        // TODO: register in the namespace
+        Ok(())
+    }
+
+    fn parse_def_buffer(&mut self) -> Result<AmlValue, AmlError> {
+        unimplemented!();   // TODO
+    }
+
+    fn parse_term_arg(&mut self) -> Result<AmlValue, AmlError> {
+        /*
+         * TermArg := Type2Opcode | DataObject | ArgObj | LocalObj
+         * DataObject := ComputationalData | DefPackage | DefVarPackage
+         */
+        if let Some(result) = self.try_parse(AmlParser::parse_computational_data)? {
+            Ok(result)
+        } else {
+            Err(AmlError::UnexpectedByte(self.stream.next()?))
+        }
+    }
+
+    fn parse_computational_data(&mut self) -> Result<AmlValue, AmlError> {
+        /*
+         * ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String |
+         *                      ConstObj | RevisionOp | DefBuffer
+         * ByteConst := 0x0a ByteData
+         * WordConst := 0x0b WordData
+         * DWordConst := 0x0c DWordData
+         * QWordConst := 0x0e QWordData
+         * String := 0x0d AsciiCharList NullChar
+         * ConstObj := ZeroOp(0x00) | OneOp(0x01) | OnesOp(0xff)
+         * RevisionOp := ExtOpPrefix(0x5B) 0x30
+         */
+        // TODO: can this be rewritten as a cleaner match?
+        if self.match_opcode(opcodes::BYTE_CONST)? {
+            self.consume_opcode(opcodes::BYTE_CONST)?;
+            Ok(AmlValue::Integer(self.stream.next()? as u64))
+        } else if self.match_opcode(opcodes::WORD_CONST)? {
+            self.consume_opcode(opcodes::WORD_CONST)?;
+            Ok(AmlValue::Integer(self.stream.next_u16()? as u64))
+        } else if self.match_opcode(opcodes::DWORD_CONST)? {
+            self.consume_opcode(opcodes::DWORD_CONST)?;
+            Ok(AmlValue::Integer(self.stream.next_u32()? as u64))
+        } else if self.match_opcode(opcodes::QWORD_CONST)? {
+            self.consume_opcode(opcodes::QWORD_CONST)?;
+            Ok(AmlValue::Integer(self.stream.next_u64()? as u64))
+        } else if self.match_opcode(opcodes::STRING_PREFIX)? {
+            unimplemented!();   // TODO
+        } else if self.match_opcode(opcodes::ZERO_OP)? {
+            self.stream.next()?;
+            Ok(AmlValue::Integer(0))
+        } else if self.match_opcode(opcodes::ONE_OP)? {
+            self.stream.next()?;
+            Ok(AmlValue::Integer(1))
+        } else if self.match_opcode(opcodes::ONES_OP)? {
+            self.stream.next()?;
+            Ok(AmlValue::Integer(u64::max_value()))
+        } else if self.match_ext_opcode(opcodes::EXT_REVISION_OP)? {
+            unimplemented!();   // TODO
+        } else {
+            self.parse_def_buffer()
+        }
+    }
+
     /// Parse a PkgLength. Returns the offset into the stream to stop parsing whatever object the
     /// PkgLength describes at.
     fn parse_pkg_length(&mut self) -> Result<u32, AmlError> {
@@ -233,15 +414,15 @@ where
          * NameSeg := <LeadNameChar NameChar NameChar NameChar>
          */
         Ok([
-            self.consume(is_lead_name_char)?,
-            self.consume(is_name_char)?,
-            self.consume(is_name_char)?,
-            self.consume(is_name_char)?,
+            self.consume_byte(is_lead_name_char)?,
+            self.consume_byte(is_name_char)?,
+            self.consume_byte(is_name_char)?,
+            self.consume_byte(is_name_char)?,
         ])
     }
 }
 
-fn matches_char(byte: u8) -> impl Fn(u8) -> bool {
+fn matches_byte(byte: u8) -> impl Fn(u8) -> bool {
     move |x| x == byte
 }
 

+ 19 - 1
src/aml/value.rs

@@ -1 +1,19 @@
-pub enum AmlValue {}
+#[derive(Debug)]
+pub enum RegionSpace {
+    SystemMemory,
+    SystemIo,
+    PciConfig,
+    EmbeddedControl,
+    SMBus,
+    SystemCmos,
+    PciBarTarget,
+    IPMI,
+    GeneralPurposeIo,
+    GenericSerialBus,
+    OemDefined(u8),
+}
+
+#[derive(Debug)]
+pub enum AmlValue {
+    Integer(u64),
+}

+ 1 - 0
src/lib.rs

@@ -1,6 +1,7 @@
 #![no_std]
 #![feature(nll)]
 #![feature(alloc)]
+#![feature(exclusive_range_pattern)]
 
 #[cfg(test)]
 #[macro_use]