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)