Browse Source

Merge #9

9: [WIP] Begin parsing AML r=IsaacWoods a=IsaacWoods

This adds the representation and mapping code for the AML tables - the DSDT, SSDT and PSDT from Version 1.0.
- [x] ~~Properly extend the size of the mapping to cover the whole table~~
- [x] ~~Nice iterator thingy for parsing the stream~~
- [x] ~~Parse `PkgLength`s and manage them correctly~~
- [x] ~~Parse term lists~~
- [x] ~~Handle the namespace properly~~
- [x] ~~Correctly resolve scopes to absolute paths in the namespace~~
- [x] ~~Parse scope definitions~~
- [x] ~~Parse operation regions~~
- [x] ~~Parse named fields~~
- [x] ~~Add fields to the namespace~~
- [x] ~~Fail gracefully~~

NOTE: This pull request does not aim to fully parse AML, just to get some functionality going

Co-authored-by: Isaac Woods <isaacwoods.home@gmail.com>
bors[bot] 6 years ago
parent
commit
ac4b3caadf
9 changed files with 951 additions and 55 deletions
  1. 1 0
      Cargo.toml
  2. 60 0
      src/aml/mod.rs
  3. 19 0
      src/aml/opcodes.rs
  4. 530 0
      src/aml/parser.rs
  5. 67 0
      src/aml/stream.rs
  6. 94 0
      src/aml/value.rs
  7. 32 2
      src/fadt.rs
  8. 52 16
      src/lib.rs
  9. 96 37
      src/sdt.rs

+ 1 - 0
Cargo.toml

@@ -9,3 +9,4 @@ license = "MIT/Apache-2.0"
 
 [dependencies]
 log = "0.*"
+bit_field = "0.9.0"

+ 60 - 0
src/aml/mod.rs

@@ -0,0 +1,60 @@
+mod opcodes;
+mod parser;
+mod stream;
+mod value;
+
+pub use self::value::AmlValue;
+
+use self::parser::AmlParser;
+use self::stream::AmlStream;
+use alloc::String;
+use core::{mem, slice};
+use sdt::SdtHeader;
+use {Acpi, AcpiError, AcpiHandler, PhysicalMapping};
+
+/// Represents a table containing AML. For ACPI Version 2+, this is just the DSDT and SSDTs.
+/// Version 1.0 may also have a PSDT.
+#[repr(C, packed)]
+pub struct AmlTable {
+    header: SdtHeader,
+    // ...
+}
+
+#[derive(Debug)]
+pub enum AmlError {
+    EndOfStream,
+    UnexpectedByte(u8),
+    IncompatibleValueConversion,
+    InvalidPath(String),
+    InvalidFieldFlags,
+    InvalidNameSeg([u8; 4]),
+}
+
+impl AmlTable {
+    /// Get the AML stream encoded in this table so it can be safely accessed
+    pub fn stream<'a>(&'a self) -> AmlStream<'a> {
+        assert!(self.header.length() as usize > mem::size_of::<SdtHeader>());
+        let stream_length = self.header.length() as usize - mem::size_of::<SdtHeader>();
+        let stream_ptr =
+            ((self as *const AmlTable as usize) + mem::size_of::<SdtHeader>()) as *const u8;
+
+        unsafe { AmlStream::new(slice::from_raw_parts(stream_ptr, stream_length)) }
+    }
+}
+
+pub(crate) fn parse_aml_table<'a, 'h, H>(
+    acpi: &'a mut Acpi<'h, H>,
+    mapping: &PhysicalMapping<AmlTable>,
+    signature: &[u8; 4],
+) -> Result<(), AcpiError>
+where
+    'h: 'a,
+    H: AcpiHandler + 'a,
+{
+    (*mapping).header.validate(signature)?;
+
+    match AmlParser::parse(acpi, "\\", (*mapping).stream()) {
+        Ok(_) => Ok(()),
+        Err(error) => Err(AcpiError::InvalidAmlTable(*signature, error)),
+    }
+}

+ 19 - 0
src/aml/opcodes.rs

@@ -0,0 +1,19 @@
+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_FIELD_OP: u8 = 0x81;
+
+pub const EXT_OPCODE_PREFIX: u8 = 0x5b;

+ 530 - 0
src/aml/parser.rs

@@ -0,0 +1,530 @@
+use super::stream::AmlStream;
+use super::value::{AmlValue, FieldFlags, RegionSpace};
+use super::{opcodes, AmlError};
+use alloc::{string::ToString, String};
+use bit_field::BitField;
+use core::str;
+use {Acpi, AcpiHandler};
+
+/// This is used internally by the parser to keep track of what we know about a field before we can
+/// add it to the namespace.
+struct FieldInfo {
+    pub name: String,
+    pub length: u64,
+}
+
+/// This is used internally by the parser. Often, we're only interested in offset of the end of the
+/// current explicit-length structure, so we know when to stop parsing it. However, various constants
+/// (e.g. the size of fields) are also encoded as PkgLengths, and so sometimes we want to access
+/// the raw data as well.
+struct PkgLength {
+    pub raw_length: u32,
+    pub end_offset: u32,
+}
+
+pub(crate) struct AmlParser<'s, 'a, 'h, H>
+where
+    'h: 'a,
+    H: AcpiHandler + 'h,
+{
+    acpi: &'a mut Acpi<'h, H>,
+    scope: String,
+    stream: AmlStream<'s>,
+}
+
+/// This macro takes a parser and one or more parsing functions and tries to parse the next part of
+/// the stream with each one. If a parsing function fails, it rolls back the stream and tries the
+/// next one. If none of the functions can parse the next part of the stream, we error on the
+/// unexpected byte.
+macro_rules! try_parse {
+    ($parser: expr, $($function: path),+) => {{
+        $(if let Some(value) = $parser.try_parse($function)? {
+            Ok(value)
+        } else)+ {
+            Err(AmlError::UnexpectedByte($parser.stream.peek()?))
+        }
+    }}
+}
+
+impl<'s, 'a, 'h, H> AmlParser<'s, 'a, 'h, H>
+where
+    'h: 'a,
+    H: AcpiHandler + 'h,
+{
+    pub(crate) fn parse(
+        acpi: &'a mut Acpi<'h, H>,
+        scope: &str,
+        stream: AmlStream<'s>,
+    ) -> Result<(), AmlError> {
+        let mut parser = AmlParser {
+            acpi,
+            scope: String::from(scope),
+            stream,
+        };
+
+        let end_offset = parser.stream.len() as u32;
+        parser.parse_term_list(end_offset)
+    }
+
+    /// Consume the next byte in the stream and check if it fulfils the given predicate. If it
+    /// does, returns `Ok(the consumed char)`. If it doesn't, returns
+    /// `Err(AmlError::UnexpectedByte(the consumed char)`. If there's an error consuming the char,
+    /// it will forward the error on from that.
+    fn consume_byte<F>(&mut self, predicate: F) -> Result<u8, AmlError>
+    where
+        F: Fn(u8) -> bool,
+    {
+        let byte = self.stream.next()?;
+
+        match predicate(byte) {
+            true => Ok(byte),
+            false => Err(AmlError::UnexpectedByte(byte)),
+        }
+    }
+
+    fn consume_opcode(&mut self, opcode: u8) -> Result<(), AmlError> {
+        self.consume_byte(matches_byte(opcode))?;
+        Ok(())
+    }
+
+    fn consume_ext_opcode(&mut self, ext_opcode: u8) -> Result<(), AmlError> {
+        self.consume_byte(matches_byte(opcodes::EXT_OPCODE_PREFIX))?;
+        self.consume_byte(matches_byte(ext_opcode))?;
+        Ok(())
+    }
+
+    /// 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>
+         *
+         * Because TermLists don't have PkgLengths, we pass the offset to stop at from whatever
+         * explicit-length object we were parsing before.
+         */
+        while self.stream.offset() <= end_offset {
+            self.parse_term_object()?;
+        }
+
+        Ok(())
+    }
+
+    fn parse_term_object(&mut self) -> Result<(), AmlError> {
+        /*
+         * TermObj := NameSpaceModifierObj | NamedObj | Type1Opcode | Type2Opcode
+         * NameSpaceModifierObj := DefAlias | DefName | DefScope
+         * NamedObj := DefBankField | DefCreateBitField | DefCreateByteField | DefCreateDWordField |
+         *             DefCreateField | DefCreateQWordField | DefCreateWordField | DefDataRegion |
+         *             DefExternal | DefOpRegion | DefPowerRes | DefProcessor | DefThermalZone
+         */
+        try_parse!(
+            self,
+            AmlParser::parse_def_scope,
+            AmlParser::parse_def_op_region,
+            AmlParser::parse_def_field //,
+                                       // AmlParser::parse_type1_opcode    TODO: reenable when we can parse them
+        )
+    }
+
+    fn parse_def_scope(&mut self) -> Result<(), AmlError> {
+        /*
+         * DefScope := 0x10 PkgLength NameString TermList
+         */
+        self.consume_opcode(opcodes::SCOPE_OP)?;
+        trace!("Parsing scope op");
+        let scope_end_offset = self.parse_pkg_length()?.end_offset;
+
+        let name_string = self.parse_name_string()?;
+        let containing_scope = self.scope.clone();
+
+        self.scope = name_string;
+        let term_list = self.parse_term_list(scope_end_offset)?;
+        self.scope = containing_scope;
+
+        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
+         */
+        self.consume_ext_opcode(opcodes::EXT_OP_REGION_OP)?;
+        info!("Parsing op region");
+
+        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 offset = self.parse_term_arg()?.as_integer()?;
+        info!("region offset: {}", offset);
+        let length = self.parse_term_arg()?.as_integer()?;
+        info!("region len: {}", length);
+
+        // Insert it into the namespace
+        let namespace_path = self.resolve_path(&name)?;
+        self.acpi.namespace.insert(
+            // self.resolve_path(name)?, TODO: I thought this would work with nll?
+            namespace_path,
+            AmlValue::OpRegion {
+                region: region_space,
+                offset,
+                length,
+            },
+        );
+
+        Ok(())
+    }
+
+    fn parse_def_field(&mut self) -> Result<(), AmlError> {
+        /*
+         * DefField = ExtOpPrefix 0x81 PkgLength NameString FieldFlags FieldList
+         * FieldList := Nothing | <FieldElement FieldList>
+         * FieldElement := NamedField | ReservedField | AccessField | ExtendedAccessField |
+         *                 ConnectField
+         * ReservedField := 0x00 PkgLength
+         * AccessField := 0x01 AccessType AccessAttrib
+         * AccessType := ByteData
+         * AccessAttrib := ByteData
+         * ConnectField := <0x02 NameString> | <0x02 BufferData>
+         */
+        self.consume_ext_opcode(opcodes::EXT_FIELD_OP)?;
+        trace!("Parsing DefField");
+        let end_offset = self.parse_pkg_length()?.end_offset;
+        trace!("end offset: {}", end_offset);
+        let name = self.parse_name_string()?;
+        trace!("name: {}", name);
+        let flags = FieldFlags::new(self.stream.next()?);
+        trace!("Field flags: {:?}", flags);
+
+        while self.stream.offset() < end_offset {
+            // TODO: parse other field types
+            let info = try_parse!(self, AmlParser::parse_named_field)?;
+
+            // TODO: add field name to this (info.name)?
+            let namespace_path = self.resolve_path(&name)?;
+            self.acpi.namespace.insert(
+                namespace_path,
+                AmlValue::Field {
+                    flags,
+                    offset: 0, // TODO: calculate offset
+                    length: info.length,
+                },
+            );
+        }
+
+        Ok(())
+    }
+
+    fn parse_named_field(&mut self) -> Result<FieldInfo, AmlError> {
+        /*
+         * NamedField := NameSeg PkgLength
+         *
+         * This encodes the size of the field using a PkgLength - it doesn't mark the length of an
+         * explicit-length structure!
+         */
+        let name = String::from(name_seg_to_string(&self.parse_name_seg()?)?);
+        let length = self.parse_pkg_length()?.raw_length as u64;
+        info!("Adding named field called {:?} with size {}", name, length);
+
+        Ok(FieldInfo { name, length })
+    }
+
+    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
+         */
+        match self.stream.peek()? {
+            opcodes::BYTE_CONST => {
+                self.consume_opcode(opcodes::BYTE_CONST)?;
+                Ok(AmlValue::Integer(self.stream.next()? as u64))
+            }
+
+            opcodes::WORD_CONST => {
+                self.consume_opcode(opcodes::WORD_CONST)?;
+                Ok(AmlValue::Integer(self.stream.next_u16()? as u64))
+            }
+
+            opcodes::DWORD_CONST => {
+                self.consume_opcode(opcodes::DWORD_CONST)?;
+                Ok(AmlValue::Integer(self.stream.next_u16()? as u64))
+            }
+
+            opcodes::QWORD_CONST => {
+                self.consume_opcode(opcodes::QWORD_CONST)?;
+                Ok(AmlValue::Integer(self.stream.next_u16()? as u64))
+            }
+
+            opcodes::STRING_PREFIX => {
+                self.consume_opcode(opcodes::STRING_PREFIX)?;
+                unimplemented!(); // TODO
+            }
+
+            opcodes::ZERO_OP => {
+                self.consume_opcode(opcodes::ZERO_OP)?;
+                Ok(AmlValue::Integer(0))
+            }
+
+            opcodes::ONE_OP => {
+                self.consume_opcode(opcodes::ONE_OP)?;
+                Ok(AmlValue::Integer(1))
+            }
+
+            opcodes::ONES_OP => {
+                self.consume_opcode(opcodes::ONES_OP)?;
+                Ok(AmlValue::Integer(u64::max_value()))
+            }
+
+            opcodes::EXT_OPCODE_PREFIX if self.stream.lookahead(1)? == opcodes::EXT_REVISION_OP => {
+                unimplemented!(); // TODO
+            }
+
+            _ => self.parse_def_buffer(),
+        }
+    }
+
+    fn parse_type1_opcode(&mut self) -> Result<(), AmlError> {
+        /*
+         * Type1Opcode := DefBreak | DefBreakPoint | DefContinue | DefFatal | DefIfElse | DefLoad |
+         *                DefNoop | DefNotify | DefRelease | DefReset | DefReturn | DefSignal |
+         *                DefSleep | DefStall | DefUnload | DefWhile
+         */
+        unimplemented!(); // TODO
+    }
+
+    /// Parse a PkgLength. Returns the offset into the stream to stop parsing whatever object the
+    /// PkgLength describes at.
+    // TODO: think hard about whether we actually need to minus the length now because we use
+    // `next()`? Isn't this already handled?
+    fn parse_pkg_length(&mut self) -> Result<PkgLength, AmlError> {
+        /*
+         * PkgLength := PkgLeadByte |
+         *              <PkgLeadByte ByteData> |
+         *              <PkgLeadByte ByteData ByteData> |
+         *              <PkgLeadByte ByteData ByteData ByteData>
+         */
+        let lead_byte = self.stream.next()?;
+        let byte_data_count = lead_byte.get_bits(6..8);
+
+        if byte_data_count == 0 {
+            let length = u32::from(lead_byte.get_bits(0..6));
+            let end_offset = self.stream.offset() + length - 1; // Minus 1 to remove the PkgLength byte
+            trace!(
+                "Parsed 1-byte PkgLength with length {}, so ends at {}(current offset={}",
+                length,
+                end_offset,
+                self.stream.offset()
+            );
+            return Ok(PkgLength {
+                raw_length: length,
+                end_offset,
+            });
+        }
+
+        let mut length = u32::from(lead_byte.get_bits(0..4));
+        for i in 0..byte_data_count {
+            length += u32::from(self.stream.next()?) << (4 + i * 8);
+        }
+
+        // Minus `byte_data_count + 1` to not include the PkgLength in the remaining bytes
+        let end_offset = self.stream.offset() + length - byte_data_count as u32 - 1;
+        trace!(
+            "Parsed PkgLength with length {}, so ends at {}(current offset={})",
+            length,
+            end_offset,
+            self.stream.offset()
+        );
+        Ok(PkgLength {
+            raw_length: length,
+            end_offset,
+        })
+    }
+
+    fn parse_name_string(&mut self) -> Result<String, AmlError> {
+        /*
+         * NameString := <RootChar('\') NamePath> | <PrefixPath NamePath>
+         * PrefixPath := Nothing | <'^' PrefixPath>
+         */
+        match self.stream.peek()? {
+            b'\\' => {
+                /*
+                 * NameString := RootChar NamePath
+                 */
+                self.stream.next()?;
+                Ok(String::from("\\") + &self.parse_name_path()?)
+            }
+
+            b'^' => {
+                unimplemented!();
+            }
+
+            _ => self.parse_name_path(),
+        }
+    }
+
+    fn parse_name_path(&mut self) -> Result<String, AmlError> {
+        /*
+         * NamePath := NameSeg | DualNamePath | MultiNamePath | NullPath
+         * DualNamePath := DualNamePrefix NameSeg NameSeg
+         * MultiNamePath := MultiNamePrefix SegCount{ByteData} NameSeg(..SegCount)
+         */
+        match self.stream.peek()? {
+            opcodes::NULL_NAME => {
+                self.stream.next()?;
+                Ok(String::from(""))
+            }
+
+            opcodes::DUAL_NAME_PREFIX => {
+                self.stream.next()?;
+                let first = self.parse_name_seg()?;
+                let second = self.parse_name_seg()?;
+
+                Ok(
+                    String::from(str::from_utf8(&first).unwrap())
+                        + str::from_utf8(&second).unwrap(),
+                )
+            }
+
+            opcodes::MULTI_NAME_PREFIX => {
+                // TODO
+                unimplemented!();
+            }
+
+            _ => Ok(String::from(
+                str::from_utf8(&self.parse_name_seg()?).unwrap(),
+            )),
+        }
+    }
+
+    fn parse_name_seg(&mut self) -> Result<[u8; 4], AmlError> {
+        /*
+         * NameSeg := <LeadNameChar NameChar NameChar NameChar>
+         */
+        Ok([
+            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)?,
+        ])
+    }
+
+    /// Resolve a given path and the current scope to an absolute path in the namespace.
+    fn resolve_path(&mut self, mut path: &str) -> Result<String, AmlError> {
+        /*
+         * TODO: how should we handle '.' as they appear in paths?
+         */
+        let original_path = path.clone();
+
+        // If the scope to resolve is from the root of the namespace, or the current scope is
+        // nothing, just return the given scope
+        if self.scope == "" || path.starts_with("\\") {
+            return Ok(String::from(path));
+        }
+
+        // "^"s at the start of a path specify to go up one level from the current scope, to its
+        // parent object
+        let mut namespace_object = self.scope.clone();
+        while path.starts_with("^") {
+            path = &path[1..];
+
+            if namespace_object.pop() == None {
+                return Err(AmlError::InvalidPath(String::from(original_path)));
+            }
+        }
+
+        Ok(namespace_object + &path)
+    }
+}
+
+fn matches_byte(byte: u8) -> impl Fn(u8) -> bool {
+    move |x| x == byte
+}
+
+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)
+}
+
+fn name_seg_to_string<'a>(seg: &'a [u8; 4]) -> Result<&'a str, AmlError> {
+    match str::from_utf8(seg) {
+        Ok(seg_str) => Ok(seg_str),
+        Err(_) => Err(AmlError::InvalidNameSeg(*seg)),
+    }
+}

+ 67 - 0
src/aml/stream.rs

@@ -0,0 +1,67 @@
+use super::AmlError;
+
+#[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?
+}
+
+impl<'a> AmlStream<'a> {
+    pub unsafe fn new(data: &'a [u8]) -> AmlStream<'a> {
+        AmlStream { data, offset: 0 }
+    }
+
+    pub fn peek(&self) -> Result<u8, AmlError> {
+        if (self.offset + 1) >= self.len() {
+            Err(AmlError::EndOfStream)
+        } else {
+            Ok(self.data[self.offset as usize])
+        }
+    }
+
+    pub fn next(&mut self) -> Result<u8, AmlError> {
+        let byte = self.peek()?;
+        self.offset += 1;
+        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 lookahead(&self, amount: u32) -> Result<u8, AmlError> {
+        match self.offset.checked_add(amount) {
+            Some(offset) => Ok(self.data[offset as usize]),
+            None => Err(AmlError::EndOfStream),
+        }
+    }
+
+    pub fn len(&self) -> u32 {
+        self.data.len() as u32
+    }
+
+    /// This gets the current offset into the stream
+    pub fn offset(&self) -> u32 {
+        self.offset
+    }
+}

+ 94 - 0
src/aml/value.rs

@@ -0,0 +1,94 @@
+use super::AmlError;
+use bit_field::BitField;
+
+#[derive(Debug)]
+pub enum RegionSpace {
+    SystemMemory,
+    SystemIo,
+    PciConfig,
+    EmbeddedControl,
+    SMBus,
+    SystemCmos,
+    PciBarTarget,
+    IPMI,
+    GeneralPurposeIo,
+    GenericSerialBus,
+    OemDefined(u8),
+}
+
+pub enum FieldAccessType {
+    Any,
+    Byte,
+    Word,
+    DWord,
+    QWord,
+    Buffer,
+    Reserved,
+}
+
+pub enum FieldUpdateRule {
+    Preserve,
+    WriteAsOnes,
+    WriteAsZeros,
+}
+
+#[derive(Clone, Copy, Debug)] // TODO: custom debug / get rid of completely
+pub struct FieldFlags(u8);
+
+impl FieldFlags {
+    pub fn new(value: u8) -> FieldFlags {
+        FieldFlags(value)
+    }
+
+    pub fn access_type(&self) -> Result<FieldAccessType, AmlError> {
+        match self.0.get_bits(0..4) {
+            0 => Ok(FieldAccessType::Any),
+            1 => Ok(FieldAccessType::Byte),
+            2 => Ok(FieldAccessType::Word),
+            3 => Ok(FieldAccessType::DWord),
+            4 => Ok(FieldAccessType::QWord),
+            5 => Ok(FieldAccessType::Buffer),
+            _ => Err(AmlError::InvalidFieldFlags),
+        }
+    }
+
+    pub fn lock_rule(&self) -> bool {
+        self.0.get_bit(4)
+    }
+
+    pub fn field_update_rule(&self) -> Result<FieldUpdateRule, AmlError> {
+        match self.0.get_bits(5..7) {
+            0 => Ok(FieldUpdateRule::Preserve),
+            1 => Ok(FieldUpdateRule::WriteAsOnes),
+            2 => Ok(FieldUpdateRule::WriteAsZeros),
+            _ => Err(AmlError::InvalidFieldFlags),
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum AmlValue {
+    Integer(u64),
+
+    OpRegion {
+        region: RegionSpace,
+        offset: u64,
+        length: u64,
+    },
+
+    Field {
+        flags: FieldFlags,
+        offset: u64,
+        length: u64,
+    },
+}
+
+impl AmlValue {
+    pub fn as_integer(&self) -> Result<u64, AmlError> {
+        match self {
+            AmlValue::Integer(value) => Ok(*value),
+
+            _ => Err(AmlError::IncompatibleValueConversion),
+        }
+    }
+}

+ 32 - 2
src/fadt.rs

@@ -1,6 +1,15 @@
+use aml::{parse_aml_table, AmlTable};
+use sdt;
 use sdt::SdtHeader;
-use {AcpiError, GenericAddress, PhysicalMapping};
+use {Acpi, AcpiError, AcpiHandler, GenericAddress, PhysicalMapping};
 
+/// Represents the Fixed ACPI Description Table (FADT). This table contains various fixed hardware
+/// details, such as the addresses of the hardware register blocks. It also contains a pointer to
+/// the Differentiated Definition Block (DSDT).
+///
+/// In cases where the FADT contains both a 32-bit and 64-bit field for the same address, we should
+/// always prefer the 64-bit one. Only if it's zero or the CPU will not allow us to access that
+/// address should the 32-bit one be used.
 #[repr(C, packed)]
 pub struct Fadt {
     header: SdtHeader,
@@ -65,9 +74,30 @@ pub struct Fadt {
     hypervisor_vendor_id: u64,
 }
 
-pub fn parse_fadt(mapping: &PhysicalMapping<Fadt>) -> Result<(), AcpiError> {
+pub(crate) fn parse_fadt<'a, 'h, H>(
+    acpi: &'a mut Acpi<'h, H>,
+    mapping: &PhysicalMapping<Fadt>,
+) -> Result<(), AcpiError>
+where
+    'h: 'a,
+    H: AcpiHandler + 'a,
+{
     (*mapping).header.validate(b"FACP")?;
 
+    let dsdt_physical_address: usize = if (*mapping).x_dsdt_address != 0 {
+        (*mapping).x_dsdt_address as usize
+    } else {
+        (*mapping).dsdt_address as usize
+    };
+
+    // Parse the DSDT
+    let dsdt_header = sdt::peek_at_sdt_header(acpi.handler, dsdt_physical_address);
+    let dsdt_mapping = acpi
+        .handler
+        .map_physical_region::<AmlTable>(dsdt_physical_address, dsdt_header.length() as usize);
+    parse_aml_table(acpi, &dsdt_mapping, b"DSDT")?;
+    acpi.handler.unmap_physical_region(dsdt_mapping);
+
     Ok(())
 }
 

+ 52 - 16
src/lib.rs

@@ -1,4 +1,7 @@
 #![no_std]
+#![feature(nll)]
+#![feature(alloc)]
+#![feature(exclusive_range_pattern)]
 
 #[cfg(test)]
 #[macro_use]
@@ -6,12 +9,17 @@ extern crate std;
 
 #[macro_use]
 extern crate log;
+extern crate alloc;
+extern crate bit_field;
 
+mod aml;
 mod fadt;
 mod hpet;
 mod rsdp;
 mod sdt;
 
+use alloc::{BTreeMap, String};
+use aml::{AmlError, AmlValue};
 use core::mem;
 use core::ops::Deref;
 use core::ptr::NonNull;
@@ -19,17 +27,18 @@ use rsdp::Rsdp;
 use sdt::SdtHeader;
 
 #[derive(Debug)]
+// TODO: manually implement Debug to print signatures correctly etc.
 pub enum AcpiError {
     RsdpIncorrectSignature,
     RsdpInvalidOemId,
     RsdpInvalidChecksum,
 
-    SdtInvalidSignature,
-    SdtInvalidOemId,
-    SdtInvalidTableId,
-    SdtInvalidChecksum,
+    SdtInvalidSignature([u8; 4]),
+    SdtInvalidOemId([u8; 4]),
+    SdtInvalidTableId([u8; 4]),
+    SdtInvalidChecksum([u8; 4]),
 
-    FadtIncorrectSignature,
+    InvalidAmlTable([u8; 4], AmlError),
 }
 
 #[repr(C, packed)]
@@ -64,17 +73,32 @@ impl<T> Deref for PhysicalMapping<T> {
 /// `acpi` to tell the kernel about the tables it's parsing, such as how the kernel should
 /// configure the APIC or PCI routing.
 pub trait AcpiHandler {
-    /// Given a starting physical address, map a region of physical memory that contains a `T`
-    /// somewhere in the virtual address space. The address doesn't have to be page-aligned, so
-    /// the implementation may have to add padding to either end. The supplied size must be large
-    /// enough to hold a `T`, but may also be larger.
-    fn map_physical_region<T>(&mut self, physical_address: usize) -> PhysicalMapping<T>;
+    /// Given a starting physical address and a size, map a region of physical memory that contains
+    /// a `T` (but may be bigger than `size_of::<T>()`). The address doesn't have to be page-aligned,
+    /// so the implementation may have to add padding to either end. The given size must be greater
+    /// or equal to the size of a `T`. The virtual address the memory is mapped to does not matter,
+    /// as long as it is accessibly by `acpi`.
+    fn map_physical_region<T>(
+        &mut self,
+        physical_address: usize,
+        size: usize,
+    ) -> PhysicalMapping<T>;
 
     /// Unmap the given physical mapping. Safe because we consume the mapping, and so it can't be
     /// used after being passed to this function.
     fn unmap_physical_region<T>(&mut self, region: PhysicalMapping<T>);
 }
 
+/// This struct manages the internal state of `acpi`. It is not visible to the user of the library.
+pub(crate) struct Acpi<'a, H>
+where
+    H: AcpiHandler + 'a,
+{
+    handler: &'a mut H,
+    acpi_revision: u8,
+    namespace: BTreeMap<String, AmlValue>,
+}
+
 /// This is the entry point of `acpi` if you have the **physical** address of the RSDP. It maps
 /// the RSDP, works out what version of ACPI the hardware supports, and passes the physical
 /// address of the RSDT/XSDT to `parse_rsdt`.
@@ -82,7 +106,7 @@ pub fn parse_rsdp<H>(handler: &mut H, rsdp_address: usize) -> Result<(), AcpiErr
 where
     H: AcpiHandler,
 {
-    let rsdp_mapping = handler.map_physical_region::<Rsdp>(rsdp_address);
+    let rsdp_mapping = handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>());
     (*rsdp_mapping).validate()?;
     let revision = (*rsdp_mapping).revision();
 
@@ -120,8 +144,16 @@ pub fn parse_rsdt<H>(
 where
     H: AcpiHandler,
 {
-    let mapping = handler.map_physical_region::<SdtHeader>(physical_address);
-    // TODO: extend the mapping to header.length
+    let mut acpi = Acpi {
+        handler,
+        acpi_revision: revision,
+        namespace: BTreeMap::new(),
+    };
+
+    let header = sdt::peek_at_sdt_header(acpi.handler, physical_address);
+    let mapping = acpi
+        .handler
+        .map_physical_region::<SdtHeader>(physical_address, header.length() as usize);
 
     if revision == 0 {
         /*
@@ -135,7 +167,8 @@ where
             ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
 
         for i in 0..num_tables {
-            sdt::dispatch_sdt(handler, unsafe { *tables_base.offset(i as isize) } as usize)?;
+            sdt::dispatch_sdt(&mut acpi, unsafe { *tables_base.offset(i as isize) }
+                as usize)?;
         }
     } else {
         /*
@@ -149,11 +182,14 @@ where
             ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
 
         for i in 0..num_tables {
-            sdt::dispatch_sdt(handler, unsafe { *tables_base.offset(i as isize) } as usize)?;
+            sdt::dispatch_sdt(&mut acpi, unsafe { *tables_base.offset(i as isize) }
+                as usize)?;
         }
     }
 
-    handler.unmap_physical_region(mapping);
+    info!("Parsed namespace: {:#?}", acpi.namespace);
+
+    acpi.handler.unmap_physical_region(mapping);
     Ok(())
 }
 

+ 96 - 37
src/sdt.rs

@@ -1,10 +1,42 @@
-use core::str;
+use core::{mem, str};
 use fadt::Fadt;
 use hpet::Hpet;
-use {AcpiError, AcpiHandler};
+use {Acpi, AcpiError, AcpiHandler};
 
 /// All SDTs share the same header, and are `length` bytes long. The signature tells us which SDT
 /// this is.
+///
+/// The ACPI Spec (Version 6.2) defines the following SDT signatures:
+///     "APIC" - Multiple APIC Descriptor Table (MADT)
+///     "BGRT" - Boot Graphics Resource Table
+///     "BERT" - Boot Error Record Table
+///     "CPEP" - Corrected Platform Error Polling Table
+///     "DSDT" - Differentiated System Descriptor Table
+///     "ECDT" - Embedded Controller Boot Resources Table
+///     "EINJ" - Error Injection Table
+///     "ERST" - Error Record Serialization Table
+///     "FACP" - Fixed ACPI Description Table (FADT)
+///     "FACS" - Firmware ACPI Control Structure
+///     "FPDT" - Firmware Performance Data Table
+///     "GTDT" - Generic Timer Description Table
+///     "HEST" - Hardware Error Source Table
+///     "HMAT" - Heterogeneous Memory Attributes Table
+///     "MSCT" - Maximum System Characteristics Table
+///     "MPST" - Memory Power State Table
+///     "NFIT" - NVDIMM Firmware Interface Table
+///     "OEMx" - Various OEM-specific tables
+///     "PDTT" - Platform Debug Trigger Table
+///     "PMTT" - Platform Memory Topology Table
+///     "PPTT" - Processor Properties Topology Table
+///     "PSDT" - Persistent System Description Table
+///     "RASF" - ACPI RAS Feature Table
+///     "RSDT" - Root System Descriptor Table
+///     "SBST" - Smart Battery Specification Table
+///     "SLIT" - System Locality Information Table
+///     "SRAT" - System Resource Affinity Table
+///     "SSDT" - Secondary System Description Table
+///     "XSDT" - eXtended System Descriptor Table
+#[derive(Clone)]
 #[repr(C, packed)]
 pub struct SdtHeader {
     signature: [u8; 4],
@@ -27,17 +59,17 @@ impl SdtHeader {
     pub fn validate(&self, signature: &[u8; 4]) -> Result<(), AcpiError> {
         // Check the signature
         if &self.signature != signature {
-            return Err(AcpiError::SdtInvalidSignature);
+            return Err(AcpiError::SdtInvalidSignature(*signature));
         }
 
         // Check the OEM id
         if str::from_utf8(&self.oem_id).is_err() {
-            return Err(AcpiError::SdtInvalidOemId);
+            return Err(AcpiError::SdtInvalidOemId(*signature));
         }
 
         // Check the OEM table id
         if str::from_utf8(&self.oem_table_id).is_err() {
-            return Err(AcpiError::SdtInvalidTableId);
+            return Err(AcpiError::SdtInvalidTableId(*signature));
         }
 
         // Validate the checksum
@@ -48,12 +80,16 @@ impl SdtHeader {
         }
 
         if sum > 0 {
-            return Err(AcpiError::SdtInvalidChecksum);
+            return Err(AcpiError::SdtInvalidChecksum(*signature));
         }
 
         Ok(())
     }
 
+    pub fn raw_signature(&self) -> [u8; 4] {
+        self.signature
+    }
+
     pub fn signature<'a>(&'a self) -> &'a str {
         // Safe to unwrap because we check signature is valid UTF8 in `validate`
         str::from_utf8(&self.signature).unwrap()
@@ -103,43 +139,66 @@ impl SdtHeader {
     }
 }
 
+/// Takes the physical address of an SDT, and maps, clones and unmaps its header. Useful for
+/// finding out how big it is to map it correctly later.
+pub(crate) fn peek_at_sdt_header<H>(handler: &mut H, physical_address: usize) -> SdtHeader
+where
+    H: AcpiHandler,
+{
+    let mapping =
+        handler.map_physical_region::<SdtHeader>(physical_address, mem::size_of::<SdtHeader>());
+    let header = (*mapping).clone();
+    handler.unmap_physical_region(mapping);
+
+    header
+}
+
 /// This takes the physical address of an SDT, maps it correctly and dispatches it to whatever
 /// function parses that table.
-pub(crate) fn dispatch_sdt<H>(handler: &mut H, physical_address: usize) -> Result<(), AcpiError>
+pub(crate) fn dispatch_sdt<'a, 'h, H>(
+    acpi: &'a mut Acpi<'h, H>,
+    physical_address: usize,
+) -> Result<(), AcpiError>
 where
-    H: AcpiHandler,
+    'h: 'a,
+    H: AcpiHandler + 'a,
 {
-    let header_mapping = handler.map_physical_region::<SdtHeader>(physical_address);
-    {
-        let signature = (*header_mapping).signature();
-        let length = (*header_mapping).length();
-
-        /*
-         * For a recognised signature, a new physical mapping should be created with the correct type
-         * and length, and then the dispatched to the correct function to actually parse the table.
-         */
-        match signature {
-            "FACP" => {
-                let fadt_mapping = handler.map_physical_region::<Fadt>(physical_address);
-                ::fadt::parse_fadt(&fadt_mapping)?;
-                handler.unmap_physical_region(fadt_mapping);
-            }
-            "HPET" => {
-                let hpet_mapping = handler.map_physical_region::<Hpet>(physical_address);
-                ::hpet::parse_hpet(&hpet_mapping)?;
-                handler.unmap_physical_region(hpet_mapping);
-            }
-
-            _ => {
-                /*
-                 * We don't recognise this signature. Early on, this probably just means we don't
-                 * have support yet, but later on maybe this should become an actual error
-                 */
-                warn!("Unsupported SDT signature: {}. Skipping.", signature);
-            }
+    let header = peek_at_sdt_header(acpi.handler, physical_address);
+    info!(
+        "Dispatching SDT with signature {:?} and length {:?}",
+        header.signature(),
+        header.length()
+    );
+
+    /*
+     * For a recognised signature, a new physical mapping should be created with the correct type
+     * and length, and then the dispatched to the correct function to actually parse the table.
+     */
+    match header.signature() {
+        "FACP" => {
+            let fadt_mapping = acpi
+                .handler
+                .map_physical_region::<Fadt>(physical_address, mem::size_of::<Fadt>());
+            ::fadt::parse_fadt(acpi, &fadt_mapping)?;
+            acpi.handler.unmap_physical_region(fadt_mapping);
+        }
+
+        "HPET" => {
+            let hpet_mapping = acpi
+                .handler
+                .map_physical_region::<Hpet>(physical_address, mem::size_of::<Hpet>());
+            ::hpet::parse_hpet(&hpet_mapping)?;
+            acpi.handler.unmap_physical_region(hpet_mapping);
+        }
+
+        signature => {
+            /*
+             * We don't recognise this signature. Early on, this probably just means we don't
+             * have support yet, but later on maybe this should become an actual error
+             */
+            warn!("Unsupported SDT signature: {}. Skipping.", signature);
         }
     }
 
-    handler.unmap_physical_region(header_mapping);
     Ok(())
 }