/** @file ACPI Sdt Protocol Driver Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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, according 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 ); }