As diagnosed by some other people (armani@, jcs@?) a while ago, our code to deal with IndexField() operators in our AML interpreter is quite broken. It works for fields that are less than a byte in size, but anything else is pretty much completely busted. I wouldn't be surprised if this is the cause of many ACPI-related problems. One that comes to mind are the ridiculous temperatures reported by acpitz(4) that have been plaguing us since basically forever.
I don't have a lot of machines that have AML with IndexField() operators. I've verified that those return the correct values, but this defenitely could use further testing on a wide variety of i386/amd64 hardware please. Index: dsdt.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/dsdt.c,v retrieving revision 1.200 diff -u -p -r1.200 dsdt.c --- dsdt.c 10 Apr 2013 01:35:55 -0000 1.200 +++ dsdt.c 20 May 2013 16:55:27 -0000 @@ -2221,8 +2221,9 @@ aml_evalhid(struct aml_node *node, struc return (0); } -void aml_rwfield(struct aml_value *, int, int, struct aml_value *, int); void aml_rwgas(struct aml_value *, int, int, struct aml_value *, int, int); +void aml_rwindexfield(struct aml_value *, struct aml_value *val, int); +void aml_rwfield(struct aml_value *, int, int, struct aml_value *, int); /* Get PCI address for opregion objects */ int @@ -2341,6 +2342,81 @@ aml_rwgas(struct aml_value *rgn, int bpo aml_freevalue(&tmp); } + +void +aml_rwindexfield(struct aml_value *fld, struct aml_value *val, int mode) +{ + struct aml_value tmp, *ref1, *ref2; + void *tbit, *vbit; + int vpos, bpos, blen; + int indexval; + int sz, len; + + ref2 = fld->v_field.ref2; + ref1 = fld->v_field.ref1; + bpos = fld->v_field.bitpos; + blen = fld->v_field.bitlen; + + memset(&tmp, 0, sizeof(tmp)); + tmp.refcnt = 99; + + /* Get field access size */ + switch (AML_FIELD_ACCESS(fld->v_field.flags)) { + case AML_FIELD_WORDACC: + sz = 2; + break; + case AML_FIELD_DWORDACC: + sz = 4; + break; + case AML_FIELD_QWORDACC: + sz = 8; + break; + default: + sz = 1; + break; + } + + if (blen > aml_intlen) { + if (mode == ACPI_IOREAD) { + /* Read from a large field: create buffer */ + _aml_setvalue(val, AML_OBJTYPE_BUFFER, + (blen + 7) << 3, 0); + } + vbit = val->v_buffer; + } else { + if (mode == ACPI_IOREAD) { + /* Read from a short field: initialize integer */ + _aml_setvalue(val, AML_OBJTYPE_INTEGER, 0, 0); + } + vbit = &val->v_integer; + } + tbit = &tmp.v_integer; + vpos = 0; + + indexval = (bpos >> 3) & ~(sz - 1); + bpos = bpos - (indexval << 3); + + while (blen > 0) { + len = min(blen, (sz << 3) - bpos); + + /* Write index register */ + _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, indexval, 0); + aml_rwfield(ref2, 0, aml_intlen, &tmp, ACPI_IOWRITE); + indexval += sz; + + /* Read/write data register */ + _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, 0, 0); + if (mode == ACPI_IOWRITE) + aml_bufcpy(tbit, 0, vbit, vpos, len); + aml_rwfield(ref1, bpos, len, &tmp, mode); + if (mode == ACPI_IOREAD) + aml_bufcpy(vbit, vpos, tbit, 0, len); + vpos += len; + blen -= len; + bpos = 0; + } +} + void aml_rwfield(struct aml_value *fld, int bpos, int blen, struct aml_value *val, int mode) @@ -2356,10 +2432,7 @@ aml_rwfield(struct aml_value *fld, int b memset(&tmp, 0, sizeof(tmp)); aml_addref(&tmp, "fld.write"); if (fld->v_field.type == AMLOP_INDEXFIELD) { - _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, fld->v_field.ref3, 0); - aml_rwfield(ref2, 0, aml_intlen, &tmp, ACPI_IOWRITE); - aml_rwfield(ref1, fld->v_field.bitpos, fld->v_field.bitlen, - val, mode); + aml_rwindexfield(fld, val, mode); } else if (fld->v_field.type == AMLOP_BANKFIELD) { _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, fld->v_field.ref3, 0); aml_rwfield(ref2, 0, aml_intlen, &tmp, ACPI_IOWRITE); @@ -2414,10 +2487,6 @@ aml_createfield(struct aml_value *field, opcode == AMLOP_BANKFIELD) ? AML_OBJTYPE_FIELDUNIT : AML_OBJTYPE_BUFFERFIELD; - if (opcode == AMLOP_INDEXFIELD) { - indexval = bpos >> 3; - bpos &= 7; - } if (field->type == AML_OBJTYPE_BUFFERFIELD && data->type != AML_OBJTYPE_BUFFER)