| /** @file | |
| * | |
| * Copyright (c) 2011-2014, ARM Limited. All rights reserved. | |
| * Copyright (c) 2014-2020, Linaro Limited. All rights reserved. | |
| * | |
| * SPDX-License-Identifier: BSD-2-Clause-Patent | |
| * | |
| **/ | |
| #include <PiPei.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/FdtLib.h> | |
| #include <Library/HobLib.h> | |
| #include <Library/PcdLib.h> | |
| #include <Library/PeiServicesLib.h> | |
| #include <Library/FdtSerialPortAddressLib.h> | |
| #include <Guid/EarlyPL011BaseAddress.h> | |
| #include <Guid/FdtHob.h> | |
| STATIC CONST EFI_PEI_PPI_DESCRIPTOR mTpm2DiscoveredPpi = { | |
| EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, | |
| &gOvmfTpmDiscoveredPpiGuid, | |
| NULL | |
| }; | |
| STATIC CONST EFI_PEI_PPI_DESCRIPTOR mTpm2InitializationDonePpi = { | |
| EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, | |
| &gPeiTpmInitializationDonePpiGuid, | |
| NULL | |
| }; | |
| EFI_STATUS | |
| EFIAPI | |
| PlatformPeim ( | |
| VOID | |
| ) | |
| { | |
| VOID *Base; | |
| VOID *NewBase; | |
| UINTN FdtSize; | |
| UINTN FdtPages; | |
| UINT64 *FdtHobData; | |
| EARLY_PL011_BASE_ADDRESS *UartHobData; | |
| FDT_SERIAL_PORTS Ports; | |
| INT32 Node, Prev; | |
| INT32 Parent, Depth; | |
| CONST CHAR8 *Compatible; | |
| CONST CHAR8 *CompItem; | |
| INT32 Len; | |
| INT32 RangesLen; | |
| CONST UINT64 *RegProp; | |
| CONST UINT32 *RangesProp; | |
| UINT64 TpmBase; | |
| EFI_STATUS Status; | |
| Base = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress); | |
| ASSERT (Base != NULL); | |
| ASSERT (FdtCheckHeader (Base) == 0); | |
| FdtSize = FdtTotalSize (Base) + PcdGet32 (PcdDeviceTreeAllocationPadding); | |
| FdtPages = EFI_SIZE_TO_PAGES (FdtSize); | |
| NewBase = AllocatePages (FdtPages); | |
| ASSERT (NewBase != NULL); | |
| FdtOpenInto (Base, NewBase, EFI_PAGES_TO_SIZE (FdtPages)); | |
| FdtHobData = BuildGuidHob (&gFdtHobGuid, sizeof *FdtHobData); | |
| ASSERT (FdtHobData != NULL); | |
| *FdtHobData = (UINTN)NewBase; | |
| UartHobData = BuildGuidHob (&gEarlyPL011BaseAddressGuid, sizeof *UartHobData); | |
| ASSERT (UartHobData != NULL); | |
| SetMem (UartHobData, sizeof *UartHobData, 0); | |
| Status = FdtSerialGetPorts (Base, "arm,pl011", &Ports); | |
| if (!EFI_ERROR (Status)) { | |
| if (Ports.NumberOfPorts == 1) { | |
| // | |
| // Just one UART; direct both SerialPortLib+console and DebugLib to it. | |
| // | |
| UartHobData->ConsoleAddress = Ports.BaseAddress[0]; | |
| UartHobData->DebugAddress = Ports.BaseAddress[0]; | |
| } else { | |
| UINT64 ConsoleAddress; | |
| Status = FdtSerialGetConsolePort (Base, &ConsoleAddress); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // At least two UARTs; but failed to get the console preference. Use the | |
| // first UART for SerialPortLib+console, and the second one for | |
| // DebugLib. | |
| // | |
| UartHobData->ConsoleAddress = Ports.BaseAddress[0]; | |
| UartHobData->DebugAddress = Ports.BaseAddress[1]; | |
| } else { | |
| // | |
| // At least two UARTs; and console preference available. Use the | |
| // preferred UART for SerialPortLib+console, and *another* UART for | |
| // DebugLib. | |
| // | |
| UartHobData->ConsoleAddress = ConsoleAddress; | |
| if (ConsoleAddress == Ports.BaseAddress[0]) { | |
| UartHobData->DebugAddress = Ports.BaseAddress[1]; | |
| } else { | |
| UartHobData->DebugAddress = Ports.BaseAddress[0]; | |
| } | |
| } | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: PL011 UART (console) @ 0x%lx\n", | |
| __func__, | |
| UartHobData->ConsoleAddress | |
| )); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: PL011 UART (debug) @ 0x%lx\n", | |
| __func__, | |
| UartHobData->DebugAddress | |
| )); | |
| } | |
| TpmBase = 0; | |
| // | |
| // Set Parent to suppress incorrect compiler/analyzer warnings. | |
| // | |
| Parent = 0; | |
| for (Prev = Depth = 0; ; Prev = Node) { | |
| Node = FdtNextNode (Base, Prev, &Depth); | |
| if (Node < 0) { | |
| break; | |
| } | |
| if (Depth == 1) { | |
| Parent = Node; | |
| } | |
| Compatible = FdtGetProp (Base, Node, "compatible", &Len); | |
| // | |
| // Iterate over the NULL-separated items in the compatible string | |
| // | |
| for (CompItem = Compatible; CompItem != NULL && CompItem < Compatible + Len; | |
| CompItem += 1 + AsciiStrLen (CompItem)) | |
| { | |
| if (FeaturePcdGet (PcdTpm2SupportEnabled) && | |
| (AsciiStrCmp (CompItem, "tcg,tpm-tis-mmio") == 0)) | |
| { | |
| RegProp = FdtGetProp (Base, Node, "reg", &Len); | |
| ASSERT (Len == 8 || Len == 16); | |
| if (Len == 8) { | |
| TpmBase = Fdt32ToCpu (RegProp[0]); | |
| } else if (Len == 16) { | |
| TpmBase = Fdt64ToCpu (ReadUnaligned64 ((UINT64 *)RegProp)); | |
| } | |
| if (Depth > 1) { | |
| // | |
| // QEMU/mach-virt may put the TPM on the platform bus, in which case | |
| // we have to take its 'ranges' property into account to translate the | |
| // MMIO address. This consists of a <child base, parent base, size> | |
| // tuple, where the child base and the size use the same number of | |
| // cells as the 'reg' property above, and the parent base uses 2 cells | |
| // | |
| RangesProp = FdtGetProp (Base, Parent, "ranges", &RangesLen); | |
| ASSERT (RangesProp != NULL); | |
| // | |
| // a plain 'ranges' attribute without a value implies a 1:1 mapping | |
| // | |
| if (RangesLen != 0) { | |
| // | |
| // assume a single translated range with 2 cells for the parent base | |
| // | |
| if (RangesLen != Len + 2 * sizeof (UINT32)) { | |
| DEBUG (( | |
| DEBUG_WARN, | |
| "%a: 'ranges' property has unexpected size %d\n", | |
| __func__, | |
| RangesLen | |
| )); | |
| break; | |
| } | |
| if (Len == 8) { | |
| TpmBase -= Fdt32ToCpu (RangesProp[0]); | |
| } else { | |
| TpmBase -= Fdt64ToCpu (ReadUnaligned64 ((UINT64 *)RangesProp)); | |
| } | |
| // | |
| // advance RangesProp to the parent bus address | |
| // | |
| RangesProp = (UINT32 *)((UINT8 *)RangesProp + Len / 2); | |
| TpmBase += Fdt64ToCpu (ReadUnaligned64 ((UINT64 *)RangesProp)); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| if (FeaturePcdGet (PcdTpm2SupportEnabled)) { | |
| if (TpmBase != 0) { | |
| DEBUG ((DEBUG_INFO, "%a: TPM @ 0x%lx\n", __func__, TpmBase)); | |
| Status = (EFI_STATUS)PcdSet64S (PcdTpmBaseAddress, TpmBase); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = PeiServicesInstallPpi (&mTpm2DiscoveredPpi); | |
| } else { | |
| Status = PeiServicesInstallPpi (&mTpm2InitializationDonePpi); | |
| } | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| BuildFvHob (PcdGet64 (PcdFvBaseAddress), PcdGet32 (PcdFvSize)); | |
| return EFI_SUCCESS; | |
| } |