Skip to content

Commit 6c7f593

Browse files
committed
aml: handle IndexField fields crossing index boundary
1 parent 6231028 commit 6c7f593

File tree

2 files changed

+85
-44
lines changed

2 files changed

+85
-44
lines changed

aml/src/value.rs

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -434,15 +434,12 @@ impl AmlValue {
434434
}
435435
}
436436

437-
/// Stores an IndexField's index (as specified by its offset) and returns the bit offset within
438-
/// the Data part of the field
439-
fn write_index(
440-
&mut self,
437+
fn index_field_access<F: FnMut(u64, usize, usize, usize) -> Result<(), AmlError>>(
441438
index_flags: &FieldFlags,
442439
offset: u64,
443440
length: u64,
444-
context: &mut AmlContext,
445-
) -> Result<usize, AmlError> {
441+
mut access: F,
442+
) -> Result<(), AmlError> {
446443
let index_align = match index_flags.access_type()? {
447444
FieldAccessType::Any => 8,
448445
FieldAccessType::Byte => 8,
@@ -452,31 +449,37 @@ impl AmlValue {
452449
FieldAccessType::Buffer => 8,
453450
};
454451

455-
// Value to write to the Index part of the field
456-
let length = length as usize;
457-
let index_value = (offset / 8) & !((index_align >> 3) - 1);
458-
let bit_offset = (offset - index_value * 8) as usize;
459-
460-
// TODO handle cases when access_type/offset/length combinations lead to crossing of index
461-
// boundary
462-
if (bit_offset + length - 1) / index_align as usize != 0 {
463-
todo!(
464-
"IndexField access crosses the index boundary (range: {:#x?}, access type: {} bits)",
465-
bit_offset..(bit_offset + length),
466-
index_align
467-
);
468-
}
452+
let mut length = length as usize;
453+
let mut index = (offset / 8) & !((index_align >> 3) - 1);
454+
455+
// Bit offset in the target Data field
456+
let mut bit_offset = (offset - index * 8) as usize;
457+
// Bit offset in the source value
458+
let mut pos = 0;
459+
460+
while length != 0 {
461+
// Bit offset within a single value
462+
let value_pos = bit_offset % index_align as usize;
463+
// Number of bits until the end of the value
464+
let value_limit = index_align as usize - value_pos;
465+
// Number of bits to access
466+
let len = cmp::min(length, value_limit);
467+
468+
access(index, pos, value_pos, len)?;
469469

470-
// Write the desired index
471-
// NOTE not sure if the spec says the index field can only be an integer one, but I can't
472-
// think of any reason for it to be anything else
473-
self.write_field(AmlValue::Integer(index_value), context)?;
470+
// Advance the bit position
471+
bit_offset += len;
472+
pos += len;
473+
length -= len;
474474

475-
Ok(bit_offset)
475+
// Move to the next index
476+
index += index_align >> 3;
477+
}
478+
479+
Ok(())
476480
}
477481

478-
/// Reads from an IndexField, returning either an `Integer` or a `Buffer` depending on the
479-
/// field size
482+
/// Reads from an IndexField, returning either an `Integer`
480483
pub fn read_index_field(&self, context: &mut AmlContext) -> Result<AmlValue, AmlError> {
481484
let AmlValue::IndexField { index, data, flags, offset, length } = self else {
482485
return Err(AmlError::IncompatibleValueConversion {
@@ -488,14 +491,22 @@ impl AmlValue {
488491
let mut index_field = context.namespace.get_mut(*index)?.clone();
489492
let data_field = context.namespace.get_mut(*data)?.clone();
490493

491-
// Write the Index part of the field
492-
let bit_offset = index_field.write_index(flags, *offset, *length, context)?;
493-
494494
// TODO buffer field accesses
495+
let mut value = 0u64;
496+
497+
Self::index_field_access(flags, *offset, *length, |index, value_offset, field_offset, length| {
498+
// Store the bit range index to the Index field
499+
index_field.write_field(AmlValue::Integer(index), context)?;
500+
501+
// Read the bit range from the Data field
502+
let data = data_field.read_field(context)?.as_integer(context)?;
503+
let bits = data.get_bits(field_offset..field_offset + length);
495504

496-
// Read the value of the Data field
497-
let field_value = data_field.read_field(context)?.as_integer(context)?;
498-
let value = field_value.get_bits(bit_offset..(bit_offset + *length as usize));
505+
// Copy the bit range to the value
506+
value.set_bits(value_offset..value_offset + length, bits);
507+
508+
Ok(())
509+
})?;
499510

500511
Ok(AmlValue::Integer(value))
501512
}
@@ -511,22 +522,24 @@ impl AmlValue {
511522
let mut index_field = context.namespace.get_mut(*index)?.clone();
512523
let mut data_field = context.namespace.get_mut(*data)?.clone();
513524

514-
// Write the Index part of the field
515-
let bit_offset = index_field.write_index(flags, *offset, *length, context)?;
525+
let value = value.as_integer(context)?;
516526

517-
// TODO handle field update rule properly
518-
// TODO buffer field accesses
527+
Self::index_field_access(flags, *offset, *length, |index, value_offset, field_offset, length| {
528+
// TODO handle the UpdateRule flag
519529

520-
// Read the old value of the Data field (to preserve bits we're not interested in)
521-
let mut field_value = data_field.read_field(context)?.as_integer(context)?;
530+
// Store the bit range index to the Index field
531+
index_field.write_field(AmlValue::Integer(index), context)?;
522532

523-
// Modify the bits
524-
field_value.set_bits(bit_offset..(bit_offset + *length as usize), value.as_integer(context)?);
533+
// Extract the bits going to this specific part of the field
534+
let bits = value.get_bits(value_offset..value_offset + length);
525535

526-
// Write the Data field back
527-
data_field.write_field(AmlValue::Integer(field_value), context)?;
536+
// Read/modify/store the data field
537+
let mut data = data_field.read_field(context)?.as_integer(context)?;
538+
data.set_bits(field_offset..field_offset + length, bits);
539+
data_field.write_field(AmlValue::Integer(data), context)?;
528540

529-
Ok(())
541+
Ok(())
542+
})
530543
}
531544

532545
/// Reads from a field of an opregion, returning either a `AmlValue::Integer` or an `AmlValue::Buffer`,

tests/index_fields.asl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
DefinitionBlock("index_fields.aml", "DSDT", 1, "RSACPI", "IDXFLD", 1) {
2+
OperationRegion (GIO0, SystemIO, 0x125, 0x100)
3+
4+
Field (GIO0, ByteAcc, NoLock, WriteAsZeros) {
5+
IDX0, 8,
6+
DAT0, 8,
7+
IDX1, 8,
8+
DAT1, 16
9+
}
10+
11+
IndexField (IDX0, DAT0, ByteAcc, NoLock, Preserve) {
12+
Offset(0x10),
13+
, 7, // Skip to offset 0x10 bit 7
14+
REG0, 2, // Covers bit 7 in 0x10 and bit 0 in 0x11
15+
}
16+
17+
IndexField (IDX1, DAT1, WordAcc, NoLock, Preserve) {
18+
Offset(0x07), // Offset 0x06, bits 8..
19+
, 7, // Skip to offset 0x06, bit 15
20+
REG1, 2, // Covers bit 15 in 0x06 and bit 0 in 0x08
21+
}
22+
23+
// Store 0b11 to REG0 (index 0x10, bit 0 + index 0x11, bit 1)
24+
Store (3, REG0)
25+
26+
// Store 0b11 to REG1 (index 0x06, bit 15 + index 0x08, bit 0)
27+
Store (3, REG1)
28+
}

0 commit comments

Comments
 (0)