/** @file Library that implements the helper functions to parse and pack a Transfer List as specified by the A-profile Firmware Handoff Specification. Copyright (c) 2022 - 2025, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Reference(s): - https://github.com/FirmwareHandoff/firmware_handoff **/ #include #include #include #include #include /** Get the TransferList from HOB list. @param[out] TransferList TransferList @retval EFI_SUCCESS TransferList is found. @retval EFI_NOT_FOUND TransferList is not found. **/ EFI_STATUS EFIAPI TransferListGetFromHobList ( OUT TRANSFER_LIST_HEADER **TransferList ) { VOID *HobList; EFI_HOB_GUID_TYPE *GuidHob; UINTN *GuidHobData; *TransferList = NULL; HobList = GetHobList (); if (HobList == NULL) { return EFI_NOT_FOUND; } GuidHob = GetNextGuidHob (&gArmTransferListHobGuid, HobList); if (GuidHob == NULL) { return EFI_NOT_FOUND; } GuidHobData = GET_GUID_HOB_DATA (GuidHob); *TransferList = (TRANSFER_LIST_HEADER *)(*GuidHobData); return EFI_SUCCESS; } /** This function verifies the checksum of the Transfer List. @param [in] TransferListHeader Pointer to the Transfer List Header @retval FALSE Invalid Checksum @retval TRUE Valid Checksum **/ BOOLEAN EFIAPI TransferListVerifyChecksum ( IN TRANSFER_LIST_HEADER *TransferListHeader ) { if (TransferListHeader == NULL) { return FALSE; } if ((TransferListHeader->Flags & TRANSFER_LIST_FL_HAS_CHECKSUM) == 0) { return TRUE; } return (CalculateSum8 ((UINT8 *)TransferListHeader, TransferListHeader->UsedSize) == 0); } /** This function checks the header of the Transfer List. @param [in] TransferListHeader Pointer to the Transfer List Header @return TRANSFER_LIST_OPS code indicating the validity of the Transfer List **/ TRANSFER_LIST_OPS EFIAPI TransferListCheckHeader ( IN TRANSFER_LIST_HEADER *TransferListHeader ) { if (TransferListHeader == NULL) { return TRANSFER_LIST_OPS_INVALID; } if (TransferListHeader->Signature != TRANSFER_LIST_SIGNATURE_64) { DEBUG ((DEBUG_ERROR, "Bad transfer list signature 0x%x\n", TransferListHeader->Signature)); return TRANSFER_LIST_OPS_INVALID; } if (TransferListHeader->TotalSize == 0) { DEBUG ((DEBUG_ERROR, "Bad transfer list total size 0x%x\n", TransferListHeader->TotalSize)); return TRANSFER_LIST_OPS_INVALID; } if (TransferListHeader->UsedSize > TransferListHeader->TotalSize) { DEBUG ((DEBUG_ERROR, "Bad transfer list used size 0x%x\n", TransferListHeader->UsedSize)); return TRANSFER_LIST_OPS_INVALID; } if (TransferListHeader->HeaderSize != sizeof (TRANSFER_LIST_HEADER)) { DEBUG ((DEBUG_ERROR, "Bad transfer list header size 0x%x\n", TransferListHeader->HeaderSize)); return TRANSFER_LIST_OPS_INVALID; } if (TransferListVerifyChecksum (TransferListHeader) == FALSE) { DEBUG ((DEBUG_ERROR, "Bad transfer list checksum 0x%x\n", TransferListHeader->Checksum)); return TRANSFER_LIST_OPS_INVALID; } if (TransferListHeader->Version == 0) { DEBUG ((DEBUG_ERROR, "Transfer list version is invalid\n")); return TRANSFER_LIST_OPS_INVALID; } else if (TransferListHeader->Version == ARM_FW_HANDOFF_PROTOCOL_VERSION) { DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Transfer list version is valid for all operations\n")); return TRANSFER_LIST_OPS_ALL; } else if (TransferListHeader->Version > ARM_FW_HANDOFF_PROTOCOL_VERSION) { DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Transfer list version is valid for read-only\n")); return TRANSFER_LIST_OPS_RO; } DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Old or custom transfer list version is detected\n")); return TRANSFER_LIST_OPS_CUSTOM; } /** Return the first Transfer Entry Node in the Transfer List. @param [in] TransferListHeader TransferListHeader @return Pointer to the Transfer Entry Node if successful otherwise NULL **/ TRANSFER_ENTRY_HEADER * EFIAPI TransferListGetFirstEntry ( IN TRANSFER_LIST_HEADER *TransferListHeader ) { return TransferListGetNextEntry (TransferListHeader, NULL); } /** Return the next Transfer Entry Node in the Transfer List from last Transfer Entry Node. @param [in] TransferListHeader Pointer to the Transfer List Header. @param [in] CurrentEntry Pointer to the Current Transfer Entry. If this is NULL, the first Transfer Entry is returned. @return Pointer to the Transfer Entry Node if successful otherwise NULL **/ TRANSFER_ENTRY_HEADER * EFIAPI TransferListGetNextEntry ( IN TRANSFER_LIST_HEADER *TransferListHeader, IN TRANSFER_ENTRY_HEADER *CurrentEntry ) { TRANSFER_ENTRY_HEADER *Entry; UINTN CurrentAddr; UINTN EndAddr; if (TransferListHeader == NULL) { return NULL; } EndAddr = (UINTN)TransferListHeader + TransferListHeader->UsedSize; if (CurrentEntry != NULL) { CurrentAddr = (UINTN)CurrentEntry + CurrentEntry->HeaderSize + CurrentEntry->DataSize; } else { CurrentAddr = (UINTN)TransferListHeader + TransferListHeader->HeaderSize; } CurrentAddr = ALIGN_VALUE (CurrentAddr, (1 << TransferListHeader->Alignment)); Entry = (TRANSFER_ENTRY_HEADER *)CurrentAddr; if (((CurrentAddr + sizeof (TRANSFER_LIST_HEADER)) < CurrentAddr) || ((CurrentAddr + sizeof (TRANSFER_ENTRY_HEADER)) > EndAddr) || ((CurrentAddr + Entry->HeaderSize + Entry->DataSize) < CurrentAddr) || ((CurrentAddr + Entry->HeaderSize + Entry->DataSize) > EndAddr)) { return NULL; } return Entry; } /** Return the first Transfer Entry Node in the Transfer List matched with given tag-id. @param [in] TransferListHeader Pointer to the Transfer List Header. @param [in] TagId Tag id @return Pointer to the Transfer Entry Node if successful otherwise NULL **/ TRANSFER_ENTRY_HEADER * EFIAPI TransferListFindFirstEntry ( IN TRANSFER_LIST_HEADER *TransferListHeader, IN UINT16 TagId ) { TRANSFER_ENTRY_HEADER *Entry; Entry = TransferListGetFirstEntry (TransferListHeader); while ((Entry != NULL) && ((Entry->TagId != TagId) || Entry->Reserved0 != 0)) { Entry = TransferListGetNextEntry (TransferListHeader, Entry); } return Entry; } /** Return the Next Transfer Entry Node in the Transfer List matched with given tag-id from last Transfer Entry Node. @param [in] TransferListHeader Pointer to the Transfer List Header. @param [in] CurrentEntry Pointer to the Current Transfer Entry. If this is NULL, the first Transfer Entry is returned. @param [in] TagId Tag id @return Pointer to the Transfer Entry Node if successful otherwise NULL **/ TRANSFER_ENTRY_HEADER * EFIAPI TransferListFindNextEntry ( IN TRANSFER_LIST_HEADER *TransferListHeader, IN TRANSFER_ENTRY_HEADER *CurrentEntry, IN UINT16 TagId ) { TRANSFER_ENTRY_HEADER *Entry; if (CurrentEntry == NULL) { return TransferListFindFirstEntry (TransferListHeader, TagId); } else { Entry = TransferListGetNextEntry (TransferListHeader, CurrentEntry); } while ((Entry != NULL) && ((Entry->TagId != TagId) || Entry->Reserved0 != 0)) { Entry = TransferListGetNextEntry (TransferListHeader, Entry); } return Entry; } /** Return the data in Transfer Entry. @param [in] TransferEntry Pointer to a Transfer Entry Header @return Pointer to the Data of Transfer Entry Node if successful otherwise NULL **/ VOID * EFIAPI TransferListGetEntryData ( IN TRANSFER_ENTRY_HEADER *TransferEntry ) { if ((TransferEntry == NULL) || (TransferEntry->DataSize == 0)) { return NULL; } return (VOID *)((UINTN)TransferEntry + TransferEntry->HeaderSize); } /** Find a Transfer Entry Node in the Transfer List matched with the given tag-id. @param [in] TransferListHeader Pointer to the Transfer List Header @param [in] TagId Tag id @return Pointer to the Transfer Entry Node if successful otherwise NULL **/ TRANSFER_ENTRY_HEADER * EFIAPI TransferListFindEntry ( IN TRANSFER_LIST_HEADER *TransferListHeader, IN UINT16 TagId ) { TRANSFER_ENTRY_HEADER *Entry = NULL; do { Entry = TransferListGetNextEntry (TransferListHeader, Entry); } while ((Entry != NULL) && (Entry->TagId != TagId)); return Entry; } /** Get TPM event log from TransferList @param [in] TransferListHeader Pointer to the Transfer List Header @param [out] EventLog Pointer to Eventlog in TransferList @param [out] EventLogSize Size of Event log @param [out] EventLogFlags Flags for Event log @return EFI_SUCCESS @return EFI_NOT_FOUND No Event log in TransferListHeader @return EFI_INVALID_PARAMETER Invalid parameters **/ EFI_STATUS EFIAPI TransferListGetEventLog ( IN TRANSFER_LIST_HEADER *TransferListHeader, OUT VOID **EventLog, OUT UINTN *EventLogSize, OUT UINT32 *EventLogFlags OPTIONAL ) { TRANSFER_ENTRY_HEADER *Entry; TRANSFER_LIST_EVENTLOG *EntryData; if ((TransferListHeader == NULL) || (EventLog == NULL) || (EventLogSize == NULL)) { return EFI_INVALID_PARAMETER; } *EventLog = NULL; *EventLogSize = 0; Entry = TransferListFindFirstEntry (TransferListHeader, TRANSFER_ENTRY_TAG_ID_TPM_EVENT_LOG); if ((Entry == NULL) || (Entry->DataSize == 0) || ((Entry->DataSize - OFFSET_OF (TRANSFER_LIST_EVENTLOG, EventLog)) == 0)) { return EFI_NOT_FOUND; } EntryData = TransferListGetEntryData (Entry); if (EventLogFlags != NULL) { *EventLogFlags = EntryData->Flags; } *EventLogSize = Entry->DataSize - OFFSET_OF (TRANSFER_LIST_EVENTLOG, EventLog); *EventLog = (VOID *)&EntryData->EventLog; return EFI_SUCCESS; } /** Dump the transfer list to the debug output. @param [in] TransferListHeader Pointer to the Transfer List Header **/ VOID EFIAPI TransferListDump ( IN TRANSFER_LIST_HEADER *TransferListHeader ) { TRANSFER_ENTRY_HEADER *Entry; UINTN Idx; Entry = NULL; Idx = 0; if (TransferListHeader == NULL) { return; } DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Dump transfer list:\n")); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "signature 0x%x\n", TransferListHeader->Signature)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "checksum 0x%x\n", TransferListHeader->Checksum)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "version 0x%x\n", TransferListHeader->Version)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "hdr_size 0x%x\n", TransferListHeader->HeaderSize)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "alignment 0x%x\n", TransferListHeader->Alignment)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "used_size 0x%x\n", TransferListHeader->UsedSize)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "total_size 0x%x\n", TransferListHeader->TotalSize)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "flags 0x%x\n", TransferListHeader->Flags)); while (TRUE) { Entry = TransferListGetNextEntry (TransferListHeader, Entry); if (Entry == NULL) { break; } DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Entry %d:\n", Idx++)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "tag_id 0x%x\n", Entry->TagId)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "hdr_size 0x%x\n", Entry->HeaderSize)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "data_size 0x%x\n", Entry->DataSize)); DEBUG ((DEBUG_INFO | DEBUG_LOAD, "data_addr 0x%lx\n", (UINTN)TransferListGetEntryData (Entry))); } }