Browse Source

Add start of AML parser

Isaac Woods 7 years ago
parent
commit
255ece369b
9 changed files with 298 additions and 18 deletions
  1. 1 0
      Cargo.toml
  2. 1 0
      example_os/serial.txt
  3. 19 12
      src/aml/mod.rs
  4. 7 0
      src/aml/opcodes.rs
  5. 258 0
      src/aml/parser.rs
  6. 1 0
      src/aml/value.rs
  7. 3 2
      src/fadt.rs
  8. 5 2
      src/lib.rs
  9. 3 2
      src/sdt.rs

+ 1 - 0
Cargo.toml

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

+ 1 - 0
example_os/serial.txt

@@ -0,0 +1 @@
+Passed

+ 19 - 12
src/aml/mod.rs

@@ -1,3 +1,10 @@
+mod opcodes;
+mod parser;
+mod value;
+
+pub use self::value::AmlValue;
+
+use self::parser::{AmlParser, AmlStream};
 use core::{mem, slice};
 use sdt::SdtHeader;
 use {Acpi, AcpiError, AcpiHandler, PhysicalMapping};
@@ -10,9 +17,10 @@ pub struct AmlTable {
     // ...
 }
 
-pub struct AmlStream<'a> {
-    data: &'a [u8],
-    count: usize,
+#[derive(Debug)]
+pub enum AmlError {
+    EndOfStream,
+    UnexpectedByte(u8),
 }
 
 impl AmlTable {
@@ -23,24 +31,23 @@ impl AmlTable {
         let stream_ptr =
             ((self as *const AmlTable as usize) + mem::size_of::<SdtHeader>()) as *const u8;
 
-        AmlStream {
-            data: unsafe { slice::from_raw_parts(stream_ptr, stream_length) },
-            count: 0,
-        }
+        unsafe { AmlStream::new(slice::from_raw_parts(stream_ptr, stream_length)) }
     }
 }
 
-pub(crate) fn parse_aml_table<'a, H>(
-    acpi: &mut Acpi<'a, H>,
+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)?;
 
-    let stream = (*mapping).stream();
-    // TODO: pass off to the AML parser
-    unimplemented!();
+    match AmlParser::parse(acpi, "\\", (*mapping).stream()) {
+        Ok(_) => Ok(()),
+        Err(error) => Err(AcpiError::InvalidAmlTable(*signature, error)),
+    }
 }

+ 7 - 0
src/aml/opcodes.rs

@@ -0,0 +1,7 @@
+pub const NULL_NAME: u8 = 0x00;
+pub const DUAL_NAME_PREFIX: u8 = 0x2E;
+pub const MULTI_NAME_PREFIX: u8 = 0x2F;
+
+pub const SCOPE_OP: u8 = 0x10;
+
+pub const EXT_OPCODE_PREFIX: u8 = 0x5b;

+ 258 - 0
src/aml/parser.rs

@@ -0,0 +1,258 @@
+use super::{opcodes, AmlError};
+use alloc::String;
+use bit_field::BitField;
+use core::str;
+use {Acpi, AcpiHandler};
+
+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(&mut 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 len(&self) -> u32 {
+        self.data.len() as u32
+    }
+
+    /// This gets the current offset into the stream
+    pub fn offset(&self) -> u32 {
+        self.offset
+    }
+}
+
+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>,
+}
+
+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)
+    }
+
+    fn consume<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(matches_char(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))?;
+        Ok(())
+    }
+
+    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
+         */
+
+        /*
+         * We parse until we reach the offset marked as the end of the structure - `end_offset`
+         */
+        while self.stream.offset() <= end_offset {
+            self.parse_term_object()?;
+        }
+
+        Ok(())
+    }
+
+    fn parse_term_object(&mut self) -> Result<(), AmlError> {
+        trace!("Parsing term object");
+        /*
+         * TermObj := NameSpaceModifierObj | NamedObj | Type1Opcode | Type2Opcode
+         * NameSpaceModifierObj := DefAlias | DefName | DefScope
+         */
+        match self.stream.peek()? {
+            opcodes::SCOPE_OP => {
+                self.parse_scope_op()?;
+            }
+
+            byte => return Err(AmlError::UnexpectedByte(byte)),
+        }
+
+        Ok(())
+    }
+
+    fn parse_scope_op(&mut self) -> Result<(), AmlError> {
+        trace!("Parsing scope op");
+        /*
+         * DefScope := 0x10 PkgLength NameString TermList
+         */
+        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(())
+    }
+
+    /// 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> {
+        /*
+         * 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 {
+            return Ok(u32::from(lead_byte.get_bits(0..6)));
+        }
+
+        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(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(is_lead_name_char)?,
+            self.consume(is_name_char)?,
+            self.consume(is_name_char)?,
+            self.consume(is_name_char)?,
+        ])
+    }
+}
+
+fn matches_char(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)
+}

+ 1 - 0
src/aml/value.rs

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

+ 3 - 2
src/fadt.rs

@@ -74,11 +74,12 @@ pub struct Fadt {
     hypervisor_vendor_id: u64,
 }
 
-pub(crate) fn parse_fadt<'a, H>(
-    acpi: &mut Acpi<'a, H>,
+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")?;

+ 5 - 2
src/lib.rs

@@ -1,4 +1,5 @@
 #![no_std]
+#![feature(nll)]
 #![feature(alloc)]
 
 #[cfg(test)]
@@ -8,6 +9,7 @@ extern crate std;
 #[macro_use]
 extern crate log;
 extern crate alloc;
+extern crate bit_field;
 
 mod aml;
 mod fadt;
@@ -16,14 +18,13 @@ mod rsdp;
 mod sdt;
 
 use alloc::{BTreeMap, String};
+use aml::{AmlError, AmlValue};
 use core::mem;
 use core::ops::Deref;
 use core::ptr::NonNull;
 use rsdp::Rsdp;
 use sdt::SdtHeader;
 
-pub struct AmlValue; // TODO: temp
-
 #[derive(Debug)]
 // TODO: manually implement Debug to print signatures correctly etc.
 pub enum AcpiError {
@@ -35,6 +36,8 @@ pub enum AcpiError {
     SdtInvalidOemId([u8; 4]),
     SdtInvalidTableId([u8; 4]),
     SdtInvalidChecksum([u8; 4]),
+
+    InvalidAmlTable([u8; 4], AmlError),
 }
 
 #[repr(C, packed)]

+ 3 - 2
src/sdt.rs

@@ -155,11 +155,12 @@ where
 
 /// 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<'a, H>(
-    acpi: &mut Acpi<'a, H>,
+pub(crate) fn dispatch_sdt<'a, 'h, H>(
+    acpi: &'a mut Acpi<'h, H>,
     physical_address: usize,
 ) -> Result<(), AcpiError>
 where
+    'h: 'a,
     H: AcpiHandler + 'a,
 {
     let header = peek_at_sdt_header(acpi.handler, physical_address);