StandaloneMmPkg: build for 32bit arm machines

This change allows to build StandaloneMmPkg components for 32bit Arm
StandaloneMm firmware.

This change mainly moves AArch64/ source files to Arm/ side directory
for several components:  StandaloneMmCpu, StandaloneMmCoreEntryPoint
and StandaloneMmMemLib. The source file is built for both 32b and 64b
Arm targets.

Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
Etienne Carriere
2021-08-09 17:19:46 +02:00
committed by mergify[bot]
parent b7f0226a46
commit a776bbabd9
18 changed files with 49 additions and 37 deletions

View File

@@ -0,0 +1,205 @@
/** @file
Creates HOB during Standalone MM Foundation entry point
on ARM platforms.
Copyright (c) 2017 - 2021, Arm Ltd. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiMm.h>
#include <PiPei.h>
#include <Guid/MmramMemoryReserve.h>
#include <Guid/MpInformation.h>
#include <Library/Arm/StandaloneMmCoreEntryPoint.h>
#include <Library/ArmMmuLib.h>
#include <Library/ArmSvcLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/SerialPortLib.h>
#include <IndustryStandard/ArmStdSmc.h>
extern EFI_HOB_HANDOFF_INFO_TABLE*
HobConstructor (
IN VOID *EfiMemoryBegin,
IN UINTN EfiMemoryLength,
IN VOID *EfiFreeMemoryBottom,
IN VOID *EfiFreeMemoryTop
);
// GUID to identify HOB with whereabouts of communication buffer with Normal
// World
extern EFI_GUID gEfiStandaloneMmNonSecureBufferGuid;
// GUID to identify HOB where the entry point of the CPU driver will be
// populated to allow this entry point driver to invoke it upon receipt of an
// event
extern EFI_GUID gEfiArmTfCpuDriverEpDescriptorGuid;
/**
Use the boot information passed by privileged firmware to populate a HOB list
suitable for consumption by the MM Core and drivers.
@param [in, out] CpuDriverEntryPoint Address of MM CPU driver entrypoint
@param [in] PayloadBootInfo Boot information passed by privileged
firmware
**/
VOID *
CreateHobListFromBootInfo (
IN OUT PI_MM_ARM_TF_CPU_DRIVER_ENTRYPOINT *CpuDriverEntryPoint,
IN EFI_SECURE_PARTITION_BOOT_INFO *PayloadBootInfo
)
{
EFI_HOB_HANDOFF_INFO_TABLE *HobStart;
EFI_RESOURCE_ATTRIBUTE_TYPE Attributes;
UINT32 Index;
UINT32 BufferSize;
UINT32 Flags;
EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *MmramRangesHob;
EFI_MMRAM_DESCRIPTOR *MmramRanges;
EFI_MMRAM_DESCRIPTOR *NsCommBufMmramRange;
MP_INFORMATION_HOB_DATA *MpInformationHobData;
EFI_PROCESSOR_INFORMATION *ProcInfoBuffer;
EFI_SECURE_PARTITION_CPU_INFO *CpuInfo;
ARM_TF_CPU_DRIVER_EP_DESCRIPTOR *CpuDriverEntryPointDesc;
// Create a hoblist with a PHIT and EOH
HobStart = HobConstructor (
(VOID *) (UINTN) PayloadBootInfo->SpMemBase,
(UINTN) PayloadBootInfo->SpMemLimit - PayloadBootInfo->SpMemBase,
(VOID *) (UINTN) PayloadBootInfo->SpHeapBase,
(VOID *) (UINTN) (PayloadBootInfo->SpHeapBase + PayloadBootInfo->SpHeapSize)
);
// Check that the Hoblist starts at the bottom of the Heap
ASSERT (HobStart == (VOID *) (UINTN) PayloadBootInfo->SpHeapBase);
// Build a Boot Firmware Volume HOB
BuildFvHob (PayloadBootInfo->SpImageBase, PayloadBootInfo->SpImageSize);
// Build a resource descriptor Hob that describes the available physical
// memory range
Attributes = (
EFI_RESOURCE_ATTRIBUTE_PRESENT |
EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
EFI_RESOURCE_ATTRIBUTE_TESTED |
EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE
);
BuildResourceDescriptorHob (
EFI_RESOURCE_SYSTEM_MEMORY,
Attributes,
(UINTN) PayloadBootInfo->SpMemBase,
PayloadBootInfo->SpMemLimit - PayloadBootInfo->SpMemBase
);
// Find the size of the GUIDed HOB with MP information
BufferSize = sizeof (MP_INFORMATION_HOB_DATA);
BufferSize += sizeof (EFI_PROCESSOR_INFORMATION) * PayloadBootInfo->NumCpus;
// Create a Guided MP information HOB to enable the ARM TF CPU driver to
// perform per-cpu allocations.
MpInformationHobData = BuildGuidHob (&gMpInformationHobGuid, BufferSize);
// Populate the MP information HOB with the topology information passed by
// privileged firmware
MpInformationHobData->NumberOfProcessors = PayloadBootInfo->NumCpus;
MpInformationHobData->NumberOfEnabledProcessors = PayloadBootInfo->NumCpus;
ProcInfoBuffer = MpInformationHobData->ProcessorInfoBuffer;
CpuInfo = PayloadBootInfo->CpuInfo;
for (Index = 0; Index < PayloadBootInfo->NumCpus; Index++) {
ProcInfoBuffer[Index].ProcessorId = CpuInfo[Index].Mpidr;
ProcInfoBuffer[Index].Location.Package = GET_CLUSTER_ID(CpuInfo[Index].Mpidr);
ProcInfoBuffer[Index].Location.Core = GET_CORE_ID(CpuInfo[Index].Mpidr);
ProcInfoBuffer[Index].Location.Thread = GET_CORE_ID(CpuInfo[Index].Mpidr);
Flags = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
if (CpuInfo[Index].Flags & CPU_INFO_FLAG_PRIMARY_CPU) {
Flags |= PROCESSOR_AS_BSP_BIT;
}
ProcInfoBuffer[Index].StatusFlag = Flags;
}
// Create a Guided HOB to tell the ARM TF CPU driver the location and length
// of the communication buffer shared with the Normal world.
NsCommBufMmramRange = (EFI_MMRAM_DESCRIPTOR *) BuildGuidHob (
&gEfiStandaloneMmNonSecureBufferGuid,
sizeof (EFI_MMRAM_DESCRIPTOR)
);
NsCommBufMmramRange->PhysicalStart = PayloadBootInfo->SpNsCommBufBase;
NsCommBufMmramRange->CpuStart = PayloadBootInfo->SpNsCommBufBase;
NsCommBufMmramRange->PhysicalSize = PayloadBootInfo->SpNsCommBufSize;
NsCommBufMmramRange->RegionState = EFI_CACHEABLE | EFI_ALLOCATED;
// Create a Guided HOB to enable the ARM TF CPU driver to share its entry
// point and populate it with the address of the shared buffer
CpuDriverEntryPointDesc = (ARM_TF_CPU_DRIVER_EP_DESCRIPTOR *) BuildGuidHob (
&gEfiArmTfCpuDriverEpDescriptorGuid,
sizeof (ARM_TF_CPU_DRIVER_EP_DESCRIPTOR)
);
*CpuDriverEntryPoint = NULL;
CpuDriverEntryPointDesc->ArmTfCpuDriverEpPtr = CpuDriverEntryPoint;
// Find the size of the GUIDed HOB with SRAM ranges
BufferSize = sizeof (EFI_MMRAM_HOB_DESCRIPTOR_BLOCK);
BufferSize += PayloadBootInfo->NumSpMemRegions * sizeof (EFI_MMRAM_DESCRIPTOR);
// Create a GUIDed HOB with SRAM ranges
MmramRangesHob = BuildGuidHob (&gEfiMmPeiMmramMemoryReserveGuid, BufferSize);
// Fill up the number of MMRAM memory regions
MmramRangesHob->NumberOfMmReservedRegions = PayloadBootInfo->NumSpMemRegions;
// Fill up the MMRAM ranges
MmramRanges = &MmramRangesHob->Descriptor[0];
// Base and size of memory occupied by the Standalone MM image
MmramRanges[0].PhysicalStart = PayloadBootInfo->SpImageBase;
MmramRanges[0].CpuStart = PayloadBootInfo->SpImageBase;
MmramRanges[0].PhysicalSize = PayloadBootInfo->SpImageSize;
MmramRanges[0].RegionState = EFI_CACHEABLE | EFI_ALLOCATED;
// Base and size of buffer shared with privileged Secure world software
MmramRanges[1].PhysicalStart = PayloadBootInfo->SpSharedBufBase;
MmramRanges[1].CpuStart = PayloadBootInfo->SpSharedBufBase;
MmramRanges[1].PhysicalSize = PayloadBootInfo->SpPcpuSharedBufSize * PayloadBootInfo->NumCpus;
MmramRanges[1].RegionState = EFI_CACHEABLE | EFI_ALLOCATED;
// Base and size of buffer used for synchronous communication with Normal
// world software
MmramRanges[2].PhysicalStart = PayloadBootInfo->SpNsCommBufBase;
MmramRanges[2].CpuStart = PayloadBootInfo->SpNsCommBufBase;
MmramRanges[2].PhysicalSize = PayloadBootInfo->SpNsCommBufSize;
MmramRanges[2].RegionState = EFI_CACHEABLE | EFI_ALLOCATED;
// Base and size of memory allocated for stacks for all cpus
MmramRanges[3].PhysicalStart = PayloadBootInfo->SpStackBase;
MmramRanges[3].CpuStart = PayloadBootInfo->SpStackBase;
MmramRanges[3].PhysicalSize = PayloadBootInfo->SpPcpuStackSize * PayloadBootInfo->NumCpus;
MmramRanges[3].RegionState = EFI_CACHEABLE | EFI_ALLOCATED;
// Base and size of heap memory shared by all cpus
MmramRanges[4].PhysicalStart = (EFI_PHYSICAL_ADDRESS) (UINTN) HobStart;
MmramRanges[4].CpuStart = (EFI_PHYSICAL_ADDRESS) (UINTN) HobStart;
MmramRanges[4].PhysicalSize = HobStart->EfiFreeMemoryBottom - (EFI_PHYSICAL_ADDRESS) (UINTN) HobStart;
MmramRanges[4].RegionState = EFI_CACHEABLE | EFI_ALLOCATED;
// Base and size of heap memory shared by all cpus
MmramRanges[5].PhysicalStart = HobStart->EfiFreeMemoryBottom;
MmramRanges[5].CpuStart = HobStart->EfiFreeMemoryBottom;
MmramRanges[5].PhysicalSize = HobStart->EfiFreeMemoryTop - HobStart->EfiFreeMemoryBottom;
MmramRanges[5].RegionState = EFI_CACHEABLE;
return HobStart;
}

View File

@@ -0,0 +1,322 @@
/** @file
Locate, get and update PE/COFF permissions during Standalone MM
Foundation Entry point on ARM platforms.
Copyright (c) 2017 - 2021, Arm Ltd. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiMm.h>
#include <PiPei.h>
#include <Guid/MmramMemoryReserve.h>
#include <Guid/MpInformation.h>
#include <Library/Arm/StandaloneMmCoreEntryPoint.h>
#include <Library/ArmMmuLib.h>
#include <Library/ArmSvcLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/SerialPortLib.h>
#include <IndustryStandard/ArmStdSmc.h>
/**
Privileged firmware assigns RO & Executable attributes to all memory occupied
by the Boot Firmware Volume. This function sets the correct permissions of
sections in the Standalone MM Core module to be able to access RO and RW data
and make further progress in the boot process.
@param [in] ImageContext Pointer to PE/COFF image context
@param [in] ImageBase Base of image in memory
@param [in] SectionHeaderOffset Offset of PE/COFF image section header
@param [in] NumberOfSections Number of Sections
@param [in] TextUpdater Function to change code permissions
@param [in] ReadOnlyUpdater Function to change RO permissions
@param [in] ReadWriteUpdater Function to change RW permissions
**/
EFI_STATUS
EFIAPI
UpdateMmFoundationPeCoffPermissions (
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
IN EFI_PHYSICAL_ADDRESS ImageBase,
IN UINT32 SectionHeaderOffset,
IN CONST UINT16 NumberOfSections,
IN REGION_PERMISSION_UPDATE_FUNC TextUpdater,
IN REGION_PERMISSION_UPDATE_FUNC ReadOnlyUpdater,
IN REGION_PERMISSION_UPDATE_FUNC ReadWriteUpdater
)
{
EFI_IMAGE_SECTION_HEADER SectionHeader;
RETURN_STATUS Status;
EFI_PHYSICAL_ADDRESS Base;
UINTN Size;
UINTN ReadSize;
UINTN Index;
ASSERT (ImageContext != NULL);
//
// Iterate over the sections
//
for (Index = 0; Index < NumberOfSections; Index++) {
//
// Read section header from file
//
Size = sizeof (EFI_IMAGE_SECTION_HEADER);
ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
SectionHeaderOffset,
&Size,
&SectionHeader
);
if (RETURN_ERROR (Status) || (Size != ReadSize)) {
DEBUG ((DEBUG_ERROR,
"%a: ImageContext->ImageRead () failed (Status = %r)\n",
__FUNCTION__, Status));
return Status;
}
DEBUG ((DEBUG_INFO,
"%a: Section %d of image at 0x%lx has 0x%x permissions\n",
__FUNCTION__, Index, ImageContext->ImageAddress, SectionHeader.Characteristics));
DEBUG ((DEBUG_INFO,
"%a: Section %d of image at 0x%lx has %a name\n",
__FUNCTION__, Index, ImageContext->ImageAddress, SectionHeader.Name));
DEBUG ((DEBUG_INFO,
"%a: Section %d of image at 0x%lx has 0x%x address\n",
__FUNCTION__, Index, ImageContext->ImageAddress,
ImageContext->ImageAddress + SectionHeader.VirtualAddress));
DEBUG ((DEBUG_INFO,
"%a: Section %d of image at 0x%lx has 0x%x data\n",
__FUNCTION__, Index, ImageContext->ImageAddress, SectionHeader.PointerToRawData));
//
// If the section is marked as XN then remove the X attribute. Furthermore,
// if it is a writeable section then mark it appropriately as well.
//
if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) == 0) {
Base = ImageBase + SectionHeader.VirtualAddress;
TextUpdater (Base, SectionHeader.Misc.VirtualSize);
if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_WRITE) != 0) {
ReadWriteUpdater (Base, SectionHeader.Misc.VirtualSize);
DEBUG ((DEBUG_INFO,
"%a: Mapping section %d of image at 0x%lx with RW-XN permissions\n",
__FUNCTION__, Index, ImageContext->ImageAddress));
} else {
DEBUG ((DEBUG_INFO,
"%a: Mapping section %d of image at 0x%lx with RO-XN permissions\n",
__FUNCTION__, Index, ImageContext->ImageAddress));
}
} else {
DEBUG ((DEBUG_INFO,
"%a: Ignoring section %d of image at 0x%lx with 0x%x permissions\n",
__FUNCTION__, Index, ImageContext->ImageAddress, SectionHeader.Characteristics));
}
SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
}
return RETURN_SUCCESS;
}
/**
Privileged firmware assigns RO & Executable attributes to all memory occupied
by the Boot Firmware Volume. This function locates the Standalone MM Core
module PE/COFF image in the BFV and returns this information.
@param [in] BfvAddress Base Address of Boot Firmware Volume
@param [in, out] TeData Pointer to address for allocating memory
for PE/COFF image data
@param [in, out] TeDataSize Pointer to size of PE/COFF image data
**/
EFI_STATUS
EFIAPI
LocateStandaloneMmCorePeCoffData (
IN EFI_FIRMWARE_VOLUME_HEADER *BfvAddress,
IN OUT VOID **TeData,
IN OUT UINTN *TeDataSize
)
{
EFI_FFS_FILE_HEADER *FileHeader;
EFI_STATUS Status;
FileHeader = NULL;
Status = FfsFindNextFile (
EFI_FV_FILETYPE_SECURITY_CORE,
BfvAddress,
&FileHeader
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Unable to locate Standalone MM FFS file - 0x%x\n",
Status));
return Status;
}
Status = FfsFindSectionData (EFI_SECTION_PE32, FileHeader, TeData, TeDataSize);
if (EFI_ERROR (Status)) {
Status = FfsFindSectionData (EFI_SECTION_TE, FileHeader, TeData, TeDataSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Unable to locate Standalone MM Section data - %r\n",
Status));
return Status;
}
}
DEBUG ((DEBUG_INFO, "Found Standalone MM PE data - 0x%x\n", *TeData));
return Status;
}
/**
Returns the PC COFF section information.
@param [in, out] ImageContext Pointer to PE/COFF image context
@param [out] ImageBase Base of image in memory
@param [out] SectionHeaderOffset Offset of PE/COFF image section header
@param [out] NumberOfSections Number of Sections
**/
STATIC
EFI_STATUS
GetPeCoffSectionInformation (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
OUT EFI_PHYSICAL_ADDRESS *ImageBase,
OUT UINT32 *SectionHeaderOffset,
OUT UINT16 *NumberOfSections
)
{
RETURN_STATUS Status;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
UINTN Size;
UINTN ReadSize;
ASSERT (ImageContext != NULL);
ASSERT (SectionHeaderOffset != NULL);
ASSERT (NumberOfSections != NULL);
Status = PeCoffLoaderGetImageInfo (ImageContext);
if (RETURN_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"%a: PeCoffLoaderGetImageInfo () failed (Status == %r)\n",
__FUNCTION__, Status));
return Status;
}
if (ImageContext->SectionAlignment < EFI_PAGE_SIZE) {
//
// The sections need to be at least 4 KB aligned, since that is the
// granularity at which we can tighten permissions.
//
if (!ImageContext->IsTeImage) {
DEBUG ((DEBUG_WARN,
"%a: non-TE Image at 0x%lx has SectionAlignment < 4 KB (%lu)\n",
__FUNCTION__, ImageContext->ImageAddress, ImageContext->SectionAlignment));
return RETURN_UNSUPPORTED;
}
ImageContext->SectionAlignment = EFI_PAGE_SIZE;
}
//
// Read the PE/COFF Header. For PE32 (32-bit) this will read in too much
// data, but that should not hurt anything. Hdr.Pe32->OptionalHeader.Magic
// determines if this is a PE32 or PE32+ image. The magic is in the same
// location in both images.
//
Hdr.Union = &HdrData;
Size = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
ImageContext->PeCoffHeaderOffset,
&Size,
Hdr.Pe32
);
if (RETURN_ERROR (Status) || (Size != ReadSize)) {
DEBUG ((DEBUG_ERROR,
"%a: TmpContext->ImageRead () failed (Status = %r)\n",
__FUNCTION__, Status));
return Status;
}
*ImageBase = ImageContext->ImageAddress;
if (!ImageContext->IsTeImage) {
ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
*SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) +
sizeof (EFI_IMAGE_FILE_HEADER);
*NumberOfSections = Hdr.Pe32->FileHeader.NumberOfSections;
switch (Hdr.Pe32->OptionalHeader.Magic) {
case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
*SectionHeaderOffset += Hdr.Pe32->FileHeader.SizeOfOptionalHeader;
break;
case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
*SectionHeaderOffset += Hdr.Pe32Plus->FileHeader.SizeOfOptionalHeader;
break;
default:
ASSERT (FALSE);
}
} else {
*SectionHeaderOffset = (UINTN)(sizeof (EFI_TE_IMAGE_HEADER));
*NumberOfSections = Hdr.Te->NumberOfSections;
*ImageBase -= (UINT32)Hdr.Te->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER);
}
return RETURN_SUCCESS;
}
/**
Privileged firmware assigns RO & Executable attributes to all memory occupied
by the Boot Firmware Volume. This function locates the section information of
the Standalone MM Core module to be able to change permissions of the
individual sections later in the boot process.
@param [in] TeData Pointer to PE/COFF image data
@param [in, out] ImageContext Pointer to PE/COFF image context
@param [out] ImageBase Pointer to ImageBase variable
@param [in, out] SectionHeaderOffset Offset of PE/COFF image section header
@param [in, out] NumberOfSections Number of Sections
**/
EFI_STATUS
EFIAPI
GetStandaloneMmCorePeCoffSections (
IN VOID *TeData,
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
OUT EFI_PHYSICAL_ADDRESS *ImageBase,
IN OUT UINT32 *SectionHeaderOffset,
IN OUT UINT16 *NumberOfSections
)
{
EFI_STATUS Status;
// Initialize the Image Context
ZeroMem (ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
ImageContext->Handle = TeData;
ImageContext->ImageRead = PeCoffLoaderImageReadFromMemory;
DEBUG ((DEBUG_INFO, "Found Standalone MM PE data - 0x%x\n", TeData));
Status = GetPeCoffSectionInformation (ImageContext, ImageBase,
SectionHeaderOffset, NumberOfSections);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Unable to locate Standalone MM Core PE-COFF Section information - %r\n", Status));
return Status;
}
DEBUG ((DEBUG_INFO, "Standalone MM Core PE-COFF SectionHeaderOffset - 0x%x, NumberOfSections - %d\n",
*SectionHeaderOffset, *NumberOfSections));
return Status;
}

View File

@@ -0,0 +1,413 @@
/** @file
Entry point to the Standalone MM Foundation when initialized during the SEC
phase on ARM platforms
Copyright (c) 2017 - 2021, Arm Ltd. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiMm.h>
#include <Library/Arm/StandaloneMmCoreEntryPoint.h>
#include <PiPei.h>
#include <Guid/MmramMemoryReserve.h>
#include <Guid/MpInformation.h>
#include <Library/ArmMmuLib.h>
#include <Library/ArmSvcLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/SerialPortLib.h>
#include <Library/PcdLib.h>
#include <IndustryStandard/ArmStdSmc.h>
#include <IndustryStandard/ArmMmSvc.h>
#include <IndustryStandard/ArmFfaSvc.h>
#define SPM_MAJOR_VER_MASK 0xFFFF0000
#define SPM_MINOR_VER_MASK 0x0000FFFF
#define SPM_MAJOR_VER_SHIFT 16
#define FFA_NOT_SUPPORTED -1
STATIC CONST UINT32 mSpmMajorVer = SPM_MAJOR_VERSION;
STATIC CONST UINT32 mSpmMinorVer = SPM_MINOR_VERSION;
STATIC CONST UINT32 mSpmMajorVerFfa = SPM_MAJOR_VERSION_FFA;
STATIC CONST UINT32 mSpmMinorVerFfa = SPM_MINOR_VERSION_FFA;
#define BOOT_PAYLOAD_VERSION 1
PI_MM_ARM_TF_CPU_DRIVER_ENTRYPOINT CpuDriverEntryPoint = NULL;
/**
Retrieve a pointer to and print the boot information passed by privileged
secure firmware.
@param [in] SharedBufAddress The pointer memory shared with privileged
firmware.
**/
EFI_SECURE_PARTITION_BOOT_INFO *
GetAndPrintBootinformation (
IN VOID *SharedBufAddress
)
{
EFI_SECURE_PARTITION_BOOT_INFO *PayloadBootInfo;
EFI_SECURE_PARTITION_CPU_INFO *PayloadCpuInfo;
UINTN Index;
PayloadBootInfo = (EFI_SECURE_PARTITION_BOOT_INFO *) SharedBufAddress;
if (PayloadBootInfo == NULL) {
DEBUG ((DEBUG_ERROR, "PayloadBootInfo NULL\n"));
return NULL;
}
if (PayloadBootInfo->Header.Version != BOOT_PAYLOAD_VERSION) {
DEBUG ((DEBUG_ERROR, "Boot Information Version Mismatch. Current=0x%x, Expected=0x%x.\n",
PayloadBootInfo->Header.Version, BOOT_PAYLOAD_VERSION));
return NULL;
}
DEBUG ((DEBUG_INFO, "NumSpMemRegions - 0x%x\n", PayloadBootInfo->NumSpMemRegions));
DEBUG ((DEBUG_INFO, "SpMemBase - 0x%lx\n", PayloadBootInfo->SpMemBase));
DEBUG ((DEBUG_INFO, "SpMemLimit - 0x%lx\n", PayloadBootInfo->SpMemLimit));
DEBUG ((DEBUG_INFO, "SpImageBase - 0x%lx\n", PayloadBootInfo->SpImageBase));
DEBUG ((DEBUG_INFO, "SpStackBase - 0x%lx\n", PayloadBootInfo->SpStackBase));
DEBUG ((DEBUG_INFO, "SpHeapBase - 0x%lx\n", PayloadBootInfo->SpHeapBase));
DEBUG ((DEBUG_INFO, "SpNsCommBufBase - 0x%lx\n", PayloadBootInfo->SpNsCommBufBase));
DEBUG ((DEBUG_INFO, "SpSharedBufBase - 0x%lx\n", PayloadBootInfo->SpSharedBufBase));
DEBUG ((DEBUG_INFO, "SpImageSize - 0x%x\n", PayloadBootInfo->SpImageSize));
DEBUG ((DEBUG_INFO, "SpPcpuStackSize - 0x%x\n", PayloadBootInfo->SpPcpuStackSize));
DEBUG ((DEBUG_INFO, "SpHeapSize - 0x%x\n", PayloadBootInfo->SpHeapSize));
DEBUG ((DEBUG_INFO, "SpNsCommBufSize - 0x%x\n", PayloadBootInfo->SpNsCommBufSize));
DEBUG ((DEBUG_INFO, "SpPcpuSharedBufSize - 0x%x\n", PayloadBootInfo->SpPcpuSharedBufSize));
DEBUG ((DEBUG_INFO, "NumCpus - 0x%x\n", PayloadBootInfo->NumCpus));
DEBUG ((DEBUG_INFO, "CpuInfo - 0x%p\n", PayloadBootInfo->CpuInfo));
PayloadCpuInfo = (EFI_SECURE_PARTITION_CPU_INFO *) PayloadBootInfo->CpuInfo;
if (PayloadCpuInfo == NULL) {
DEBUG ((DEBUG_ERROR, "PayloadCpuInfo NULL\n"));
return NULL;
}
for (Index = 0; Index < PayloadBootInfo->NumCpus; Index++) {
DEBUG ((DEBUG_INFO, "Mpidr - 0x%lx\n", PayloadCpuInfo[Index].Mpidr));
DEBUG ((DEBUG_INFO, "LinearId - 0x%x\n", PayloadCpuInfo[Index].LinearId));
DEBUG ((DEBUG_INFO, "Flags - 0x%x\n", PayloadCpuInfo[Index].Flags));
}
return PayloadBootInfo;
}
/**
A loop to delegated events.
@param [in] EventCompleteSvcArgs Pointer to the event completion arguments.
**/
VOID
EFIAPI
DelegatedEventLoop (
IN ARM_SVC_ARGS *EventCompleteSvcArgs
)
{
BOOLEAN FfaEnabled;
EFI_STATUS Status;
UINTN SvcStatus;
while (TRUE) {
ArmCallSvc (EventCompleteSvcArgs);
DEBUG ((DEBUG_INFO, "Received delegated event\n"));
DEBUG ((DEBUG_INFO, "X0 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg0));
DEBUG ((DEBUG_INFO, "X1 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg1));
DEBUG ((DEBUG_INFO, "X2 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg2));
DEBUG ((DEBUG_INFO, "X3 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg3));
DEBUG ((DEBUG_INFO, "X4 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg4));
DEBUG ((DEBUG_INFO, "X5 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg5));
DEBUG ((DEBUG_INFO, "X6 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg6));
DEBUG ((DEBUG_INFO, "X7 : 0x%x\n", (UINT32) EventCompleteSvcArgs->Arg7));
FfaEnabled = FeaturePcdGet (PcdFfaEnable);
if (FfaEnabled) {
Status = CpuDriverEntryPoint (
EventCompleteSvcArgs->Arg0,
EventCompleteSvcArgs->Arg6,
EventCompleteSvcArgs->Arg3
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Failed delegated event 0x%x, Status 0x%x\n",
EventCompleteSvcArgs->Arg3, Status));
}
} else {
Status = CpuDriverEntryPoint (
EventCompleteSvcArgs->Arg0,
EventCompleteSvcArgs->Arg3,
EventCompleteSvcArgs->Arg1
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Failed delegated event 0x%x, Status 0x%x\n",
EventCompleteSvcArgs->Arg0, Status));
}
}
switch (Status) {
case EFI_SUCCESS:
SvcStatus = ARM_SVC_SPM_RET_SUCCESS;
break;
case EFI_INVALID_PARAMETER:
SvcStatus = ARM_SVC_SPM_RET_INVALID_PARAMS;
break;
case EFI_ACCESS_DENIED:
SvcStatus = ARM_SVC_SPM_RET_DENIED;
break;
case EFI_OUT_OF_RESOURCES:
SvcStatus = ARM_SVC_SPM_RET_NO_MEMORY;
break;
case EFI_UNSUPPORTED:
SvcStatus = ARM_SVC_SPM_RET_NOT_SUPPORTED;
break;
default:
SvcStatus = ARM_SVC_SPM_RET_NOT_SUPPORTED;
break;
}
if (FfaEnabled) {
EventCompleteSvcArgs->Arg0 = ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP;
EventCompleteSvcArgs->Arg1 = 0;
EventCompleteSvcArgs->Arg2 = 0;
EventCompleteSvcArgs->Arg3 = ARM_SVC_ID_SP_EVENT_COMPLETE;
EventCompleteSvcArgs->Arg4 = SvcStatus;
} else {
EventCompleteSvcArgs->Arg0 = ARM_SVC_ID_SP_EVENT_COMPLETE;
EventCompleteSvcArgs->Arg1 = SvcStatus;
}
}
}
/**
Query the SPM version, check compatibility and return success if compatible.
@retval EFI_SUCCESS SPM versions compatible.
@retval EFI_UNSUPPORTED SPM versions not compatible.
**/
STATIC
EFI_STATUS
GetSpmVersion (VOID)
{
EFI_STATUS Status;
UINT16 CalleeSpmMajorVer;
UINT16 CallerSpmMajorVer;
UINT16 CalleeSpmMinorVer;
UINT16 CallerSpmMinorVer;
UINT32 SpmVersion;
ARM_SVC_ARGS SpmVersionArgs;
if (FeaturePcdGet (PcdFfaEnable)) {
SpmVersionArgs.Arg0 = ARM_SVC_ID_FFA_VERSION_AARCH32;
SpmVersionArgs.Arg1 = mSpmMajorVerFfa << SPM_MAJOR_VER_SHIFT;
SpmVersionArgs.Arg1 |= mSpmMinorVerFfa;
CallerSpmMajorVer = mSpmMajorVerFfa;
CallerSpmMinorVer = mSpmMinorVerFfa;
} else {
SpmVersionArgs.Arg0 = ARM_SVC_ID_SPM_VERSION_AARCH32;
CallerSpmMajorVer = mSpmMajorVer;
CallerSpmMinorVer = mSpmMinorVer;
}
ArmCallSvc (&SpmVersionArgs);
SpmVersion = SpmVersionArgs.Arg0;
if (SpmVersion == FFA_NOT_SUPPORTED) {
return EFI_UNSUPPORTED;
}
CalleeSpmMajorVer = ((SpmVersion & SPM_MAJOR_VER_MASK) >> SPM_MAJOR_VER_SHIFT);
CalleeSpmMinorVer = ((SpmVersion & SPM_MINOR_VER_MASK) >> 0);
// Different major revision values indicate possibly incompatible functions.
// For two revisions, A and B, for which the major revision values are
// identical, if the minor revision value of revision B is greater than
// the minor revision value of revision A, then every function in
// revision A must work in a compatible way with revision B.
// However, it is possible for revision B to have a higher
// function count than revision A.
if ((CalleeSpmMajorVer == CallerSpmMajorVer) &&
(CalleeSpmMinorVer >= CallerSpmMinorVer))
{
DEBUG ((DEBUG_INFO, "SPM Version: Major=0x%x, Minor=0x%x\n",
CalleeSpmMajorVer, CalleeSpmMinorVer));
Status = EFI_SUCCESS;
}
else
{
DEBUG ((DEBUG_INFO, "Incompatible SPM Versions.\n Callee Version: Major=0x%x, Minor=0x%x.\n Caller: Major=0x%x, Minor>=0x%x.\n",
CalleeSpmMajorVer, CalleeSpmMinorVer, CallerSpmMajorVer, CallerSpmMinorVer));
Status = EFI_UNSUPPORTED;
}
return Status;
}
/**
Initialize parameters to be sent via SVC call.
@param[out] InitMmFoundationSvcArgs Args structure
@param[out] Ret Return Code
**/
STATIC
VOID
InitArmSvcArgs (
OUT ARM_SVC_ARGS *InitMmFoundationSvcArgs,
OUT INT32 *Ret
)
{
if (FeaturePcdGet (PcdFfaEnable)) {
InitMmFoundationSvcArgs->Arg0 = ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP;
InitMmFoundationSvcArgs->Arg1 = 0;
InitMmFoundationSvcArgs->Arg2 = 0;
InitMmFoundationSvcArgs->Arg3 = ARM_SVC_ID_SP_EVENT_COMPLETE;
InitMmFoundationSvcArgs->Arg4 = *Ret;
} else {
InitMmFoundationSvcArgs->Arg0 = ARM_SVC_ID_SP_EVENT_COMPLETE;
InitMmFoundationSvcArgs->Arg1 = *Ret;
}
}
/**
The entry point of Standalone MM Foundation.
@param [in] SharedBufAddress Pointer to the Buffer between SPM and SP.
@param [in] SharedBufSize Size of the shared buffer.
@param [in] cookie1 Cookie 1
@param [in] cookie2 Cookie 2
**/
VOID
EFIAPI
_ModuleEntryPoint (
IN VOID *SharedBufAddress,
IN UINT64 SharedBufSize,
IN UINT64 cookie1,
IN UINT64 cookie2
)
{
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
EFI_SECURE_PARTITION_BOOT_INFO *PayloadBootInfo;
ARM_SVC_ARGS InitMmFoundationSvcArgs;
EFI_STATUS Status;
INT32 Ret;
UINT32 SectionHeaderOffset;
UINT16 NumberOfSections;
VOID *HobStart;
VOID *TeData;
UINTN TeDataSize;
EFI_PHYSICAL_ADDRESS ImageBase;
// Get Secure Partition Manager Version Information
Status = GetSpmVersion ();
if (EFI_ERROR (Status)) {
goto finish;
}
PayloadBootInfo = GetAndPrintBootinformation (SharedBufAddress);
if (PayloadBootInfo == NULL) {
Status = EFI_UNSUPPORTED;
goto finish;
}
// Locate PE/COFF File information for the Standalone MM core module
Status = LocateStandaloneMmCorePeCoffData (
(EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) PayloadBootInfo->SpImageBase,
&TeData,
&TeDataSize
);
if (EFI_ERROR (Status)) {
goto finish;
}
// Obtain the PE/COFF Section information for the Standalone MM core module
Status = GetStandaloneMmCorePeCoffSections (
TeData,
&ImageContext,
&ImageBase,
&SectionHeaderOffset,
&NumberOfSections
);
if (EFI_ERROR (Status)) {
goto finish;
}
//
// ImageBase may deviate from ImageContext.ImageAddress if we are dealing
// with a TE image, in which case the latter points to the actual offset
// of the image, whereas ImageBase refers to the address where the image
// would start if the stripped PE headers were still in place. In either
// case, we need to fix up ImageBase so it refers to the actual current
// load address.
//
ImageBase += (UINTN)TeData - ImageContext.ImageAddress;
// Update the memory access permissions of individual sections in the
// Standalone MM core module
Status = UpdateMmFoundationPeCoffPermissions (
&ImageContext,
ImageBase,
SectionHeaderOffset,
NumberOfSections,
ArmSetMemoryRegionNoExec,
ArmSetMemoryRegionReadOnly,
ArmClearMemoryRegionReadOnly
);
if (EFI_ERROR (Status)) {
goto finish;
}
if (ImageContext.ImageAddress != (UINTN)TeData) {
ImageContext.ImageAddress = (UINTN)TeData;
ArmSetMemoryRegionNoExec (ImageBase, SIZE_4KB);
ArmClearMemoryRegionReadOnly (ImageBase, SIZE_4KB);
Status = PeCoffLoaderRelocateImage (&ImageContext);
ASSERT_EFI_ERROR (Status);
}
//
// Create Hoblist based upon boot information passed by privileged software
//
HobStart = CreateHobListFromBootInfo (&CpuDriverEntryPoint, PayloadBootInfo);
//
// Call the MM Core entry point
//
ProcessModuleEntryPointList (HobStart);
DEBUG ((DEBUG_INFO, "Shared Cpu Driver EP %p\n", (VOID *) CpuDriverEntryPoint));
finish:
if (Status == RETURN_UNSUPPORTED) {
Ret = -1;
} else if (Status == RETURN_INVALID_PARAMETER) {
Ret = -2;
} else if (Status == EFI_NOT_FOUND) {
Ret = -7;
} else {
Ret = 0;
}
ZeroMem (&InitMmFoundationSvcArgs, sizeof(InitMmFoundationSvcArgs));
InitArmSvcArgs (&InitMmFoundationSvcArgs, &Ret);
DelegatedEventLoop (&InitMmFoundationSvcArgs);
}