diff --git a/src/aml/mod.rs b/src/aml/mod.rs index 694569fa..9ad9c514 100644 --- a/src/aml/mod.rs +++ b/src/aml/mod.rs @@ -839,19 +839,17 @@ where } Opcode::DerefOf => { extract_args!(op => [Argument::Object(object)]); - let result = match **object { - Object::Reference { kind: _, inner: _ } => object.clone().unwrap_reference(), - Object::String(_) => { - let path = AmlName::from_str(&object.as_string().unwrap())?; + let object = object.clone().unwrap_reference(); + let result = match &*object { + Object::BufferField { .. } => object.read_buffer_field(self.integer_size)?.wrap(), + Object::FieldUnit(field) => self.do_field_read(field)?, + Object::String(path) => { + let path = AmlName::from_str(path)?; let (_, object) = self.namespace.lock().search(&path, &context.current_scope)?; object.clone() } - _ => { - return Err(AmlError::ObjectNotOfExpectedType { - expected: ObjectType::Reference, - got: object.typ(), - }); - } + Object::Reference { .. } => unreachable!(), + _ => object, }; context.contribute_arg(Argument::Object(result)); context.retire_op(op); @@ -1675,7 +1673,7 @@ where Opcode::FindSetLeftBit | Opcode::FindSetRightBit => { context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) } - Opcode::DerefOf => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg])), + Opcode::DerefOf => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), Opcode::ConcatRes => context.start(OpInFlight::new( opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target], diff --git a/src/aml/object.rs b/src/aml/object.rs index 7d78447a..780d8cda 100644 --- a/src/aml/object.rs +++ b/src/aml/object.rs @@ -261,10 +261,16 @@ impl Object { pub fn read_buffer_field(&self, integer_size: IntegerSize) -> Result { if let Self::BufferField { buffer, offset, length } = self { - let buffer = match **buffer { - Object::Buffer(ref buffer) => buffer.as_slice(), - Object::String(ref string) => string.as_bytes(), - _ => panic!(), + let buffer = buffer.clone().unwrap_transparent_reference(); + let buffer = match &*buffer { + Object::Buffer(buffer) => buffer.as_slice(), + Object::String(string) => string.as_bytes(), + typ => { + return Err(AmlError::InvalidOperationOnObject { + op: Operation::ReadBufferField, + typ: typ.typ(), + }); + } }; if *length <= integer_size as usize { let mut dst = [0u8; 8]; @@ -283,12 +289,18 @@ impl Object { pub fn write_buffer_field(&mut self, value: &[u8], token: &ObjectToken) -> Result<(), AmlError> { // TODO: bounds check the buffer first to avoid panicking if let Self::BufferField { buffer, offset, length } = self { + let buffer = buffer.clone().unwrap_transparent_reference(); let buffer = match unsafe { buffer.gain_mut(token) } { Object::Buffer(buffer) => buffer.as_mut_slice(), // XXX: this unfortunately requires us to trust AML to keep the string as valid // UTF8... maybe there is a better way? Object::String(string) => unsafe { string.as_bytes_mut() }, - _ => panic!(), + typ => { + return Err(AmlError::InvalidOperationOnObject { + op: Operation::WriteBufferField, + typ: typ.typ(), + }); + } }; copy_bits(value, 0, buffer, *offset, *length); Ok(()) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index 5a85651e..0e40e1dd 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -14,6 +14,30 @@ pub enum Resource { Dma(DMADescriptor), } +const UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[ + (0x01, "24-bit Memory Range Descriptor"), + (0x02, "Generic Register Descriptor"), + (0x03, "0x03 Reserved"), + (0x04, "Vendor-defined Descriptor"), + (0x05, "32-bit Memory Range Descriptor"), + (0x0b, "Extended Address Space Descriptor"), + (0x0c, "GPIO Connection Descriptor"), + (0x0d, "Pin Function Descriptor"), + (0x0e, "GenericSerialBus Connection Descriptor"), + (0x0f, "Pin Configuration Descriptor"), + (0x10, "Pin Group Descriptor"), + (0x11, "Pin Group Function Descriptor"), + (0x12, "Pin Group Configuration Descriptor"), +]; + +const UNSUPPORTED_SMALL_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[ + (0x06, "Start Dependent Functions Descriptor"), + (0x07, "End Dependent Functions Descriptor"), + (0x09, "Fixed Location IO Port Descriptor"), + (0x0a, "Fixed DMA Descriptor"), + (0x0e, "Vendor Defined Descriptor"), +]; + /// Parse a `ResourceDescriptor` buffer into a list of resources. pub fn resource_descriptor_list(descriptor: WrappedObject) -> Result, AmlError> { if let Object::Buffer(ref bytes) = *descriptor { @@ -25,10 +49,8 @@ pub fn resource_descriptor_list(descriptor: WrappedObject) -> Result Result Option<&'static str> { + descriptors.iter().find_map(|(typ, name)| (*typ == descriptor_type).then_some(*name)) +} + +fn skip_unsupported_resource<'a>( + size: &str, + descriptor_type: u8, + descriptor_name: &str, + remaining_bytes: &'a [u8], +) -> Result<(Option, &'a [u8]), AmlError> { + log::warn!("skipping unsupported {size} resource descriptor type {descriptor_type:#x}: {descriptor_name}"); + Ok((None, remaining_bytes)) +} + fn resource_descriptor(bytes: &[u8]) -> Result<(Option, &[u8]), AmlError> { /* * If bit 7 of Byte 0 is set, it's a large descriptor. If not, it's a small descriptor. @@ -73,27 +109,19 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option, &[u8]), AmlErr let (descriptor_bytes, remaining_bytes) = bytes.split_at(length + 3); let descriptor = match descriptor_type { - 0x01 => unimplemented!("24-bit Memory Range Descriptor"), - 0x02 => unimplemented!("Generic Register Descriptor"), - 0x03 => unimplemented!("0x03 Reserved"), - 0x04 => unimplemented!("Vendor-defined Descriptor"), - 0x05 => unimplemented!("32-bit Memory Range Descriptor"), 0x06 => fixed_memory_descriptor(descriptor_bytes), 0x07 => address_space_descriptor::(descriptor_bytes), 0x08 => address_space_descriptor::(descriptor_bytes), 0x09 => extended_interrupt_descriptor(descriptor_bytes), 0x0a => address_space_descriptor::(descriptor_bytes), - 0x0b => unimplemented!("Extended Address Space Descriptor"), - 0x0c => unimplemented!("GPIO Connection Descriptor"), - 0x0d => unimplemented!("Pin Function Descriptor"), - 0x0e => unimplemented!("GenericSerialBus Connection Descriptor"), - 0x0f => unimplemented!("Pin Configuration Descriptor"), - 0x10 => unimplemented!("Pin Group Descriptor"), - 0x11 => unimplemented!("Pin Group Function Descriptor"), - 0x12 => unimplemented!("Pin Group Configuration Descriptor"), 0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor), 0x80..=0xff => unreachable!(), + _ => { + let descriptor_name = + unsupported_resource_descriptor_name(UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS, descriptor_type).unwrap(); + return skip_unsupported_resource("large", descriptor_type, descriptor_name, remaining_bytes); + } }?; Ok((Some(descriptor), remaining_bytes)) @@ -127,15 +155,15 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option, &[u8]), AmlErr 0x00..=0x03 => Err(AmlError::InvalidResourceDescriptor), 0x04 => irq_format_descriptor(descriptor_bytes), 0x05 => dma_format_descriptor(descriptor_bytes), - 0x06 => unimplemented!("Start Dependent Functions Descriptor"), - 0x07 => unimplemented!("End Dependent Functions Descriptor"), 0x08 => io_port_descriptor(descriptor_bytes), - 0x09 => unimplemented!("Fixed Location IO Port Descriptor"), - 0x0A => unimplemented!("Fixed DMA Descriptor"), 0x0B..=0x0D => Err(AmlError::InvalidResourceDescriptor), - 0x0E => unimplemented!("Vendor Defined Descriptor"), 0x0F => return Ok((None, &[])), 0x10..=0xFF => unreachable!(), + _ => { + let descriptor_name = + unsupported_resource_descriptor_name(UNSUPPORTED_SMALL_RESOURCE_DESCRIPTORS, descriptor_type).unwrap(); + return skip_unsupported_resource("small", descriptor_type, descriptor_name, remaining_bytes); + } }?; Ok((Some(descriptor), remaining_bytes)) @@ -760,6 +788,31 @@ mod tests { ); } + #[test] + fn skips_unsupported_large_resource_descriptors() { + let bytes: Vec = [ + // GenericSerialBus descriptor with one byte of payload. + 0x8e, 0x01, 0x00, 0x00, + // Memory32Fixed(ReadWrite, 0x1000, 0x1000) + 0x86, 0x09, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + // End tag. + 0x79, 0x00, + ] + .to_vec(); + + let value = Object::Buffer(bytes).wrap(); + let resources = resource_descriptor_list(value).unwrap(); + + assert_eq!( + resources, + Vec::from([Resource::MemoryRange(MemoryRangeDescriptor::FixedLocation { + is_writable: true, + base_address: 0x1000, + range_length: 0x1000, + })]) + ); + } + #[test] fn test_fdc_crs() { let bytes: Vec = [ diff --git a/tests/de_ref_of.rs b/tests/de_ref_of.rs index 0bd0a576..9f5267e4 100644 --- a/tests/de_ref_of.rs +++ b/tests/de_ref_of.rs @@ -26,3 +26,72 @@ DefinitionBlock ("", "SSDT", 2, "RSACPI", "DerefOf", 0x00000002) { let handler = NullHandler {}; test_infra::run_aml_test(AML, handler); } + +#[test] +fn test_deref_of_direct_buffer_field() { + const AML: &str = r#" +DefinitionBlock ("", "SSDT", 2, "RSACPI", "DerefOf", 0x00000002) { + Name (ADAT, Buffer (0x01) { 0xaa }) + CreateByteField (ADAT, 0x00, BF00) + + Method(MAIN, 0, NotSerialized) { + Return (DerefOf(BF00) - 0xaa) + } +} +"#; + + let handler = NullHandler {}; + test_infra::run_aml_test(AML, handler); +} + +#[test] +fn test_deref_of_local_buffer_field_after_store() { + const AML: &str = r#" +DefinitionBlock ("", "SSDT", 2, "RSACPI", "DerefOf", 0x00000002) { + Method(MAIN, 0, NotSerialized) { + Local0 = Buffer (0x01) { 0x00 } + CreateByteField (Local0, 0x00, BF00) + BF00 = 0xaa + Return (DerefOf(BF00) - 0xaa) + } +} +"#; + + let handler = NullHandler {}; + test_infra::run_aml_test(AML, handler); +} + +#[test] +fn test_deref_of_named_buffer_field_stores_value() { + const AML: &str = r#" +DefinitionBlock ("", "SSDT", 2, "RSACPI", "DerefOf", 0x00000002) { + Name (ADAT, Buffer (0x01) { 0xaa }) + CreateByteField (ADAT, 0x00, BF00) + + Method(MAIN, 0, NotSerialized) { + Local0 = DerefOf(BF00) + Return (Local0 - 0xaa) + } +} +"#; + + let handler = NullHandler {}; + test_infra::run_aml_test(AML, handler); +} + +#[test] +fn test_deref_of_named_string_path() { + const AML: &str = r#" +DefinitionBlock ("", "SSDT", 2, "RSACPI", "DerefOf", 0x00000002) { + Name (TGT, 0xaa) + Name (STR, "\\TGT") + + Method(MAIN, 0, NotSerialized) { + Return (DerefOf(STR) - 0xaa) + } +} +"#; + + let handler = NullHandler {}; + test_infra::run_aml_test(AML, handler); +} diff --git a/tests/operation_region.rs b/tests/operation_region.rs index d92e5fbf..1c95cf2a 100644 --- a/tests/operation_region.rs +++ b/tests/operation_region.rs @@ -1,4 +1,12 @@ -use aml_test_tools::handlers::std_test_handler::{Command, construct_std_handler, create_mutex, read_u8, write_pci_u8, write_u8, read_pci_u8}; +use aml_test_tools::handlers::std_test_handler::{ + Command, + construct_std_handler, + create_mutex, + read_pci_u8, + read_u8, + write_pci_u8, + write_u8, +}; use pci_types::PciAddress; mod test_infra; diff --git a/tests/test_infra/mod.rs b/tests/test_infra/mod.rs index 03bfd60e..36ea6093 100644 --- a/tests/test_infra/mod.rs +++ b/tests/test_infra/mod.rs @@ -1,5 +1,12 @@ use acpi::Handler; -use aml_test_tools::{RunTestResult, TestResult, handlers::logging_handler::LoggingHandler, new_interpreter, run_test_for_string, run_test_for_opcodes}; +use aml_test_tools::{ + RunTestResult, + TestResult, + handlers::logging_handler::LoggingHandler, + new_interpreter, + run_test_for_opcodes, + run_test_for_string, +}; // The following two functions are very similar in structure, but whilst there are only two of them // it's not worth adding complexity to make them DRY. diff --git a/tests/uacpi_examples.rs b/tests/uacpi_examples.rs index 4d76d669..4c1aa82d 100644 --- a/tests/uacpi_examples.rs +++ b/tests/uacpi_examples.rs @@ -9,7 +9,7 @@ mod test_infra; -use aml_test_tools::{handlers::null_handler::NullHandler}; +use aml_test_tools::handlers::null_handler::NullHandler; #[test] fn expressions_with_package() { diff --git a/tools/aml_test_tools/src/handlers/check_cmd_handler.rs b/tools/aml_test_tools/src/handlers/check_cmd_handler.rs index bb5c81f5..6d18a04a 100644 --- a/tools/aml_test_tools/src/handlers/check_cmd_handler.rs +++ b/tools/aml_test_tools/src/handlers/check_cmd_handler.rs @@ -271,8 +271,8 @@ where #[cfg(test)] mod test { - use crate::handlers::null_handler::NullHandler; use super::*; + use crate::handlers::null_handler::NullHandler; #[test] fn handler_basic_functions() { @@ -322,4 +322,4 @@ mod test { let handler = CheckCommandHandler::new(test_commands, NullHandler {}); handler.read_io_u8(2); } -} \ No newline at end of file +} diff --git a/tools/aml_test_tools/src/handlers/std_test_handler.rs b/tools/aml_test_tools/src/handlers/std_test_handler.rs index cfbdd64d..20859427 100644 --- a/tools/aml_test_tools/src/handlers/std_test_handler.rs +++ b/tools/aml_test_tools/src/handlers/std_test_handler.rs @@ -1,12 +1,12 @@ //! Rather than defining a [`Handler`], this module defines useful functions to streamline the most- //! used case of a [`CheckCommandHandler`] wrapping a [`ListedResponseHandler`]. -use pci_types::PciAddress; use crate::handlers::{ check_cmd_handler::{AcpiCommands as Check, CheckCommandHandler}, listed_response_handler::{AcpiCommands as Response, ListedResponseHandler}, }; use acpi::{Handle, Handler}; +use pci_types::PciAddress; /// Simplifies the construction of a standard test [`Handler`]. /// @@ -162,4 +162,4 @@ pub const fn acquire(mutex: Handle, timeout: u16) -> Command { /// A simple helper to generate a [`Command`] for [`Handler::release`]. pub const fn release(mutex: Handle) -> Command { (Check::Release(mutex), Response::Skip()) -} \ No newline at end of file +}