Skip to content

Commit 3762e79

Browse files
committed
aml: handle IndexField fields crossing index boundary
1 parent 916d921 commit 3762e79

File tree

2 files changed

+85
-44
lines changed

2 files changed

+85
-44
lines changed

aml/src/value.rs

+57-44
Original file line numberDiff line numberDiff line change
@@ -417,15 +417,12 @@ impl AmlValue {
417417
}
418418
}
419419

420-
/// Stores an IndexField's index (as specified by its offset) and returns the bit offset within
421-
/// the Data part of the field
422-
fn write_index(
423-
&mut self,
420+
fn index_field_access<F: FnMut(u64, usize, usize, usize) -> Result<(), AmlError>>(
424421
index_flags: &FieldFlags,
425422
offset: u64,
426423
length: u64,
427-
context: &mut AmlContext,
428-
) -> Result<usize, AmlError> {
424+
mut access: F,
425+
) -> Result<(), AmlError> {
429426
let index_align = match index_flags.access_type()? {
430427
FieldAccessType::Any => 8,
431428
FieldAccessType::Byte => 8,
@@ -435,31 +432,37 @@ impl AmlValue {
435432
FieldAccessType::Buffer => 8,
436433
};
437434

438-
// Value to write to the Index part of the field
439-
let length = length as usize;
440-
let index_value = (offset / 8) & !((index_align >> 3) - 1);
441-
let bit_offset = (offset - index_value * 8) as usize;
442-
443-
// TODO handle cases when access_type/offset/length combinations lead to crossing of index
444-
// boundary
445-
if (bit_offset + length - 1) / index_align as usize != 0 {
446-
todo!(
447-
"IndexField access crosses the index boundary (range: {:#x?}, access type: {} bits)",
448-
bit_offset..(bit_offset + length),
449-
index_align
450-
);
451-
}
435+
let mut length = length as usize;
436+
let mut index = (offset / 8) & !((index_align >> 3) - 1);
437+
438+
// Bit offset in the target Data field
439+
let mut bit_offset = (offset - index * 8) as usize;
440+
// Bit offset in the source value
441+
let mut pos = 0;
442+
443+
while length != 0 {
444+
// Bit offset within a single value
445+
let value_pos = bit_offset % index_align as usize;
446+
// Number of bits until the end of the value
447+
let value_limit = index_align as usize - value_pos;
448+
// Number of bits to access
449+
let len = cmp::min(length, value_limit);
450+
451+
access(index, pos, value_pos, len)?;
452452

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

458-
Ok(bit_offset)
458+
// Move to the next index
459+
index += index_align >> 3;
460+
}
461+
462+
Ok(())
459463
}
460464

461-
/// Reads from an IndexField, returning either an `Integer` or a `Buffer` depending on the
462-
/// field size
465+
/// Reads from an IndexField, returning either an `Integer`
463466
pub fn read_index_field(&self, context: &mut AmlContext) -> Result<AmlValue, AmlError> {
464467
let AmlValue::IndexField { index, data, flags, offset, length } = self else {
465468
return Err(AmlError::IncompatibleValueConversion {
@@ -471,14 +474,22 @@ impl AmlValue {
471474
let mut index_field = context.namespace.get_mut(*index)?.clone();
472475
let data_field = context.namespace.get_mut(*data)?.clone();
473476

474-
// Write the Index part of the field
475-
let bit_offset = index_field.write_index(flags, *offset, *length, context)?;
476-
477477
// TODO buffer field accesses
478+
let mut value = 0u64;
479+
480+
Self::index_field_access(flags, *offset, *length, |index, value_offset, field_offset, length| {
481+
// Store the bit range index to the Index field
482+
index_field.write_field(AmlValue::Integer(index), context)?;
483+
484+
// Read the bit range from the Data field
485+
let data = data_field.read_field(context)?.as_integer(context)?;
486+
let bits = data.get_bits(field_offset..field_offset + length);
478487

479-
// Read the value of the Data field
480-
let field_value = data_field.read_field(context)?.as_integer(context)?;
481-
let value = field_value.get_bits(bit_offset..(bit_offset + *length as usize));
488+
// Copy the bit range to the value
489+
value.set_bits(value_offset..value_offset + length, bits);
490+
491+
Ok(())
492+
})?;
482493

483494
Ok(AmlValue::Integer(value))
484495
}
@@ -494,22 +505,24 @@ impl AmlValue {
494505
let mut index_field = context.namespace.get_mut(*index)?.clone();
495506
let mut data_field = context.namespace.get_mut(*data)?.clone();
496507

497-
// Write the Index part of the field
498-
let bit_offset = index_field.write_index(flags, *offset, *length, context)?;
508+
let value = value.as_integer(context)?;
499509

500-
// TODO handle field update rule properly
501-
// TODO buffer field accesses
510+
Self::index_field_access(flags, *offset, *length, |index, value_offset, field_offset, length| {
511+
// TODO handle the UpdateRule flag
502512

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

506-
// Modify the bits
507-
field_value.set_bits(bit_offset..(bit_offset + *length as usize), value.as_integer(context)?);
516+
// Extract the bits going to this specific part of the field
517+
let bits = value.get_bits(value_offset..value_offset + length);
508518

509-
// Write the Data field back
510-
data_field.write_field(AmlValue::Integer(field_value), context)?;
519+
// Read/modify/store the data field
520+
let mut data = data_field.read_field(context)?.as_integer(context)?;
521+
data.set_bits(field_offset..field_offset + length, bits);
522+
data_field.write_field(AmlValue::Integer(data), context)?;
511523

512-
Ok(())
524+
Ok(())
525+
})
513526
}
514527

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

tests/index_fields.asl

+28
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)