From 62310280e4dec11bd6b112e134d2418ffc321b7e Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Wed, 23 Aug 2023 12:30:44 +0300 Subject: [PATCH 1/3] aml: (WIP) basic `DefIndexField` implementation --- aml/src/lib.rs | 13 ++++- aml/src/opcode.rs | 1 + aml/src/term_object.rs | 97 +++++++++++++++++++++++++++++++++++- aml/src/value.rs | 110 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 216 insertions(+), 5 deletions(-) diff --git a/aml/src/lib.rs b/aml/src/lib.rs index d5bd791..bf5f6ec 100644 --- a/aml/src/lib.rs +++ b/aml/src/lib.rs @@ -335,8 +335,17 @@ impl AmlContext { match self.namespace.get(handle).unwrap().type_of() { AmlType::FieldUnit => { let mut field = self.namespace.get(handle).unwrap().clone(); - field.write_field(value, self)?; - field.read_field(self) + match field { + AmlValue::Field { .. } => { + field.write_field(value, self)?; + field.read_field(self) + } + AmlValue::IndexField { .. } => { + field.write_index_field(value, self)?; + field.read_index_field(self) + } + _ => unreachable!(), + } } AmlType::BufferField => { let mut buffer_field = self.namespace.get(handle).unwrap().clone(); diff --git a/aml/src/opcode.rs b/aml/src/opcode.rs index 3915f2b..2997707 100644 --- a/aml/src/opcode.rs +++ b/aml/src/opcode.rs @@ -43,6 +43,7 @@ pub const EXT_DEF_DEVICE_OP: u8 = 0x82; pub const EXT_DEF_PROCESSOR_OP: u8 = 0x83; pub const EXT_DEF_POWER_RES_OP: u8 = 0x84; pub const EXT_DEF_THERMAL_ZONE_OP: u8 = 0x85; +pub const EXT_DEF_INDEX_FIELD_OP: u8 = 0x86; /* * Statement opcodes diff --git a/aml/src/term_object.rs b/aml/src/term_object.rs index bdb2cff..e0f78f5 100644 --- a/aml/src/term_object.rs +++ b/aml/src/term_object.rs @@ -17,7 +17,7 @@ use crate::{ Parser, Propagate, }, - pkg_length::{pkg_length, region_pkg_length, PkgLength}, + pkg_length::{pkg_length, raw_pkg_length, region_pkg_length, PkgLength}, statement::statement_opcode, value::{AmlValue, FieldFlags, MethodCode, MethodFlags, RegionSpace}, AmlContext, @@ -110,7 +110,8 @@ where def_processor(), def_power_res(), def_thermal_zone(), - def_mutex() + def_mutex(), + def_index_field() ), ) } @@ -884,6 +885,98 @@ where .map(|((), result)| Ok(result)) } +fn index_field_element<'a, 'c>( + index_handle: AmlHandle, + data_handle: AmlHandle, + flags: FieldFlags, + current_offset: u64, +) -> impl Parser<'a, 'c, u64> +where + 'c: 'a, +{ + /* + * Reserved fields shouldn't actually be added to the namespace; they seem to show gaps in + * the operation region that aren't used for anything. + * + * NOTE: had to use raw_pkg_length() instead of pkg_length() here because pkg_length() performs + * checks against aml code slice, and in this specific context the check should be + * performed against the parent field's size + */ + let reserved_field = + opcode(opcode::RESERVED_FIELD).then(raw_pkg_length()).map(|((), length)| Ok(length as u64)); + + let named_field = name_seg().then(pkg_length()).map_with_context(move |(name_seg, length), context| { + try_with_context!( + context, + context.namespace.add_value_at_resolved_path( + AmlName::from_name_seg(name_seg), + &context.current_scope, + AmlValue::IndexField { + index: index_handle, + data: data_handle, + flags, + offset: current_offset, + length: length.raw_length as u64, + } + ) + ); + + (Ok(length.raw_length as u64), context) + }); + + choice!(reserved_field, named_field) +} + +pub fn def_index_field<'a, 'c>() -> impl Parser<'a, 'c, ()> +where + 'c: 'a, +{ + /* + * DefIndexField := IndexFieldOp PkgLength NameString NameString FieldFlags FieldList + * IndexFieldOp := ExtOpPrefix 0x86 + */ + // Declare a group of field that must be accessed by writing to an index + // register and then reading/writing from a data register. + ext_opcode(opcode::EXT_DEF_INDEX_FIELD_OP) + .then(comment_scope( + DebugVerbosity::Scopes, + "DefIndexField", + pkg_length() + .then(name_string()) + .then(name_string()) + .then(take()) + .map_with_context(|(((list_length, index_name), data_name), flags), context| { + let (_, index_handle) = + try_with_context!(context, context.namespace.search(&index_name, &context.current_scope)); + let (_, data_handle) = + try_with_context!(context, context.namespace.search(&data_name, &context.current_scope)); + + (Ok((list_length, index_handle, data_handle, flags)), context) + }) + .feed(|(list_length, index_handle, data_handle, flags)| { + move |mut input: &'a [u8], mut context: &'c mut AmlContext| -> ParseResult<'a, 'c, ()> { + let mut current_offset = 0; + while list_length.still_parsing(input) { + let (new_input, new_context, field_length) = index_field_element( + index_handle, + data_handle, + FieldFlags::new(flags), + current_offset, + ) + .parse(input, context)?; + + input = new_input; + context = new_context; + current_offset += field_length; + } + + Ok((input, context, ())) + } + }), + )) + .discard_result() +} + pub fn term_arg<'a, 'c>() -> impl Parser<'a, 'c, AmlValue> where 'c: 'a, diff --git a/aml/src/value.rs b/aml/src/value.rs index 99bc3be..0b52b78 100644 --- a/aml/src/value.rs +++ b/aml/src/value.rs @@ -194,6 +194,13 @@ pub enum AmlValue { offset: u64, length: u64, }, + IndexField { + index: AmlHandle, + data: AmlHandle, + flags: FieldFlags, + offset: u64, + length: u64, + }, Device, Method { flags: MethodFlags, @@ -252,7 +259,7 @@ impl AmlValue { AmlValue::Integer(_) => AmlType::Integer, AmlValue::String(_) => AmlType::String, AmlValue::OpRegion { .. } => AmlType::OpRegion, - AmlValue::Field { .. } => AmlType::FieldUnit, + AmlValue::Field { .. } | AmlValue::IndexField { .. } => AmlType::FieldUnit, AmlValue::Device => AmlType::Device, AmlValue::Method { .. } => AmlType::Method, AmlValue::Buffer(_) => AmlType::Buffer, @@ -314,6 +321,8 @@ impl AmlValue { * `as_integer` on the result. */ AmlValue::Field { .. } => self.read_field(context)?.as_integer(context), + // TODO: IndexField cannot be accessed through an immutable &AmlContext + AmlValue::IndexField { .. } => todo!(), AmlValue::BufferField { .. } => self.read_buffer_field(context)?.as_integer(context), _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::Integer }), @@ -325,6 +334,8 @@ impl AmlValue { AmlValue::Buffer(ref bytes) => Ok(bytes.clone()), // TODO: implement conversion of String and Integer to Buffer AmlValue::Field { .. } => self.read_field(context)?.as_buffer(context), + // TODO: IndexField cannot be accessed through an immutable &AmlContext + AmlValue::IndexField { .. } => todo!(), AmlValue::BufferField { .. } => self.read_buffer_field(context)?.as_buffer(context), _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::Buffer }), } @@ -335,6 +346,8 @@ impl AmlValue { AmlValue::String(ref string) => Ok(string.clone()), // TODO: implement conversion of Buffer to String AmlValue::Field { .. } => self.read_field(context)?.as_string(context), + // TODO: IndexField cannot be accessed through an immutable &AmlContext + AmlValue::IndexField { .. } => todo!(), _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::String }), } } @@ -421,6 +434,101 @@ impl AmlValue { } } + /// Stores an IndexField's index (as specified by its offset) and returns the bit offset within + /// the Data part of the field + fn write_index( + &mut self, + index_flags: &FieldFlags, + offset: u64, + length: u64, + context: &mut AmlContext, + ) -> Result { + let index_align = match index_flags.access_type()? { + FieldAccessType::Any => 8, + FieldAccessType::Byte => 8, + FieldAccessType::Word => 16, + FieldAccessType::DWord => 32, + FieldAccessType::QWord => 64, + FieldAccessType::Buffer => 8, + }; + + // Value to write to the Index part of the field + let length = length as usize; + let index_value = (offset / 8) & !((index_align >> 3) - 1); + let bit_offset = (offset - index_value * 8) as usize; + + // TODO handle cases when access_type/offset/length combinations lead to crossing of index + // boundary + if (bit_offset + length - 1) / index_align as usize != 0 { + todo!( + "IndexField access crosses the index boundary (range: {:#x?}, access type: {} bits)", + bit_offset..(bit_offset + length), + index_align + ); + } + + // Write the desired index + // NOTE not sure if the spec says the index field can only be an integer one, but I can't + // think of any reason for it to be anything else + self.write_field(AmlValue::Integer(index_value), context)?; + + Ok(bit_offset) + } + + /// Reads from an IndexField, returning either an `Integer` or a `Buffer` depending on the + /// field size + pub fn read_index_field(&self, context: &mut AmlContext) -> Result { + let AmlValue::IndexField { index, data, flags, offset, length } = self else { + return Err(AmlError::IncompatibleValueConversion { + current: self.type_of(), + target: AmlType::FieldUnit, + }); + }; + + let mut index_field = context.namespace.get_mut(*index)?.clone(); + let data_field = context.namespace.get_mut(*data)?.clone(); + + // Write the Index part of the field + let bit_offset = index_field.write_index(flags, *offset, *length, context)?; + + // TODO buffer field accesses + + // Read the value of the Data field + let field_value = data_field.read_field(context)?.as_integer(context)?; + let value = field_value.get_bits(bit_offset..(bit_offset + *length as usize)); + + Ok(AmlValue::Integer(value)) + } + + pub fn write_index_field(&self, value: AmlValue, context: &mut AmlContext) -> Result<(), AmlError> { + let AmlValue::IndexField { index, data, flags, offset, length } = self else { + return Err(AmlError::IncompatibleValueConversion { + current: self.type_of(), + target: AmlType::FieldUnit, + }); + }; + + let mut index_field = context.namespace.get_mut(*index)?.clone(); + let mut data_field = context.namespace.get_mut(*data)?.clone(); + + // Write the Index part of the field + let bit_offset = index_field.write_index(flags, *offset, *length, context)?; + + // TODO handle field update rule properly + // TODO buffer field accesses + + // Read the old value of the Data field (to preserve bits we're not interested in) + let mut field_value = data_field.read_field(context)?.as_integer(context)?; + + // Modify the bits + field_value.set_bits(bit_offset..(bit_offset + *length as usize), value.as_integer(context)?); + + // Write the Data field back + data_field.write_field(AmlValue::Integer(field_value), context)?; + + Ok(()) + } + /// Reads from a field of an opregion, returning either a `AmlValue::Integer` or an `AmlValue::Buffer`, /// depending on the size of the field. pub fn read_field(&self, context: &AmlContext) -> Result { From 6c7f593be52069f54064898ba7a32b7f5549363e Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Wed, 23 Aug 2023 13:55:58 +0300 Subject: [PATCH 2/3] aml: handle IndexField fields crossing index boundary --- aml/src/value.rs | 101 +++++++++++++++++++++++------------------ tests/index_fields.asl | 28 ++++++++++++ 2 files changed, 85 insertions(+), 44 deletions(-) create mode 100644 tests/index_fields.asl diff --git a/aml/src/value.rs b/aml/src/value.rs index 0b52b78..5938558 100644 --- a/aml/src/value.rs +++ b/aml/src/value.rs @@ -434,15 +434,12 @@ impl AmlValue { } } - /// Stores an IndexField's index (as specified by its offset) and returns the bit offset within - /// the Data part of the field - fn write_index( - &mut self, + fn index_field_access Result<(), AmlError>>( index_flags: &FieldFlags, offset: u64, length: u64, - context: &mut AmlContext, - ) -> Result { + mut access: F, + ) -> Result<(), AmlError> { let index_align = match index_flags.access_type()? { FieldAccessType::Any => 8, FieldAccessType::Byte => 8, @@ -452,31 +449,37 @@ impl AmlValue { FieldAccessType::Buffer => 8, }; - // Value to write to the Index part of the field - let length = length as usize; - let index_value = (offset / 8) & !((index_align >> 3) - 1); - let bit_offset = (offset - index_value * 8) as usize; - - // TODO handle cases when access_type/offset/length combinations lead to crossing of index - // boundary - if (bit_offset + length - 1) / index_align as usize != 0 { - todo!( - "IndexField access crosses the index boundary (range: {:#x?}, access type: {} bits)", - bit_offset..(bit_offset + length), - index_align - ); - } + let mut length = length as usize; + let mut index = (offset / 8) & !((index_align >> 3) - 1); + + // Bit offset in the target Data field + let mut bit_offset = (offset - index * 8) as usize; + // Bit offset in the source value + let mut pos = 0; + + while length != 0 { + // Bit offset within a single value + let value_pos = bit_offset % index_align as usize; + // Number of bits until the end of the value + let value_limit = index_align as usize - value_pos; + // Number of bits to access + let len = cmp::min(length, value_limit); + + access(index, pos, value_pos, len)?; - // Write the desired index - // NOTE not sure if the spec says the index field can only be an integer one, but I can't - // think of any reason for it to be anything else - self.write_field(AmlValue::Integer(index_value), context)?; + // Advance the bit position + bit_offset += len; + pos += len; + length -= len; - Ok(bit_offset) + // Move to the next index + index += index_align >> 3; + } + + Ok(()) } - /// Reads from an IndexField, returning either an `Integer` or a `Buffer` depending on the - /// field size + /// Reads from an IndexField, returning either an `Integer` pub fn read_index_field(&self, context: &mut AmlContext) -> Result { let AmlValue::IndexField { index, data, flags, offset, length } = self else { return Err(AmlError::IncompatibleValueConversion { @@ -488,14 +491,22 @@ impl AmlValue { let mut index_field = context.namespace.get_mut(*index)?.clone(); let data_field = context.namespace.get_mut(*data)?.clone(); - // Write the Index part of the field - let bit_offset = index_field.write_index(flags, *offset, *length, context)?; - // TODO buffer field accesses + let mut value = 0u64; + + Self::index_field_access(flags, *offset, *length, |index, value_offset, field_offset, length| { + // Store the bit range index to the Index field + index_field.write_field(AmlValue::Integer(index), context)?; + + // Read the bit range from the Data field + let data = data_field.read_field(context)?.as_integer(context)?; + let bits = data.get_bits(field_offset..field_offset + length); - // Read the value of the Data field - let field_value = data_field.read_field(context)?.as_integer(context)?; - let value = field_value.get_bits(bit_offset..(bit_offset + *length as usize)); + // Copy the bit range to the value + value.set_bits(value_offset..value_offset + length, bits); + + Ok(()) + })?; Ok(AmlValue::Integer(value)) } @@ -511,22 +522,24 @@ impl AmlValue { let mut index_field = context.namespace.get_mut(*index)?.clone(); let mut data_field = context.namespace.get_mut(*data)?.clone(); - // Write the Index part of the field - let bit_offset = index_field.write_index(flags, *offset, *length, context)?; + let value = value.as_integer(context)?; - // TODO handle field update rule properly - // TODO buffer field accesses + Self::index_field_access(flags, *offset, *length, |index, value_offset, field_offset, length| { + // TODO handle the UpdateRule flag - // Read the old value of the Data field (to preserve bits we're not interested in) - let mut field_value = data_field.read_field(context)?.as_integer(context)?; + // Store the bit range index to the Index field + index_field.write_field(AmlValue::Integer(index), context)?; - // Modify the bits - field_value.set_bits(bit_offset..(bit_offset + *length as usize), value.as_integer(context)?); + // Extract the bits going to this specific part of the field + let bits = value.get_bits(value_offset..value_offset + length); - // Write the Data field back - data_field.write_field(AmlValue::Integer(field_value), context)?; + // Read/modify/store the data field + let mut data = data_field.read_field(context)?.as_integer(context)?; + data.set_bits(field_offset..field_offset + length, bits); + data_field.write_field(AmlValue::Integer(data), context)?; - Ok(()) + Ok(()) + }) } /// Reads from a field of an opregion, returning either a `AmlValue::Integer` or an `AmlValue::Buffer`, diff --git a/tests/index_fields.asl b/tests/index_fields.asl new file mode 100644 index 0000000..180470e --- /dev/null +++ b/tests/index_fields.asl @@ -0,0 +1,28 @@ +DefinitionBlock("index_fields.aml", "DSDT", 1, "RSACPI", "IDXFLD", 1) { + OperationRegion (GIO0, SystemIO, 0x125, 0x100) + + Field (GIO0, ByteAcc, NoLock, WriteAsZeros) { + IDX0, 8, + DAT0, 8, + IDX1, 8, + DAT1, 16 + } + + IndexField (IDX0, DAT0, ByteAcc, NoLock, Preserve) { + Offset(0x10), + , 7, // Skip to offset 0x10 bit 7 + REG0, 2, // Covers bit 7 in 0x10 and bit 0 in 0x11 + } + + IndexField (IDX1, DAT1, WordAcc, NoLock, Preserve) { + Offset(0x07), // Offset 0x06, bits 8.. + , 7, // Skip to offset 0x06, bit 15 + REG1, 2, // Covers bit 15 in 0x06 and bit 0 in 0x08 + } + + // Store 0b11 to REG0 (index 0x10, bit 0 + index 0x11, bit 1) + Store (3, REG0) + + // Store 0b11 to REG1 (index 0x06, bit 15 + index 0x08, bit 0) + Store (3, REG1) +} From e65ccf4ec79f54aa66d4b98bde63ea2f1f97ec99 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 3 Oct 2023 11:36:35 +0300 Subject: [PATCH 3/3] aml: Fix tests not building with IndexField --- aml/src/test_utils.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/aml/src/test_utils.rs b/aml/src/test_utils.rs index e061687..cda6d94 100644 --- a/aml/src/test_utils.rs +++ b/aml/src/test_utils.rs @@ -158,6 +158,18 @@ pub(crate) fn crudely_cmp_values(a: &AmlValue, b: &AmlValue) -> bool { } _ => false, }, + AmlValue::IndexField { index, data, flags, offset, length } => match b { + AmlValue::IndexField { + index: b_index, + data: b_data, + flags: b_flags, + offset: b_offset, + length: b_length, + } => { + index == b_index && data == b_data && flags == b_flags && offset == b_offset && length == b_length + } + _ => false, + }, AmlValue::Device => match b { AmlValue::Device => true, _ => false,