On ARM systems, whether SMC or HVC instructions need to be used to issue monitor calls is typically dependent on the exception level, but there are also cases where EL1 might use SMC instructions, so there is no hard and fast rule. For ArmVirtQemu, this does depend strictly on the exception level, so set the default to HVC (for EL1 execution) and override it to SMC when booted at EL2. Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Leif Lindholm <quic_llindhol@quicinc.com> Cc: Sami Mujawar <sami.mujawar@arm.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Committed-by: Ard Biesheuvel <ardb@kernel.org> Signed-off-by: Doug Flick [MSFT] <doug.edk2@gmail.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
243 lines
7.3 KiB
C
243 lines
7.3 KiB
C
/** @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/HobLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/PeiServicesLib.h>
|
|
#include <Library/FdtSerialPortAddressLib.h>
|
|
#include <libfdt.h>
|
|
|
|
#include <Chipset/AArch64.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 (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 <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 = 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;
|
|
}
|