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 d7a7fb25fd
commit 5171c4ac88

View File

@ -1,7 +1,7 @@
/******************************************************************************
*
* Module Name: exfldio - Aml Field I/O
* $Revision: 96 $
* $Revision: 100 $
*
*****************************************************************************/
@ -139,7 +139,8 @@
* RETURN: Status
*
* 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,
* evaluate them now and save the results.
*/
if (!(RgnDesc->Region.Flags & AOPOBJ_DATA_VALID))
if (!(RgnDesc->Common.Flags & AOPOBJ_DATA_VALID))
{
Status = AcpiDsGetRegionArguments (RgnDesc);
if (ACPI_FAILURE (Status))
@ -188,6 +189,20 @@ AcpiExSetupRegion (
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
* length of one field datum (access width) must fit within the region.
@ -206,8 +221,9 @@ AcpiExSetupRegion (
*/
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,
"Field [%4.4s] access width (%d bytes) too large for region [%4.4s] (length %X)\n",
ObjDesc->CommonField.Node->Name.Ascii, ObjDesc->CommonField.AccessByteWidth,
RgnDesc->Region.Node->Name.Ascii, RgnDesc->Region.Length));
AcpiUtGetNodeName (ObjDesc->CommonField.Node),
ObjDesc->CommonField.AccessByteWidth,
AcpiUtGetNodeName (RgnDesc->Region.Node), RgnDesc->Region.Length));
}
/*
@ -216,9 +232,10 @@ AcpiExSetupRegion (
*/
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",
ObjDesc->CommonField.Node->Name.Ascii, ObjDesc->CommonField.BaseByteOffset,
AcpiUtGetNodeName (ObjDesc->CommonField.Node),
ObjDesc->CommonField.BaseByteOffset,
FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth,
RgnDesc->Region.Node->Name.Ascii, RgnDesc->Region.Length));
AcpiUtGetNodeName (RgnDesc->Region.Node), RgnDesc->Region.Length));
#ifndef ACPICA_PEDANTIC
{
@ -311,13 +328,13 @@ AcpiExAccessRegion (
}
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),
RgnDesc->Region.SpaceId,
ObjDesc->CommonField.AccessByteWidth,
ObjDesc->CommonField.BaseByteOffset,
FieldDatumByteOffset,
ACPI_HIDWORD (Address), ACPI_LODWORD (Address)));
ACPI_FORMAT_UINT64 (Address)));
/* Invoke the appropriate AddressSpace/OpRegion handler */
@ -589,13 +606,15 @@ AcpiExFieldDatumIo (
{
if (ReadWrite == ACPI_READ)
{
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Read=%8.8X%8.8X\n",
ACPI_HIDWORD (*Value), ACPI_LODWORD (*Value)));
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Read %8.8X%8.8X, Width %d\n",
ACPI_FORMAT_UINT64 (*Value),
ObjDesc->CommonField.AccessByteWidth));
}
else
{
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Written=%8.8X%8.8X\n",
ACPI_HIDWORD (*Value), ACPI_LODWORD (*Value)));
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Written %8.8X%8.8X, Width %d\n",
ACPI_FORMAT_UINT64 (*Value),
ObjDesc->CommonField.AccessByteWidth));
}
}
@ -657,6 +676,11 @@ AcpiExWriteWithUpdateRule (
*/
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&CurrentValue, ACPI_READ);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
MergedValue |= (CurrentValue & ~Mask);
}
break;
@ -676,6 +700,7 @@ AcpiExWriteWithUpdateRule (
break;
default:
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR,
"WriteWithUpdateRule: Unknown UpdateRule setting: %X\n",
(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 */
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&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);
}
@ -728,7 +754,7 @@ AcpiExGetBufferDatum (
UINT32 Index;
ACPI_FUNCTION_ENTRY ();
ACPI_FUNCTION_TRACE_U32 ("ExGetBufferDatum", ByteGranularity);
/* Get proper index into buffer (handles big/little endian) */
@ -763,6 +789,8 @@ AcpiExGetBufferDatum (
/* Should not get here */
break;
}
return_VOID;
}
@ -794,7 +822,8 @@ AcpiExSetBufferDatum (
{
UINT32 Index;
ACPI_FUNCTION_ENTRY ();
ACPI_FUNCTION_TRACE_U32 ("ExSetBufferDatum", ByteGranularity);
/* Get proper index into buffer (handles big/little endian) */
@ -829,6 +858,8 @@ AcpiExSetBufferDatum (
/* Should not get here */
break;
}
return_VOID;
}
@ -853,12 +884,13 @@ AcpiExExtractFromField (
{
ACPI_STATUS Status;
UINT32 FieldDatumByteOffset;
UINT32 DatumOffset;
ACPI_INTEGER PreviousRawDatum;
UINT32 BufferDatumOffset;
ACPI_INTEGER PreviousRawDatum = 0;
ACPI_INTEGER ThisRawDatum = 0;
ACPI_INTEGER MergedDatum = 0;
UINT32 ByteFieldLength;
UINT32 DatumCount;
UINT32 i;
ACPI_FUNCTION_TRACE ("ExExtractFromField");
@ -882,83 +914,80 @@ AcpiExExtractFromField (
DatumCount = ACPI_ROUND_UP_TO (ByteFieldLength,
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,
"ByteLen=%X, DatumLen=%X, ByteGran=%X\n",
"ByteLen %X, DatumLen %X, ByteGran %X\n",
ByteFieldLength, DatumCount,ObjDesc->CommonField.AccessByteWidth));
/*
* Clear the caller's buffer (the whole buffer length as given)
* This is very important, especially in the cases where a byte is read,
* but the buffer is really a UINT32 (4 bytes).
* This is very important, especially in the cases where the buffer
* is longer than the size of the field.
*/
ACPI_MEMSET (Buffer, 0, BufferLength);
/* Read the first raw datum to prime the loop */
FieldDatumByteOffset = 0;
DatumOffset= 0;
BufferDatumOffset= 0;
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&PreviousRawDatum, ACPI_READ);
if (ACPI_FAILURE (Status))
/* Read the entire field */
for (i = 0; i < DatumCount; i++)
{
return_ACPI_STATUS (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)
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&ThisRawDatum, ACPI_READ);
if (ACPI_FAILURE (Status))
{
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,
ObjDesc->CommonField.AccessByteWidth, DatumOffset);
if ((DatumCount == 1) &&
(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)
{
FieldDatumByteOffset += ObjDesc->CommonField.AccessByteWidth;
/* Store the datum to the caller buffer */
/*
* If the field is aligned on a byte boundary, we don't want
* to perform a final read, since this would potentially read
* past the end of the region.
*
* 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.StartFieldBitOffset != 0) ||
((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
(DatumOffset < (DatumCount -1))))
AcpiExSetBufferDatum (MergedDatum, Buffer, BufferLength,
ObjDesc->CommonField.AccessByteWidth, BufferDatumOffset);
return_ACPI_STATUS (AE_OK);
}
/* Special handling for the last datum to ignore extra bits */
if ((i >= (DatumCount -1)) &&
(ObjDesc->CommonField.EndFieldValidBits))
{
/*
* Get the next raw datum, it contains some or all bits
* of the current field datum
* This is the last iteration of the loop. We need to clear
* 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, ACPI_READ);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
ThisRawDatum &=
ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndFieldValidBits);
}
/*
@ -968,51 +997,51 @@ AcpiExExtractFromField (
{
/* Field is not skewed and we can just copy the datum */
MergedDatum = PreviousRawDatum;
AcpiExSetBufferDatum (ThisRawDatum, Buffer, BufferLength,
ObjDesc->CommonField.AccessByteWidth, BufferDatumOffset);
BufferDatumOffset++;
}
else
{
/*
* 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);
/* Not aligned -- on the first iteration, just save the datum */
/* 2) Insert the second datum "above" the first datum */
MergedDatum |= (ThisRawDatum << ObjDesc->CommonField.DatumValidBits);
if ((DatumOffset >= (DatumCount -1)))
if (i != 0)
{
/*
* This is the last iteration of the loop. We need to clear
* any unused bits (bits that are not part of this field) that
* came from the last raw datum before we store the final
* merged datum into the caller buffer.
* 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
*/
if (ObjDesc->CommonField.EndBufferValidBits)
{
MergedDatum &=
ACPI_MASK_BITS_ABOVE (ObjDesc->CommonField.EndBufferValidBits);
}
MergedDatum = (PreviousRawDatum >> ObjDesc->CommonField.StartFieldBitOffset);
/* 2) Insert the second datum "above" the first datum */
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;
}
/*
* 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);
FieldDatumByteOffset += ObjDesc->CommonField.AccessByteWidth;
}
/*
* Save the raw datum that was just acquired since it may contain bits
* of the *next* field datum. Update offsets
*/
PreviousRawDatum = ThisRawDatum;
DatumOffset++;
/* For non-aligned case, there is one last datum to insert */
if (ObjDesc->CommonField.StartFieldBitOffset != 0)
{
MergedDatum = (ThisRawDatum >> ObjDesc->CommonField.StartFieldBitOffset);
AcpiExSetBufferDatum (MergedDatum, Buffer, BufferLength,
ObjDesc->CommonField.AccessByteWidth, BufferDatumOffset);
}
return_ACPI_STATUS (AE_OK);
@ -1058,21 +1087,28 @@ AcpiExInsertIntoField (
* larger than the field, this typically happens when an integer is
* 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)
{
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));
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 */
DatumCount = ACPI_ROUND_UP_TO (ByteFieldLength, ObjDesc->CommonField.AccessByteWidth);
DatumCount = ACPI_ROUND_UP_TO (ByteFieldLength,
ObjDesc->CommonField.AccessByteWidth);
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));
/*
@ -1125,6 +1161,10 @@ AcpiExInsertIntoField (
return_ACPI_STATUS (Status);
}
/* We just wrote the first datum */
DatumOffset++;
/* If the entire field fits within one datum, we are done. */
if ((DatumCount == 1) &&
@ -1146,7 +1186,6 @@ AcpiExInsertIntoField (
*/
while (DatumOffset < DatumCount)
{
DatumOffset++;
FieldDatumByteOffset += ObjDesc->CommonField.AccessByteWidth;
/*
@ -1180,37 +1219,37 @@ AcpiExInsertIntoField (
* a datum boundary. Update Rule must be applied to the bits outside
* the field.
*/
if (DatumOffset == DatumCount)
DatumOffset++;
if ((DatumOffset == DatumCount) &&
(ObjDesc->CommonField.EndFieldValidBits))
{
/*
* 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
*/
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))
{
/*
* 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);
}
return_ACPI_STATUS (Status);
}
}
else
{
/* Normal case -- write the completed datum */
/* Normal (aligned) case -- write the completed datum */
Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
&MergedDatum, ACPI_WRITE);