/** @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 #include #include #include #include #include #include #include #include #include #include #include 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 (fdt_check_header (Base) == 0); FdtSize = fdt_totalsize (Base) + PcdGet32 (PcdDeviceTreeAllocationPadding); FdtPages = EFI_SIZE_TO_PAGES (FdtSize); NewBase = AllocatePages (FdtPages); ASSERT (NewBase != NULL); fdt_open_into (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 = fdt_next_node (Base, Prev, &Depth); if (Node < 0) { break; } if (Depth == 1) { Parent = Node; } Compatible = fdt_getprop (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 = fdt_getprop (Base, Node, "reg", &Len); ASSERT (Len == 8 || Len == 16); if (Len == 8) { TpmBase = fdt32_to_cpu (RegProp[0]); } else if (Len == 16) { TpmBase = fdt64_to_cpu (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 // 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 = fdt_getprop (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 -= fdt32_to_cpu (RangesProp[0]); } else { TpmBase -= fdt64_to_cpu (ReadUnaligned64 ((UINT64 *)RangesProp)); } // // advance RangesProp to the parent bus address // RangesProp = (UINT32 *)((UINT8 *)RangesProp + Len / 2); TpmBase += fdt64_to_cpu (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)); #ifdef MDE_CPU_AARCH64 // // Set the SMCCC conduit to SMC if executing at EL2, which is typically the // exception level that services HVCs rather than the one that invokes them. // if (ArmReadCurrentEL () == AARCH64_EL2) { Status = PcdSetBoolS (PcdMonitorConduitHvc, FALSE); ASSERT_EFI_ERROR (Status); } #endif return EFI_SUCCESS; }