diff options
-rw-r--r-- | acpi_tables/src/aml.rs | 1966 | ||||
-rw-r--r-- | acpi_tables/src/lib.rs | 1 |
2 files changed, 1967 insertions, 0 deletions
diff --git a/acpi_tables/src/aml.rs b/acpi_tables/src/aml.rs new file mode 100644 index 0000000..7d16153 --- /dev/null +++ b/acpi_tables/src/aml.rs @@ -0,0 +1,1966 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// The trait Aml can be implemented by the ACPI objects to translate itself +/// into the AML raw data. So that these AML raw data can be added into the +/// ACPI DSDT for guest. +pub trait Aml { + /// Translate an ACPI object into AML code and append to the vector + /// buffer. + /// * `bytes` - The vector used to append the AML code. + fn to_aml_bytes(&self, bytes: &mut Vec<u8>); +} + +// AML byte stream defines +const ZEROOP: u8 = 0x00; +const ONEOP: u8 = 0x01; +const NAMEOP: u8 = 0x08; +const BYTEPREFIX: u8 = 0x0a; +const WORDPREFIX: u8 = 0x0b; +const DWORDPREFIX: u8 = 0x0c; +const STRINGOP: u8 = 0x0d; +const QWORDPREFIX: u8 = 0x0e; +const SCOPEOP: u8 = 0x10; +const BUFFEROP: u8 = 0x11; +const PACKAGEOP: u8 = 0x12; +const METHODOP: u8 = 0x14; +const DUALNAMEPREFIX: u8 = 0x2e; +const MULTINAMEPREFIX: u8 = 0x2f; +const NAMECHARBASE: u8 = 0x40; + +const EXTOPPREFIX: u8 = 0x5b; +const MUTEXOP: u8 = 0x01; +const ACQUIREOP: u8 = 0x23; +const RELEASEOP: u8 = 0x27; +const OPREGIONOP: u8 = 0x80; +const FIELDOP: u8 = 0x81; +const DEVICEOP: u8 = 0x82; + +const LOCAL0OP: u8 = 0x60; +const ARG0OP: u8 = 0x68; +const STOREOP: u8 = 0x70; +const ADDOP: u8 = 0x72; +const CONCATOP: u8 = 0x73; +const SUBTRACTOP: u8 = 0x74; +const MULTIPLYOP: u8 = 0x77; +const SHIFTLEFTOP: u8 = 0x79; +const SHIFTRIGHTOP: u8 = 0x7a; +const ANDOP: u8 = 0x7b; +const NANDOP: u8 = 0x7c; +const OROP: u8 = 0x7d; +const NOROP: u8 = 0x7e; +const XOROP: u8 = 0x7f; +const CONCATRESOP: u8 = 0x84; +const MODOP: u8 = 0x85; +const NOTIFYOP: u8 = 0x86; +const INDEXOP: u8 = 0x88; +const LEQUALOP: u8 = 0x93; +const LLESSOP: u8 = 0x95; +const TOSTRINGOP: u8 = 0x9c; +const IFOP: u8 = 0xa0; +const WHILEOP: u8 = 0xa2; +const RETURNOP: u8 = 0xa4; +const ONESOP: u8 = 0xff; + +// AML resouce data fields +const IOPORTDESC: u8 = 0x47; +const ENDTAG: u8 = 0x79; +const MEMORY32FIXEDDESC: u8 = 0x86; +const DWORDADDRSPACEDESC: u8 = 0x87; +const WORDADDRSPACEDESC: u8 = 0x88; +const EXTIRQDESC: u8 = 0x89; +const QWORDADDRSPACEDESC: u8 = 0x8A; + +/// Zero object in ASL. +pub const ZERO: Zero = Zero {}; +pub struct Zero {} + +impl Aml for Zero { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.append(&mut vec![ZEROOP]); + } +} + +/// One object in ASL. +pub const ONE: One = One {}; +pub struct One {} + +impl Aml for One { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.append(&mut vec![ONEOP]); + } +} + +/// Ones object represents all bits 1. +pub const ONES: Ones = Ones {}; +pub struct Ones {} + +impl Aml for Ones { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.append(&mut vec![ONESOP]); + } +} + +/// Represents Namestring to construct ACPI objects like +/// Name/Device/Method/Scope and so on... +pub struct Path { + root: bool, + name_parts: Vec<[u8; 4]>, +} + +impl Aml for Path { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + if self.root { + bytes.push(b'\\'); + } + + match self.name_parts.len() { + 0 => panic!("Name cannot be empty"), + 1 => {} + 2 => { + bytes.push(DUALNAMEPREFIX); + } + n => { + bytes.push(MULTINAMEPREFIX); + bytes.push(n as u8); + } + }; + + for part in self.name_parts.clone().iter_mut() { + bytes.append(&mut part.to_vec()); + } + } +} + +impl Path { + /// Per ACPI Spec, the Namestring split by "." has 4 bytes long. So any name + /// not has 4 bytes will not be accepted. + pub fn new(name: &str) -> Self { + let root = name.starts_with('\\'); + let offset = root as usize; + let mut name_parts = Vec::new(); + for part in name[offset..].split('.') { + assert_eq!(part.len(), 4); + let mut name_part = [0u8; 4]; + name_part.copy_from_slice(part.as_bytes()); + name_parts.push(name_part); + } + + Path { root, name_parts } + } +} + +impl From<&str> for Path { + fn from(s: &str) -> Self { + Path::new(s) + } +} + +pub type Byte = u8; + +impl Aml for Byte { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(BYTEPREFIX); + bytes.push(*self); + } +} + +pub type Word = u16; + +impl Aml for Word { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(WORDPREFIX); + bytes.append(&mut self.to_le_bytes().to_vec()); + } +} + +pub type DWord = u32; + +impl Aml for DWord { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(DWORDPREFIX); + bytes.append(&mut self.to_le_bytes().to_vec()); + } +} + +pub type QWord = u64; + +impl Aml for QWord { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(QWORDPREFIX); + bytes.append(&mut self.to_le_bytes().to_vec()); + } +} + +/// Name object. bytes represents the raw AML data for it. +pub struct Name { + bytes: Vec<u8>, +} + +impl Aml for Name { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.append(&mut self.bytes.clone()); + } +} + +impl Name { + /// Create Name object: + /// + /// * `path` - The namestring. + /// * `inner` - AML objects contained in this namespace. + pub fn new(path: Path, inner: &dyn Aml) -> Self { + let mut bytes = Vec::new(); + bytes.push(NAMEOP); + path.to_aml_bytes(&mut bytes); + inner.to_aml_bytes(&mut bytes); + Name { bytes } + } +} + +/// Package object. 'children' represents the ACPI objects contained in this package. +pub struct Package<'a> { + children: Vec<&'a dyn Aml>, +} + +impl<'a> Aml for Package<'a> { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + bytes.push(self.children.len() as u8); + for child in &self.children { + child.to_aml_bytes(&mut bytes); + } + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, PACKAGEOP); + + aml.append(&mut bytes); + } +} + +impl<'a> Package<'a> { + /// Create Package object: + pub fn new(children: Vec<&'a dyn Aml>) -> Self { + Package { children } + } +} + +/* + +From the ACPI spec for PkgLength: + +"The high 2 bits of the first byte reveal how many follow bytes are in the PkgLength. If the +PkgLength has only one byte, bit 0 through 5 are used to encode the package length (in other +words, values 0-63). If the package length value is more than 63, more than one byte must be +used for the encoding in which case bit 4 and 5 of the PkgLeadByte are reserved and must be zero. +If the multiple bytes encoding is used, bits 0-3 of the PkgLeadByte become the least significant 4 +bits of the resulting package length value. The next ByteData will become the next least +significant 8 bits of the resulting value and so on, up to 3 ByteData bytes. Thus, the maximum +package length is 2**28." + +*/ + +/* Also used for NamedField but in that case the length is not included in itself */ +fn create_pkg_length(data: &[u8], include_self: bool) -> Vec<u8> { + let mut result = Vec::new(); + + /* PkgLength is inclusive and includes the length bytes */ + let length_length = if data.len() < (2usize.pow(6) - 1) { + 1 + } else if data.len() < (2usize.pow(12) - 2) { + 2 + } else if data.len() < (2usize.pow(20) - 3) { + 3 + } else { + 4 + }; + + let length = data.len() + if include_self { length_length } else { 0 }; + + match length_length { + 1 => result.push(length as u8), + 2 => { + result.push((1u8 << 6) | (length & 0xf) as u8); + result.push((length >> 4) as u8) + } + 3 => { + result.push((2u8 << 6) | (length & 0xf) as u8); + result.push((length >> 4) as u8); + result.push((length >> 12) as u8); + } + _ => { + result.push((3u8 << 6) | (length & 0xf) as u8); + result.push((length >> 4) as u8); + result.push((length >> 12) as u8); + result.push((length >> 20) as u8); + } + } + + result +} + +/// EISAName object. 'value' means the encoded u32 EisaIdString. +pub struct EISAName { + value: DWord, +} + +impl EISAName { + /// Per ACPI Spec, the EisaIdString must be a String + /// object of the form UUUNNNN, where U is an uppercase letter + /// and N is a hexadecimal digit. No asterisks or other characters + /// are allowed in the string. + pub fn new(name: &str) -> Self { + assert_eq!(name.len(), 7); + + let data = name.as_bytes(); + + let value: u32 = (u32::from(data[0].checked_sub(NAMECHARBASE).unwrap()) << 26 + | u32::from(data[1].checked_sub(NAMECHARBASE).unwrap()) << 21 + | u32::from(data[2].checked_sub(NAMECHARBASE).unwrap()) << 16 + | name.chars().nth(3).unwrap().to_digit(16).unwrap() << 12 + | name.chars().nth(4).unwrap().to_digit(16).unwrap() << 8 + | name.chars().nth(5).unwrap().to_digit(16).unwrap() << 4 + | name.chars().nth(6).unwrap().to_digit(16).unwrap()) + .swap_bytes(); + + EISAName { value } + } +} + +impl Aml for EISAName { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + self.value.to_aml_bytes(bytes); + } +} + +fn create_integer(v: usize, bytes: &mut Vec<u8>) { + if v <= u8::max_value().into() { + (v as u8).to_aml_bytes(bytes); + } else if v <= u16::max_value().into() { + (v as u16).to_aml_bytes(bytes); + } else if v <= u32::max_value() as usize { + (v as u32).to_aml_bytes(bytes); + } else { + (v as u64).to_aml_bytes(bytes); + } +} + +pub type Usize = usize; + +impl Aml for Usize { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + create_integer(*self, bytes); + } +} + +fn create_aml_string(v: &str) -> Vec<u8> { + let mut data = Vec::new(); + data.push(STRINGOP); + data.extend_from_slice(v.as_bytes()); + data.push(0x0); /* NullChar */ + data +} + +/// implement Aml trait for 'str' so that 'str' can be directly append to the aml vector +pub type AmlStr = &'static str; + +impl Aml for AmlStr { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.append(&mut create_aml_string(self)); + } +} + +/// implement Aml trait for 'String'. So purpose with str. +pub type AmlString = String; + +impl Aml for AmlString { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.append(&mut create_aml_string(self)); + } +} + +/// ResouceTemplate object. 'children' represents the ACPI objects in it. +pub struct ResourceTemplate<'a> { + children: Vec<&'a dyn Aml>, +} + +impl<'a> Aml for ResourceTemplate<'a> { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + + // Add buffer data + for child in &self.children { + child.to_aml_bytes(&mut bytes); + } + + // Mark with end and mark checksum as as always valid + bytes.push(ENDTAG); + bytes.push(0); /* zero checksum byte */ + + // Buffer length is an encoded integer including buffer data + // and EndTag and checksum byte + let mut buffer_length = Vec::new(); + bytes.len().to_aml_bytes(&mut buffer_length); + buffer_length.reverse(); + for byte in buffer_length { + bytes.insert(0, byte); + } + + // PkgLength is everything else + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, BUFFEROP); + + aml.append(&mut bytes); + } +} + +impl<'a> ResourceTemplate<'a> { + /// Create ResouceTemplate object + pub fn new(children: Vec<&'a dyn Aml>) -> Self { + ResourceTemplate { children } + } +} + +/// Memory32Fixed object with read_write accessing type, and the base address/length. +pub struct Memory32Fixed { + read_write: bool, /* true for read & write, false for read only */ + base: u32, + length: u32, +} + +impl Memory32Fixed { + /// Create Memory32Fixed object. + pub fn new(read_write: bool, base: u32, length: u32) -> Self { + Memory32Fixed { + read_write, + base, + length, + } + } +} + +impl Aml for Memory32Fixed { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(MEMORY32FIXEDDESC); /* 32bit Fixed Memory Range Descriptor */ + bytes.append(&mut 9u16.to_le_bytes().to_vec()); + + // 9 bytes of payload + bytes.push(self.read_write as u8); + bytes.append(&mut self.base.to_le_bytes().to_vec()); + bytes.append(&mut self.length.to_le_bytes().to_vec()); + } +} + +#[derive(Copy, Clone)] +enum AddressSpaceType { + Memory, + IO, + BusNumber, +} + +/// AddressSpaceCachable represent cache types for AddressSpace object +#[derive(Copy, Clone)] +pub enum AddressSpaceCachable { + NotCacheable, + Cacheable, + WriteCombining, + PreFetchable, +} + +/// AddressSpace structure with type, resouce range and flags to +/// construct Memory/IO/BusNumber objects +pub struct AddressSpace<T> { + type_: AddressSpaceType, + min: T, + max: T, + type_flags: u8, +} + +impl<T> AddressSpace<T> { + /// Create DWordMemory/QWordMemory object + pub fn new_memory(cacheable: AddressSpaceCachable, read_write: bool, min: T, max: T) -> Self { + AddressSpace { + type_: AddressSpaceType::Memory, + min, + max, + type_flags: (cacheable as u8) << 1 | read_write as u8, + } + } + + /// Create WordIO/DWordIO/QWordIO object + pub fn new_io(min: T, max: T) -> Self { + AddressSpace { + type_: AddressSpaceType::IO, + min, + max, + type_flags: 3, /* EntireRange */ + } + } + + /// Create WordBusNumber object + pub fn new_bus_number(min: T, max: T) -> Self { + AddressSpace { + type_: AddressSpaceType::BusNumber, + min, + max, + type_flags: 0, + } + } + + fn push_header(&self, bytes: &mut Vec<u8>, descriptor: u8, length: usize) { + bytes.push(descriptor); /* Word Address Space Descriptor */ + bytes.append(&mut (length as u16).to_le_bytes().to_vec()); + bytes.push(self.type_ as u8); /* type */ + let generic_flags = 1 << 2 /* Min Fixed */ | 1 << 3; /* Max Fixed */ + bytes.push(generic_flags); + bytes.push(self.type_flags); + } +} + +impl Aml for AddressSpace<u16> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + self.push_header( + bytes, + WORDADDRSPACEDESC, /* Word Address Space Descriptor */ + 3 + 5 * std::mem::size_of::<u16>(), /* 3 bytes of header + 5 u16 fields */ + ); + + bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Granularity */ + bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */ + bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */ + bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Translation */ + let len = self.max - self.min + 1; + bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */ + } +} + +impl Aml for AddressSpace<u32> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + self.push_header( + bytes, + DWORDADDRSPACEDESC, /* DWord Address Space Descriptor */ + 3 + 5 * std::mem::size_of::<u32>(), /* 3 bytes of header + 5 u32 fields */ + ); + + bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Granularity */ + bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */ + bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */ + bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Translation */ + let len = self.max - self.min + 1; + bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */ + } +} + +impl Aml for AddressSpace<u64> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + self.push_header( + bytes, + QWORDADDRSPACEDESC, /* QWord Address Space Descriptor */ + 3 + 5 * std::mem::size_of::<u64>(), /* 3 bytes of header + 5 u64 fields */ + ); + + bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Granularity */ + bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */ + bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */ + bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Translation */ + let len = self.max - self.min + 1; + bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */ + } +} + +/// IO resouce object with the IO range, alignment and length +pub struct IO { + min: u16, + max: u16, + alignment: u8, + length: u8, +} + +impl IO { + /// Create IO object + pub fn new(min: u16, max: u16, alignment: u8, length: u8) -> Self { + IO { + min, + max, + alignment, + length, + } + } +} + +impl Aml for IO { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(IOPORTDESC); /* IO Port Descriptor */ + bytes.push(1); /* IODecode16 */ + bytes.append(&mut self.min.to_le_bytes().to_vec()); + bytes.append(&mut self.max.to_le_bytes().to_vec()); + bytes.push(self.alignment); + bytes.push(self.length); + } +} + +/// Interrupt resouce object with the interrupt characters. +pub struct Interrupt { + consumer: bool, + edge_triggered: bool, + active_low: bool, + shared: bool, + number: u32, +} + +impl Interrupt { + /// Create Interrupt object + pub fn new( + consumer: bool, + edge_triggered: bool, + active_low: bool, + shared: bool, + number: u32, + ) -> Self { + Interrupt { + consumer, + edge_triggered, + active_low, + shared, + number, + } + } +} + +impl Aml for Interrupt { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(EXTIRQDESC); /* Extended IRQ Descriptor */ + bytes.append(&mut 6u16.to_le_bytes().to_vec()); + let flags = (self.shared as u8) << 3 + | (self.active_low as u8) << 2 + | (self.edge_triggered as u8) << 1 + | self.consumer as u8; + bytes.push(flags); + bytes.push(1u8); /* count */ + bytes.append(&mut self.number.to_le_bytes().to_vec()); + } +} + +/// Device object with its device name and children objects in it. +pub struct Device<'a> { + path: Path, + children: Vec<&'a dyn Aml>, +} + +impl<'a> Aml for Device<'a> { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.path.to_aml_bytes(&mut bytes); + for child in &self.children { + child.to_aml_bytes(&mut bytes); + } + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, DEVICEOP); /* DeviceOp */ + bytes.insert(0, EXTOPPREFIX); /* ExtOpPrefix */ + aml.append(&mut bytes) + } +} + +impl<'a> Device<'a> { + /// Create Device object + pub fn new(path: Path, children: Vec<&'a dyn Aml>) -> Self { + Device { path, children } + } +} + +/// Scope object with its name and children objects in it. +pub struct Scope<'a> { + path: Path, + children: Vec<&'a dyn Aml>, +} + +impl<'a> Aml for Scope<'a> { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.path.to_aml_bytes(&mut bytes); + for child in &self.children { + child.to_aml_bytes(&mut bytes); + } + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, SCOPEOP); + aml.append(&mut bytes) + } +} + +impl<'a> Scope<'a> { + /// Create Scope object + pub fn new(path: Path, children: Vec<&'a dyn Aml>) -> Self { + Scope { path, children } + } +} + +/// Method object with its name, children objects, arguments and serialized character. +pub struct Method<'a> { + path: Path, + children: Vec<&'a dyn Aml>, + args: u8, + serialized: bool, +} + +impl<'a> Method<'a> { + /// Create Method object. + pub fn new(path: Path, args: u8, serialized: bool, children: Vec<&'a dyn Aml>) -> Self { + Method { + path, + children, + args, + serialized, + } + } +} + +impl<'a> Aml for Method<'a> { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.path.to_aml_bytes(&mut bytes); + let flags: u8 = (self.args & 0x7) | (self.serialized as u8) << 3; + bytes.push(flags); + for child in &self.children { + child.to_aml_bytes(&mut bytes); + } + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, METHODOP); + aml.append(&mut bytes) + } +} + +/// Return object with its return value. +pub struct Return<'a> { + value: &'a dyn Aml, +} + +impl<'a> Return<'a> { + /// Create Return object + pub fn new(value: &'a dyn Aml) -> Self { + Return { value } + } +} + +impl<'a> Aml for Return<'a> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(RETURNOP); + self.value.to_aml_bytes(bytes); + } +} + +/// FiledAccessType defines the filed accessing types. +#[derive(Clone, Copy)] +pub enum FieldAccessType { + Any, + Byte, + Word, + DWord, + QWord, + Buffer, +} + +/// FiledUpdateRule defines the rules to update the filed. +#[derive(Clone, Copy)] +pub enum FieldUpdateRule { + Preserve = 0, + WriteAsOnes = 1, + WriteAsZeroes = 2, +} + +/// FiledEntry defines the filed entry. +pub enum FieldEntry { + Named([u8; 4], usize), + Reserved(usize), +} + +/// Field object with the region name, filed entries, access type and update rules. +pub struct Field { + path: Path, + + fields: Vec<FieldEntry>, + access_type: FieldAccessType, + update_rule: FieldUpdateRule, +} + +impl Field { + /// Create Field object + pub fn new( + path: Path, + access_type: FieldAccessType, + update_rule: FieldUpdateRule, + fields: Vec<FieldEntry>, + ) -> Self { + Field { + path, + access_type, + update_rule, + fields, + } + } +} + +impl Aml for Field { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.path.to_aml_bytes(&mut bytes); + + let flags: u8 = self.access_type as u8 | (self.update_rule as u8) << 5; + bytes.push(flags); + + for field in self.fields.iter() { + match field { + FieldEntry::Named(name, length) => { + bytes.extend_from_slice(name); + bytes.append(&mut create_pkg_length(&vec![0; *length], false)); + } + FieldEntry::Reserved(length) => { + bytes.push(0x0); + bytes.append(&mut create_pkg_length(&vec![0; *length], false)); + } + } + } + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, FIELDOP); + bytes.insert(0, EXTOPPREFIX); + aml.append(&mut bytes) + } +} + +/// The space type for OperationRegion object +#[derive(Clone, Copy)] +pub enum OpRegionSpace { + SystemMemory, + SystemIO, + PCIConfig, + EmbeddedControl, + SMBus, + SystemCMOS, + PciBarTarget, + IPMI, + GeneralPurposeIO, + GenericSerialBus, +} + +/// OperationRegion object with region name, region space type, its offset and length. +pub struct OpRegion { + path: Path, + space: OpRegionSpace, + offset: usize, + length: usize, +} + +impl OpRegion { + /// Create OperationRegion object. + pub fn new(path: Path, space: OpRegionSpace, offset: usize, length: usize) -> Self { + OpRegion { + path, + space, + offset, + length, + } + } +} + +impl Aml for OpRegion { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.path.to_aml_bytes(&mut bytes); + bytes.push(self.space as u8); + self.offset.to_aml_bytes(&mut bytes); /* RegionOffset */ + self.length.to_aml_bytes(&mut bytes); /* RegionLen */ + bytes.insert(0, OPREGIONOP); + bytes.insert(0, EXTOPPREFIX); + aml.append(&mut bytes) + } +} + +/// If object with the if condition(predicate) and the body presented by the if_children objects. +pub struct If<'a> { + predicate: &'a dyn Aml, + if_children: Vec<&'a dyn Aml>, +} + +impl<'a> If<'a> { + /// Create If object. + pub fn new(predicate: &'a dyn Aml, if_children: Vec<&'a dyn Aml>) -> Self { + If { + predicate, + if_children, + } + } +} + +impl<'a> Aml for If<'a> { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.predicate.to_aml_bytes(&mut bytes); + for child in self.if_children.iter() { + child.to_aml_bytes(&mut bytes); + } + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, IFOP); + aml.append(&mut bytes) + } +} + +/// Equal object with its right part and left part, which are both ACPI objects. +pub struct Equal<'a> { + right: &'a dyn Aml, + left: &'a dyn Aml, +} + +impl<'a> Equal<'a> { + /// Create Equal object. + pub fn new(left: &'a dyn Aml, right: &'a dyn Aml) -> Self { + Equal { left, right } + } +} + +impl<'a> Aml for Equal<'a> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(LEQUALOP); + self.left.to_aml_bytes(bytes); + self.right.to_aml_bytes(bytes); + } +} + +/// LessThan object with its right part and left part, which are both ACPI objects. +pub struct LessThan<'a> { + right: &'a dyn Aml, + left: &'a dyn Aml, +} + +impl<'a> LessThan<'a> { + /// Create LessThan object. + pub fn new(left: &'a dyn Aml, right: &'a dyn Aml) -> Self { + LessThan { left, right } + } +} + +impl<'a> Aml for LessThan<'a> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(LLESSOP); + self.left.to_aml_bytes(bytes); + self.right.to_aml_bytes(bytes); + } +} + +/// Argx object. +pub struct Arg(pub u8); + +impl Aml for Arg { + /// Per ACPI spec, there is maximum 7 Argx objects from + /// Arg0 ~ Arg6. Any other Arg object will not be accepted. + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + assert!(self.0 <= 6); + bytes.push(ARG0OP + self.0); + } +} + +/// Localx object. +pub struct Local(pub u8); + +impl Aml for Local { + /// Per ACPI spec, there is maximum 8 Localx objects from + /// Local0 ~ Local7. Any other Local object will not be accepted. + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + assert!(self.0 <= 7); + bytes.push(LOCAL0OP + self.0); + } +} + +/// Store object with the ACPI object name which can be stored to and +/// the ACPI object value which is to store. +pub struct Store<'a> { + name: &'a dyn Aml, + value: &'a dyn Aml, +} + +impl<'a> Store<'a> { + /// Create Store object. + pub fn new(name: &'a dyn Aml, value: &'a dyn Aml) -> Self { + Store { name, value } + } +} + +impl<'a> Aml for Store<'a> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(STOREOP); + self.value.to_aml_bytes(bytes); + self.name.to_aml_bytes(bytes); + } +} + +/// Mutex object with a mutex name and a synchronization level. +pub struct Mutex { + path: Path, + sync_level: u8, +} + +impl Mutex { + /// Create Mutex object. + pub fn new(path: Path, sync_level: u8) -> Self { + Self { path, sync_level } + } +} + +impl Aml for Mutex { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(EXTOPPREFIX); + bytes.push(MUTEXOP); + self.path.to_aml_bytes(bytes); + bytes.push(self.sync_level); + } +} + +/// Acquire object with a Mutex object and timeout value. +pub struct Acquire { + mutex: Path, + timeout: u16, +} + +impl Acquire { + /// Create Acquire object. + pub fn new(mutex: Path, timeout: u16) -> Self { + Acquire { mutex, timeout } + } +} + +impl Aml for Acquire { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(EXTOPPREFIX); + bytes.push(ACQUIREOP); + self.mutex.to_aml_bytes(bytes); + bytes.extend_from_slice(&self.timeout.to_le_bytes()); + } +} + +/// Release object with a Mutex object to release. +pub struct Release { + mutex: Path, +} + +impl Release { + /// Create Release object. + pub fn new(mutex: Path) -> Self { + Release { mutex } + } +} + +impl Aml for Release { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(EXTOPPREFIX); + bytes.push(RELEASEOP); + self.mutex.to_aml_bytes(bytes); + } +} + +/// Notify object with an object which is to be notified with the value. +pub struct Notify<'a> { + object: &'a dyn Aml, + value: &'a dyn Aml, +} + +impl<'a> Notify<'a> { + /// Create Notify object. + pub fn new(object: &'a dyn Aml, value: &'a dyn Aml) -> Self { + Notify { object, value } + } +} + +impl<'a> Aml for Notify<'a> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push(NOTIFYOP); + self.object.to_aml_bytes(bytes); + self.value.to_aml_bytes(bytes); + } +} + +/// While object with the while condition objects(predicate) and +/// the while body objects(while_children). +pub struct While<'a> { + predicate: &'a dyn Aml, + while_children: Vec<&'a dyn Aml>, +} + +impl<'a> While<'a> { + /// Create While object. + pub fn new(predicate: &'a dyn Aml, while_children: Vec<&'a dyn Aml>) -> Self { + While { + predicate, + while_children, + } + } +} + +impl<'a> Aml for While<'a> { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.predicate.to_aml_bytes(&mut bytes); + for child in self.while_children.iter() { + child.to_aml_bytes(&mut bytes); + } + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, WHILEOP); + aml.append(&mut bytes) + } +} + +macro_rules! binary_op { + ($name:ident, $opcode:expr) => { + /// General operation object with the operator a/b and a target. + pub struct $name<'a> { + a: &'a dyn Aml, + b: &'a dyn Aml, + target: &'a dyn Aml, + } + + impl<'a> $name<'a> { + /// Create the object. + pub fn new(target: &'a dyn Aml, a: &'a dyn Aml, b: &'a dyn Aml) -> Self { + $name { target, a, b } + } + } + + impl<'a> Aml for $name<'a> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + bytes.push($opcode); /* Op for the binary operator */ + self.a.to_aml_bytes(bytes); + self.b.to_aml_bytes(bytes); + self.target.to_aml_bytes(bytes); + } + } + }; +} + +binary_op!(Add, ADDOP); +binary_op!(Concat, CONCATOP); +binary_op!(Subtract, SUBTRACTOP); +binary_op!(Multiply, MULTIPLYOP); +binary_op!(ShiftLeft, SHIFTLEFTOP); +binary_op!(ShiftRight, SHIFTRIGHTOP); +binary_op!(And, ANDOP); +binary_op!(Nand, NANDOP); +binary_op!(Or, OROP); +binary_op!(Nor, NOROP); +binary_op!(Xor, XOROP); +binary_op!(ConcatRes, CONCATRESOP); +binary_op!(Mod, MODOP); +binary_op!(Index, INDEXOP); +binary_op!(ToString, TOSTRINGOP); + +/// MethodCall object with the method name and parameter objects. +pub struct MethodCall<'a> { + name: Path, + args: Vec<&'a dyn Aml>, +} + +impl<'a> MethodCall<'a> { + /// Create MethodCall object. + pub fn new(name: Path, args: Vec<&'a dyn Aml>) -> Self { + MethodCall { name, args } + } +} + +impl<'a> Aml for MethodCall<'a> { + fn to_aml_bytes(&self, bytes: &mut Vec<u8>) { + self.name.to_aml_bytes(bytes); + for arg in self.args.iter() { + arg.to_aml_bytes(bytes); + } + } +} + +/// Buffer object with the data in it. +pub struct Buffer { + data: Vec<u8>, +} + +impl Buffer { + /// Create Buffer object. + pub fn new(data: Vec<u8>) -> Self { + Buffer { data } + } +} + +impl Aml for Buffer { + fn to_aml_bytes(&self, aml: &mut Vec<u8>) { + let mut bytes = Vec::new(); + self.data.len().to_aml_bytes(&mut bytes); + bytes.extend_from_slice(&self.data); + + let mut pkg_length = create_pkg_length(&bytes, true); + pkg_length.reverse(); + for byte in pkg_length { + bytes.insert(0, byte); + } + + bytes.insert(0, BUFFEROP); + + aml.append(&mut bytes) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_device() { + /* + Device (_SB.COM1) + { + Name (_HID, EisaId ("PNP0501") /* 16550A-compatible COM Serial Port */) // _HID: Hardware ID + Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings + { + Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) + { + 0x00000004, + } + IO (Decode16, + 0x03F8, // Range Minimum + 0x03F8, // Range Maximum + 0x00, // Alignment + 0x08, // Length + ) + } + } + */ + let com1_device = [ + 0x5B, 0x82, 0x30, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x43, 0x4F, 0x4D, 0x31, 0x08, 0x5F, + 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, + 0x16, 0x0A, 0x13, 0x89, 0x06, 0x00, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x47, 0x01, + 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x79, 0x00, + ]; + let mut aml = Vec::new(); + + Device::new( + "_SB_.COM1".into(), + vec![ + &Name::new("_HID".into(), &EISAName::new("PNP0501")), + &Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![ + &Interrupt::new(true, true, false, false, 4), + &IO::new(0x3f8, 0x3f8, 0, 0x8), + ]), + ), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &com1_device[..]); + } + + #[test] + fn test_scope() { + /* + Scope (_SB.MBRD) + { + Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings + { + Memory32Fixed (ReadWrite, + 0xE8000000, // Address Base + 0x10000000, // Address Length + ) + }) + } + */ + + let mbrd_scope = [ + 0x10, 0x21, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x42, 0x52, 0x44, 0x08, 0x5F, 0x43, + 0x52, 0x53, 0x11, 0x11, 0x0A, 0x0E, 0x86, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE8, + 0x00, 0x00, 0x00, 0x10, 0x79, 0x00, + ]; + let mut aml = Vec::new(); + + Scope::new( + "_SB_.MBRD".into(), + vec![&Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![&Memory32Fixed::new(true, 0xE800_0000, 0x1000_0000)]), + )], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &mbrd_scope[..]); + } + + #[test] + fn test_resource_template() { + /* + Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings + { + Memory32Fixed (ReadWrite, + 0xE8000000, // Address Base + 0x10000000, // Address Length + ) + }) + */ + let crs_memory_32_fixed = [ + 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x11, 0x0A, 0x0E, 0x86, 0x09, 0x00, 0x01, 0x00, + 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x10, 0x79, 0x00, + ]; + let mut aml = Vec::new(); + + Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![&Memory32Fixed::new(true, 0xE800_0000, 0x1000_0000)]), + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, crs_memory_32_fixed); + + /* + Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings + { + WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, // Granularity + 0x0000, // Range Minimum + 0x00FF, // Range Maximum + 0x0000, // Translation Offset + 0x0100, // Length + ,, ) + WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Granularity + 0x0000, // Range Minimum + 0x0CF7, // Range Maximum + 0x0000, // Translation Offset + 0x0CF8, // Length + ,, , TypeStatic, DenseTranslation) + WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Granularity + 0x0D00, // Range Minimum + 0xFFFF, // Range Maximum + 0x0000, // Translation Offset + 0xF300, // Length + ,, , TypeStatic, DenseTranslation) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Granularity + 0x000A0000, // Range Minimum + 0x000BFFFF, // Range Maximum + 0x00000000, // Translation Offset + 0x00020000, // Length + ,, , AddressRangeMemory, TypeStatic) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x00000000, // Granularity + 0xC0000000, // Range Minimum + 0xFEBFFFFF, // Range Maximum + 0x00000000, // Translation Offset + 0x3EC00000, // Length + ,, , AddressRangeMemory, TypeStatic) + QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x0000000000000000, // Granularity + 0x0000000800000000, // Range Minimum + 0x0000000FFFFFFFFF, // Range Maximum + 0x0000000000000000, // Translation Offset + 0x0000000800000000, // Length + ,, , AddressRangeMemory, TypeStatic) + }) + */ + + // WordBusNumber from above + let crs_word_bus_number = [ + 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x15, 0x0A, 0x12, 0x88, 0x0D, 0x00, 0x02, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x79, 0x00, + ]; + aml.clear(); + + Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![&AddressSpace::new_bus_number(0x0u16, 0xffu16)]), + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &crs_word_bus_number); + + // WordIO blocks (x 2) from above + let crs_word_io = [ + 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x25, 0x0A, 0x22, 0x88, 0x0D, 0x00, 0x01, 0x0C, + 0x03, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xF8, 0x0C, 0x88, 0x0D, 0x00, + 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF3, 0x79, + 0x00, + ]; + aml.clear(); + + Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![ + &AddressSpace::new_io(0x0u16, 0xcf7u16), + &AddressSpace::new_io(0xd00u16, 0xffffu16), + ]), + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &crs_word_io[..]); + + // DWordMemory blocks (x 2) from above + let crs_dword_memory = [ + 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x39, 0x0A, 0x36, 0x87, 0x17, 0x00, 0x00, 0x0C, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x0B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xBF, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xC0, 0x3E, 0x79, 0x00, + ]; + aml.clear(); + + Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![ + &AddressSpace::new_memory( + AddressSpaceCachable::Cacheable, + true, + 0xa_0000u32, + 0xb_ffffu32, + ), + &AddressSpace::new_memory( + AddressSpaceCachable::NotCacheable, + true, + 0xc000_0000u32, + 0xfebf_ffffu32, + ), + ]), + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &crs_dword_memory[..]); + + // QWordMemory from above + let crs_qword_memory = [ + 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x33, 0x0A, 0x30, 0x8A, 0x2B, 0x00, 0x00, 0x0C, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x79, + 0x00, + ]; + aml.clear(); + Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![&AddressSpace::new_memory( + AddressSpaceCachable::Cacheable, + true, + 0x8_0000_0000u64, + 0xf_ffff_ffffu64, + )]), + ) + .to_aml_bytes(&mut aml); + + assert_eq!(aml, &crs_qword_memory[..]); + + /* + Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings + { + Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) + { + 0x00000004, + } + IO (Decode16, + 0x03F8, // Range Minimum + 0x03F8, // Range Maximum + 0x00, // Alignment + 0x08, // Length + ) + }) + + */ + let interrupt_io_data = [ + 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x16, 0x0A, 0x13, 0x89, 0x06, 0x00, 0x03, 0x01, + 0x04, 0x00, 0x00, 0x00, 0x47, 0x01, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x79, 0x00, + ]; + aml.clear(); + Name::new( + "_CRS".into(), + &ResourceTemplate::new(vec![ + &Interrupt::new(true, true, false, false, 4), + &IO::new(0x3f8, 0x3f8, 0, 0x8), + ]), + ) + .to_aml_bytes(&mut aml); + + assert_eq!(aml, &interrupt_io_data[..]); + } + + #[test] + fn test_pkg_length() { + assert_eq!(create_pkg_length(&[0u8; 62].to_vec(), true), vec![63]); + assert_eq!( + create_pkg_length(&[0u8; 64].to_vec(), true), + vec![1 << 6 | (66 & 0xf), 66 >> 4] + ); + assert_eq!( + create_pkg_length(&[0u8; 4096].to_vec(), true), + vec![ + 2 << 6 | (4099 & 0xf) as u8, + (4099 >> 4) as u8, + (4099 >> 12) as u8 + ] + ); + } + + #[test] + fn test_package() { + /* + Name (_S5, Package (0x01) // _S5_: S5 System State + { + 0x05 + }) + */ + let s5_sleep_data = [0x08, 0x5F, 0x53, 0x35, 0x5F, 0x12, 0x04, 0x01, 0x0A, 0x05]; + let mut aml = Vec::new(); + + Name::new("_S5_".into(), &Package::new(vec![&5u8])).to_aml_bytes(&mut aml); + + assert_eq!(s5_sleep_data.to_vec(), aml); + } + + #[test] + fn test_eisa_name() { + let mut aml = Vec::new(); + Name::new("_HID".into(), &EISAName::new("PNP0501")).to_aml_bytes(&mut aml); + assert_eq!( + aml, + [0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01], + ) + } + #[test] + fn test_name_path() { + let mut aml = Vec::new(); + (&"_SB_".into() as &Path).to_aml_bytes(&mut aml); + assert_eq!(aml, [0x5Fu8, 0x53, 0x42, 0x5F]); + aml.clear(); + (&"\\_SB_".into() as &Path).to_aml_bytes(&mut aml); + assert_eq!(aml, [0x5C, 0x5F, 0x53, 0x42, 0x5F]); + aml.clear(); + (&"_SB_.COM1".into() as &Path).to_aml_bytes(&mut aml); + assert_eq!(aml, [0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x43, 0x4F, 0x4D, 0x31]); + aml.clear(); + (&"_SB_.PCI0._HID".into() as &Path).to_aml_bytes(&mut aml); + assert_eq!( + aml, + [0x2F, 0x03, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x30, 0x5F, 0x48, 0x49, 0x44] + ); + } + + #[test] + fn test_numbers() { + let mut aml = Vec::new(); + 128u8.to_aml_bytes(&mut aml); + assert_eq!(aml, [0x0a, 0x80]); + aml.clear(); + 1024u16.to_aml_bytes(&mut aml); + assert_eq!(aml, [0x0b, 0x0, 0x04]); + aml.clear(); + (16u32 << 20).to_aml_bytes(&mut aml); + assert_eq!(aml, [0x0c, 0x00, 0x00, 0x0, 0x01]); + aml.clear(); + 0xdeca_fbad_deca_fbadu64.to_aml_bytes(&mut aml); + assert_eq!(aml, [0x0e, 0xad, 0xfb, 0xca, 0xde, 0xad, 0xfb, 0xca, 0xde]); + } + + #[test] + fn test_name() { + let mut aml = Vec::new(); + Name::new("_SB_.PCI0._UID".into(), &0x1234u16).to_aml_bytes(&mut aml); + assert_eq!( + aml, + [ + 0x08, /* NameOp */ + 0x2F, /* MultiNamePrefix */ + 0x03, /* 3 name parts */ + 0x5F, 0x53, 0x42, 0x5F, /* _SB_ */ + 0x50, 0x43, 0x49, 0x30, /* PCI0 */ + 0x5F, 0x55, 0x49, 0x44, /* _UID */ + 0x0b, /* WordPrefix */ + 0x34, 0x12 + ] + ); + } + + #[test] + fn test_string() { + let mut aml = Vec::new(); + (&"ACPI" as &dyn Aml).to_aml_bytes(&mut aml); + assert_eq!(aml, [0x0d, b'A', b'C', b'P', b'I', 0]); + aml.clear(); + "ACPI".to_owned().to_aml_bytes(&mut aml); + assert_eq!(aml, [0x0d, b'A', b'C', b'P', b'I', 0]); + } + + #[test] + fn test_method() { + let mut aml = Vec::new(); + Method::new("_STA".into(), 0, false, vec![&Return::new(&0xfu8)]).to_aml_bytes(&mut aml); + assert_eq!( + aml, + [0x14, 0x09, 0x5F, 0x53, 0x54, 0x41, 0x00, 0xA4, 0x0A, 0x0F] + ); + } + + #[test] + fn test_field() { + /* + Field (PRST, ByteAcc, NoLock, WriteAsZeros) + { + Offset (0x04), + CPEN, 1, + CINS, 1, + CRMV, 1, + CEJ0, 1, + Offset (0x05), + CCMD, 8 + } + + */ + + let field_data = [ + 0x5Bu8, 0x81, 0x23, 0x50, 0x52, 0x53, 0x54, 0x41, 0x00, 0x20, 0x43, 0x50, 0x45, 0x4E, + 0x01, 0x43, 0x49, 0x4E, 0x53, 0x01, 0x43, 0x52, 0x4D, 0x56, 0x01, 0x43, 0x45, 0x4A, + 0x30, 0x01, 0x00, 0x04, 0x43, 0x43, 0x4D, 0x44, 0x08, + ]; + let mut aml = Vec::new(); + + Field::new( + "PRST".into(), + FieldAccessType::Byte, + FieldUpdateRule::WriteAsZeroes, + vec![ + FieldEntry::Reserved(32), + FieldEntry::Named(*b"CPEN", 1), + FieldEntry::Named(*b"CINS", 1), + FieldEntry::Named(*b"CRMV", 1), + FieldEntry::Named(*b"CEJ0", 1), + FieldEntry::Reserved(4), + FieldEntry::Named(*b"CCMD", 8), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &field_data[..]); + + /* + Field (PRST, DWordAcc, NoLock, Preserve) + { + CSEL, 32, + Offset (0x08), + CDAT, 32 + } + */ + + let field_data = [ + 0x5Bu8, 0x81, 0x12, 0x50, 0x52, 0x53, 0x54, 0x03, 0x43, 0x53, 0x45, 0x4C, 0x20, 0x00, + 0x20, 0x43, 0x44, 0x41, 0x54, 0x20, + ]; + aml.clear(); + + Field::new( + "PRST".into(), + FieldAccessType::DWord, + FieldUpdateRule::Preserve, + vec![ + FieldEntry::Named(*b"CSEL", 32), + FieldEntry::Reserved(32), + FieldEntry::Named(*b"CDAT", 32), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &field_data[..]); + } + + #[test] + fn test_op_region() { + /* + OperationRegion (PRST, SystemIO, 0x0CD8, 0x0C) + */ + let op_region_data = [ + 0x5Bu8, 0x80, 0x50, 0x52, 0x53, 0x54, 0x01, 0x0B, 0xD8, 0x0C, 0x0A, 0x0C, + ]; + let mut aml = Vec::new(); + + OpRegion::new("PRST".into(), OpRegionSpace::SystemIO, 0xcd8, 0xc).to_aml_bytes(&mut aml); + assert_eq!(aml, &op_region_data[..]); + } + + #[test] + fn test_arg_if() { + /* + Method(TEST, 1, NotSerialized) { + If (Arg0 == Zero) { + Return(One) + } + Return(Zero) + } + */ + let arg_if_data = [ + 0x14, 0x0F, 0x54, 0x45, 0x53, 0x54, 0x01, 0xA0, 0x06, 0x93, 0x68, 0x00, 0xA4, 0x01, + 0xA4, 0x00, + ]; + let mut aml = Vec::new(); + + Method::new( + "TEST".into(), + 1, + false, + vec![ + &If::new(&Equal::new(&Arg(0), &ZERO), vec![&Return::new(&ONE)]), + &Return::new(&ZERO), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &arg_if_data); + } + + #[test] + fn test_local_if() { + /* + Method(TEST, 0, NotSerialized) { + Local0 = One + If (Local0 == Zero) { + Return(One) + } + Return(Zero) + } + */ + let local_if_data = [ + 0x14, 0x12, 0x54, 0x45, 0x53, 0x54, 0x00, 0x70, 0x01, 0x60, 0xA0, 0x06, 0x93, 0x60, + 0x00, 0xA4, 0x01, 0xA4, 0x00, + ]; + let mut aml = Vec::new(); + + Method::new( + "TEST".into(), + 0, + false, + vec![ + &Store::new(&Local(0), &ONE), + &If::new(&Equal::new(&Local(0), &ZERO), vec![&Return::new(&ONE)]), + &Return::new(&ZERO), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &local_if_data); + } + + #[test] + fn test_mutex() { + /* + Device (_SB_.MHPC) + { + Name (_HID, EisaId("PNP0A06") /* Generic Container Device */) // _HID: Hardware ID + Mutex (MLCK, 0x00) + Method (TEST, 0, NotSerialized) + { + Acquire (MLCK, 0xFFFF) + Local0 = One + Release (MLCK) + } + } + */ + + let mutex_data = [ + 0x5B, 0x82, 0x33, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x48, 0x50, 0x43, 0x08, 0x5F, + 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x06, 0x5B, 0x01, 0x4D, 0x4C, 0x43, 0x4B, + 0x00, 0x14, 0x17, 0x54, 0x45, 0x53, 0x54, 0x00, 0x5B, 0x23, 0x4D, 0x4C, 0x43, 0x4B, + 0xFF, 0xFF, 0x70, 0x01, 0x60, 0x5B, 0x27, 0x4D, 0x4C, 0x43, 0x4B, + ]; + let mut aml = Vec::new(); + + let mutex = Mutex::new("MLCK".into(), 0); + Device::new( + "_SB_.MHPC".into(), + vec![ + &Name::new("_HID".into(), &EISAName::new("PNP0A06")), + &mutex, + &Method::new( + "TEST".into(), + 0, + false, + vec![ + &Acquire::new("MLCK".into(), 0xffff), + &Store::new(&Local(0), &ONE), + &Release::new("MLCK".into()), + ], + ), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &mutex_data[..]); + } + + #[test] + fn test_notify() { + /* + Device (_SB.MHPC) + { + Name (_HID, EisaId ("PNP0A06") /* Generic Container Device */) // _HID: Hardware ID + Method (TEST, 0, NotSerialized) + { + Notify (MHPC, One) // Device Check + } + } + */ + let notify_data = [ + 0x5B, 0x82, 0x21, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x48, 0x50, 0x43, 0x08, 0x5F, + 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x06, 0x14, 0x0C, 0x54, 0x45, 0x53, 0x54, + 0x00, 0x86, 0x4D, 0x48, 0x50, 0x43, 0x01, + ]; + let mut aml = Vec::new(); + + Device::new( + "_SB_.MHPC".into(), + vec![ + &Name::new("_HID".into(), &EISAName::new("PNP0A06")), + &Method::new( + "TEST".into(), + 0, + false, + vec![&Notify::new(&Path::new("MHPC"), &ONE)], + ), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, ¬ify_data[..]); + } + + #[test] + fn test_while() { + /* + Device (_SB.MHPC) + { + Name (_HID, EisaId ("PNP0A06") /* Generic Container Device */) // _HID: Hardware ID + Method (TEST, 0, NotSerialized) + { + Local0 = Zero + While ((Local0 < 0x04)) + { + Local0 += One + } + } + } + */ + + let while_data = [ + 0x5B, 0x82, 0x28, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x4D, 0x48, 0x50, 0x43, 0x08, 0x5F, + 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x06, 0x14, 0x13, 0x54, 0x45, 0x53, 0x54, + 0x00, 0x70, 0x00, 0x60, 0xA2, 0x09, 0x95, 0x60, 0x0A, 0x04, 0x72, 0x60, 0x01, 0x60, + ]; + let mut aml = Vec::new(); + + Device::new( + "_SB_.MHPC".into(), + vec![ + &Name::new("_HID".into(), &EISAName::new("PNP0A06")), + &Method::new( + "TEST".into(), + 0, + false, + vec![ + &Store::new(&Local(0), &ZERO), + &While::new( + &LessThan::new(&Local(0), &4usize), + vec![&Add::new(&Local(0), &Local(0), &ONE)], + ), + ], + ), + ], + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &while_data[..]) + } + + #[test] + fn test_method_call() { + /* + Method (TST1, 1, NotSerialized) + { + TST2 (One, One) + } + + Method (TST2, 2, NotSerialized) + { + TST1 (One) + } + */ + let test_data = [ + 0x14, 0x0C, 0x54, 0x53, 0x54, 0x31, 0x01, 0x54, 0x53, 0x54, 0x32, 0x01, 0x01, 0x14, + 0x0B, 0x54, 0x53, 0x54, 0x32, 0x02, 0x54, 0x53, 0x54, 0x31, 0x01, + ]; + + let mut methods = Vec::new(); + Method::new( + "TST1".into(), + 1, + false, + vec![&MethodCall::new("TST2".into(), vec![&ONE, &ONE])], + ) + .to_aml_bytes(&mut methods); + Method::new( + "TST2".into(), + 2, + false, + vec![&MethodCall::new("TST1".into(), vec![&ONE])], + ) + .to_aml_bytes(&mut methods); + assert_eq!(&methods[..], &test_data[..]) + } + + #[test] + fn test_buffer() { + /* + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 /* ........ */ + }) + */ + let buffer_data = [ + 0x08, 0x5F, 0x4D, 0x41, 0x54, 0x11, 0x0B, 0x0A, 0x08, 0x00, 0x08, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, + ]; + let mut aml = Vec::new(); + + Name::new( + "_MAT".into(), + &Buffer::new(vec![0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]), + ) + .to_aml_bytes(&mut aml); + assert_eq!(aml, &buffer_data[..]) + } +} diff --git a/acpi_tables/src/lib.rs b/acpi_tables/src/lib.rs index 49cf760..e45aa40 100644 --- a/acpi_tables/src/lib.rs +++ b/acpi_tables/src/lib.rs @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +pub mod aml; pub mod rsdp; pub mod sdt; |