// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use util::byte_code::ByteCode; const ACPI_NAME_SEG_MAX: u8 = 4; /// This trait is used for converting AML Data structure to byte stream. pub trait AmlBuilder { /// Transfer this struct to byte stream. fn aml_bytes(&self) -> Vec<u8>; } /// This trait is used for adding children to AML Data structure that represents /// a scope, such as `AmlDevice`, `AmlScope`. pub trait AmlScopeBuilder: AmlBuilder { /// Append a child to this AML scope structure. /// /// # Arguments /// /// * `child` - Child that will be appended to the end of this scope. fn append_child<T: AmlBuilder>(&mut self, child: T); } /// Macro that helps to define `AmlZero`, `AmlOne`, `AmlOnes` /// /// # Arguments /// /// * `$name` - struct name /// * `$byte` - corresponding byte of this structure macro_rules! zero_one_define { ($name: ident, $byte: expr) => { pub struct $name; impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { vec![$byte] } } }; } zero_one_define!(AmlZero, 0x00); zero_one_define!(AmlOne, 0x01); zero_one_define!(AmlOnes, 0xFF); /// Macro that helps to define `AmlByte`, `AmlWord`, `AmlDWord`, `AmlQWord`. /// /// # Arguments /// /// * `$name` - struct name /// * `$op` - corresponding Opcode of this structure /// * `$ty` - inner field of this struct. macro_rules! aml_bytes_type_define { ($name:ident, $op:expr, $ty:tt) => { pub struct $name(pub $ty); impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![$op]; bytes.extend(self.0.as_bytes()); bytes } } }; } aml_bytes_type_define!(AmlByte, 0x0A, u8); aml_bytes_type_define!(AmlWord, 0x0B, u16); aml_bytes_type_define!(AmlDWord, 0x0C, u32); aml_bytes_type_define!(AmlQWord, 0x0E, u64); /// Integer, max value u64::MAX. pub struct AmlInteger(pub u64); impl AmlBuilder for AmlInteger { fn aml_bytes(&self) -> Vec<u8> { match self.0 { 0x00 => AmlZero.aml_bytes(), 0x01 => AmlOne.aml_bytes(), 0x02..=0xFF => AmlByte(self.0 as u8).aml_bytes(), 0x100..=0xFFFF => AmlWord(self.0 as u16).aml_bytes(), 0x10000..=0xFFFF_FFFF => AmlDWord(self.0 as u32).aml_bytes(), _ => AmlQWord(self.0).aml_bytes(), } } } /// String pub struct AmlString(pub String); impl AmlBuilder for AmlString { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x0D]; bytes.extend(self.0.as_bytes().to_vec()); bytes.push(0x0); bytes } } /// Parse and check a name-segment, and convert it to byte stream. fn build_name_seg(name: &str) -> Vec<u8> { if name.len() > usize::from(ACPI_NAME_SEG_MAX) { panic!("the length of NameSeg is larger than 4."); } let mut bytes = name.as_bytes().to_vec(); bytes.extend(vec![b'_'; ACPI_NAME_SEG_MAX as usize - name.len()]); bytes } /// Parse a name-string and convert it to byte stream fn build_name_string(name: &str) -> Vec<u8> { let strs = name.split('.').collect::<Vec<&str>>(); if strs.is_empty() || strs.len() > 255 { panic!("Invalid ACPI name string, length: {}", strs.len()); } let mut bytes = Vec::new(); // parse the first segment. let mut first_str = strs[0].to_string(); let mut index = 0; for (i, ch) in first_str.chars().enumerate() { if ch == '\\' || ch == '^' { bytes.push(ch as u8); } else { index = i; break; } } let remain_first = first_str.drain(index..).collect::<String>(); match strs.len() { 1 => { if remain_first.is_empty() { bytes.push(0x00); } else { bytes.append(&mut build_name_seg(&remain_first)); } } 2 => { bytes.push(0x2E); bytes.append(&mut build_name_seg(&remain_first)); bytes.append(&mut build_name_seg(strs[1])); } _ => { bytes.push(0x2F); bytes.push(strs.len() as u8); bytes.append(&mut build_name_seg(&remain_first)); strs.iter().skip(1).for_each(|s| { bytes.extend(build_name_seg(s)); }) } } bytes } /// This struct represents declaration of a named object pub struct AmlNameDecl { /// Name of the object. name: String, /// The corresponding object that be named. obj: Vec<u8>, } impl AmlNameDecl { pub fn new<T: AmlBuilder>(name: &str, obj: T) -> AmlNameDecl { AmlNameDecl { name: name.to_string(), obj: obj.aml_bytes(), } } } impl AmlBuilder for AmlNameDecl { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x08]; bytes.extend(build_name_string(self.name.as_ref())); bytes.extend(self.obj.clone()); bytes } } /// AmlName represents a Named object that has been declared before. #[derive(Clone)] pub struct AmlName(pub String); impl AmlBuilder for AmlName { fn aml_bytes(&self) -> Vec<u8> { build_name_string(self.0.as_ref()) } } /// EISA ID String pub struct AmlEisaId { name: String, } impl AmlEisaId { pub fn new(name: &str) -> AmlEisaId { if name.len() != 7 { panic!("Eisa: String length is not 7."); } AmlEisaId { name: name.to_string(), } } } impl AmlBuilder for AmlEisaId { fn aml_bytes(&self) -> Vec<u8> { let chars = self.name.chars().collect::<Vec<_>>(); let dword: u32 = (chars[0] as u32 - 0x40) << 26 | (chars[1] as u32 - 0x40) << 21 | (chars[2] as u32 - 0x40) << 16 | chars[3].to_digit(16).unwrap() << 12 | chars[4].to_digit(16).unwrap() << 8 | chars[5].to_digit(16).unwrap() << 4 | chars[6].to_digit(16).unwrap(); let mut bytes = dword.as_bytes().to_vec(); bytes.reverse(); bytes.insert(0, 0x0C); bytes } } /// Convert an ASCII string to a 128-bit buffer. /// format: aabbccdd-eeff-gghh-iijj-kkllmmnnoopp pub struct AmlToUuid { name: String, } impl AmlToUuid { pub fn new(str: &str) -> AmlToUuid { let name = str.to_string(); if !Self::check_valid_uuid(&name) { panic!("Invalid UUID"); } AmlToUuid { name } } /// Check if the uuid is valid. fn check_valid_uuid(uuid: &str) -> bool { if uuid.len() != 36 { return false; } // Char located at 8, 13, 18, 23 should be `-` let indexs = &[8, 13, 18, 23]; for i in indexs { if uuid.chars().nth(*i).unwrap() != '-' { return false; } } for ch in uuid.chars() { if ch != '-' && (!ch.is_ascii_hexdigit()) { return false; } } true } } impl AmlBuilder for AmlToUuid { fn aml_bytes(&self) -> Vec<u8> { let mut uuid_bytes = Vec::new(); // If the UUID is "aabbccdd-eeff-gghh-iijj-kkllmmnnoopp", then the encoded order is: // dd cc bb aa ff ee hh gg ii jj kk ll mm nn oo pp let index = &[6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34]; for i in index { let mut chars = self.name.chars(); uuid_bytes.push( (chars.nth(*i).unwrap().to_digit(16).unwrap() as u8) << 4 | chars.next().unwrap().to_digit(16).unwrap() as u8, ); } let mut bytes = vec![0x11]; // ToUUID is a Buffer, so the `opcode` and `pkg_length` have to be added in the front. let len_bytes = AmlInteger(uuid_bytes.len() as u64).aml_bytes(); bytes.extend(build_pkg_length(len_bytes.len() + uuid_bytes.len(), true)); bytes.extend(len_bytes); bytes.extend(uuid_bytes); bytes } } // Follow ACPI spec: 5.4 Definition Block Encoding // The lower two bits indicates how many bytes are used for PkgLength // The 3,4 bits are only used if PkgLength consists of one bytes. // Therefore, the max value of PkgLength is 0x3F(one-byte encoding), // 0xF_FF(two-byte encoding), 0xF_FF_FF(three-byte encoding), 0xF_FF_FF_FF(four-byte encoding). /// Calculate PkgLength according to the length, and convert it to bytes. fn build_pkg_length(length: usize, include_self: bool) -> Vec<u8> { let pkg_1byte_shift = 6; let pkg_2byte_shift = 4; let pkg_3byte_shift = 12; let pkg_4byte_shift = 20; let mut pkg_length = length; let mut bytes = Vec::new(); let bytes_count = if length + 1 < (1 << pkg_1byte_shift) { 1 } else if length + 2 < (1 << pkg_3byte_shift) { 2 } else if length + 3 < (1 << pkg_4byte_shift) { 3 } else { 4 }; if include_self { pkg_length += bytes_count; } match bytes_count { 1 => { bytes.push(pkg_length as u8); } 2 => { bytes.push((1 << pkg_1byte_shift | (pkg_length & 0xF)) as u8); bytes.push((pkg_length >> pkg_2byte_shift) as u8); } 3 => { bytes.push((2 << pkg_1byte_shift | (pkg_length & 0xF)) as u8); bytes.push((pkg_length >> pkg_2byte_shift) as u8); bytes.push((pkg_length >> pkg_3byte_shift) as u8); } 4 => { bytes.push((3 << pkg_1byte_shift | (pkg_length & 0xF)) as u8); bytes.push((pkg_length >> pkg_2byte_shift) as u8); bytes.push((pkg_length >> pkg_3byte_shift) as u8); bytes.push((pkg_length >> pkg_4byte_shift) as u8); } _ => panic!("Undefined PkgLength"), } bytes } /// Buffer declaration, represents an array of bytes. /// When a byte stream cannot by `AmlByte`, `AmlQWord`, etc, Buffer can be used. pub struct AmlBuffer(pub Vec<u8>); impl AmlBuilder for AmlBuffer { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x11]; let len_bytes = AmlInteger(self.0.len() as u64).aml_bytes(); bytes.extend(build_pkg_length(len_bytes.len() + self.0.len(), true)); bytes.extend(len_bytes); bytes.extend(self.0.clone()); bytes } } /// Package contains an array of other objects. pub struct AmlPackage { elem_count: u8, buf: Vec<u8>, } impl AmlPackage { pub fn new(elem_count: u8) -> AmlPackage { AmlPackage { elem_count, buf: vec![elem_count], } } } impl AmlBuilder for AmlPackage { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x12]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlPackage { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// Variable-sized Package. pub struct AmlVarPackage { elem_count: u8, buf: Vec<u8>, } impl AmlVarPackage { pub fn new(elem_count: u8) -> AmlVarPackage { AmlVarPackage { elem_count, buf: vec![elem_count], } } } impl AmlBuilder for AmlVarPackage { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x13]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlVarPackage { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// Operation region address space type. #[allow(clippy::upper_case_acronyms)] #[derive(Copy, Clone)] pub enum AmlAddressSpaceType { /// System memory SystemMemory = 0, /// System IO SystemIO = 1, /// PCI config space PCIConfig = 2, /// Embedded controller space EmbeddedControl = 3, /// SMBus SMBus = 4, /// CMOS SystemCMOS = 5, /// PCI Bar target PciBarTarget = 6, /// IPMI IPMI = 7, /// General Purpose IO GeneralPurposeIO = 8, /// Generic serial bus GenericSerialBus = 9, /// Platform Communications Channel PCC = 10, } /// Operation Region: defines a named objects of certain type: SystemMemory, SystemIo, etc. /// OperationRegion also contains the start address and size. pub struct AmlOpRegion { /// Name of Operation region. name: String, /// System space type. space_type: AmlAddressSpaceType, /// Start address. offset: u64, /// Range length. length: u64, } impl AmlOpRegion { pub fn new( name: &str, space_type: AmlAddressSpaceType, offset: u64, length: u64, ) -> AmlOpRegion { AmlOpRegion { name: name.to_string(), space_type, offset, length, } } } impl AmlBuilder for AmlOpRegion { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x5B, 0x80]; bytes.extend(build_name_string(self.name.as_ref())); bytes.push(self.space_type as u8); bytes.extend(AmlInteger(self.offset).aml_bytes()); bytes.extend(AmlInteger(self.length).aml_bytes()); bytes } } /// Access width of this field. #[derive(Copy, Clone)] pub enum AmlFieldAccessType { Any = 0, Byte = 1, Word = 2, DWord = 3, QWord = 4, Buffer = 5, } /// Flag that indicates whether the Global Lock is to be used /// when accessing this field #[derive(Copy, Clone)] pub enum AmlFieldLockRule { NoLock = 0, Lock = 1, } /// Flag that indicates how the unmodified bits of a field are treated #[derive(Copy, Clone)] pub enum AmlFieldUpdateRule { Preserve = 0, WriteAsOnes = 1, WriteAsZeros = 2, } /// Field represents several bits in Operation Field. pub struct AmlField { /// The name of corresponding OperationRegion. name: String, /// The access type of this Field. access_type: AmlFieldAccessType, /// Global lock is to be used or not when accessing this field. lock_rule: AmlFieldLockRule, /// Unmodified bits of a field are treated as Ones/Zeros/Preserve. update_rule: AmlFieldUpdateRule, /// Field Unit list. buf: Vec<u8>, } impl AmlField { pub fn new( name: &str, acc_ty: AmlFieldAccessType, lock_r: AmlFieldLockRule, update_r: AmlFieldUpdateRule, ) -> AmlField { let mut bytes = Vec::new(); let flag = ((update_r as u8) << 5) | ((lock_r as u8) << 1) | (acc_ty as u8 & 0x0F); bytes.extend(build_name_string(name)); bytes.push(flag); AmlField { name: name.to_string(), access_type: acc_ty, lock_rule: lock_r, update_rule: update_r, buf: bytes, } } } impl AmlBuilder for AmlField { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x5B, 0x81]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlField { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// Field unit that defines inside Field-scope. pub struct AmlFieldUnit { /// The name of this Field unit. `None` value indicate that this field is reserved. name: Option<String>, /// The Byte length of this Field unit. length: u32, } impl AmlFieldUnit { pub fn new(name: Option<&str>, length: u32) -> AmlFieldUnit { AmlFieldUnit { name: name.map(|s| s.to_string()), length, } } } impl AmlBuilder for AmlFieldUnit { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = self .name .as_ref() .map(|str| build_name_seg(str)) .unwrap_or_else(|| vec![0x0_u8]); bytes.extend(build_pkg_length(self.length as usize, false)); bytes } } /// Open a named Scope, can refer any scope within the namespace. pub struct AmlScope { /// The name of scope. name: String, /// Contains objects created inside the scope, which are encodes to bytes. buf: Vec<u8>, } impl AmlScope { pub fn new(name: &str) -> AmlScope { AmlScope { name: name.to_string(), buf: build_name_string(name), } } pub fn append(&mut self, data: &[u8]) { self.buf.extend(data); } } impl AmlBuilder for AmlScope { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x10]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlScope { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// Device object that represents a processor, a device, etc. pub struct AmlDevice { name: String, buf: Vec<u8>, } impl AmlDevice { pub fn new(name: &str) -> AmlDevice { AmlDevice { name: name.to_string(), buf: build_name_string(name), } } } impl AmlBuilder for AmlDevice { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x5B, 0x82]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlDevice { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// Method definition. pub struct AmlMethod { /// The name of this method. name: String, /// Count of Arguments. default value is zero. args_count: u8, /// Whether this method is Serialized or not. serialized: bool, /// The body of this method, which has been converted to byte stream. buf: Vec<u8>, } impl AmlMethod { pub fn new(name: &str, args_count: u8, serialized: bool) -> AmlMethod { if args_count > 7 { panic!("Up to 7 arguments are supported, given {}", args_count); } // Method Flags: // bit 0-2: ArgCount (0-7) // bit 3: SerializeFlag // 0 NotSerialized // 1 Serialized let mut flag = args_count; if serialized { flag |= 1 << 3; } let mut bytes = build_name_string(name); bytes.push(flag); AmlMethod { name: name.to_string(), args_count, serialized, buf: bytes, } } } impl AmlBuilder for AmlMethod { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x14]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlMethod { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// Local variables that can be used in method, Local(0)~Local(7) #[derive(Copy, Clone)] pub struct AmlLocal(pub u8); impl AmlBuilder for AmlLocal { fn aml_bytes(&self) -> Vec<u8> { if self.0 > 7 { panic!("Up to 8 Local are supported, given {}", self.0); } vec![0x60 + self.0] } } /// Arguments passed to method, Arg(0)~Arg(6) #[derive(Copy, Clone)] pub struct AmlArg(pub u8); impl AmlBuilder for AmlArg { fn aml_bytes(&self) -> Vec<u8> { if self.0 > 6 { panic!("Up to 7 Arguments are supported, given {}", self.0); } vec![0x68 + self.0] } } /// Return from method pub struct AmlReturn { value: Vec<u8>, } impl AmlReturn { /// Return with nothing. #[allow(clippy::new_without_default)] pub fn new() -> AmlReturn { AmlReturn { value: vec![0x00] } } /// Return an object or reference. pub fn with_value<T: AmlBuilder>(val: T) -> AmlReturn { AmlReturn { value: val.aml_bytes(), } } } impl AmlBuilder for AmlReturn { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0xA4]; bytes.extend(self.value.clone()); bytes } } // Macro that helps to define `MethodCallWithArgsx`. macro_rules! method_call_define { ($name: ident) => { #[derive(Clone)] /// Call a method. pub struct $name { /// Name of the method that will be called. name: String, /// The arguments that will be passed to the method. Note that /// the arguments provided must match to the arguments' count of the method. buf: Vec<u8>, } impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = Vec::new(); bytes.extend(build_name_string(&self.name)); bytes.extend(self.buf.clone()); bytes } } }; } // AmlCallWithArgs1 represents calling method with 1 argument. method_call_define!(AmlCallWithArgs1); // AmlCallWithArgs2 represents calling method with 2 arguments. method_call_define!(AmlCallWithArgs2); // AmlCallWithArgs3 represents calling method with 3 arguments. method_call_define!(AmlCallWithArgs3); // AmlCallWithArgs4 represents calling method with 4 arguments. method_call_define!(AmlCallWithArgs4); // AmlCallWithArgs5 represents calling method with 5 arguments. method_call_define!(AmlCallWithArgs5); impl AmlCallWithArgs1 { pub fn new<A: AmlBuilder>(name: &str, arg0: A) -> AmlCallWithArgs1 { let mut bytes = Vec::new(); bytes.extend(arg0.aml_bytes()); AmlCallWithArgs1 { name: name.to_string(), buf: bytes, } } } impl AmlCallWithArgs2 { pub fn new<A: AmlBuilder, B: AmlBuilder>(name: &str, arg0: A, arg1: B) -> AmlCallWithArgs2 { let mut bytes = Vec::new(); bytes.extend(arg0.aml_bytes()); bytes.extend(arg1.aml_bytes()); AmlCallWithArgs2 { name: name.to_string(), buf: bytes, } } } impl AmlCallWithArgs3 { pub fn new<A: AmlBuilder, B: AmlBuilder, C: AmlBuilder>( name: &str, arg0: A, arg1: B, arg2: C, ) -> AmlCallWithArgs3 { let mut bytes = Vec::new(); bytes.extend(arg0.aml_bytes()); bytes.extend(arg1.aml_bytes()); bytes.extend(arg2.aml_bytes()); AmlCallWithArgs3 { name: name.to_string(), buf: bytes, } } } impl AmlCallWithArgs4 { pub fn new<A: AmlBuilder, B: AmlBuilder, C: AmlBuilder, D: AmlBuilder>( name: &str, arg0: A, arg1: B, arg2: C, arg3: D, ) -> AmlCallWithArgs4 { let mut bytes = Vec::new(); bytes.extend(arg0.aml_bytes()); bytes.extend(arg1.aml_bytes()); bytes.extend(arg2.aml_bytes()); bytes.extend(arg3.aml_bytes()); AmlCallWithArgs4 { name: name.to_string(), buf: bytes, } } } impl AmlCallWithArgs5 { pub fn new<A: AmlBuilder, B: AmlBuilder, C: AmlBuilder, D: AmlBuilder, E: AmlBuilder>( name: &str, arg0: A, arg1: B, arg2: C, arg3: D, arg4: E, ) -> AmlCallWithArgs5 { let mut bytes = Vec::new(); bytes.extend(arg0.aml_bytes()); bytes.extend(arg1.aml_bytes()); bytes.extend(arg2.aml_bytes()); bytes.extend(arg3.aml_bytes()); bytes.extend(arg4.aml_bytes()); AmlCallWithArgs5 { name: name.to_string(), buf: bytes, } } } /// Macro that helps to define Aml Data structures that /// contains two arguments and one destination. macro_rules! ops_2args_dst_define { ($name:ident, $op:expr) => { pub struct $name { arg1: Vec<u8>, arg2: Vec<u8>, dst: Vec<u8>, } impl $name { pub fn new<A: AmlBuilder, B: AmlBuilder, D: AmlBuilder>(a1: A, a2: B, d: D) -> $name { $name { arg1: a1.aml_bytes(), arg2: a2.aml_bytes(), dst: d.aml_bytes(), } } } impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![$op]; bytes.extend(self.arg1.clone()); bytes.extend(self.arg2.clone()); bytes.extend(self.dst.clone()); bytes } } }; } ops_2args_dst_define!(AmlAdd, 0x72); ops_2args_dst_define!(AmlSubtract, 0x74); // Concatenate two strings, integers or buffers and store the result in Destination. ops_2args_dst_define!(AmlConcat, 0x73); ops_2args_dst_define!(AmlAnd, 0x7B); ops_2args_dst_define!(AmlOr, 0x7D); ops_2args_dst_define!(AmlShiftLeft, 0x79); ops_2args_dst_define!(AmlShiftRight, 0x7A); // Indexed Reference to member object. // The first argument refers to the source that can be Buffer, Package or String. // The second argument refers to the index. // The corresponding reference to the field is stored in Destination. ops_2args_dst_define!(AmlIndex, 0x88); /// Macro that helps to define Aml Data structures that contains one argument. macro_rules! ops_1arg_define { ($name:ident, $op:expr) => { pub struct $name { arg: Vec<u8>, } impl $name { pub fn new<T: AmlBuilder>(a1: T) -> $name { $name { arg: a1.aml_bytes(), } } } impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![$op]; bytes.extend(self.arg.clone()); bytes } } }; } // Increment/Decrement: Arg1 is an Integer. ops_1arg_define!(AmlIncrement, 0x75); ops_1arg_define!(AmlDecrement, 0x76); // Logical Not: Arg1 is an Integer. ops_1arg_define!(AmlLNot, 0x92); // SizeOf: Arg1 must be an Buffer, String or Package. ops_1arg_define!(AmlSizeOf, 0x87); // DeRefOf is necessary in the first operand of the Store operator // in order to get the actual object, rather than just a reference to the object. // If DeRefOf were not used, then `Index` would contain an object reference to the element. // Below is an example: // // ``` // let buffer = AmlBuffer::new(vec![0x1, 0x2, 0x3]); // let arg1 = AmlDeRefOf::new(AmlIndex::new(buffer, 1)); // let store = AmlStore::new(arg1, Local(0)); // ``` ops_1arg_define!(AmlDeRefOf, 0x83); /// Macro that helps to define Aml Data structures that contains two arguments. macro_rules! ops_2arg_define { ($name:ident, $op:expr) => { pub struct $name { arg1: Vec<u8>, arg2: Vec<u8>, } impl $name { pub fn new<A: AmlBuilder, B: AmlBuilder>(a1: A, a2: B) -> $name { $name { arg1: a1.aml_bytes(), arg2: a2.aml_bytes(), } } } impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![$op]; bytes.extend(self.arg1.clone()); bytes.extend(self.arg2.clone()); bytes } } }; } // Store the source value(Arg1) to the destination(Arg2). ops_2arg_define!(AmlStore, 0x70); // Notify the Object(Arg1) that the NotificationValue(Arg2) has occurred. ops_2arg_define!(AmlNotify, 0x86); // Logical Equal/Greater/Less/And/Or: // Arg1 and Arg2 must be the same type. Integer, String and Buffer are supported. // Return True or False; ops_2arg_define!(AmlEqual, 0x93); ops_2arg_define!(AmlLGreater, 0x94); ops_2arg_define!(AmlLLess, 0x95); ops_2arg_define!(AmlLAnd, 0x90); ops_2arg_define!(AmlLOr, 0x91); /// If scope pub struct AmlIf { /// Predicate and the operations in the if-scope is converted to byte stream. buf: Vec<u8>, } impl AmlIf { pub fn new<T: AmlBuilder>(predicate: T) -> AmlIf { AmlIf { buf: predicate.aml_bytes(), } } } impl AmlBuilder for AmlIf { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0xA0]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlIf { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// Else scope pub struct AmlElse { /// Predicate and the operations in the else-scope is converted to byte stream. buf: Vec<u8>, } impl AmlElse { #[allow(clippy::new_without_default)] pub fn new() -> AmlElse { AmlElse { buf: Vec::new() } } } impl AmlBuilder for AmlElse { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0xA1]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlElse { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// While scope pub struct AmlWhile { /// Predicate and the operations in the while-scope is converted to byte stream. buf: Vec<u8>, } impl AmlWhile { pub fn new<T: AmlBuilder>(predicate: T) -> AmlWhile { AmlWhile { buf: predicate.aml_bytes(), } } } impl AmlBuilder for AmlWhile { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0xA2]; bytes.extend(build_pkg_length(self.buf.len(), true)); bytes.extend(self.buf.clone()); bytes } } impl AmlScopeBuilder for AmlWhile { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// This struct represents a Mutex. pub struct AmlMutex { /// Name of the mutex. name: String, /// The synchronization level of the mutex. Default value is zero. sync_level: u8, } impl AmlMutex { pub fn new(name: &str, sync_level: u8) -> AmlMutex { if sync_level > 15 { panic!( "Supported sync level of mutex is 0~15, given {}", sync_level ); } AmlMutex { name: name.to_string(), sync_level, } } } impl AmlBuilder for AmlMutex { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x5B, 0x01]; bytes.extend(build_name_string(self.name.as_ref())); bytes.push(self.sync_level); bytes } } /// Acquire a mutex. Returns True if timeout occurs or can not acquire the mutex. pub struct AmlAcquire { /// The mutex object is converted to byte stream. mutex: Vec<u8>, /// If the mutex is owned by others, current thread suspends and waits for `timeout` **milliseconds** /// `timeout` being set as 0xFFFF indicates that there is no timeout and /// the acquire mutex operation will keeping waiting. time_out: u16, } impl AmlAcquire { pub fn new<T: AmlBuilder>(mtx: T, time_out: u16) -> AmlAcquire { AmlAcquire { mutex: mtx.aml_bytes(), time_out, } } } impl AmlBuilder for AmlAcquire { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x5B, 0x23]; bytes.extend(self.mutex.clone()); bytes.extend(self.time_out.as_bytes()); bytes } } /// Release the ownership of the mutex. pub struct AmlRelease { /// the mutex that has been acquired before. mutex: Vec<u8>, } impl AmlRelease { fn new<T: AmlBuilder>(mtx: T) -> AmlRelease { AmlRelease { mutex: mtx.aml_bytes(), } } } impl AmlBuilder for AmlRelease { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x5B, 0x27]; bytes.extend(self.mutex.clone()); bytes } } /// Create arbitrary-length field of Buffer. pub struct AmlCreateField { /// The name of this field. name: String, /// The source Buffer, which has been converted to bytes. src: Vec<u8>, /// the start index in the Buffer, which has been converted to bytes. /// `bit_index` has to be an Integer. bit_index: Vec<u8>, /// the length of this bit range, which has been converted to bytes. /// `bit_count` has to be an Integer and must not be zero. /// Note that the bit range (bit_index, bit_index + bit_count) must not exceed the bound of Buffer. bit_count: Vec<u8>, } impl AmlCreateField { pub fn new<S: AmlBuilder, T: AmlBuilder, C: AmlBuilder>( src: S, bit_index: T, bit_count: C, name: &str, ) -> AmlCreateField { AmlCreateField { name: name.to_string(), src: src.aml_bytes(), bit_index: bit_index.aml_bytes(), bit_count: bit_count.aml_bytes(), } } } impl AmlBuilder for AmlCreateField { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x5B, 0x13]; bytes.extend(self.src.clone()); bytes.extend(self.bit_index.clone()); bytes.extend(self.bit_count.clone()); bytes.extend(build_name_string(self.name.as_ref())); bytes } } /// Macro helps to define CreateWordField/CreateDWordField/CreateQWordField. macro_rules! create_word_field_define { ($name: ident, $op: expr) => { pub struct $name { name: String, src: Vec<u8>, bit_index: Vec<u8>, } impl $name { pub fn new<S: AmlBuilder, T: AmlBuilder>(src: S, bit_index: T, name: &str) -> $name { $name { name: name.to_string(), src: src.aml_bytes(), bit_index: bit_index.aml_bytes(), } } } impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![$op]; bytes.extend(self.src.clone()); bytes.extend(self.bit_index.clone()); bytes.extend(build_name_string(self.name.as_ref())); bytes } } }; } // As for the below operations, The length of field are fixed. create_word_field_define!(AmlCreateWordField, 0x8B); create_word_field_define!(AmlCreateDWordField, 0x8A); create_word_field_define!(AmlCreateQWordField, 0x8F); /// Resource Template are used to create resource descriptors. /// In its scope, multiple resource descriptors are listed to /// specify the resource owned by a device. pub struct AmlResTemplate { buf: Vec<u8>, } impl AmlResTemplate { #[allow(clippy::new_without_default)] pub fn new() -> AmlResTemplate { AmlResTemplate { buf: Vec::new() } } } impl AmlBuilder for AmlResTemplate { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = Vec::new(); // fill buffer and end tag let end_tag_len = 2_u64; bytes.extend(AmlInteger(self.buf.len() as u64 + end_tag_len).aml_bytes()); bytes.extend(self.buf.clone()); bytes.push(0x79); // end tag bytes.push(0x00); // fill prepend PkgLength let pkg_length = build_pkg_length(bytes.len(), true); pkg_length.iter().rev().for_each(|b| { bytes.insert(0, *b); }); // fill resource template opcode. bytes.insert(0, 0x11); bytes } } impl AmlScopeBuilder for AmlResTemplate { fn append_child<T: AmlBuilder>(&mut self, child: T) { self.buf.extend(child.aml_bytes()); } } /// The type of DMA cycle. #[derive(Copy, Clone)] pub enum AmlDmaType { /// ISA compatible. Compatibility = 0, /// EISA TypeA. TypeA = 1, /// EISA TypeB. TypeB = 2, /// EISA TypeF. TypeF = 3, } /// The size of DMA cycles that the device is capable of generating. #[derive(Copy, Clone)] pub enum AmlDmaTransSize { Size8 = 0, Size8_16 = 1, Size16 = 2, } #[derive(Copy, Clone)] pub struct AmlDmaResource { /// The type of DMA cycle dma_type: AmlDmaType, /// Whether the device can generate DMA bus master cycles. is_master: bool, /// The size of DMA cycle. trans_sz: AmlDmaTransSize, /// DMA channel used by the device, range 0~7. channel: u8, } impl AmlDmaResource { pub fn new( dma_type: AmlDmaType, is_master: bool, trans_sz: AmlDmaTransSize, channel: u8, ) -> AmlDmaResource { if channel > 7 { panic!("acpi: DMA channel exceeds range 0~7."); } AmlDmaResource { dma_type, is_master, trans_sz, channel, } } } impl AmlBuilder for AmlDmaResource { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x2A, 1 << self.channel]; let mut flags = (self.trans_sz as u8) | (self.dma_type as u8) << 5; if self.is_master { flags |= 1 << 2; // Bit-2 represents bus master } bytes.push(flags); bytes } } /// Decode type of IO resource. #[derive(Copy, Clone)] pub enum AmlIoDecode { /// 10-bit decode is used. Decode10 = 0, /// 16-bit decode is used. Decode16 = 1, } /// IO Resource descriptor. #[derive(Copy, Clone)] pub struct AmlIoResource { /// Decode type. decode: AmlIoDecode, /// The minimum acceptable start address. min_addr: u16, /// The maximum acceptable start address. max_addr: u16, /// The alignment granularity for the I/O address assigned. align: u8, /// The number of bytes in the I/O range. length: u8, } impl AmlIoResource { pub fn new( decode: AmlIoDecode, min_addr: u16, max_addr: u16, align: u8, length: u8, ) -> AmlIoResource { AmlIoResource { decode, min_addr, max_addr, align, length, } } } impl AmlBuilder for AmlIoResource { fn aml_bytes(&self) -> Vec<u8> { vec![ 0x47, self.decode as u8, (self.min_addr & 0xFF) as u8, (self.min_addr >> 8) as u8, (self.max_addr & 0xFF) as u8, (self.max_addr >> 8) as u8, self.align, self.length, ] } } /// Access status of memory resource. #[derive(Copy, Clone)] pub enum AmlReadAndWrite { /// Non-writeable (read-only) ReadOnly = 0, /// Writeable (read/write) ReadWrite = 1, } /// Memory resource within 32-bit address space. pub struct AmlMemory32Fixed { /// Access right. rw_access: AmlReadAndWrite, /// Start address. addr: u32, /// Range length. length: u32, } impl AmlMemory32Fixed { pub fn new(rw_access: AmlReadAndWrite, addr: u32, length: u32) -> AmlMemory32Fixed { AmlMemory32Fixed { rw_access, addr, length, } } } impl AmlBuilder for AmlMemory32Fixed { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x86, 0x09, 0x00, self.rw_access as u8]; bytes.extend(self.addr.as_bytes()); bytes.extend(self.length.as_bytes()); bytes } } /// Cacheable features. #[derive(Copy, Clone)] pub enum AmlCacheable { NonCacheable = 0, Cacheable = 1, WriteCombining = 2, Prefetchable = 3, } /// This enum determines how the IO resource are limited. #[allow(clippy::upper_case_acronyms)] #[derive(Copy, Clone)] pub enum AmlISARanges { /// Limited to valid ISA I/O ranges. ISAOnly = 1, /// Limited to valid non-ISA I/O ranges. NonISAOnly = 2, /// No limitation EntireRange = 3, } /// The type of resource. #[allow(clippy::upper_case_acronyms)] #[derive(Copy, Clone)] pub enum AmlResourceType { Memory = 0, IO = 1, BusNumber = 2, } /// Decode type of bus number range. #[derive(Copy, Clone)] pub enum AmlAddressSpaceDecode { /// Positive decode. Positive = 0, /// Subtract decode. Subtract = 1, } /// Macro that helps to define Word/DWord/QWord address space descriptors. macro_rules! space_desc_define { ($name: ident, $op:expr, $ml: expr, $ty: tt) => { pub struct $name { res_type: AmlResourceType, decode: AmlAddressSpaceDecode, type_flags: u8, granularity: $ty, addr_min: $ty, addr_max: $ty, addr_trans: $ty, length: $ty, } impl AmlBuilder for $name { fn aml_bytes(&self) -> Vec<u8> { // min_addr and max_addr are fixed let flags = (self.decode as u8) << 1 | 1 << 2 | 1 << 3; let mut bytes = vec![$op, $ml, 0x00, self.res_type as u8, flags, self.type_flags]; bytes.extend(self.granularity.as_bytes()); bytes.extend(self.addr_min.as_bytes()); bytes.extend(self.addr_max.as_bytes()); bytes.extend(self.addr_trans.as_bytes()); bytes.extend(self.length.as_bytes()); bytes } } }; } /// Macro that helps to define DWordDesc/QWordDesc's construct function. macro_rules! struct_new_memory_define { ($name: ident, $ty: tt) => { impl $name { #[allow(clippy::too_many_arguments)] pub fn new_memory( decode: AmlAddressSpaceDecode, cache: AmlCacheable, rw: AmlReadAndWrite, gran: $ty, min: $ty, max: $ty, trans: $ty, len: $ty, ) -> $name { let type_flags = rw as u8 | (cache as u8) << 1; $name { res_type: AmlResourceType::Memory, decode, type_flags, granularity: gran, addr_min: min, addr_max: max, addr_trans: trans, length: len, } } } }; } /// Macro that helps to define WordDesc/DWordDesc/QWordDesc's construct function. macro_rules! struct_new_io_define { ($name: ident, $ty: tt) => { impl $name { #[allow(clippy::too_many_arguments)] pub fn new_io( decode: AmlAddressSpaceDecode, isa_ranges: AmlISARanges, gran: $ty, min: $ty, max: $ty, trans: $ty, len: $ty, ) -> $name { $name { res_type: AmlResourceType::IO, decode, type_flags: isa_ranges as u8, granularity: gran, addr_min: min, addr_max: max, addr_trans: trans, length: len, } } } }; } // Word address space descriptor, which can be used to describe IO and BusNumber. space_desc_define!(AmlWordDesc, 0x88, 0x0D, u16); // Define `AmlWordDesc::new_io()` function to construct WordIO-type resource. struct_new_io_define!(AmlWordDesc, u16); // Define `AmlWordDesc::new_bus_number()` function to construct WordBusNumber-type resource. impl AmlWordDesc { pub fn new_bus_number( decode: AmlAddressSpaceDecode, gran: u16, min: u16, max: u16, trans: u16, len: u16, ) -> AmlWordDesc { AmlWordDesc { res_type: AmlResourceType::BusNumber, decode, type_flags: 0, granularity: gran, addr_min: min, addr_max: max, addr_trans: trans, length: len, } } } // DWord address space descriptor, which can be used to describe IO and Memory. space_desc_define!(AmlDWordDesc, 0x87, 23, u32); // Define `AmlDWordDesc::new_io()` function to construct DWordIO-type resource. struct_new_io_define!(AmlDWordDesc, u32); // Define `AmlDWordDesc::new_memory()` function to construct DWordMemory-type resource. struct_new_memory_define!(AmlDWordDesc, u32); // QWord address space descriptor, which can be used to describe Memory. space_desc_define!(AmlQWordDesc, 0x8A, 0x2B, u64); // Define `AmlQWordDesc::new_memory()` function to construct QWordMemory-type resource. struct_new_memory_define!(AmlQWordDesc, u64); /// Active-high, edge-triggered IRQ resource descriptor. pub struct AmlIrqNoFlags { /// Irq number. irq: u8, } impl AmlIrqNoFlags { pub fn new(irq: u8) -> AmlIrqNoFlags { if irq > 15 { panic!("acpi: Irq exceeds range 0~15."); } AmlIrqNoFlags { irq } } } impl AmlBuilder for AmlIrqNoFlags { fn aml_bytes(&self) -> Vec<u8> { let irq_mask = 1 << (self.irq as u16); vec![0x22, (irq_mask & 0xFF) as u8, (irq_mask >> 8) as u8] } } /// Flags that indicates whether device consume or produce the interrupt resource. #[derive(Copy, Clone)] pub enum AmlResourceUsage { /// Device will produce the interrupt for use by child device. Producer = 0, /// Device will consume the interrupt. Consumer = 1, } /// Flags that indicates how the interrupt been triggered. #[derive(Copy, Clone)] pub enum AmlEdgeLevel { /// Level triggered. Level = 0, /// Edge triggered. Edge = 1, } /// Flags that indicates the interrupt is actively high or low. #[derive(Copy, Clone)] pub enum AmlActiveLevel { /// Active-high. High = 0, /// Active-low. Low = 1, } /// Flags that indicates whether the interrupt can be shared with other device. #[derive(Copy, Clone)] pub enum AmlIntShare { /// Cannot be shared. Not Wake Capable. Exclusive = 0, /// Can share with other device. Not Wake Capable. Share = 1, /// Cannot be shared. Wake Capable. ExclusiveWake = 2, /// Can share with other device. Wake Capable. ShareWake = 3, } /// Extended interrupt descriptor. pub struct AmlExtendedInterrupt { /// Produce or consume the interrupt. usage: AmlResourceUsage, /// Trigger mode. int_mode: AmlEdgeLevel, /// Active level. int_polar: AmlActiveLevel, /// Can be share or not. share: AmlIntShare, /// interrupt list. irq_list: Vec<u32>, } impl AmlExtendedInterrupt { pub fn new( usage: AmlResourceUsage, mode: AmlEdgeLevel, polar: AmlActiveLevel, share: AmlIntShare, irq_list: Vec<u32>, ) -> AmlExtendedInterrupt { if irq_list.is_empty() { panic!("the list of irqs must not be empty."); } AmlExtendedInterrupt { usage, int_mode: mode, int_polar: polar, share, irq_list, } } } impl AmlBuilder for AmlExtendedInterrupt { fn aml_bytes(&self) -> Vec<u8> { let mut bytes = vec![0x89]; let header_len = 2_u16; let total_len = header_len + (self.irq_list.len() * std::mem::size_of::<u32>()) as u16; // the length is at least 6, for only one element in irq-list bytes.extend(total_len.as_bytes()); let flags = self.usage as u8 | (self.int_mode as u8) << 1 | (self.int_polar as u8) << 2 | (self.share as u8) << 3; bytes.push(flags); bytes.push(self.irq_list.len() as u8); self.irq_list .iter() .for_each(|irq| bytes.extend(irq.as_bytes())); bytes } } #[cfg(test)] mod test { use super::*; #[test] fn test_uuid() { let uuid = AmlToUuid::new("33DB4D5B-1FF7-401C-9657-7441C03DD766"); assert_eq!( uuid.aml_bytes(), &[ 0x11, 0x13, 0x0a, 0x10, 0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66 ] ); } #[test] fn test_package() { // Name (PKG1, Package(3){0x1234, "Hello world", INT1}) let mut pkg1 = AmlPackage::new(3); pkg1.append_child(AmlInteger(0x1234)); pkg1.append_child(AmlString("Hello world".to_string())); pkg1.append_child(AmlName("INT1".to_string())); let named_pkg1 = AmlNameDecl::new("PKG1", pkg1); let pkg1_bytes = vec![ 0x08, 0x50, 0x4B, 0x47, 0x31, 0x12, 0x16, 0x03, 0x0B, 0x34, 0x12, 0x0D, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x00, 0x49, 0x4E, 0x54, 0x31, ]; assert_eq!(named_pkg1.aml_bytes(), pkg1_bytes); // Name (PKG2, Package(){INT1, "Good bye"}) let mut pkg2 = AmlPackage::new(2); pkg2.append_child(AmlName("INT1".to_string())); pkg2.append_child(AmlString("Good bye".to_string())); let named_pkg2 = AmlNameDecl::new("PKG2", pkg2); let pkg2_bytes = vec![ 0x08, 0x50, 0x4B, 0x47, 0x32, 0x12, 0x10, 0x02, 0x49, 0x4E, 0x54, 0x31, 0x0D, 0x47, 0x6F, 0x6F, 0x64, 0x20, 0x62, 0x79, 0x65, 0x00, ]; assert_eq!(named_pkg2.aml_bytes(), pkg2_bytes); // Name (PKG3, Package(){ // "ASL is fun", // Package() {0xff, 0xfe, 0xfd}, // Buffer() {0x01, 0x02} // }) let mut pkg3 = AmlPackage::new(3); pkg3.append_child(AmlString("ASL is fun".to_string())); let mut pkg32 = AmlPackage::new(3); pkg32.append_child(AmlInteger(0xff)); pkg32.append_child(AmlInteger(0xfe)); pkg32.append_child(AmlInteger(0xfd)); pkg3.append_child(pkg32); let buffer = AmlBuffer(vec![0x01, 0x02]); pkg3.append_child(buffer); let named_pkg3 = AmlNameDecl::new("PKG3", pkg3); let pkg3_bytes = vec![ 0x08, 0x50, 0x4B, 0x47, 0x33, 0x12, 0x1D, 0x03, 0x0D, 0x41, 0x53, 0x4C, 0x20, 0x69, 0x73, 0x20, 0x66, 0x75, 0x6E, 0x00, 0x12, 0x08, 0x03, 0x0A, 0xFF, 0x0A, 0xFE, 0x0A, 0xFD, 0x11, 0x05, 0x0A, 0x02, 0x01, 0x02, ]; assert_eq!(named_pkg3.aml_bytes(), pkg3_bytes); } #[test] fn test_op_region() { // OperationRegion(OPR1, SystemMemory, 0x10000, 0x5) // Field (OPR1, ByteAcc, NoLock, WriteAsZeros) // { // FLD1, 8, // FLD2, 8, // Offset (3), //Start the next field unit at byte offset 3 // FLD3, 4, // FLD4, 12, // } let op_region = AmlOpRegion::new("OPR1", AmlAddressSpaceType::SystemMemory, 0x10000, 0x5); let target = vec![ 0x5B, 0x80, 0x4F, 0x50, 0x52, 0x31, 0x00, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x05, ]; assert_eq!(op_region.aml_bytes(), target); let mut field = AmlField::new( "OPR1", AmlFieldAccessType::Byte, AmlFieldLockRule::NoLock, AmlFieldUpdateRule::WriteAsZeros, ); let elem1 = AmlFieldUnit::new(Some("FLD1"), 8); let elem2 = AmlFieldUnit::new(Some("FLD2"), 8); let elem3 = AmlFieldUnit::new(None, 8); let elem4 = AmlFieldUnit::new(Some("FLD3"), 4); let elem5 = AmlFieldUnit::new(Some("FLD4"), 12); for e in vec![elem1, elem2, elem3, elem4, elem5] { field.append_child(e); } let target = vec![ 0x5B, 0x81, 0x1C, 0x4F, 0x50, 0x52, 0x31, 0x41, 0x46, 0x4C, 0x44, 0x31, 0x08, 0x46, 0x4C, 0x44, 0x32, 0x08, 0x00, 0x08, 0x46, 0x4C, 0x44, 0x33, 0x04, 0x46, 0x4C, 0x44, 0x34, 0x0C, ]; assert_eq!(field.aml_bytes(), target); } #[test] fn test_device() { // Device (PCI0) // { // Name (_HID, EisaId ("PNP0A03")) // } let mut device = AmlDevice::new("PCI0"); let hid = AmlNameDecl::new("_HID", AmlEisaId::new("PNP0A03")); device.append_child(hid); let bytes = device.aml_bytes(); let target = vec![ 0x5B, 0x82, 0x0F, 0x50, 0x43, 0x49, 0x30, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x03, ]; assert_eq!(bytes, target); } #[test] fn test_scope() { // Scope (_SB) { // Name (INT1, 0xABCD) // } let mut scope = AmlScope::new("_SB"); scope.append_child(AmlNameDecl::new("INT1", AmlInteger(0xABCD))); let target = vec![ 0x10, 0x0D, 0x5F, 0x53, 0x42, 0x5F, 0x08, 0x49, 0x4E, 0x54, 0x31, 0x0B, 0xCD, 0xAB, ]; assert_eq!(scope.aml_bytes(), target); } #[test] fn test_method() { // Scope(\_SB.PCI4) { // OperationRegion(LED1, SystemIO, 0x10C0, 0x20) // Field(LED1, AnyAcc, NoLock, Preserve) // { // LED controls // S0LE, 1, // Slot 0 Ejection Progress LED // S0LF, 1, // Slot 0 Ejection Failure LED // S1LE, 1, // Slot 1 Ejection Progress LED // S1LF, 1, // Slot 1 Ejection Failure LED // S2LE, 1, // Slot 2 Ejection Progress LED // S2LF, 1, // Slot 2 Ejection Failure LED // S3LE, 1, // Slot 3 Ejection Progress LED // S3LF, 1 // Slot 3 Ejection Failure LED // } // Device(SLT3) { // hot plug device // Name(_ADR, 0x000C0003) // Method(_OST, 3, Serialized) { // If(LEqual(Arg0,Ones)) // Unspecified event // { // Store(Zero, Arg1) // } // Store(Zero, Arg2) // Turn off Ejection Progress LED // Store(One, Arg0) // Turn on Ejection Failure LED // } // } // } let mut scope1 = AmlScope::new("\\_SB.PCI4"); let op_region = AmlOpRegion::new("LED1", AmlAddressSpaceType::SystemIO, 0x10C0, 0x20); let mut field = AmlField::new( "LED1", AmlFieldAccessType::Any, AmlFieldLockRule::NoLock, AmlFieldUpdateRule::Preserve, ); let mut elems = Vec::new(); elems.push(AmlFieldUnit::new(Some("S0LE"), 1)); elems.push(AmlFieldUnit::new(Some("S0LF"), 1)); elems.push(AmlFieldUnit::new(Some("S1LE"), 1)); elems.push(AmlFieldUnit::new(Some("S1LF"), 1)); elems.push(AmlFieldUnit::new(Some("S2LE"), 1)); elems.push(AmlFieldUnit::new(Some("S2LF"), 1)); elems.push(AmlFieldUnit::new(Some("S3LE"), 1)); elems.push(AmlFieldUnit::new(Some("S3LF"), 1)); for e in elems { field.append_child(e); } let mut device = AmlDevice::new("SLT3"); let name1 = AmlNameDecl::new("_ADR", AmlInteger(0x000C0003)); let mut method1 = AmlMethod::new("_OST", 3, true); let mut if_scope = AmlIf::new(AmlEqual::new(AmlArg(0), AmlOnes)); let store1 = AmlStore::new(AmlZero, AmlArg(1)); if_scope.append_child(store1); let store2 = AmlStore::new(AmlZero, AmlArg(2)); let store3 = AmlStore::new(AmlOne, AmlArg(0)); method1.append_child(if_scope); method1.append_child(store2); method1.append_child(store3); device.append_child(name1); device.append_child(method1); scope1.append_child(op_region); scope1.append_child(field); scope1.append_child(device); let scope1_bytes = vec![ 0x10, 0x4E, 0x06, 0x5C, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x34, 0x5B, 0x80, 0x4C, 0x45, 0x44, 0x31, 0x01, 0x0B, 0xC0, 0x10, 0x0A, 0x20, 0x5B, 0x81, 0x2E, 0x4C, 0x45, 0x44, 0x31, 0x00, 0x53, 0x30, 0x4C, 0x45, 0x01, 0x53, 0x30, 0x4C, 0x46, 0x01, 0x53, 0x31, 0x4C, 0x45, 0x01, 0x53, 0x31, 0x4C, 0x46, 0x01, 0x53, 0x32, 0x4C, 0x45, 0x01, 0x53, 0x32, 0x4C, 0x46, 0x01, 0x53, 0x33, 0x4C, 0x45, 0x01, 0x53, 0x33, 0x4C, 0x46, 0x01, 0x5B, 0x82, 0x24, 0x53, 0x4C, 0x54, 0x33, 0x08, 0x5F, 0x41, 0x44, 0x52, 0x0C, 0x03, 0x00, 0x0C, 0x00, 0x14, 0x14, 0x5F, 0x4F, 0x53, 0x54, 0x0B, 0xA0, 0x07, 0x93, 0x68, 0xFF, 0x70, 0x00, 0x69, 0x70, 0x00, 0x6A, 0x70, 0x01, 0x68, ]; assert_eq!(scope1.aml_bytes(), scope1_bytes); // Scope (\_GPE) // { // Method(_E13) // { // Store(One, \_SB.PCI4.S3LE) // Turn on ejection request LED // Notify(\_SB.PCI4.SLT3, 3) // Ejection request driven from GPE13 // } // } let mut scope2 = AmlScope::new("\\_GPE"); let mut method2 = AmlMethod::new("_E13", 0, false); let store4 = AmlStore::new(AmlOne, AmlName("\\_SB.PCI4.S3LE".to_string())); let notify = AmlNotify::new(AmlName("\\_SB.PCI4.SLT3".to_string()), AmlInteger(3)); method2.append_child(store4); method2.append_child(notify); scope2.append_child(method2); let scope2_bytes = vec![ 0x10, 0x30, 0x5C, 0x5F, 0x47, 0x50, 0x45, 0x14, 0x29, 0x5F, 0x45, 0x31, 0x33, 0x00, 0x70, 0x01, 0x5C, 0x2F, 0x03, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x34, 0x53, 0x33, 0x4C, 0x45, 0x86, 0x5C, 0x2F, 0x03, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x34, 0x53, 0x4C, 0x54, 0x33, 0x0A, 0x03, ]; assert_eq!(scope2.aml_bytes(), scope2_bytes); } #[test] fn test_arithmetic_ops() { // Method(INCR, 3) { // If (Arg1 == Arg2) { // Return // } // Local0 = Arg0 // While (Local0 < Arg1) { // Local0++; // } // Local0--; // Local0 += 2; // If (Local0 > Arg2) { // Local0 -= Arg2; // } // } let mut method1 = AmlMethod::new("INCR", 3, false); let mut if_scope1 = AmlIf::new(AmlEqual::new(AmlArg(1), AmlArg(2))); if_scope1.append_child(AmlReturn::new()); method1.append_child(if_scope1); let store1 = AmlStore::new(AmlArg(0), AmlLocal(0).clone()); method1.append_child(store1); let mut while_scope = AmlWhile::new(AmlLLess::new(AmlLocal(0), AmlArg(1))); while_scope.append_child(AmlIncrement::new(AmlLocal(0))); method1.append_child(while_scope); method1.append_child(AmlDecrement::new(AmlLocal(0))); method1.append_child(AmlAdd::new(AmlLocal(0), AmlInteger(2), AmlLocal(0))); let mut if_scope2 = AmlIf::new(AmlLGreater::new(AmlLocal(0), AmlArg(2))); if_scope2.append_child(AmlSubtract::new(AmlLocal(0), AmlArg(2), AmlLocal(0))); method1.append_child(if_scope2); let method1_bytes = vec![ 0x14, 0x27, 0x49, 0x4E, 0x43, 0x52, 0x03, 0xA0, 0x06, 0x93, 0x69, 0x6A, 0xA4, 0x00, 0x70, 0x68, 0x60, 0xA2, 0x06, 0x95, 0x60, 0x69, 0x75, 0x60, 0x76, 0x60, 0x72, 0x60, 0x0A, 0x02, 0x60, 0xA0, 0x08, 0x94, 0x60, 0x6A, 0x74, 0x60, 0x6A, 0x60, ]; assert_eq!(method1.aml_bytes(), method1_bytes); // Method(MTD2, 1) { // Local0 = SizeOf(Arg0) // Name (PKG1, Package () { // 0x01, 0x03F8, 0x03FF // }) // // Name (PKG2, Package(2){0x1234, "Hello world"}) // // Store (DeRefOf (Index (PKG1, 5)), Local0) // Concatenate(PKG1, PKG2, Local1) // // Return(Local0) // } let mut method2 = AmlMethod::new("MTD2", 1, false); let store2 = AmlStore::new(AmlSizeOf::new(AmlArg(0)), AmlLocal(0)); method2.append_child(store2); let mut pkg1 = AmlPackage::new(3); vec![0x01, 0x03F8, 0x03FF].iter().for_each(|&x| { pkg1.append_child(AmlInteger(x as u64)); }); let named_pkg1 = AmlNameDecl::new("PKG1", pkg1); method2.append_child(named_pkg1); let mut pkg2 = AmlPackage::new(2); pkg2.append_child(AmlInteger(0x1234)); pkg2.append_child(AmlString("Hello world".to_string())); let named_pkg2 = AmlNameDecl::new("PKG2", pkg2); method2.append_child(named_pkg2); let pkg1_str = AmlName("PKG1".to_string()); let pkg2_str = AmlName("PKG2".to_string()); let store3 = AmlStore::new( AmlDeRefOf::new(AmlIndex::new(pkg1_str.clone(), AmlInteger(5), AmlZero)), AmlLocal(0), ); let concat = AmlConcat::new(pkg1_str, pkg2_str, AmlLocal(1)); method2.append_child(store3); method2.append_child(concat); let return_value = AmlReturn::with_value(AmlLocal(0)); method2.append_child(return_value); let method2_bytes = vec![ 0x14, 0x49, 0x04, 0x4D, 0x54, 0x44, 0x32, 0x01, 0x70, 0x87, 0x68, 0x60, 0x08, 0x50, 0x4B, 0x47, 0x31, 0x12, 0x09, 0x03, 0x01, 0x0B, 0xF8, 0x03, 0x0B, 0xFF, 0x03, 0x08, 0x50, 0x4B, 0x47, 0x32, 0x12, 0x12, 0x02, 0x0B, 0x34, 0x12, 0x0D, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x00, 0x70, 0x83, 0x88, 0x50, 0x4B, 0x47, 0x31, 0x0A, 0x05, 0x00, 0x60, 0x73, 0x50, 0x4B, 0x47, 0x31, 0x50, 0x4B, 0x47, 0x32, 0x61, 0xA4, 0x60, ]; assert_eq!(method2.aml_bytes(), method2_bytes); } #[test] fn test_logical_ops() { // Method(MTD3, 1) { // Local0 = Arg0 // // Local1 = (Local0 << 3) | 0xFF // Local2 = (Local0 >> 3) & 0xFF // // If (Local1 && Local2) { // Return(2) // } // // if (Local1 || Local2) { // Return(1) // } // // Return(0) // } let mut method = AmlMethod::new("MTD3", 1, false); let store1 = AmlStore::new(AmlArg(0), AmlLocal(0)); method.append_child(store1); let shift_left = AmlShiftLeft::new(AmlLocal(0), AmlInteger(0x3), AmlZero); let store2 = AmlOr::new(shift_left, AmlInteger(0xFF), AmlLocal(1)); method.append_child(store2); let shift_right = AmlShiftRight::new(AmlLocal(0), AmlInteger(0x3), AmlZero); let store3 = AmlAnd::new(shift_right, AmlInteger(0xFF), AmlLocal(2)); method.append_child(store3); let mut if_scope1 = AmlIf::new(AmlLAnd::new(AmlLocal(1), AmlLocal(2))); if_scope1.append_child(AmlReturn::with_value(AmlInteger(2))); method.append_child(if_scope1); let mut if_scope2 = AmlIf::new(AmlLOr::new(AmlLocal(1), AmlLocal(2))); if_scope2.append_child(AmlReturn::with_value(AmlInteger(1))); method.append_child(if_scope2); method.append_child(AmlReturn::with_value(AmlInteger(0))); let method_bytes = vec![ 0x14, 0x2C, 0x4D, 0x54, 0x44, 0x33, 0x01, 0x70, 0x68, 0x60, 0x7D, 0x79, 0x60, 0x0A, 0x03, 0x00, 0x0A, 0xFF, 0x61, 0x7B, 0x7A, 0x60, 0x0A, 0x03, 0x00, 0x0A, 0xFF, 0x62, 0xA0, 0x07, 0x90, 0x61, 0x62, 0xA4, 0x0A, 0x02, 0xA0, 0x06, 0x91, 0x61, 0x62, 0xA4, 0x01, 0xA4, 0x00, ]; assert_eq!(method.aml_bytes(), method_bytes); } #[test] fn test_mutex() { // Device (PCI0) // { // Name (_HID, EisaId ("PNP0A03")) // Mutex (MTX1, 0) // // Method(MTD1, 2) { // Local0 = 0 // Local1 = Arg0 // while (Local0 < Local1) { // Acquire(MTX1, 0xFFFF); // Arg1 += 1 // Local0 += 1 // Release(MTX1) // } // } // } let mut device = AmlDevice::new("PCI0"); let hid = AmlNameDecl::new("_HID", AmlEisaId::new("PNP0A03")); device.append_child(hid); let mutex = AmlMutex::new("MTX1", 0); device.append_child(mutex); let mut method = AmlMethod::new("MTD1", 2, false); let store1 = AmlStore::new(AmlInteger(0x0), AmlLocal(0)); let store2 = AmlStore::new(AmlArg(0), AmlLocal(1)); method.append_child(store1); method.append_child(store2); let mut while1 = AmlWhile::new(AmlLLess::new(AmlLocal(0), AmlLocal(1))); while1.append_child(AmlAcquire::new(AmlName("MTX1".to_string()), 0xFFFF)); while1.append_child(AmlAdd::new(AmlArg(1), AmlInteger(1), AmlArg(1))); while1.append_child(AmlAdd::new(AmlLocal(0), AmlInteger(1), AmlLocal(0))); while1.append_child(AmlRelease::new(AmlName("MTX1".to_string()))); method.append_child(while1); device.append_child(method); let mutex_bytes = vec![ 0x5B, 0x82, 0x3E, 0x50, 0x43, 0x49, 0x30, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x03, 0x5B, 0x01, 0x4D, 0x54, 0x58, 0x31, 0x00, 0x14, 0x27, 0x4D, 0x54, 0x44, 0x31, 0x02, 0x70, 0x00, 0x60, 0x70, 0x68, 0x61, 0xA2, 0x1A, 0x95, 0x60, 0x61, 0x5B, 0x23, 0x4D, 0x54, 0x58, 0x31, 0xFF, 0xFF, 0x72, 0x69, 0x01, 0x69, 0x72, 0x60, 0x01, 0x60, 0x5B, 0x27, 0x4D, 0x54, 0x58, 0x31, ]; assert_eq!(device.aml_bytes(), mutex_bytes); } #[test] fn test_create_field() { // Method(MTD1,1) // { // CreateDWordField (Arg0, 0, REVS) // CreateDWordField (Arg0, 4, SIZE) // CreateWordField (Arg0, 8, MINV) // CreateQWordField (Arg0, 10, MAXV) // CreateField (Arg0, 64, 8, TEMP) // // TEMP = MINV | MAXV // Concatenate (REVS, SIZE, Local0) // // Return(Local0) // } let mut method = AmlMethod::new("MTD1", 1, false); let revs = AmlCreateDWordField::new(AmlArg(0), AmlInteger(0), "REVS"); let size = AmlCreateDWordField::new(AmlArg(0), AmlInteger(4), "SIZE"); let minv = AmlCreateWordField::new(AmlArg(0), AmlInteger(8), "MINV"); let maxv = AmlCreateQWordField::new(AmlArg(0), AmlInteger(10), "MAXV"); let temp = AmlCreateField::new(AmlArg(0), AmlInteger(64), AmlInteger(8), "TEMP"); method.append_child(revs); method.append_child(size); method.append_child(minv); method.append_child(maxv); method.append_child(temp); let store = AmlOr::new( AmlName("MINV".to_string()), AmlName("MAXV".to_string()), AmlName("TEMP".to_string()), ); let concat = AmlConcat::new( AmlName("REVS".to_string()), AmlName("SIZE".to_string()), AmlLocal(0), ); method.append_child(store); method.append_child(concat); method.append_child(AmlReturn::with_value(AmlLocal(0))); let method_bytes = vec![ 0x14, 0x4A, 0x04, 0x4D, 0x54, 0x44, 0x31, 0x01, 0x8A, 0x68, 0x00, 0x52, 0x45, 0x56, 0x53, 0x8A, 0x68, 0x0A, 0x04, 0x53, 0x49, 0x5A, 0x45, 0x8B, 0x68, 0x0A, 0x08, 0x4D, 0x49, 0x4E, 0x56, 0x8F, 0x68, 0x0A, 0x0A, 0x4D, 0x41, 0x58, 0x56, 0x5B, 0x13, 0x68, 0x0A, 0x40, 0x0A, 0x08, 0x54, 0x45, 0x4D, 0x50, 0x7D, 0x4D, 0x49, 0x4E, 0x56, 0x4D, 0x41, 0x58, 0x56, 0x54, 0x45, 0x4D, 0x50, 0x73, 0x52, 0x45, 0x56, 0x53, 0x53, 0x49, 0x5A, 0x45, 0x60, 0xA4, 0x60, ]; assert_eq!(method.aml_bytes(), method_bytes); } #[test] fn test_res_template() { // ResourceTemplate(){ // IO(Decode16, 0x62, 0x62, 0, 1) // IO(Decode10, 0x66, 0x66, 0, 1) // DMA(Compatibility, BusMaster, Transfer8_16) {0x3} // Memory32Fixed(ReadOnly, 0xfed00000, 0x400) // } let io_res1 = AmlIoResource::new(AmlIoDecode::Decode16, 0x62, 0x62, 0, 0x1); let io_res2 = AmlIoResource::new(AmlIoDecode::Decode10, 0x66, 0x66, 0, 0x1); let dma_res = AmlDmaResource::new( AmlDmaType::Compatibility, true, AmlDmaTransSize::Size8_16, 0x3, ); let fixed_mem32 = AmlMemory32Fixed::new(AmlReadAndWrite::ReadOnly, 0xfed00000, 0x400); let mut resource = AmlResTemplate::new(); resource.append_child(io_res1); resource.append_child(io_res2); resource.append_child(dma_res); resource.append_child(fixed_mem32); let target = vec![ 0x11, 0x24, 0x0A, 0x21, 0x47, 0x01, 0x62, 0x00, 0x62, 0x00, 0x00, 0x01, 0x47, 0x00, 0x66, 0x00, 0x66, 0x00, 0x00, 0x01, 0x2A, 0x08, 0x05, 0x86, 0x09, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xFE, 0x00, 0x04, 0x00, 0x00, 0x79, 0x00, ]; assert_eq!(resource.aml_bytes(), target); } #[test] fn test_interrupt() { // ResourceTemplate () // { // Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) {41} // Interrupt(ResourceConsumer, Edge, ActiveHigh, Shared) {42} // Interrupt(ResourceProducer, Level, ActiveHigh, ExclusiveAndWake) {43} // IRQNoFlags(INT4) {7} // } let mut resource = AmlResTemplate::new(); let int1 = AmlExtendedInterrupt::new( AmlResourceUsage::Consumer, AmlEdgeLevel::Level, AmlActiveLevel::High, AmlIntShare::Exclusive, vec![41], ); let int2 = AmlExtendedInterrupt::new( AmlResourceUsage::Consumer, AmlEdgeLevel::Edge, AmlActiveLevel::High, AmlIntShare::Share, vec![42], ); let int3 = AmlExtendedInterrupt::new( AmlResourceUsage::Producer, AmlEdgeLevel::Level, AmlActiveLevel::High, AmlIntShare::ExclusiveWake, vec![43], ); let int4 = AmlIrqNoFlags::new(7); resource.append_child(int1); resource.append_child(int2); resource.append_child(int3); resource.append_child(int4); let res_bytes = vec![ 0x11, 0x23, 0x0A, 0x20, 0x89, 0x06, 0x00, 0x01, 0x01, 0x29, 0x00, 0x00, 0x00, 0x89, 0x06, 0x00, 0x0B, 0x01, 0x2A, 0x00, 0x00, 0x00, 0x89, 0x06, 0x00, 0x10, 0x01, 0x2B, 0x00, 0x00, 0x00, 0x22, 0x80, 0x00, 0x79, 0x00, ]; assert_eq!(resource.aml_bytes(), res_bytes); } #[test] fn test_res_template_64() { // ResourceTemplate(){ // QWordMemory(ResourceConsumer, SubDecode, MinFixed, MaxFixed, // WriteCombining, ReadOnly, 0x0, 0x0, 0x7FFFFFFFFF, 0x0, 0x8000000000) // DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, // Prefetchable, ReadWrite, 0x0, 0x0, 0xFFFFFFFE, 0x0, 0xFFFFFFFF) // WordBusNumber(ResourceProducer, MinFixed, MaxFixed, PosDecode, // 0x0, 0x0, 0xFFFC, 0xAAAA, 0xFFFD) // DWordIO(ResourceConsumer, MinFixed, MaxFixed, SubDecode, // EntireRange, 0x0, 0x0, 0xFFFF, 0x3eff0000, 0x00010000) // WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, // EntireRange, 0x0, 0x0, 0x0CF7, 0x0, 0x0CF8) // } let q_mem = AmlQWordDesc::new_memory( AmlAddressSpaceDecode::Subtract, AmlCacheable::WriteCombining, AmlReadAndWrite::ReadOnly, 0x0, 0x0, 0x7FFFFFFFFF, 0x0, 0x8000000000, ); let d_mem = AmlDWordDesc::new_memory( AmlAddressSpaceDecode::Positive, AmlCacheable::Prefetchable, AmlReadAndWrite::ReadWrite, 0x0, 0x0, 0xFFFFFFFE, 0x0, 0xFFFFFFFF, ); let bus_num = AmlWordDesc::new_bus_number( AmlAddressSpaceDecode::Positive, 0x0, 0x0, 0xFFFC, 0xAAAA, 0xFFFD, ); let d_io = AmlDWordDesc::new_io( AmlAddressSpaceDecode::Subtract, AmlISARanges::EntireRange, 0x0, 0x0, 0xFFFF, 0x3eff0000, 0x00010000, ); let w_io = AmlWordDesc::new_io( AmlAddressSpaceDecode::Positive, AmlISARanges::EntireRange, 0x0, 0x0, 0x0CF7, 0x0, 0x0CF8, ); let mut resource = AmlResTemplate::new(); resource.append_child(q_mem); resource.append_child(d_mem); resource.append_child(bus_num); resource.append_child(d_io); resource.append_child(w_io); let target = vec![ 0x11, 0x48, 0x08, 0x0A, 0x84, 0x8A, 0x2B, 0x00, 0x00, 0x0E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x0D, 0x00, 0x02, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xAA, 0xAA, 0xFD, 0xFF, 0x87, 0x17, 0x00, 0x01, 0x0E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3E, 0x00, 0x00, 0x01, 0x00, 0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xF8, 0x0C, 0x79, 0x00, ]; assert_eq!(resource.aml_bytes(), target); } }