| /** @file | |
| * | |
| * Copyright (c) 2014-2015, ARM Limited. All rights reserved. | |
| * Copyright (c) 2021, Ampere Computing LLC. All rights reserved. | |
| * | |
| * SPDX-License-Identifier: BSD-2-Clause-Patent | |
| * | |
| **/ | |
| #include <Uefi.h> | |
| #include <Library/AcpiLib.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Protocol/AcpiSystemDescriptionTable.h> | |
| #include <Protocol/AcpiTable.h> | |
| #include <Protocol/FirmwareVolume2.h> | |
| #include <IndustryStandard/Acpi.h> | |
| /** | |
| Locate and Install the ACPI tables from the Firmware Volume if it verifies | |
| the function condition. | |
| @param AcpiFile Guid of the ACPI file into the Firmware Volume | |
| @param CheckAcpiTableFunction Function that checks if the ACPI table should be installed | |
| @return EFI_SUCCESS The function completed successfully. | |
| @return EFI_NOT_FOUND The protocol could not be located. | |
| @return EFI_OUT_OF_RESOURCES There are not enough resources to find the protocol. | |
| **/ | |
| EFI_STATUS | |
| LocateAndInstallAcpiFromFvConditional ( | |
| IN CONST EFI_GUID *AcpiFile, | |
| IN EFI_LOCATE_ACPI_CHECK CheckAcpiTableFunction | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol; | |
| EFI_HANDLE *HandleBuffer; | |
| UINTN NumberOfHandles; | |
| UINT32 FvStatus; | |
| UINTN Index; | |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *FvInstance; | |
| INTN SectionInstance; | |
| UINTN SectionSize; | |
| EFI_ACPI_COMMON_HEADER *AcpiTable; | |
| UINTN AcpiTableSize; | |
| UINTN AcpiTableKey; | |
| BOOLEAN Valid; | |
| BOOLEAN FoundAcpiFile; | |
| FoundAcpiFile = FALSE; | |
| // Ensure the ACPI Table is present | |
| Status = gBS->LocateProtocol ( | |
| &gEfiAcpiTableProtocolGuid, | |
| NULL, | |
| (VOID **)&AcpiProtocol | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| FvStatus = 0; | |
| SectionInstance = 0; | |
| // Locate all the Firmware Volume protocols. | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| NULL, | |
| &NumberOfHandles, | |
| &HandleBuffer | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // Looking for FV with ACPI storage file | |
| for (Index = 0; Index < NumberOfHandles; Index++) { | |
| // | |
| // Get the protocol on this handle | |
| // This should not fail because of LocateHandleBuffer | |
| // | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[Index], | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| (VOID **)&FvInstance | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto FREE_HANDLE_BUFFER; | |
| } | |
| while (Status == EFI_SUCCESS) { | |
| // AcpiTable must be allocated by ReadSection (ie: AcpiTable == NULL) | |
| AcpiTable = NULL; | |
| // See if it has the ACPI storage file | |
| Status = FvInstance->ReadSection ( | |
| FvInstance, | |
| AcpiFile, | |
| EFI_SECTION_RAW, | |
| SectionInstance, | |
| (VOID **)&AcpiTable, | |
| &SectionSize, | |
| &FvStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| break; | |
| } | |
| FoundAcpiFile = TRUE; | |
| AcpiTableKey = 0; | |
| AcpiTableSize = ((EFI_ACPI_DESCRIPTION_HEADER *)AcpiTable)->Length; | |
| ASSERT (SectionSize >= AcpiTableSize); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "- Found '%c%c%c%c' ACPI Table\n", | |
| (((EFI_ACPI_DESCRIPTION_HEADER *)AcpiTable)->Signature & 0xFF), | |
| ((((EFI_ACPI_DESCRIPTION_HEADER *)AcpiTable)->Signature >> 8) & 0xFF), | |
| ((((EFI_ACPI_DESCRIPTION_HEADER *)AcpiTable)->Signature >> 16) & 0xFF), | |
| ((((EFI_ACPI_DESCRIPTION_HEADER *)AcpiTable)->Signature >> 24) & 0xFF) | |
| )); | |
| // Is the ACPI table valid? | |
| if (CheckAcpiTableFunction != NULL) { | |
| Valid = CheckAcpiTableFunction ((EFI_ACPI_DESCRIPTION_HEADER *)AcpiTable); | |
| } else { | |
| Valid = TRUE; | |
| } | |
| // Install the ACPI Table | |
| if (Valid) { | |
| Status = AcpiProtocol->InstallAcpiTable ( | |
| AcpiProtocol, | |
| AcpiTable, | |
| AcpiTableSize, | |
| &AcpiTableKey | |
| ); | |
| } | |
| // Free memory allocated by ReadSection | |
| gBS->FreePool (AcpiTable); | |
| // Increment the section instance | |
| SectionInstance++; | |
| } | |
| } | |
| FREE_HANDLE_BUFFER: | |
| // | |
| // Free any allocated buffers | |
| // | |
| gBS->FreePool (HandleBuffer); | |
| return (FoundAcpiFile ? EFI_SUCCESS : EFI_NOT_FOUND); | |
| } | |
| /** | |
| Locate and Install the ACPI tables from the Firmware Volume | |
| @param AcpiFile Guid of the ACPI file into the Firmware Volume | |
| @return EFI_SUCCESS The function completed successfully. | |
| @return EFI_NOT_FOUND The protocol could not be located. | |
| @return EFI_OUT_OF_RESOURCES There are not enough resources to find the protocol. | |
| **/ | |
| EFI_STATUS | |
| LocateAndInstallAcpiFromFv ( | |
| IN CONST EFI_GUID *AcpiFile | |
| ) | |
| { | |
| return LocateAndInstallAcpiFromFvConditional (AcpiFile, NULL); | |
| } | |
| /** | |
| This function calculates and updates a UINT8 checksum | |
| in an ACPI description table header. | |
| @param Buffer Pointer to buffer to checksum | |
| @param Size Number of bytes to checksum | |
| @retval EFI_SUCCESS The function completed successfully. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AcpiUpdateChecksum ( | |
| IN OUT UINT8 *Buffer, | |
| IN UINTN Size | |
| ) | |
| { | |
| UINTN ChecksumOffset; | |
| if ((Buffer == NULL) || (Size == 0)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| ChecksumOffset = OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, Checksum); | |
| // | |
| // Set checksum to 0 first | |
| // | |
| Buffer[ChecksumOffset] = 0; | |
| // | |
| // Update checksum value | |
| // | |
| Buffer[ChecksumOffset] = CalculateCheckSum8 (Buffer, Size); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function uses the ACPI SDT protocol to search an ACPI table | |
| with a given signature. | |
| @param AcpiTableSdtProtocol Pointer to ACPI SDT protocol. | |
| @param TableSignature ACPI table signature. | |
| @param Index The zero-based index of the table where to search the table. | |
| The index will be updated to the next instance if the table | |
| is found with the matched TableSignature. | |
| @param Table Pointer to the table. | |
| @param TableKey Pointer to the table key. | |
| @return EFI_SUCCESS The function completed successfully. | |
| @return EFI_INVALID_PARAMETER At least one of parameters is invalid. | |
| @retval EFI_NOT_FOUND The requested index is too large and a table was not found. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AcpiLocateTableBySignature ( | |
| IN EFI_ACPI_SDT_PROTOCOL *AcpiSdtProtocol, | |
| IN UINT32 TableSignature, | |
| IN OUT UINTN *Index, | |
| OUT EFI_ACPI_DESCRIPTION_HEADER **Table, | |
| OUT UINTN *TableKey | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_ACPI_SDT_HEADER *TempTable; | |
| EFI_ACPI_TABLE_VERSION TableVersion; | |
| UINTN TableIndex; | |
| if ( (AcpiSdtProtocol == NULL) | |
| || (Table == NULL) | |
| || (TableKey == NULL)) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Status = EFI_SUCCESS; | |
| // | |
| // Search for ACPI Table with matching signature | |
| // | |
| TableVersion = 0; | |
| TableIndex = *Index; | |
| while (!EFI_ERROR (Status)) { | |
| Status = AcpiSdtProtocol->GetAcpiTable ( | |
| TableIndex, | |
| &TempTable, | |
| &TableVersion, | |
| TableKey | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| TableIndex++; | |
| if (((EFI_ACPI_DESCRIPTION_HEADER *)TempTable)->Signature == TableSignature) { | |
| *Table = (EFI_ACPI_DESCRIPTION_HEADER *)TempTable; | |
| *Index = TableIndex; | |
| break; | |
| } | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| This function updates the integer value of an AML Object. | |
| @param AcpiTableSdtProtocol Pointer to ACPI SDT protocol. | |
| @param TableHandle Points to the table representing the starting point | |
| for the object path search. | |
| @param AsciiObjectPath Pointer to the ACPI path of the object being updated. | |
| @param Value New value to write to the object. | |
| @return EFI_SUCCESS The function completed successfully. | |
| @return EFI_INVALID_PARAMETER At least one of parameters is invalid or the data type | |
| of the ACPI object is not an integer value. | |
| @retval EFI_NOT_FOUND The object is not found with the given path. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AcpiAmlObjectUpdateInteger ( | |
| IN EFI_ACPI_SDT_PROTOCOL *AcpiSdtProtocol, | |
| IN EFI_ACPI_HANDLE TableHandle, | |
| IN CHAR8 *AsciiObjectPath, | |
| IN UINTN Value | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_ACPI_HANDLE ObjectHandle; | |
| EFI_ACPI_HANDLE DataHandle; | |
| EFI_ACPI_DATA_TYPE DataType; | |
| UINT8 *Buffer; | |
| UINTN BufferSize; | |
| UINTN DataSize; | |
| if ((AcpiSdtProtocol == NULL) || (AsciiObjectPath == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| ObjectHandle = NULL; | |
| DataHandle = NULL; | |
| Status = AcpiSdtProtocol->FindPath (TableHandle, AsciiObjectPath, &ObjectHandle); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = AcpiSdtProtocol->GetOption (ObjectHandle, 0, &DataType, (VOID *)&Buffer, &BufferSize); | |
| if (EFI_ERROR (Status)) { | |
| Status = EFI_NOT_FOUND; | |
| goto Exit; | |
| } | |
| ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE); | |
| ASSERT (Buffer != NULL); | |
| if (Buffer[0] != AML_NAME_OP) { | |
| Status = EFI_NOT_FOUND; | |
| goto Exit; | |
| } | |
| // | |
| // Get handle of data object | |
| // | |
| Status = AcpiSdtProtocol->GetChild (ObjectHandle, &DataHandle); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = AcpiSdtProtocol->GetOption (DataHandle, 0, &DataType, (VOID *)&Buffer, &BufferSize); | |
| ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE); | |
| ASSERT (Buffer != NULL); | |
| if ((Buffer[0] == AML_ZERO_OP) || (Buffer[0] == AML_ONE_OP)) { | |
| Status = AcpiSdtProtocol->SetOption (DataHandle, 0, (VOID *)&Value, sizeof (UINT8)); | |
| ASSERT_EFI_ERROR (Status); | |
| } else { | |
| // | |
| // Check the size of data object | |
| // | |
| switch (Buffer[0]) { | |
| case AML_BYTE_PREFIX: | |
| DataSize = sizeof (UINT8); | |
| break; | |
| case AML_WORD_PREFIX: | |
| DataSize = sizeof (UINT16); | |
| break; | |
| case AML_DWORD_PREFIX: | |
| DataSize = sizeof (UINT32); | |
| break; | |
| case AML_QWORD_PREFIX: | |
| DataSize = sizeof (UINT64); | |
| break; | |
| default: | |
| // The data type of the ACPI object is not an integer | |
| Status = EFI_INVALID_PARAMETER; | |
| goto Exit; | |
| } | |
| Status = AcpiSdtProtocol->SetOption (DataHandle, 1, (VOID *)&Value, DataSize); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| Exit: | |
| AcpiSdtProtocol->Close (DataHandle); | |
| AcpiSdtProtocol->Close (ObjectHandle); | |
| return Status; | |
| } |