Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions src/aml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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],
Expand Down
22 changes: 17 additions & 5 deletions src/aml/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,16 @@ impl Object {

pub fn read_buffer_field(&self, integer_size: IntegerSize) -> Result<Object, AmlError> {
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];
Expand All @@ -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(())
Expand Down
95 changes: 74 additions & 21 deletions src/aml/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<Resource>, AmlError> {
if let Object::Buffer(ref bytes) = *descriptor {
Expand All @@ -25,10 +49,8 @@ pub fn resource_descriptor_list(descriptor: WrappedObject) -> Result<Vec<Resourc

if let Some(descriptor) = descriptor {
descriptors.push(descriptor);
bytes = remaining_bytes;
} else {
break;
}
bytes = remaining_bytes;
}

Ok(descriptors)
Expand All @@ -37,6 +59,20 @@ pub fn resource_descriptor_list(descriptor: WrappedObject) -> Result<Vec<Resourc
}
}

fn unsupported_resource_descriptor_name(descriptors: &[(u8, &'static str)], descriptor_type: u8) -> 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<Resource>, &'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<Resource>, &[u8]), AmlError> {
/*
* If bit 7 of Byte 0 is set, it's a large descriptor. If not, it's a small descriptor.
Expand Down Expand Up @@ -73,27 +109,19 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option<Resource>, &[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::<u32>(descriptor_bytes),
0x08 => address_space_descriptor::<u16>(descriptor_bytes),
0x09 => extended_interrupt_descriptor(descriptor_bytes),
0x0a => address_space_descriptor::<u64>(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))
Expand Down Expand Up @@ -127,15 +155,15 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option<Resource>, &[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))
Expand Down Expand Up @@ -760,6 +788,31 @@ mod tests {
);
}

#[test]
fn skips_unsupported_large_resource_descriptors() {
let bytes: Vec<u8> = [
// 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<u8> = [
Expand Down
69 changes: 69 additions & 0 deletions tests/de_ref_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
10 changes: 9 additions & 1 deletion tests/operation_region.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
9 changes: 8 additions & 1 deletion tests/test_infra/mod.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion tests/uacpi_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
4 changes: 2 additions & 2 deletions tools/aml_test_tools/src/handlers/check_cmd_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -322,4 +322,4 @@ mod test {
let handler = CheckCommandHandler::new(test_commands, NullHandler {});
handler.read_io_u8(2);
}
}
}
4 changes: 2 additions & 2 deletions tools/aml_test_tools/src/handlers/std_test_handler.rs
Original file line number Diff line number Diff line change
@@ -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`].
///
Expand Down Expand Up @@ -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())
}
}