libaml: fix IndexField and BankField implementations (thanks Michael Forney)

IndexField is supposed to increment the index value when an
access is done with a bigger size than the data field.
The index value is always a byte offset.

Now that we always calculate the offset for each field unit
access for IndexField, rename the indexv to bank (the bank
value), as it is only used for that. Also, do not compare
it with nil, as it is a integer constant which can be
encoded as nil to mean zero.

For BankField, the banking field was written using store(),
which does nothing when the destination is a Field*.
Use rwfield() to fix it in the new rwfieldunit().

Resolve all the Name*'s when IndexField, BankField and
Field are created. Now, Field.reg points to eigther
Buffer object, Region or Field (data Field of an IndexField).

PS: initial bug report by Michael Forney follows below:

In /dev/kmesg on my T14, I saw a message

	amlmapio: [0xffffff18-0x100000018] overlaps usable memory
	amlmapio: mapping \_SB.FRTP failed

Here is the relevant snippet from my DSDT:

    Scope (_SB)
    {
        ...

        OperationRegion (ECMC, SystemIO, 0x72, 0x02)
        Field (ECMC, AnyAcc, NoLock, Preserve)
        {
            ECMI,   8,
            ECMD,   8
        }

        IndexField (ECMI, ECMD, ByteAcc, NoLock, Preserve)
        {
            Offset (0x08),
            FRTB,   32
        }

        OperationRegion (FRTP, SystemMemory, FRTB, 0x0100)
        Field (FRTP, AnyAcc, NoLock, Preserve)
        {
		...
        }
    }

With some debugging output:

	amlmapio(\_SB.ECMC): Io       72 - 74
	rwreg(\_SB.ECMC): Io       [72+0]/1 <- 8
	rwreg(\_SB.ECMC): Io       [72+1]/1 -> 18
	amlmapio(\_SB.FRTP): Mem      ffffff18 - 100000018
	amlmapio: [0xffffff18-0x100000018) overlaps usable memory
	amlmapio: mapping \_SB.FRTP failed

It seems that libaml does not handle IndexField correctly and just did
a single read from ECMD after setting ECMI to 8, causing the FRTP
region to be evaluated as 0xffffff18-0x100000018. Instead, it should
be reading 4 bytes [18 c0 22 cc], evaluating it as
0xcc22c018-0xcc22118:

	amlmapio(\_SB.ECMC): Io       72 - 74
	rwreg(\_SB.ECMC): Io       [72+0]/1 <- 8
	rwreg(\_SB.ECMC): Io       [72+1]/1 -> 18
	rwreg(\_SB.ECMC): Io       [72+0]/1 <- 9
	rwreg(\_SB.ECMC): Io       [72+1]/1 -> c0
	rwreg(\_SB.ECMC): Io       [72+0]/1 <- a
	rwreg(\_SB.ECMC): Io       [72+1]/1 -> 22
	rwreg(\_SB.ECMC): Io       [72+0]/1 <- b
	rwreg(\_SB.ECMC): Io       [72+1]/1 -> cc
	amlmapio(\_SB.FRTP): Mem      cc22c018 - cc22c118

I wrote a patch (attached) to fix this, and it seems to work. Though,
it's not clear to me when things should be dereferenced. Previously,
the data field was dereferenced at evalfield, but the region and index
field were not until rwfield. After the patch, the index field is
also dereferenced in evalfield.

For BankField, the index *is* dereferenced in evalfield. I'm pretty
sure that this means that BankField does not work currently, since
store() just returns nil for 'f' objects. The bank selector will
never get set.

Anyway, I don't know if this solves any real problems; it's just
something I noticed and thought I'd try to fix.
This commit is contained in:
cinap_lenrek 2021-02-22 19:27:49 +01:00
parent 472958e3e7
commit e77fa31516

View file

@ -70,9 +70,9 @@ struct Region {
};
struct Field {
void *reg; /* Buffer or Region */
Field *index;
void *indexv;
void *reg; /* Buffer or Region or data Field */
void *bank; /* bank value */
Field *index; /* bank or index Field */
int flags;
int bitoff;
int bitlen;
@ -217,8 +217,8 @@ gcmark(void *p)
case 'u':
f = p;
gcmark(f->reg);
gcmark(f->bank);
gcmark(f->index);
gcmark(f->indexv);
break;
}
}
@ -608,18 +608,46 @@ fieldalign(int flags)
}
}
static void *rwfield(Field *f, void *v, int write);
static uvlong
rwfieldunit(Field *f, int off, int len, uvlong v, int write)
{
if(f->index){
if(TAG(f->reg) == 'f'){
void *b;
/* set index field */
rwfield(f->index, mki(off), 1);
/* set data field */
f = f->reg;
if(write){
b = mk('b', len);
putle(b, len, v);
rwfield(f, b, 1);
}else{
b = rwfield(f, nil, 0);
v = getle(b, len);
}
return v;
}
/* set bank field */
rwfield(f->index, f->bank, 1);
}
return rwreg(f->reg, off, len, v, write);
}
static void*
rwfield(Field *f, void *v, int write)
{
int boff, blen, wo, ws, wl, wa, wd, i;
uvlong w, m;
void *reg;
uchar *b;
if(f == nil || (reg = deref(f->reg)) == nil)
if(f == nil)
return nil;
if(f->index)
store(f->indexv, f->index);
blen = f->bitlen;
if(write){
if(v && TAG(v) == 'b'){
@ -649,11 +677,11 @@ rwfield(Field *f, void *v, int write)
w <<= ws;
if(wl != wd){
m = ((1ULL<<wl)-1) << ws;
w |= rwreg(reg, wo*wa, wa, 0, 0) & ~m;
w |= rwfieldunit(f, wo*wa, wa, 0, 0) & ~m;
}
rwreg(reg, wo*wa, wa, w, 1);
rwfieldunit(f, wo*wa, wa, w, 1);
} else {
w = rwreg(reg, wo*wa, wa, 0, 0) >> ws;
w = rwfieldunit(f, wo*wa, wa, 0, 0) >> ws;
for(i = 0; i < wl; i++, boff++){
b[boff/8] |= (w&1)<<(boff%8);
w >>= 1;
@ -937,9 +965,14 @@ Vfmt(Fmt *f)
/* no break */
case 'f':
l = p;
if(l->index)
return fmtprint(f, "IndexField(%x, %x, %x) @ %V[%V]",
l->flags, l->bitoff, l->bitlen, l->index, l->indexv);
if(l->index){
if(TAG(l->reg) == 'f')
return fmtprint(f, "IndexField(%x, %x, %x) @ %V[%V]",
l->flags, l->bitoff, l->bitlen, l->reg, l->index);
else
return fmtprint(f, "BankField(%x, %x, %x, %V=%V) @ %V",
l->flags, l->bitoff, l->bitlen, l->index, l->bank, l->reg);
}
return fmtprint(f, "Field(%x, %x, %x) @ %V",
l->flags, l->bitoff, l->bitlen, l->reg);
default:
@ -1374,28 +1407,36 @@ evalcfield(void)
static void*
evalfield(void)
{
int flags, bitoff, wa, n;
Field *f, *df;
int flags, bitoff, n;
Field *f, *index;
void *reg, *bank;
Name *d;
uchar *p;
df = nil;
flags = 0;
bank = nil;
index = nil;
bitoff = 0;
switch(FP->op - optab){
default:
goto Out;
case Ofld:
if((reg = deref(FP->arg[0])) == nil || TAG(reg) != 'r')
goto Out;
flags = ival(FP->arg[1]);
break;
case Oxfld:
df = deref(FP->arg[1]);
if(df == nil || TAG(df) != 'f')
if((index = deref(FP->arg[0])) == nil || TAG(index) != 'f')
goto Out;
if((reg = deref(FP->arg[1])) == nil || TAG(reg) != 'f') /* data field */
goto Out;
flags = ival(FP->arg[2]);
break;
case Obfld:
df = deref(FP->arg[1]);
if(df == nil || TAG(df) != 'f')
if((reg = deref(FP->arg[0])) == nil || TAG(reg) != 'r')
goto Out;
if((index = deref(FP->arg[1])) == nil || TAG(index) != 'f')
goto Out;
bank = FP->arg[2];
flags = ival(FP->arg[3]);
break;
}
@ -1425,25 +1466,10 @@ evalfield(void)
f = mk('f', sizeof(Field));
f->flags = flags;
f->bitlen = n;
switch(FP->op - optab){
case Ofld:
f->reg = FP->arg[0];
f->bitoff = bitoff;
break;
case Oxfld:
wa = fieldalign(df->flags);
f->reg = df->reg;
f->bitoff = df->bitoff + (bitoff % (wa*8));
f->indexv = mki((bitoff/(wa*8))*wa);
f->index = FP->arg[0];
break;
case Obfld:
f->reg = FP->arg[0];
f->bitoff = bitoff;
f->indexv = FP->arg[2];
f->index = df;
break;
}
f->bitoff = bitoff;
f->reg = reg;
f->bank = bank;
f->index = index;
bitoff += n;
d->v = f;
}