/** @file | |
ACPI Sdt Protocol Driver | |
Copyright (c) 2010, Intel Corporation. All rights reserved. <BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "AcpiTable.h" | |
/** | |
Retrieve option term according to AmlByteEncoding and Buffer. | |
@param[in] AmlByteEncoding AML Byte Encoding. | |
@param[in] Buffer AML buffer. | |
@param[in] MaxBufferSize AML buffer MAX size. The parser can not parse any data exceed this region. | |
@param[in] TermIndex Index of the data to retrieve from the object. | |
@param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists | |
for the specified index. | |
@param[out] Data Upon return, points to the pointer to the data. | |
@param[out] DataSize Upon return, points to the size of Data. | |
@retval EFI_SUCCESS Success. | |
@retval EFI_INVALID_PARAMETER Buffer does not refer to a valid ACPI object. | |
**/ | |
EFI_STATUS | |
AmlParseOptionTerm ( | |
IN AML_BYTE_ENCODING *AmlByteEncoding, | |
IN UINT8 *Buffer, | |
IN UINTN MaxBufferSize, | |
IN AML_OP_PARSE_INDEX TermIndex, | |
OUT EFI_ACPI_DATA_TYPE *DataType, | |
OUT VOID **Data, | |
OUT UINTN *DataSize | |
) | |
{ | |
AML_BYTE_ENCODING *ChildAmlByteEncoding; | |
EFI_STATUS Status; | |
if (DataType != NULL) { | |
*DataType = AmlTypeToAcpiType (AmlByteEncoding->Format[TermIndex - 1]); | |
} | |
if (Data != NULL) { | |
*Data = Buffer; | |
} | |
// | |
// Parse term according to AML type | |
// | |
switch (AmlByteEncoding->Format[TermIndex - 1]) { | |
case AML_UINT8: | |
*DataSize = sizeof(UINT8); | |
break; | |
case AML_UINT16: | |
*DataSize = sizeof(UINT16); | |
break; | |
case AML_UINT32: | |
*DataSize = sizeof(UINT32); | |
break; | |
case AML_UINT64: | |
*DataSize = sizeof(UINT64); | |
break; | |
case AML_STRING: | |
*DataSize = AsciiStrSize((CHAR8 *)Buffer); | |
break; | |
case AML_NAME: | |
Status = AmlGetNameStringSize (Buffer, DataSize); | |
if (EFI_ERROR (Status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
break; | |
case AML_OBJECT: | |
ChildAmlByteEncoding = AmlSearchByOpByte (Buffer); | |
if (ChildAmlByteEncoding == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// NOTE: We need override DataType here, if there is a case the AML_OBJECT is AML_NAME. | |
// We need convert type from EFI_ACPI_DATA_TYPE_CHILD to EFI_ACPI_DATA_TYPE_NAME_STRING. | |
// We should not return CHILD because there is NO OpCode for NameString. | |
// | |
if ((ChildAmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) { | |
if (DataType != NULL) { | |
*DataType = AmlTypeToAcpiType (AML_NAME); | |
} | |
Status = AmlGetNameStringSize (Buffer, DataSize); | |
if (EFI_ERROR (Status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
break; | |
} | |
// | |
// It is real AML_OBJECT | |
// | |
*DataSize = AmlGetObjectSize ( | |
ChildAmlByteEncoding, | |
Buffer, | |
MaxBufferSize | |
); | |
if (*DataSize == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
break; | |
case AML_NONE: | |
// | |
// No term | |
// | |
case AML_OPCODE: | |
default: | |
ASSERT (FALSE); | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*DataSize > MaxBufferSize) { | |
return EFI_INVALID_PARAMETER; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Retrieve information according to AmlByteEncoding and Buffer. | |
@param[in] AmlByteEncoding AML Byte Encoding. | |
@param[in] Buffer AML buffer. | |
@param[in] MaxBufferSize AML buffer MAX size. The parser can not parse any data exceed this region. | |
@param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right | |
in the ACPI encoding, with index 0 always being the ACPI opcode. | |
@param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists | |
for the specified index. | |
@param[out] Data Upon return, points to the pointer to the data. | |
@param[out] DataSize Upon return, points to the size of Data. | |
@retval EFI_SUCCESS Success. | |
@retval EFI_INVALID_PARAMETER Buffer does not refer to a valid ACPI object. | |
**/ | |
EFI_STATUS | |
AmlParseOptionCommon ( | |
IN AML_BYTE_ENCODING *AmlByteEncoding, | |
IN UINT8 *Buffer, | |
IN UINTN MaxBufferSize, | |
IN AML_OP_PARSE_INDEX Index, | |
OUT EFI_ACPI_DATA_TYPE *DataType, | |
OUT VOID **Data, | |
OUT UINTN *DataSize | |
) | |
{ | |
UINT8 *CurrentBuffer; | |
UINTN PkgLength; | |
UINTN OpLength; | |
UINTN PkgOffset; | |
AML_OP_PARSE_INDEX TermIndex; | |
EFI_STATUS Status; | |
ASSERT ((Index <= AmlByteEncoding->MaxIndex) || (Index == AML_OP_PARSE_INDEX_GET_SIZE)); | |
// | |
// 0. Check if this is NAME string. | |
// | |
if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) { | |
// | |
// Only allow GET_SIZE | |
// | |
if (Index != AML_OP_PARSE_INDEX_GET_SIZE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// return NameString size | |
// | |
Status = AmlGetNameStringSize (Buffer, DataSize); | |
if (EFI_ERROR (Status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*DataSize > MaxBufferSize) { | |
return EFI_INVALID_PARAMETER; | |
} | |
return EFI_SUCCESS; | |
} | |
// | |
// Not NAME string, start parsing | |
// | |
CurrentBuffer = Buffer; | |
// | |
// 1. Get OpCode | |
// | |
if (Index != AML_OP_PARSE_INDEX_GET_SIZE) { | |
*DataType = EFI_ACPI_DATA_TYPE_OPCODE; | |
*Data = (VOID *)CurrentBuffer; | |
} | |
if (*CurrentBuffer == AML_EXT_OP) { | |
OpLength = 2; | |
} else { | |
OpLength = 1; | |
} | |
*DataSize = OpLength; | |
if (Index == AML_OP_PARSE_INDEX_GET_OPCODE) { | |
return EFI_SUCCESS; | |
} | |
if (OpLength > MaxBufferSize) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CurrentBuffer += OpLength; | |
// | |
// 2. Skip PkgLength field, if have | |
// | |
if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { | |
PkgOffset = AmlGetPkgLength(CurrentBuffer, &PkgLength); | |
// | |
// Override MaxBufferSize if it is valid PkgLength | |
// | |
if (OpLength + PkgLength > MaxBufferSize) { | |
return EFI_INVALID_PARAMETER; | |
} else { | |
MaxBufferSize = OpLength + PkgLength; | |
} | |
} else { | |
PkgOffset = 0; | |
PkgLength = 0; | |
} | |
CurrentBuffer += PkgOffset; | |
// | |
// 3. Get Term one by one. | |
// | |
TermIndex = AML_OP_PARSE_INDEX_GET_TERM1; | |
while ((Index >= TermIndex) && (TermIndex <= AmlByteEncoding->MaxIndex) && ((UINTN)CurrentBuffer < (UINTN)Buffer + MaxBufferSize)) { | |
Status = AmlParseOptionTerm ( | |
AmlByteEncoding, | |
CurrentBuffer, | |
(UINTN)Buffer + MaxBufferSize - (UINTN)CurrentBuffer, | |
TermIndex, | |
DataType, | |
Data, | |
DataSize | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Index == TermIndex) { | |
// | |
// Done | |
// | |
return EFI_SUCCESS; | |
} | |
// | |
// Parse next one | |
// | |
CurrentBuffer += *DataSize; | |
TermIndex ++; | |
} | |
// | |
// Finish all options, but no option found. | |
// | |
if ((UINTN)CurrentBuffer > (UINTN)Buffer + MaxBufferSize) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((UINTN)CurrentBuffer == (UINTN)Buffer + MaxBufferSize) { | |
if (Index != AML_OP_PARSE_INDEX_GET_SIZE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
// | |
// 4. Finish parsing all node, return size | |
// | |
ASSERT (Index == AML_OP_PARSE_INDEX_GET_SIZE); | |
if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { | |
*DataSize = OpLength + PkgLength; | |
} else { | |
*DataSize = (UINTN)CurrentBuffer - (UINTN)Buffer; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Return object size. | |
@param[in] AmlByteEncoding AML Byte Encoding. | |
@param[in] Buffer AML object buffer. | |
@param[in] MaxBufferSize AML object buffer MAX size. The parser can not parse any data exceed this region. | |
@return Size of the object. | |
**/ | |
UINTN | |
AmlGetObjectSize ( | |
IN AML_BYTE_ENCODING *AmlByteEncoding, | |
IN UINT8 *Buffer, | |
IN UINTN MaxBufferSize | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN DataSize; | |
Status = AmlParseOptionCommon ( | |
AmlByteEncoding, | |
Buffer, | |
MaxBufferSize, | |
AML_OP_PARSE_INDEX_GET_SIZE, | |
NULL, | |
NULL, | |
&DataSize | |
); | |
if (EFI_ERROR (Status)) { | |
return 0; | |
} else { | |
return DataSize; | |
} | |
} | |
/** | |
Return object name. | |
@param[in] AmlHandle AML handle. | |
@return Name of the object. | |
**/ | |
CHAR8 * | |
AmlGetObjectName ( | |
IN EFI_AML_HANDLE *AmlHandle | |
) | |
{ | |
AML_BYTE_ENCODING *AmlByteEncoding; | |
VOID *NameString; | |
UINTN NameSize; | |
AML_OP_PARSE_INDEX TermIndex; | |
EFI_STATUS Status; | |
EFI_ACPI_DATA_TYPE DataType; | |
AmlByteEncoding = AmlHandle->AmlByteEncoding; | |
ASSERT ((AmlByteEncoding->Attribute & AML_IN_NAMESPACE) != 0); | |
// | |
// Find out Last Name index, accroding to OpCode table. | |
// The last name will be the node name by design. | |
// | |
TermIndex = AmlByteEncoding->MaxIndex; | |
for (TermIndex = AmlByteEncoding->MaxIndex; TermIndex > 0; TermIndex--) { | |
if (AmlByteEncoding->Format[TermIndex - 1] == AML_NAME) { | |
break; | |
} | |
} | |
ASSERT (TermIndex != 0); | |
// | |
// Get Name for this node. | |
// | |
Status = AmlParseOptionHandleCommon ( | |
AmlHandle, | |
TermIndex, | |
&DataType, | |
&NameString, | |
&NameSize | |
); | |
if (EFI_ERROR (Status)) { | |
return NULL; | |
} | |
ASSERT (DataType == EFI_ACPI_DATA_TYPE_NAME_STRING); | |
return NameString; | |
} | |
/** | |
Return offset of last option. | |
@param[in] AmlHandle AML Handle. | |
@param[out] Buffer Upon return, points to the offset after last option. | |
@retval EFI_SUCCESS Success. | |
@retval EFI_INVALID_PARAMETER AmlHandle does not refer to a valid ACPI object. | |
**/ | |
EFI_STATUS | |
AmlGetOffsetAfterLastOption ( | |
IN EFI_AML_HANDLE *AmlHandle, | |
OUT UINT8 **Buffer | |
) | |
{ | |
EFI_ACPI_DATA_TYPE DataType; | |
VOID *Data; | |
UINTN DataSize; | |
EFI_STATUS Status; | |
Status = AmlParseOptionHandleCommon ( | |
AmlHandle, | |
AmlHandle->AmlByteEncoding->MaxIndex, | |
&DataType, | |
&Data, | |
&DataSize | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// We need to parse the rest buffer after last node. | |
// | |
*Buffer = (UINT8 *)((UINTN)Data + DataSize); | |
// | |
// We need skip PkgLength if no Option | |
// | |
if (DataType == EFI_ACPI_DATA_TYPE_OPCODE) { | |
*Buffer += AmlGetPkgLength (*Buffer, &DataSize); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Retrieve information according to AmlHandle | |
@param[in] AmlHandle AML handle. | |
@param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right | |
in the ACPI encoding, with index 0 always being the ACPI opcode. | |
@param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists | |
for the specified index. | |
@param[out] Data Upon return, points to the pointer to the data. | |
@param[out] DataSize Upon return, points to the size of Data. | |
@retval EFI_SUCCESS Success. | |
@retval EFI_INVALID_PARAMETER AmlHandle does not refer to a valid ACPI object. | |
**/ | |
EFI_STATUS | |
AmlParseOptionHandleCommon ( | |
IN EFI_AML_HANDLE *AmlHandle, | |
IN AML_OP_PARSE_INDEX Index, | |
OUT EFI_ACPI_DATA_TYPE *DataType, | |
OUT VOID **Data, | |
OUT UINTN *DataSize | |
) | |
{ | |
return AmlParseOptionCommon ( | |
AmlHandle->AmlByteEncoding, | |
AmlHandle->Buffer, | |
AmlHandle->Size, | |
Index, | |
DataType, | |
Data, | |
DataSize | |
); | |
} |