Local change: Allow access to the field if it is within the region

size rounded up to a multiple of the access byte width.  This overcomes
"off-by-one" programming errors in the AML often found in Toshiba
laptops.
This commit is contained in:
njl 2003-12-09 02:54:47 +00:00
parent d46f5d8f89
commit 328a52e248

View File

@ -1,7 +1,7 @@
/****************************************************************************** /******************************************************************************
* *
* Module Name: exfldio - Aml Field I/O * Module Name: exfldio - Aml Field I/O
* $Revision: 96 $ * $Revision: 100 $
* *
*****************************************************************************/ *****************************************************************************/
@ -139,7 +139,8 @@
* RETURN: Status * RETURN: Status
* *
* DESCRIPTION: Common processing for AcpiExExtractFromField and * DESCRIPTION: Common processing for AcpiExExtractFromField and
* AcpiExInsertIntoField. Initialize the * AcpiExInsertIntoField. Initialize the Region if necessary and
* validate the request.
* *
******************************************************************************/ ******************************************************************************/
@ -172,7 +173,7 @@ AcpiExSetupRegion (
* If the Region Address and Length have not been previously evaluated, * If the Region Address and Length have not been previously evaluated,
* evaluate them now and save the results. * evaluate them now and save the results.
*/ */
if (!(RgnDesc->Region.Flags & AOPOBJ_DATA_VALID)) if (!(RgnDesc->Common.Flags & AOPOBJ_DATA_VALID))
{ {
Status = AcpiDsGetRegionArguments (RgnDesc); Status = AcpiDsGetRegionArguments (RgnDesc);
if (ACPI_FAILURE (Status)) if (ACPI_FAILURE (Status))
@ -188,6 +189,20 @@ AcpiExSetupRegion (
return_ACPI_STATUS (AE_OK); return_ACPI_STATUS (AE_OK);
} }
#ifdef ACPI_UNDER_DEVELOPMENT
/*
* If the Field access is AnyAcc, we can now compute the optimal
* access (because we know know the length of the parent region)
*/
if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
{
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
}
#endif
/* /*
* Validate the request. The entire request from the byte offset for a * Validate the request. The entire request from the byte offset for a
* length of one field datum (access width) must fit within the region. * length of one field datum (access width) must fit within the region.
@ -206,8 +221,9 @@ AcpiExSetupRegion (
*/ */
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,
"Field [%4.4s] access width (%d bytes) too large for region [%4.4s] (length %X)\n", "Field [%4.4s] access width (%d bytes) too large for region [%4.4s] (length %X)\n",
ObjDesc->CommonField.Node->Name.Ascii, ObjDesc->CommonField.AccessByteWidth, AcpiUtGetNodeName (ObjDesc->CommonField.Node),
RgnDesc->Region.Node->Name.Ascii, RgnDesc->Region.Length)); ObjDesc->CommonField.AccessByteWidth,
AcpiUtGetNodeName (RgnDesc->Region.Node), RgnDesc->Region.Length));
} }
/* /*
@ -216,9 +232,10 @@ AcpiExSetupRegion (
*/ */
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,
"Field [%4.4s] Base+Offset+Width %X+%X+%X is beyond end of region [%4.4s] (length %X)\n", "Field [%4.4s] Base+Offset+Width %X+%X+%X is beyond end of region [%4.4s] (length %X)\n",
ObjDesc->CommonField.Node->Name.Ascii, ObjDesc->CommonField.BaseByteOffset, AcpiUtGetNodeName (ObjDesc->CommonField.Node),
ObjDesc->CommonField.BaseByteOffset,
FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth, FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth,
RgnDesc->Region.Node->Name.Ascii, RgnDesc->Region.Length)); AcpiUtGetNodeName (RgnDesc->Region.Node), RgnDesc->Region.Length));
#ifndef ACPICA_PEDANTIC #ifndef ACPICA_PEDANTIC
{ {
@ -311,13 +328,13 @@ AcpiExAccessRegion (
} }
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD, ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD,
" Region[%s-%X] Access %X Base:Off %X:%X at %8.8X%8.8X\n", " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
AcpiUtGetRegionName (RgnDesc->Region.SpaceId), AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
RgnDesc->Region.SpaceId, RgnDesc->Region.SpaceId,
ObjDesc->CommonField.AccessByteWidth, ObjDesc->CommonField.AccessByteWidth,
ObjDesc->CommonField.BaseByteOffset, ObjDesc->CommonField.BaseByteOffset,
FieldDatumByteOffset, FieldDatumByteOffset,
ACPI_HIDWORD (Address), ACPI_LODWORD (Address))); ACPI_FORMAT_UINT64 (Address)));
/* Invoke the appropriate AddressSpace/OpRegion handler */ /* Invoke the appropriate AddressSpace/OpRegion handler */
@ -589,13 +606,15 @@ AcpiExFieldDatumIo (
{ {
if (ReadWrite == ACPI_READ) if (ReadWrite == ACPI_READ)
{ {
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Read=%8.8X%8.8X\n", ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Read %8.8X%8.8X, Width %d\n",
ACPI_HIDWORD (*Value), ACPI_LODWORD (*Value))); ACPI_FORMAT_UINT64 (*Value),
ObjDesc->CommonField.AccessByteWidth));
} }
else else
{ {
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Written=%8.8X%8.8X\n", ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Written %8.8X%8.8X, Width %d\n",
ACPI_HIDWORD (*Value), ACPI_LODWORD (*Value))); ACPI_FORMAT_UINT64 (*Value),
ObjDesc->CommonField.AccessByteWidth));
} }
} }
@ -657,6 +676,11 @@ AcpiExWriteWithUpdateRule (
*/ */
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset, Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&CurrentValue, ACPI_READ); &CurrentValue, ACPI_READ);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
MergedValue |= (CurrentValue & ~Mask); MergedValue |= (CurrentValue & ~Mask);
} }
break; break;
@ -676,6 +700,7 @@ AcpiExWriteWithUpdateRule (
break; break;
default: default:
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,
"WriteWithUpdateRule: Unknown UpdateRule setting: %X\n", "WriteWithUpdateRule: Unknown UpdateRule setting: %X\n",
(ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK))); (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)));
@ -683,18 +708,19 @@ AcpiExWriteWithUpdateRule (
} }
} }
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
ACPI_FORMAT_UINT64 (Mask),
FieldDatumByteOffset,
ObjDesc->CommonField.AccessByteWidth,
ACPI_FORMAT_UINT64 (FieldValue),
ACPI_FORMAT_UINT64 (MergedValue)));
/* Write the merged value */ /* Write the merged value */
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset, Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&MergedValue, ACPI_WRITE); &MergedValue, ACPI_WRITE);
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"Mask %8.8X%8.8X DatumOffset %X Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
ACPI_HIDWORD (Mask), ACPI_LODWORD (Mask),
FieldDatumByteOffset,
ACPI_HIDWORD (FieldValue), ACPI_LODWORD (FieldValue),
ACPI_HIDWORD (MergedValue),ACPI_LODWORD (MergedValue)));
return_ACPI_STATUS (Status); return_ACPI_STATUS (Status);
} }
@ -728,7 +754,7 @@ AcpiExGetBufferDatum (
UINT32 Index; UINT32 Index;
ACPI_FUNCTION_ENTRY (); ACPI_FUNCTION_TRACE_U32 ("ExGetBufferDatum", ByteGranularity);
/* Get proper index into buffer (handles big/little endian) */ /* Get proper index into buffer (handles big/little endian) */
@ -763,6 +789,8 @@ AcpiExGetBufferDatum (
/* Should not get here */ /* Should not get here */
break; break;
} }
return_VOID;
} }
@ -794,7 +822,8 @@ AcpiExSetBufferDatum (
{ {
UINT32 Index; UINT32 Index;
ACPI_FUNCTION_ENTRY ();
ACPI_FUNCTION_TRACE_U32 ("ExSetBufferDatum", ByteGranularity);
/* Get proper index into buffer (handles big/little endian) */ /* Get proper index into buffer (handles big/little endian) */
@ -829,6 +858,8 @@ AcpiExSetBufferDatum (
/* Should not get here */ /* Should not get here */
break; break;
} }
return_VOID;
} }
@ -853,12 +884,13 @@ AcpiExExtractFromField (
{ {
ACPI_STATUS Status; ACPI_STATUS Status;
UINT32 FieldDatumByteOffset; UINT32 FieldDatumByteOffset;
UINT32 DatumOffset; UINT32 BufferDatumOffset;
ACPI_INTEGER PreviousRawDatum; ACPI_INTEGER PreviousRawDatum = 0;
ACPI_INTEGER ThisRawDatum = 0; ACPI_INTEGER ThisRawDatum = 0;
ACPI_INTEGER MergedDatum = 0; ACPI_INTEGER MergedDatum = 0;
UINT32 ByteFieldLength; UINT32 ByteFieldLength;
UINT32 DatumCount; UINT32 DatumCount;
UINT32 i;
ACPI_FUNCTION_TRACE ("ExExtractFromField"); ACPI_FUNCTION_TRACE ("ExExtractFromField");
@ -882,83 +914,80 @@ AcpiExExtractFromField (
DatumCount = ACPI_ROUND_UP_TO (ByteFieldLength, DatumCount = ACPI_ROUND_UP_TO (ByteFieldLength,
ObjDesc->CommonField.AccessByteWidth); ObjDesc->CommonField.AccessByteWidth);
/*
* If the field is not aligned on a datum boundary and does not
* fit within a single datum, we must read an extra datum.
*
* We could just split the aligned and non-aligned cases since the
* aligned case is so very simple, but this would require more code.
*/
if ((ObjDesc->CommonField.EndFieldValidBits != 0) &&
(!(ObjDesc->CommonField.Flags & AOPOBJ_SINGLE_DATUM)))
{
DatumCount++;
}
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"ByteLen=%X, DatumLen=%X, ByteGran=%X\n", "ByteLen %X, DatumLen %X, ByteGran %X\n",
ByteFieldLength, DatumCount,ObjDesc->CommonField.AccessByteWidth)); ByteFieldLength, DatumCount,ObjDesc->CommonField.AccessByteWidth));
/* /*
* Clear the caller's buffer (the whole buffer length as given) * Clear the caller's buffer (the whole buffer length as given)
* This is very important, especially in the cases where a byte is read, * This is very important, especially in the cases where the buffer
* but the buffer is really a UINT32 (4 bytes). * is longer than the size of the field.
*/ */
ACPI_MEMSET (Buffer, 0, BufferLength); ACPI_MEMSET (Buffer, 0, BufferLength);
/* Read the first raw datum to prime the loop */
FieldDatumByteOffset = 0; FieldDatumByteOffset = 0;
DatumOffset= 0; BufferDatumOffset= 0;
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset, /* Read the entire field */
&PreviousRawDatum, ACPI_READ);
if (ACPI_FAILURE (Status)) for (i = 0; i < DatumCount; i++)
{ {
return_ACPI_STATUS (Status); Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
} &ThisRawDatum, ACPI_READ);
if (ACPI_FAILURE (Status))
/* We might actually be done if the request fits in one datum */
if ((DatumCount == 1) &&
(ObjDesc->CommonField.Flags & AOPOBJ_SINGLE_DATUM))
{
/* 1) Shift the valid data bits down to start at bit 0 */
MergedDatum = (PreviousRawDatum >> ObjDesc->CommonField.StartFieldBitOffset);
/* 2) Mask off any upper unused bits (bits not part of the field) */
if (ObjDesc->CommonField.EndBufferValidBits)
{ {
MergedDatum &= ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndBufferValidBits); return_ACPI_STATUS (Status);
} }
/* Store the datum to the caller buffer */ /* We might actually be done if the request fits in one datum */
AcpiExSetBufferDatum (MergedDatum, Buffer, BufferLength, if ((DatumCount == 1) &&
ObjDesc->CommonField.AccessByteWidth, DatumOffset); (ObjDesc->CommonField.Flags & AOPOBJ_SINGLE_DATUM))
{
/* 1) Shift the valid data bits down to start at bit 0 */
return_ACPI_STATUS (AE_OK); MergedDatum = (ThisRawDatum >> ObjDesc->CommonField.StartFieldBitOffset);
}
/* 2) Mask off any upper unused bits (bits not part of the field) */
/* We need to get more raw data to complete one or more field data */ if (ObjDesc->CommonField.EndBufferValidBits)
{
MergedDatum &= ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndBufferValidBits);
}
while (DatumOffset < DatumCount) /* Store the datum to the caller buffer */
{
FieldDatumByteOffset += ObjDesc->CommonField.AccessByteWidth;
/* AcpiExSetBufferDatum (MergedDatum, Buffer, BufferLength,
* If the field is aligned on a byte boundary, we don't want ObjDesc->CommonField.AccessByteWidth, BufferDatumOffset);
* to perform a final read, since this would potentially read
* past the end of the region. return_ACPI_STATUS (AE_OK);
* }
* We could just split the aligned and non-aligned cases since the
* aligned case is so very simple, but this would require more code. /* Special handling for the last datum to ignore extra bits */
*/
if ((ObjDesc->CommonField.StartFieldBitOffset != 0) || if ((i >= (DatumCount -1)) &&
((ObjDesc->CommonField.StartFieldBitOffset == 0) && (ObjDesc->CommonField.EndFieldValidBits))
(DatumOffset < (DatumCount -1))))
{ {
/* /*
* Get the next raw datum, it contains some or all bits * This is the last iteration of the loop. We need to clear
* of the current field datum * any unused bits (bits that are not part of this field) before
* we store the final merged datum into the caller buffer.
*/ */
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset, ThisRawDatum &=
&ThisRawDatum, ACPI_READ); ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndFieldValidBits);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
} }
/* /*
@ -968,51 +997,51 @@ AcpiExExtractFromField (
{ {
/* Field is not skewed and we can just copy the datum */ /* Field is not skewed and we can just copy the datum */
MergedDatum = PreviousRawDatum; AcpiExSetBufferDatum (ThisRawDatum, Buffer, BufferLength,
ObjDesc->CommonField.AccessByteWidth, BufferDatumOffset);
BufferDatumOffset++;
} }
else else
{ {
/* /* Not aligned -- on the first iteration, just save the datum */
* Put together the appropriate bits of the two raw data to make a
* single complete field datum
*
* 1) Normalize the first datum down to bit 0
*/
MergedDatum = (PreviousRawDatum >> ObjDesc->CommonField.StartFieldBitOffset);
/* 2) Insert the second datum "above" the first datum */ if (i != 0)
MergedDatum |= (ThisRawDatum << ObjDesc->CommonField.DatumValidBits);
if ((DatumOffset >= (DatumCount -1)))
{ {
/* /*
* This is the last iteration of the loop. We need to clear * Put together the appropriate bits of the two raw data to make a
* any unused bits (bits that are not part of this field) that * single complete field datum
* came from the last raw datum before we store the final *
* merged datum into the caller buffer. * 1) Normalize the first datum down to bit 0
*/ */
if (ObjDesc->CommonField.EndBufferValidBits) MergedDatum = (PreviousRawDatum >> ObjDesc->CommonField.StartFieldBitOffset);
{
MergedDatum &= /* 2) Insert the second datum "above" the first datum */
ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndBufferValidBits);
} MergedDatum |= (ThisRawDatum << ObjDesc->CommonField.DatumValidBits);
AcpiExSetBufferDatum (MergedDatum, Buffer, BufferLength,
ObjDesc->CommonField.AccessByteWidth, BufferDatumOffset);
BufferDatumOffset++;
} }
/*
* Save the raw datum that was just acquired since it may contain bits
* of the *next* field datum
*/
PreviousRawDatum = ThisRawDatum;
} }
/* FieldDatumByteOffset += ObjDesc->CommonField.AccessByteWidth;
* Store the merged field datum in the caller's buffer, according to }
* the granularity of the field (size of each datum).
*/
AcpiExSetBufferDatum (MergedDatum, Buffer, BufferLength,
ObjDesc->CommonField.AccessByteWidth, DatumOffset);
/* /* For non-aligned case, there is one last datum to insert */
* Save the raw datum that was just acquired since it may contain bits
* of the *next* field datum. Update offsets if (ObjDesc->CommonField.StartFieldBitOffset != 0)
*/ {
PreviousRawDatum = ThisRawDatum; MergedDatum = (ThisRawDatum >> ObjDesc->CommonField.StartFieldBitOffset);
DatumOffset++;
AcpiExSetBufferDatum (MergedDatum, Buffer, BufferLength,
ObjDesc->CommonField.AccessByteWidth, BufferDatumOffset);
} }
return_ACPI_STATUS (AE_OK); return_ACPI_STATUS (AE_OK);
@ -1058,21 +1087,28 @@ AcpiExInsertIntoField (
* larger than the field, this typically happens when an integer is * larger than the field, this typically happens when an integer is
* written to a field that is actually smaller than an integer. * written to a field that is actually smaller than an integer.
*/ */
ByteFieldLength = ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->CommonField.BitLength); ByteFieldLength = ACPI_ROUND_BITS_UP_TO_BYTES (
ObjDesc->CommonField.BitLength);
if (BufferLength < ByteFieldLength) if (BufferLength < ByteFieldLength)
{ {
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Buffer length %X too small for field %X\n", ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"Buffer length %X too small for field %X\n",
BufferLength, ByteFieldLength)); BufferLength, ByteFieldLength));
return_ACPI_STATUS (AE_BUFFER_OVERFLOW); return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
} }
ByteFieldLength = ACPI_ROUND_BITS_UP_TO_BYTES (
ObjDesc->CommonField.StartFieldBitOffset +
ObjDesc->CommonField.BitLength);
/* Convert byte count to datum count, round up if necessary */ /* Convert byte count to datum count, round up if necessary */
DatumCount = ACPI_ROUND_UP_TO (ByteFieldLength, ObjDesc->CommonField.AccessByteWidth); DatumCount = ACPI_ROUND_UP_TO (ByteFieldLength,
ObjDesc->CommonField.AccessByteWidth);
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"ByteLen=%X, DatumLen=%X, ByteGran=%X\n", "Bytes %X, Datums %X, ByteGran %X\n",
ByteFieldLength, DatumCount, ObjDesc->CommonField.AccessByteWidth)); ByteFieldLength, DatumCount, ObjDesc->CommonField.AccessByteWidth));
/* /*
@ -1125,6 +1161,10 @@ AcpiExInsertIntoField (
return_ACPI_STATUS (Status); return_ACPI_STATUS (Status);
} }
/* We just wrote the first datum */
DatumOffset++;
/* If the entire field fits within one datum, we are done. */ /* If the entire field fits within one datum, we are done. */
if ((DatumCount == 1) && if ((DatumCount == 1) &&
@ -1146,7 +1186,6 @@ AcpiExInsertIntoField (
*/ */
while (DatumOffset < DatumCount) while (DatumOffset < DatumCount)
{ {
DatumOffset++;
FieldDatumByteOffset += ObjDesc->CommonField.AccessByteWidth; FieldDatumByteOffset += ObjDesc->CommonField.AccessByteWidth;
/* /*
@ -1180,37 +1219,37 @@ AcpiExInsertIntoField (
* a datum boundary. Update Rule must be applied to the bits outside * a datum boundary. Update Rule must be applied to the bits outside
* the field. * the field.
*/ */
if (DatumOffset == DatumCount) DatumOffset++;
if ((DatumOffset == DatumCount) &&
(ObjDesc->CommonField.EndFieldValidBits))
{ {
/* /*
* If there are dangling non-aligned bits, perform one more merged write * If there are dangling non-aligned bits, perform one more merged write
* Else - field is aligned at the end, no need for any more writes * Else - field is aligned at the end, no need for any more writes
*/ */
if (ObjDesc->CommonField.EndFieldValidBits)
/*
* Part3:
* This is the last datum and the field does not end on a datum boundary.
* Build the partial datum and write with the update rule.
*
* Mask off the unused bits above (after) the end-of-field
*/
Mask = ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndFieldValidBits);
MergedDatum &= Mask;
/* Write the last datum with the update rule */
Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask, MergedDatum,
FieldDatumByteOffset);
if (ACPI_FAILURE (Status))
{ {
/* return_ACPI_STATUS (Status);
* Part3:
* This is the last datum and the field does not end on a datum boundary.
* Build the partial datum and write with the update rule.
*
* Mask off the unused bits above (after) the end-of-field
*/
Mask = ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndFieldValidBits);
MergedDatum &= Mask;
/* Write the last datum with the update rule */
Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask, MergedDatum,
FieldDatumByteOffset);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
} }
} }
else else
{ {
/* Normal case -- write the completed datum */ /* Normal (aligned) case -- write the completed datum */
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset, Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&MergedDatum, ACPI_WRITE); &MergedDatum, ACPI_WRITE);