Add SEC code for LoongArch virtual machine.
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
---
OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S | 183 +++++++
OvmfPkg/LoongArchVirt/Sec/SecMain.c | 507 ++++++++++++++++++
OvmfPkg/LoongArchVirt/Sec/SecMain.inf | 53 ++
3 files changed, 743 insertions(+)
create mode 100644 OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.c
create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.inf
diff --git a/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
new file mode 100644
index 0000000000..145f8ffc59
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
@@ -0,0 +1,183 @@
+#------------------------------------------------------------------------------
+#
+# Start for Loongson LoongArch processor
+#
+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+# @par Glossary:
+# - CSR - CPU Status Register
+# - EBASE - Exception Base Address
+#------------------------------------------------------------------------------
+#ifndef __ASSEMBLY__
+#define __ASSEMBLY__
+#endif
+
+#include <Library/BaseMemoryLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include <Protocol/DebugSupport.h>
+
+#define BOOTCORE_ID 0
+
+//
+// For coding convenience, define the maximum valid
+// LoongArch exception.
+// Since UEFI V2.11, it will be present in DebugSupport.h.
+//
+#define MAX_LOONGARCH_EXCEPTION 64
+
+ASM_GLOBAL ASM_PFX(_ModuleEntryPoint)
+ASM_PFX(_ModuleEntryPoint):
+ /* Disable interrupt */
+ li.d $t0, (1 << 2)
+ csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
+
+ /* Read physical cpu number id */
+ bl GetApicId
+ li.d $t0, BOOTCORE_ID //0
+ bne $a0, $t0, SlaveMain
+
+ /* Configure BSP reset ebase */
+ li.d $a0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
+ bl SetExceptionBaseAddress
+ move $t1, $a0
+
+ /* Set BSP stack */
+ li.d $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize) # stack base
+ move $sp, $t0
+ addi.d $sp, $sp, -0x8
+
+ /* Construct SEC and PEI step exception environment */
+ la.pcrel $a1, ExceptionEntryStart
+ la.pcrel $t0, ExceptionEntryEnd
+ sub.d $a2, $t0, $a1
+ li.w $t0, (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512
+ bgeu $a2, $t0, DeadLoop
+ move $a0, $t1
+ bl CopyMem
+
+CallEntry:
+ /* Call C function make sure parameter true */
+ li.d $a0, FixedPcdGet64(PcdOvmfFdBaseAddress) # FW base
+ addi.d $a1, $sp, 0x8
+ bl SecCoreStartupWithStack
+# End of _ModuleEntryPoint
+
+ASM_PFX(ClearMailBox):
+ /* Clear mailbox */
+ li.d $t1, LOONGARCH_IOCSR_MBUF3
+ iocsrwr.d $zero, $t1
+ li.d $t1, LOONGARCH_IOCSR_MBUF2
+ iocsrwr.d $zero, $t1
+ li.d $t1, LOONGARCH_IOCSR_MBUF1
+ iocsrwr.d $zero, $t1
+ li.d $t1, LOONGARCH_IOCSR_MBUF0
+ iocsrwr.d $zero, $t1
+ jirl $zero, $ra, 0
+# End of ClearMailBox
+
+ASM_PFX(EnableIPI):
+ /* Enable IPI interrupt */
+ li.d $t1, (1 << 12)
+ csrxchg $t1, $t1, LOONGARCH_CSR_ECFG
+
+ addi.d $t2, $zero, -1
+ li.d $t1, LOONGARCH_IOCSR_IPI_EN
+ iocsrwr.w $t2, $t1
+ jirl $zero, $ra, 0
+# End of EeableIPI
+
+#/**
+# Get APIC ID for every CPU.
+#
+# @param NULL
+# @return APICID
+#
+# UINTN
+# EFI_API
+# GetApicId (
+# VOID
+# )
+#**/
+ASM_PFX(GetApicId):
+ csrrd $a0, LOONGARCH_CSR_CPUNUM
+ andi $a0, $a0, 0x3ff
+ jirl $zero, $ra, 0
+# End of GetApicId
+
+ASM_PFX(ApInitStack):
+ li.d $t1, SIZE_1KB
+ csrrd $t0, LOONGARCH_CSR_TMID
+ mul.d $t1, $t0, $t1
+ li.d $t2, FixedPcdGet32(PcdCpuMaxLogicalProcessorNumber)
+ bgeu $t0, $t2, DeadLoop
+ li.d $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize) - SIZE_64KB
+ sub.d $sp, $t0, $t1
+ addi.d $sp, $sp, -0x8
+ jirl $zero, $ra, 0
+# End of ApInitStack
+
+ASM_PFX(SlaveMain):
+ /* Set AP exception handle in flash */
+ la.pcrel $a0, ApException
+ bl SetExceptionBaseAddress
+
+ /* Clean up local mail box and open INT */
+ bl ClearMailBox
+ bl EnableIPI
+ bl EnableInterrupts
+
+WaitForWake:
+ /* Wait for wakeup */
+ bl CpuSleep
+ li.d $t1, LOONGARCH_IOCSR_MBUF0
+ iocsrrd.w $t2, $t1
+ beqz $t2, WaitForWake
+
+ /* Disable global interrupt */
+ bl DisableInterrupts
+
+ /* Disable IPI interrupt */
+ li.d $t0, (1 << 12)
+ csrxchg $zero, $t0, LOONGARCH_CSR_ECFG
+
+ /* Read mail buf and jump to specified entry */
+ li.d $t1, LOONGARCH_IOCSR_MBUF0
+ iocsrrd.d $t0, $t1
+ li.d $t1, LOONGARCH_IOCSR_MBUF3
+ iocsrrd.d $a1, $t1
+ bl ClearMailBox
+ beqz $a1, NoParameterCall
+
+ //
+ // If the parameters are not NULL, then calling happened in FW ENV.
+ // Set the EBASE to be the same as BSP.
+ //
+ li.d $a0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
+ bl SetExceptionBaseAddress
+
+ bl ApInitStack
+ bl GetApicId
+NoParameterCall:
+ move $ra, $t0
+ jirl $zero, $ra, 0x0
+# End of SlaveMain
+
+.align 12
+ASM_PFX(ApException):
+ csrrd $t0, LOONGARCH_CSR_ESTAT
+ srli.d $t0, $t0, 12
+ beqz $t0, DeadLoop
+
+ li.d $t0, LOONGARCH_IOCSR_IPI_STATUS
+ iocsrrd.w $t1, $t0
+ li.d $t0, LOONGARCH_IOCSR_IPI_CLEAR
+ iocsrwr.w $t1, $t0
+ ertn
+# End of ApException
+
+ASM_PFX(DeadLoop):
+ b DeadLoop
+# End of DeadLoop
+.end
diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.c b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
new file mode 100644
index 0000000000..a7f4324811
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
@@ -0,0 +1,507 @@
+/** @file
+ Main SEC phase code. Transitions to PEI.
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugAgentLib.h>
+#include <Library/DebugLib.h>
+#include <Library/ExtractGuidedSectionLib.h>
+#include <Library/IoLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeCoffLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/PeCoffExtraActionLib.h>
+#include <Library/PeimEntryPoint.h>
+
+#include <Ppi/TemporaryRamSupport.h>
+
+/**
+ temporary memory to permanent memory and do stack switching.
+
+ @param[in] PeiServices Pointer to the PEI Services Table.
+ @param[in] TemporaryMemoryBase Temporary Memory Base address.
+ @param[in] PermanentMemoryBase Permanent Memory Base address.
+ @param[in] CopySize The size of memory that needs to be migrated.
+
+ @retval EFI_SUCCESS Migration successful.
+**/
+EFI_STATUS
+EFIAPI
+TemporaryRamMigration (
+ IN CONST EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
+ IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
+ IN UINTN CopySize
+ );
+
+EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mTemporaryRamSupportPpi = {
+ TemporaryRamMigration
+};
+
+EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTable[] = {
+ {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiTemporaryRamSupportPpiGuid,
+ &mTemporaryRamSupportPpi
+ },
+};
+
+/**
+ Locates a section within a series of sections
+ with the specified section type.
+
+ The Instance parameter indicates which instance of the section
+ type to return. (0 is first instance, 1 is second...)
+
+ @param[in] Sections The sections to search
+ @param[in] SizeOfSections Total size of all sections
+ @param[in] SectionType The section type to locate
+ @param[in] Instance The section instance number
+ @param[out] FoundSection The FFS section if found
+
+ @retval EFI_SUCCESS The file and section was found
+ @retval EFI_NOT_FOUND The file and section was not found
+ @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
+**/
+EFI_STATUS
+FindFfsSectionInstance (
+ IN VOID *Sections,
+ IN UINTN SizeOfSections,
+ IN EFI_SECTION_TYPE SectionType,
+ IN UINTN Instance,
+ OUT EFI_COMMON_SECTION_HEADER **FoundSection
+ )
+{
+ EFI_PHYSICAL_ADDRESS CurrentAddress;
+ UINT32 Size;
+ EFI_PHYSICAL_ADDRESS EndOfSections;
+ EFI_COMMON_SECTION_HEADER *Section;
+ EFI_PHYSICAL_ADDRESS EndOfSection;
+
+ //
+ // Loop through the FFS file sections within the PEI Core FFS file
+ //
+ EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN)Sections;
+ EndOfSections = EndOfSection + SizeOfSections;
+ for ( ; ; ) {
+ if (EndOfSection == EndOfSections) {
+ break;
+ }
+
+ CurrentAddress = (EndOfSection + 3) & ~(3ULL);
+ if (CurrentAddress >= EndOfSections) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ Section = (EFI_COMMON_SECTION_HEADER *)(UINTN)CurrentAddress;
+
+ Size = SECTION_SIZE (Section);
+ if (Size < sizeof (*Section)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ EndOfSection = CurrentAddress + Size;
+ if (EndOfSection > EndOfSections) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ //
+ // Look for the requested section type
+ //
+ if (Section->Type == SectionType) {
+ if (Instance == 0) {
+ *FoundSection = Section;
+ return EFI_SUCCESS;
+ } else {
+ Instance--;
+ }
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Locates a section within a series of sections
+ with the specified section type.
+
+ @param[in] Sections The sections to search
+ @param[in] SizeOfSections Total size of all sections
+ @param[in] SectionType The section type to locate
+ @param[out] FoundSection The FFS section if found
+
+ @retval EFI_SUCCESS The file and section was found
+ @retval EFI_NOT_FOUND The file and section was not found
+ @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
+**/
+EFI_STATUS
+FindFfsSectionInSections (
+ IN VOID *Sections,
+ IN UINTN SizeOfSections,
+ IN EFI_SECTION_TYPE SectionType,
+ OUT EFI_COMMON_SECTION_HEADER **FoundSection
+ )
+{
+ return FindFfsSectionInstance (
+ Sections,
+ SizeOfSections,
+ SectionType,
+ 0,
+ FoundSection
+ );
+}
+
+/**
+ Locates a FFS file with the specified file type and a section
+ within that file with the specified section type.
+
+ @param[in] Fv The firmware volume to search
+ @param[in] FileType The file type to locate
+ @param[in] SectionType The section type to locate
+ @param[out] FoundSection The FFS section if found
+
+ @retval EFI_SUCCESS The file and section was found
+ @retval EFI_NOT_FOUND The file and section was not found
+ @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
+**/
+EFI_STATUS
+FindFfsFileAndSection (
+ IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
+ IN EFI_FV_FILETYPE FileType,
+ IN EFI_SECTION_TYPE SectionType,
+ OUT EFI_COMMON_SECTION_HEADER **FoundSection
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS CurrentAddress;
+ EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume;
+ EFI_FFS_FILE_HEADER *File;
+ UINT32 Size;
+ EFI_PHYSICAL_ADDRESS EndOfFile;
+
+ if (Fv->Signature != EFI_FVH_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, "FV at %p does not have FV header signature\n", Fv));
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Fv;
+ EndOfFirmwareVolume = CurrentAddress + Fv->FvLength;
+
+ //
+ // Loop through the FFS files in the Boot Firmware Volume
+ //
+ for (EndOfFile = CurrentAddress + Fv->HeaderLength; ; ) {
+ CurrentAddress = (EndOfFile + 7) & ~(7ULL);
+ if (CurrentAddress > EndOfFirmwareVolume) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ File = (EFI_FFS_FILE_HEADER *)(UINTN)CurrentAddress;
+ Size = *(UINT32 *)File->Size & 0xffffff;
+ if (Size < (sizeof (*File) + sizeof (EFI_COMMON_SECTION_HEADER))) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ EndOfFile = CurrentAddress + Size;
+ if (EndOfFile > EndOfFirmwareVolume) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ //
+ // Look for the request file type
+ //
+ if (File->Type != FileType) {
+ continue;
+ }
+
+ Status = FindFfsSectionInSections (
+ (VOID *)(File + 1),
+ (UINTN)EndOfFile - (UINTN)(File + 1),
+ SectionType,
+ FoundSection
+ );
+ if (!EFI_ERROR (Status) ||
+ (Status == EFI_VOLUME_CORRUPTED))
+ {
+ return Status;
+ }
+ }
+}
+
+/**
+ Locates the PEI Core entry point address
+
+ @param[in] Fv The firmware volume to search
+ @param[out] PeiCoreEntryPoint The entry point of the PEI Core image
+
+ @retval EFI_SUCCESS The file and section was found
+ @retval EFI_NOT_FOUND The file and section was not found
+ @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
+**/
+EFI_STATUS
+FindPeiCoreImageBaseInFv (
+ IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
+ OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase
+ )
+{
+ EFI_STATUS Status;
+ EFI_COMMON_SECTION_HEADER *Section;
+
+ Status = FindFfsFileAndSection (
+ Fv,
+ EFI_FV_FILETYPE_PEI_CORE,
+ EFI_SECTION_PE32,
+ &Section
+ );
+ if (EFI_ERROR (Status)) {
+ Status = FindFfsFileAndSection (
+ Fv,
+ EFI_FV_FILETYPE_PEI_CORE,
+ EFI_SECTION_TE,
+ &Section
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Unable to find PEI Core image\n"));
+ return Status;
+ }
+ }
+
+ *PeiCoreImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)(Section + 1);
+ return EFI_SUCCESS;
+}
+
+/**
+ Find and return Pei Core entry point.
+
+ It also find SEC and PEI Core file debug information. It will report them if
+ remote debug is enabled.
+**/
+VOID
+FindAndReportEntryPoints (
+ IN EFI_FIRMWARE_VOLUME_HEADER **BootFirmwareVolumePtr,
+ OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS PeiCoreImageBase = 0;
+ PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
+
+ Status = FindPeiCoreImageBaseInFv (*BootFirmwareVolumePtr, &PeiCoreImageBase);
+ ASSERT (Status == EFI_SUCCESS);
+
+ ZeroMem ((VOID *)&ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
+
+ //
+ // Report PEI Core debug information when remote debug is enabled
+ //
+ ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiCoreImageBase;
+ ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageContext.ImageAddress);
+ PeCoffLoaderRelocateImageExtraAction (&ImageContext);
+
+ //
+ // Find PEI Core entry point
+ //
+ Status = PeCoffLoaderGetEntryPoint ((VOID *)(UINTN)PeiCoreImageBase, (VOID **)PeiCoreEntryPoint);
+ if (EFI_ERROR (Status)) {
+ *PeiCoreEntryPoint = 0;
+ }
+
+ return;
+}
+
+/**
+ Find the peicore entry point and jump to the entry point to execute.
+
+ @param[in] Context The first input parameter of InitializeDebugAgent().
+**/
+VOID
+EFIAPI
+SecStartupPhase2 (
+ IN VOID *Context
+ )
+{
+ EFI_SEC_PEI_HAND_OFF *SecCoreData;
+ EFI_FIRMWARE_VOLUME_HEADER *BootFv;
+ EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint;
+
+ SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context;
+
+ //
+ // Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
+ // is enabled.
+ //
+ BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
+ FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
+ SecCoreData->BootFirmwareVolumeBase = BootFv;
+ SecCoreData->BootFirmwareVolumeSize = (UINTN)BootFv->FvLength;
+
+ DEBUG ((DEBUG_INFO, "Find Pei EntryPoint=%p\n", PeiCoreEntryPoint));
+
+ //
+ // Transfer the control to the PEI core
+ //
+ DEBUG ((DEBUG_INFO, "SecStartupPhase2 %p\n", PeiCoreEntryPoint));
+
+ (*PeiCoreEntryPoint)(SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);
+
+ //
+ // If we get here then the PEI Core returned, which is not recoverable.
+ //
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}
+
+/**
+ Entry point to the C language phase of SEC. initialize some temporary memory and set up the stack,
+ the control is transferred to this function.
+
+ @param[in] BootFv The pointer to the PEI FV in memory.
+ @param[in] TopOfCurrentStack Top of Current Stack.
+**/
+VOID
+EFIAPI
+SecCoreStartupWithStack (
+ IN EFI_FIRMWARE_VOLUME_HEADER *BootFv,
+ IN VOID *TopOfCurrentStack
+ )
+{
+ EFI_SEC_PEI_HAND_OFF SecCoreData;
+ EFI_FIRMWARE_VOLUME_HEADER *BootPeiFv = (EFI_FIRMWARE_VOLUME_HEADER *)BootFv;
+
+ DEBUG ((DEBUG_INFO, "Entering C environment\n"));
+
+ ProcessLibraryConstructorList (NULL, NULL);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "SecCoreStartupWithStack (0x%lx, 0x%lx)\n",
+ (UINTN)BootFv,
+ (UINTN)TopOfCurrentStack
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "(0x%lx, 0x%lx)\n",
+ (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase)),
+ (UINTN)(FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
+ ));
+
+ // |-------------| <-- TopOfCurrentStack
+ // | BSP Stack | 32k
+ // |-------------|
+ // | BSP Heap | 32k
+ // |-------------| <-- SecCoreData.TemporaryRamBase
+ // | Ap Stack | 384k
+ // |-------------|
+ // | Exception | 64k
+ // |-------------| <-- PcdOvmfSecPeiTempRamBase
+
+ ASSERT (
+ (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) +
+ FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) ==
+ (UINTN)TopOfCurrentStack
+ );
+
+ //
+ // Initialize SEC hand-off state
+ //
+ SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
+
+ SecCoreData.TemporaryRamSize = (UINTN)SIZE_64KB;
+ SecCoreData.TemporaryRamBase = (VOID *)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize) - SecCoreData.TemporaryRamSize);
+
+ SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase;
+ SecCoreData.PeiTemporaryRamSize = SecCoreData.TemporaryRamSize >> 1;
+
+ SecCoreData.StackBase = (UINT8 *)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize;
+ SecCoreData.StackSize = SecCoreData.TemporaryRamSize >> 1;
+
+ SecCoreData.BootFirmwareVolumeBase = BootPeiFv;
+ SecCoreData.BootFirmwareVolumeSize = (UINTN)BootPeiFv->FvLength;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "&SecCoreData.BootFirmwareVolumeBase=%lx SecCoreData.BootFirmwareVolumeBase=%lx\n",
+ (UINT64)&(SecCoreData.BootFirmwareVolumeBase),
+ (UINT64)(SecCoreData.BootFirmwareVolumeBase)
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "&SecCoreData.BootFirmwareVolumeSize=%lx SecCoreData.BootFirmwareVolumeSize=%lx\n",
+ (UINT64)&(SecCoreData.BootFirmwareVolumeSize),
+ (UINT64)(SecCoreData.BootFirmwareVolumeSize)
+ ));
+
+ //
+ // Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready.
+ //
+ InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, NULL, NULL);
+ SecStartupPhase2 (&SecCoreData);
+}
+
+/**
+ temporary memory to permanent memory and do stack switching.
+
+ @param[in] PeiServices Pointer to the PEI Services Table.
+ @param[in] TemporaryMemoryBase Temporary Memory Base address.
+ @param[in] PermanentMemoryBase Permanent Memory Base address.
+ @param[in] CopySize The size of memory that needs to be migrated.
+
+ @retval EFI_SUCCESS Migration successful.
+**/
+EFI_STATUS
+EFIAPI
+TemporaryRamMigration (
+ IN CONST EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
+ IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
+ IN UINTN CopySize
+ )
+{
+ VOID *OldHeap;
+ VOID *NewHeap;
+ VOID *OldStack;
+ VOID *NewStack;
+ BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "TemporaryRamMigration (0x%Lx, 0x%Lx, 0x%Lx)\n",
+ TemporaryMemoryBase,
+ PermanentMemoryBase,
+ (UINT64)CopySize
+ ));
+
+ OldHeap = (VOID *)(UINTN)TemporaryMemoryBase;
+ NewHeap = (VOID *)((UINTN)PermanentMemoryBase + (CopySize >> 1));
+
+ OldStack = (VOID *)((UINTN)TemporaryMemoryBase + (CopySize >> 1));
+ NewStack = (VOID *)(UINTN)PermanentMemoryBase;
+
+ //
+ // Migrate Heap
+ //
+ CopyMem (NewHeap, OldHeap, CopySize >> 1);
+
+ //
+ // Migrate Stack
+ //
+ CopyMem (NewStack, OldStack, CopySize >> 1);
+
+ // Use SetJump ()/LongJump () to switch to a new stack.
+ //
+ if (SetJump (&JumpBuffer) == 0) {
+ JumpBuffer.SP = JumpBuffer.SP - (UINTN)OldStack + (UINTN)NewStack;
+ LongJump (&JumpBuffer, (UINTN)-1);
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.inf b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
new file mode 100644
index 0000000000..af29ba0551
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
@@ -0,0 +1,53 @@
+## @file
+# SEC Driver
+#
+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SecMain
+ FILE_GUID = 57d02d4f-5a5d-4bfa-b7d6-ba0a4d2c72ce
+ MODULE_TYPE = SEC
+ VERSION_STRING = 1.0
+
+#
+# VALID_ARCHITECTURES = LOONGARCH64
+#
+
+[Sources]
+ LoongArch64/Start.S
+ SecMain.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ CpuExceptionHandlerLib
+ DebugAgentLib
+ DebugLib
+ IoLib
+ PcdLib
+ PeCoffLib
+ PeCoffGetEntryPointLib
+ PeCoffExtraActionLib
+
+[Ppis]
+ gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED
+
+[FixedPcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
+
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
+
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
--
2.27.0
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#112976): https://edk2.groups.io/g/devel/message/112976
Mute This Topic: https://groups.io/mt/103398640/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
On 2023/12/28 下午6:07, Chao Li wrote:
> Add SEC code for LoongArch virtual machine.
>
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Chao Li <lichao@loongson.cn>
> Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
> Co-authored-by: Bibo Mao <maobibo@loongson.cn>
> ---
> OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S | 183 +++++++
> OvmfPkg/LoongArchVirt/Sec/SecMain.c | 507 ++++++++++++++++++
> OvmfPkg/LoongArchVirt/Sec/SecMain.inf | 53 ++
> 3 files changed, 743 insertions(+)
> create mode 100644 OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
> create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.c
> create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.inf
>
> diff --git a/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
> new file mode 100644
> index 0000000000..145f8ffc59
> --- /dev/null
> +++ b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
> @@ -0,0 +1,183 @@
> +#------------------------------------------------------------------------------
> +#
> +# Start for Loongson LoongArch processor
> +#
> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +# @par Glossary:
> +# - CSR - CPU Status Register
> +# - EBASE - Exception Base Address
> +#------------------------------------------------------------------------------
> +#ifndef __ASSEMBLY__
> +#define __ASSEMBLY__
> +#endif
> +
> +#include <Library/BaseMemoryLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +#include <Protocol/DebugSupport.h>
> +
> +#define BOOTCORE_ID 0
> +
> +//
> +// For coding convenience, define the maximum valid
> +// LoongArch exception.
> +// Since UEFI V2.11, it will be present in DebugSupport.h.
> +//
> +#define MAX_LOONGARCH_EXCEPTION 64
> +
> +ASM_GLOBAL ASM_PFX(_ModuleEntryPoint)
> +ASM_PFX(_ModuleEntryPoint):
> + /* Disable interrupt */
> + li.d $t0, (1 << 2)
> + csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
> +
> + /* Read physical cpu number id */
> + bl GetApicId
> + li.d $t0, BOOTCORE_ID //0
> + bne $a0, $t0, SlaveMain
> +
> + /* Configure BSP reset ebase */
> + li.d $a0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
> + bl SetExceptionBaseAddress
> + move $t1, $a0
what is usage of PcdCpuExceptionVectorBaseAddress about BSP here?
Is there actual exception handler in PcdCpuExceptionVectorBaseAddress in
sec stage?
> +
> + /* Set BSP stack */
> + li.d $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize) # stack base
> + move $sp, $t0
> + addi.d $sp, $sp, -0x8
> +
> + /* Construct SEC and PEI step exception environment */
> + la.pcrel $a1, ExceptionEntryStart
> + la.pcrel $t0, ExceptionEntryEnd
> + sub.d $a2, $t0, $a1
> + li.w $t0, (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512
> + bgeu $a2, $t0, DeadLoop
> + move $a0, $t1
> + bl CopyMem
> +
> +CallEntry:
> + /* Call C function make sure parameter true */
> + li.d $a0, FixedPcdGet64(PcdOvmfFdBaseAddress) # FW base
> + addi.d $a1, $sp, 0x8
> + bl SecCoreStartupWithStack
> +# End of _ModuleEntryPoint
> +
> +ASM_PFX(ClearMailBox):
> + /* Clear mailbox */
> + li.d $t1, LOONGARCH_IOCSR_MBUF3
> + iocsrwr.d $zero, $t1
> + li.d $t1, LOONGARCH_IOCSR_MBUF2
> + iocsrwr.d $zero, $t1
> + li.d $t1, LOONGARCH_IOCSR_MBUF1
> + iocsrwr.d $zero, $t1
> + li.d $t1, LOONGARCH_IOCSR_MBUF0
> + iocsrwr.d $zero, $t1
> + jirl $zero, $ra, 0
> +# End of ClearMailBox
> +
> +ASM_PFX(EnableIPI):
> + /* Enable IPI interrupt */
> + li.d $t1, (1 << 12)
> + csrxchg $t1, $t1, LOONGARCH_CSR_ECFG
> +
> + addi.d $t2, $zero, -1
> + li.d $t1, LOONGARCH_IOCSR_IPI_EN
> + iocsrwr.w $t2, $t1
> + jirl $zero, $ra, 0
> +# End of EeableIPI
> +
> +#/**
> +# Get APIC ID for every CPU.
> +#
> +# @param NULL
> +# @return APICID
> +#
> +# UINTN
> +# EFI_API
> +# GetApicId (
> +# VOID
> +# )
> +#**/
> +ASM_PFX(GetApicId):
> + csrrd $a0, LOONGARCH_CSR_CPUNUM
> + andi $a0, $a0, 0x3ff
> + jirl $zero, $ra, 0
> +# End of GetApicId
> +
> +ASM_PFX(ApInitStack):
> + li.d $t1, SIZE_1KB
> + csrrd $t0, LOONGARCH_CSR_TMID
> + mul.d $t1, $t0, $t1
> + li.d $t2, FixedPcdGet32(PcdCpuMaxLogicalProcessorNumber)
> + bgeu $t0, $t2, DeadLoop
> + li.d $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize) - SIZE_64KB
> + sub.d $sp, $t0, $t1
> + addi.d $sp, $sp, -0x8
> + jirl $zero, $ra, 0
> +# End of ApInitStack
> +
> +ASM_PFX(SlaveMain):
> + /* Set AP exception handle in flash */
> + la.pcrel $a0, ApException
> + bl SetExceptionBaseAddress
> +
> + /* Clean up local mail box and open INT */
> + bl ClearMailBox
> + bl EnableIPI
> + bl EnableInterrupts
There will be potential problem if ipi interrupt happens after
EnableInterrupts and before CpuSleep.
> +
> +WaitForWake:
> + /* Wait for wakeup */
> + bl CpuSleep
> + li.d $t1, LOONGARCH_IOCSR_MBUF0
> + iocsrrd.w $t2, $t1
> + beqz $t2, WaitForWake
> +
> + /* Disable global interrupt */
> + bl DisableInterrupts
> +
> + /* Disable IPI interrupt */
> + li.d $t0, (1 << 12)
> + csrxchg $zero, $t0, LOONGARCH_CSR_ECFG
> +
> + /* Read mail buf and jump to specified entry */
> + li.d $t1, LOONGARCH_IOCSR_MBUF0
> + iocsrrd.d $t0, $t1
> + li.d $t1, LOONGARCH_IOCSR_MBUF3
What is usage of mail buffer LOONGARCH_IOCSR_MBUF3 here? Do we have
documented Multi-processor boot protocol?
Regards
Bibo Mao
> + iocsrrd.d $a1, $t1
> + bl ClearMailBox
> + beqz $a1, NoParameterCall
> +
> + //
> + // If the parameters are not NULL, then calling happened in FW ENV.
> + // Set the EBASE to be the same as BSP.
> + //
> + li.d $a0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
> + bl SetExceptionBaseAddress
> +
> + bl ApInitStack
> + bl GetApicId
> +NoParameterCall:
> + move $ra, $t0
> + jirl $zero, $ra, 0x0
> +# End of SlaveMain
> +
> +.align 12
> +ASM_PFX(ApException):
> + csrrd $t0, LOONGARCH_CSR_ESTAT
> + srli.d $t0, $t0, 12
> + beqz $t0, DeadLoop
> +
> + li.d $t0, LOONGARCH_IOCSR_IPI_STATUS
> + iocsrrd.w $t1, $t0
> + li.d $t0, LOONGARCH_IOCSR_IPI_CLEAR
> + iocsrwr.w $t1, $t0
> + ertn
> +# End of ApException
> +
> +ASM_PFX(DeadLoop):
> + b DeadLoop
> +# End of DeadLoop
> +.end
> diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.c b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
> new file mode 100644
> index 0000000000..a7f4324811
> --- /dev/null
> +++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
> @@ -0,0 +1,507 @@
> +/** @file
> + Main SEC phase code. Transitions to PEI.
> +
> + Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiPei.h>
> +
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugAgentLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/ExtractGuidedSectionLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/PeiServicesLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/PeCoffLib.h>
> +#include <Library/PeCoffGetEntryPointLib.h>
> +#include <Library/PeCoffExtraActionLib.h>
> +#include <Library/PeimEntryPoint.h>
> +
> +#include <Ppi/TemporaryRamSupport.h>
> +
> +/**
> + temporary memory to permanent memory and do stack switching.
> +
> + @param[in] PeiServices Pointer to the PEI Services Table.
> + @param[in] TemporaryMemoryBase Temporary Memory Base address.
> + @param[in] PermanentMemoryBase Permanent Memory Base address.
> + @param[in] CopySize The size of memory that needs to be migrated.
> +
> + @retval EFI_SUCCESS Migration successful.
> +**/
> +EFI_STATUS
> +EFIAPI
> +TemporaryRamMigration (
> + IN CONST EFI_PEI_SERVICES **PeiServices,
> + IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
> + IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
> + IN UINTN CopySize
> + );
> +
> +EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mTemporaryRamSupportPpi = {
> + TemporaryRamMigration
> +};
> +
> +EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTable[] = {
> + {
> + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> + &gEfiTemporaryRamSupportPpiGuid,
> + &mTemporaryRamSupportPpi
> + },
> +};
> +
> +/**
> + Locates a section within a series of sections
> + with the specified section type.
> +
> + The Instance parameter indicates which instance of the section
> + type to return. (0 is first instance, 1 is second...)
> +
> + @param[in] Sections The sections to search
> + @param[in] SizeOfSections Total size of all sections
> + @param[in] SectionType The section type to locate
> + @param[in] Instance The section instance number
> + @param[out] FoundSection The FFS section if found
> +
> + @retval EFI_SUCCESS The file and section was found
> + @retval EFI_NOT_FOUND The file and section was not found
> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
> +**/
> +EFI_STATUS
> +FindFfsSectionInstance (
> + IN VOID *Sections,
> + IN UINTN SizeOfSections,
> + IN EFI_SECTION_TYPE SectionType,
> + IN UINTN Instance,
> + OUT EFI_COMMON_SECTION_HEADER **FoundSection
> + )
> +{
> + EFI_PHYSICAL_ADDRESS CurrentAddress;
> + UINT32 Size;
> + EFI_PHYSICAL_ADDRESS EndOfSections;
> + EFI_COMMON_SECTION_HEADER *Section;
> + EFI_PHYSICAL_ADDRESS EndOfSection;
> +
> + //
> + // Loop through the FFS file sections within the PEI Core FFS file
> + //
> + EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN)Sections;
> + EndOfSections = EndOfSection + SizeOfSections;
> + for ( ; ; ) {
> + if (EndOfSection == EndOfSections) {
> + break;
> + }
> +
> + CurrentAddress = (EndOfSection + 3) & ~(3ULL);
> + if (CurrentAddress >= EndOfSections) {
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + Section = (EFI_COMMON_SECTION_HEADER *)(UINTN)CurrentAddress;
> +
> + Size = SECTION_SIZE (Section);
> + if (Size < sizeof (*Section)) {
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + EndOfSection = CurrentAddress + Size;
> + if (EndOfSection > EndOfSections) {
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + //
> + // Look for the requested section type
> + //
> + if (Section->Type == SectionType) {
> + if (Instance == 0) {
> + *FoundSection = Section;
> + return EFI_SUCCESS;
> + } else {
> + Instance--;
> + }
> + }
> + }
> +
> + return EFI_NOT_FOUND;
> +}
> +
> +/**
> + Locates a section within a series of sections
> + with the specified section type.
> +
> + @param[in] Sections The sections to search
> + @param[in] SizeOfSections Total size of all sections
> + @param[in] SectionType The section type to locate
> + @param[out] FoundSection The FFS section if found
> +
> + @retval EFI_SUCCESS The file and section was found
> + @retval EFI_NOT_FOUND The file and section was not found
> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
> +**/
> +EFI_STATUS
> +FindFfsSectionInSections (
> + IN VOID *Sections,
> + IN UINTN SizeOfSections,
> + IN EFI_SECTION_TYPE SectionType,
> + OUT EFI_COMMON_SECTION_HEADER **FoundSection
> + )
> +{
> + return FindFfsSectionInstance (
> + Sections,
> + SizeOfSections,
> + SectionType,
> + 0,
> + FoundSection
> + );
> +}
> +
> +/**
> + Locates a FFS file with the specified file type and a section
> + within that file with the specified section type.
> +
> + @param[in] Fv The firmware volume to search
> + @param[in] FileType The file type to locate
> + @param[in] SectionType The section type to locate
> + @param[out] FoundSection The FFS section if found
> +
> + @retval EFI_SUCCESS The file and section was found
> + @retval EFI_NOT_FOUND The file and section was not found
> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
> +**/
> +EFI_STATUS
> +FindFfsFileAndSection (
> + IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
> + IN EFI_FV_FILETYPE FileType,
> + IN EFI_SECTION_TYPE SectionType,
> + OUT EFI_COMMON_SECTION_HEADER **FoundSection
> + )
> +{
> + EFI_STATUS Status;
> + EFI_PHYSICAL_ADDRESS CurrentAddress;
> + EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume;
> + EFI_FFS_FILE_HEADER *File;
> + UINT32 Size;
> + EFI_PHYSICAL_ADDRESS EndOfFile;
> +
> + if (Fv->Signature != EFI_FVH_SIGNATURE) {
> + DEBUG ((DEBUG_ERROR, "FV at %p does not have FV header signature\n", Fv));
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Fv;
> + EndOfFirmwareVolume = CurrentAddress + Fv->FvLength;
> +
> + //
> + // Loop through the FFS files in the Boot Firmware Volume
> + //
> + for (EndOfFile = CurrentAddress + Fv->HeaderLength; ; ) {
> + CurrentAddress = (EndOfFile + 7) & ~(7ULL);
> + if (CurrentAddress > EndOfFirmwareVolume) {
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + File = (EFI_FFS_FILE_HEADER *)(UINTN)CurrentAddress;
> + Size = *(UINT32 *)File->Size & 0xffffff;
> + if (Size < (sizeof (*File) + sizeof (EFI_COMMON_SECTION_HEADER))) {
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + EndOfFile = CurrentAddress + Size;
> + if (EndOfFile > EndOfFirmwareVolume) {
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + //
> + // Look for the request file type
> + //
> + if (File->Type != FileType) {
> + continue;
> + }
> +
> + Status = FindFfsSectionInSections (
> + (VOID *)(File + 1),
> + (UINTN)EndOfFile - (UINTN)(File + 1),
> + SectionType,
> + FoundSection
> + );
> + if (!EFI_ERROR (Status) ||
> + (Status == EFI_VOLUME_CORRUPTED))
> + {
> + return Status;
> + }
> + }
> +}
> +
> +/**
> + Locates the PEI Core entry point address
> +
> + @param[in] Fv The firmware volume to search
> + @param[out] PeiCoreEntryPoint The entry point of the PEI Core image
> +
> + @retval EFI_SUCCESS The file and section was found
> + @retval EFI_NOT_FOUND The file and section was not found
> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
> +**/
> +EFI_STATUS
> +FindPeiCoreImageBaseInFv (
> + IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
> + OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase
> + )
> +{
> + EFI_STATUS Status;
> + EFI_COMMON_SECTION_HEADER *Section;
> +
> + Status = FindFfsFileAndSection (
> + Fv,
> + EFI_FV_FILETYPE_PEI_CORE,
> + EFI_SECTION_PE32,
> + &Section
> + );
> + if (EFI_ERROR (Status)) {
> + Status = FindFfsFileAndSection (
> + Fv,
> + EFI_FV_FILETYPE_PEI_CORE,
> + EFI_SECTION_TE,
> + &Section
> + );
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "Unable to find PEI Core image\n"));
> + return Status;
> + }
> + }
> +
> + *PeiCoreImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)(Section + 1);
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Find and return Pei Core entry point.
> +
> + It also find SEC and PEI Core file debug information. It will report them if
> + remote debug is enabled.
> +**/
> +VOID
> +FindAndReportEntryPoints (
> + IN EFI_FIRMWARE_VOLUME_HEADER **BootFirmwareVolumePtr,
> + OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint
> + )
> +{
> + EFI_STATUS Status;
> + EFI_PHYSICAL_ADDRESS PeiCoreImageBase = 0;
> + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
> +
> + Status = FindPeiCoreImageBaseInFv (*BootFirmwareVolumePtr, &PeiCoreImageBase);
> + ASSERT (Status == EFI_SUCCESS);
> +
> + ZeroMem ((VOID *)&ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
> +
> + //
> + // Report PEI Core debug information when remote debug is enabled
> + //
> + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiCoreImageBase;
> + ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageContext.ImageAddress);
> + PeCoffLoaderRelocateImageExtraAction (&ImageContext);
> +
> + //
> + // Find PEI Core entry point
> + //
> + Status = PeCoffLoaderGetEntryPoint ((VOID *)(UINTN)PeiCoreImageBase, (VOID **)PeiCoreEntryPoint);
> + if (EFI_ERROR (Status)) {
> + *PeiCoreEntryPoint = 0;
> + }
> +
> + return;
> +}
> +
> +/**
> + Find the peicore entry point and jump to the entry point to execute.
> +
> + @param[in] Context The first input parameter of InitializeDebugAgent().
> +**/
> +VOID
> +EFIAPI
> +SecStartupPhase2 (
> + IN VOID *Context
> + )
> +{
> + EFI_SEC_PEI_HAND_OFF *SecCoreData;
> + EFI_FIRMWARE_VOLUME_HEADER *BootFv;
> + EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint;
> +
> + SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context;
> +
> + //
> + // Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
> + // is enabled.
> + //
> + BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
> + FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
> + SecCoreData->BootFirmwareVolumeBase = BootFv;
> + SecCoreData->BootFirmwareVolumeSize = (UINTN)BootFv->FvLength;
> +
> + DEBUG ((DEBUG_INFO, "Find Pei EntryPoint=%p\n", PeiCoreEntryPoint));
> +
> + //
> + // Transfer the control to the PEI core
> + //
> + DEBUG ((DEBUG_INFO, "SecStartupPhase2 %p\n", PeiCoreEntryPoint));
> +
> + (*PeiCoreEntryPoint)(SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);
> +
> + //
> + // If we get here then the PEI Core returned, which is not recoverable.
> + //
> + ASSERT (FALSE);
> + CpuDeadLoop ();
> +}
> +
> +/**
> + Entry point to the C language phase of SEC. initialize some temporary memory and set up the stack,
> + the control is transferred to this function.
> +
> + @param[in] BootFv The pointer to the PEI FV in memory.
> + @param[in] TopOfCurrentStack Top of Current Stack.
> +**/
> +VOID
> +EFIAPI
> +SecCoreStartupWithStack (
> + IN EFI_FIRMWARE_VOLUME_HEADER *BootFv,
> + IN VOID *TopOfCurrentStack
> + )
> +{
> + EFI_SEC_PEI_HAND_OFF SecCoreData;
> + EFI_FIRMWARE_VOLUME_HEADER *BootPeiFv = (EFI_FIRMWARE_VOLUME_HEADER *)BootFv;
> +
> + DEBUG ((DEBUG_INFO, "Entering C environment\n"));
> +
> + ProcessLibraryConstructorList (NULL, NULL);
> +
> + DEBUG ((
> + DEBUG_INFO,
> + "SecCoreStartupWithStack (0x%lx, 0x%lx)\n",
> + (UINTN)BootFv,
> + (UINTN)TopOfCurrentStack
> + ));
> + DEBUG ((
> + DEBUG_INFO,
> + "(0x%lx, 0x%lx)\n",
> + (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase)),
> + (UINTN)(FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
> + ));
> +
> + // |-------------| <-- TopOfCurrentStack
> + // | BSP Stack | 32k
> + // |-------------|
> + // | BSP Heap | 32k
> + // |-------------| <-- SecCoreData.TemporaryRamBase
> + // | Ap Stack | 384k
> + // |-------------|
> + // | Exception | 64k
> + // |-------------| <-- PcdOvmfSecPeiTempRamBase
> +
> + ASSERT (
> + (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) +
> + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) ==
> + (UINTN)TopOfCurrentStack
> + );
> +
> + //
> + // Initialize SEC hand-off state
> + //
> + SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
> +
> + SecCoreData.TemporaryRamSize = (UINTN)SIZE_64KB;
> + SecCoreData.TemporaryRamBase = (VOID *)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize) - SecCoreData.TemporaryRamSize);
> +
> + SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase;
> + SecCoreData.PeiTemporaryRamSize = SecCoreData.TemporaryRamSize >> 1;
> +
> + SecCoreData.StackBase = (UINT8 *)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize;
> + SecCoreData.StackSize = SecCoreData.TemporaryRamSize >> 1;
> +
> + SecCoreData.BootFirmwareVolumeBase = BootPeiFv;
> + SecCoreData.BootFirmwareVolumeSize = (UINTN)BootPeiFv->FvLength;
> +
> + DEBUG ((
> + DEBUG_INFO,
> + "&SecCoreData.BootFirmwareVolumeBase=%lx SecCoreData.BootFirmwareVolumeBase=%lx\n",
> + (UINT64)&(SecCoreData.BootFirmwareVolumeBase),
> + (UINT64)(SecCoreData.BootFirmwareVolumeBase)
> + ));
> + DEBUG ((
> + DEBUG_INFO,
> + "&SecCoreData.BootFirmwareVolumeSize=%lx SecCoreData.BootFirmwareVolumeSize=%lx\n",
> + (UINT64)&(SecCoreData.BootFirmwareVolumeSize),
> + (UINT64)(SecCoreData.BootFirmwareVolumeSize)
> + ));
> +
> + //
> + // Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready.
> + //
> + InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, NULL, NULL);
> + SecStartupPhase2 (&SecCoreData);
> +}
> +
> +/**
> + temporary memory to permanent memory and do stack switching.
> +
> + @param[in] PeiServices Pointer to the PEI Services Table.
> + @param[in] TemporaryMemoryBase Temporary Memory Base address.
> + @param[in] PermanentMemoryBase Permanent Memory Base address.
> + @param[in] CopySize The size of memory that needs to be migrated.
> +
> + @retval EFI_SUCCESS Migration successful.
> +**/
> +EFI_STATUS
> +EFIAPI
> +TemporaryRamMigration (
> + IN CONST EFI_PEI_SERVICES **PeiServices,
> + IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
> + IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
> + IN UINTN CopySize
> + )
> +{
> + VOID *OldHeap;
> + VOID *NewHeap;
> + VOID *OldStack;
> + VOID *NewStack;
> + BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
> +
> + DEBUG ((
> + DEBUG_INFO,
> + "TemporaryRamMigration (0x%Lx, 0x%Lx, 0x%Lx)\n",
> + TemporaryMemoryBase,
> + PermanentMemoryBase,
> + (UINT64)CopySize
> + ));
> +
> + OldHeap = (VOID *)(UINTN)TemporaryMemoryBase;
> + NewHeap = (VOID *)((UINTN)PermanentMemoryBase + (CopySize >> 1));
> +
> + OldStack = (VOID *)((UINTN)TemporaryMemoryBase + (CopySize >> 1));
> + NewStack = (VOID *)(UINTN)PermanentMemoryBase;
> +
> + //
> + // Migrate Heap
> + //
> + CopyMem (NewHeap, OldHeap, CopySize >> 1);
> +
> + //
> + // Migrate Stack
> + //
> + CopyMem (NewStack, OldStack, CopySize >> 1);
> +
> + // Use SetJump ()/LongJump () to switch to a new stack.
> + //
> + if (SetJump (&JumpBuffer) == 0) {
> + JumpBuffer.SP = JumpBuffer.SP - (UINTN)OldStack + (UINTN)NewStack;
> + LongJump (&JumpBuffer, (UINTN)-1);
> + }
> +
> + return EFI_SUCCESS;
> +}
> diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.inf b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
> new file mode 100644
> index 0000000000..af29ba0551
> --- /dev/null
> +++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
> @@ -0,0 +1,53 @@
> +## @file
> +# SEC Driver
> +#
> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010005
> + BASE_NAME = SecMain
> + FILE_GUID = 57d02d4f-5a5d-4bfa-b7d6-ba0a4d2c72ce
> + MODULE_TYPE = SEC
> + VERSION_STRING = 1.0
> +
> +#
> +# VALID_ARCHITECTURES = LOONGARCH64
> +#
> +
> +[Sources]
> + LoongArch64/Start.S
> + SecMain.c
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + OvmfPkg/OvmfPkg.dec
> + UefiCpuPkg/UefiCpuPkg.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + CpuExceptionHandlerLib
> + DebugAgentLib
> + DebugLib
> + IoLib
> + PcdLib
> + PeCoffLib
> + PeCoffGetEntryPointLib
> + PeCoffExtraActionLib
> +
> +[Ppis]
> + gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED
> +
> +[FixedPcd]
> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
> +
> + gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
> + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
> +
> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113110): https://edk2.groups.io/g/devel/message/113110
Mute This Topic: https://groups.io/mt/103398640/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
Hi Bibo,
Here are my comments:
Thanks,
Chao
On 2024/1/4 09:49, maobibo wrote:
>
>
> On 2023/12/28 下午6:07, Chao Li wrote:
>> Add SEC code for LoongArch virtual machine.
>>
>> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Gerd Hoffmann <kraxel@redhat.com>
>> Signed-off-by: Chao Li <lichao@loongson.cn>
>> Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
>> Co-authored-by: Bibo Mao <maobibo@loongson.cn>
>> ---
>> OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S | 183 +++++++
>> OvmfPkg/LoongArchVirt/Sec/SecMain.c | 507 ++++++++++++++++++
>> OvmfPkg/LoongArchVirt/Sec/SecMain.inf | 53 ++
>> 3 files changed, 743 insertions(+)
>> create mode 100644 OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
>> create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.c
>> create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.inf
>>
>> diff --git a/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
>> b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
>> new file mode 100644
>> index 0000000000..145f8ffc59
>> --- /dev/null
>> +++ b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
>> @@ -0,0 +1,183 @@
>> +#------------------------------------------------------------------------------
>>
>> +#
>> +# Start for Loongson LoongArch processor
>> +#
>> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All
>> rights reserved.<BR>
>> +#
>> +# SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +# @par Glossary:
>> +# - CSR - CPU Status Register
>> +# - EBASE - Exception Base Address
>> +#------------------------------------------------------------------------------
>>
>> +#ifndef __ASSEMBLY__
>> +#define __ASSEMBLY__
>> +#endif
>> +
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +#include <Protocol/DebugSupport.h>
>> +
>> +#define BOOTCORE_ID 0
>> +
>> +//
>> +// For coding convenience, define the maximum valid
>> +// LoongArch exception.
>> +// Since UEFI V2.11, it will be present in DebugSupport.h.
>> +//
>> +#define MAX_LOONGARCH_EXCEPTION 64
>> +
>> +ASM_GLOBAL ASM_PFX(_ModuleEntryPoint)
>> +ASM_PFX(_ModuleEntryPoint):
>> + /* Disable interrupt */
>> + li.d $t0, (1 << 2)
>> + csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
>> +
>> + /* Read physical cpu number id */
>> + bl GetApicId
>> + li.d $t0, BOOTCORE_ID //0
>> + bne $a0, $t0, SlaveMain
>> +
>> + /* Configure BSP reset ebase */
>> + li.d $a0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
>> + bl SetExceptionBaseAddress
>> + move $t1, $a0
> what is usage of PcdCpuExceptionVectorBaseAddress about BSP here?
> Is there actual exception handler in PcdCpuExceptionVectorBaseAddress
> in sec stage?
There are some mistake in the order between setting up exception base
and copying the exception code, I will fix them in V6.
>
>> +
>> + /* Set BSP stack */
>> + li.d $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) +
>> FixedPcdGet32(PcdOvmfSecPeiTempRamSize) # stack base
>> + move $sp, $t0
>> + addi.d $sp, $sp, -0x8
>> +
>> + /* Construct SEC and PEI step exception environment */
>> + la.pcrel $a1, ExceptionEntryStart
>> + la.pcrel $t0, ExceptionEntryEnd
>> + sub.d $a2, $t0, $a1
>> + li.w $t0, (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT)
>> * 512
>> + bgeu $a2, $t0, DeadLoop
>> + move $a0, $t1
>> + bl CopyMem
>> +
>> +CallEntry:
>> + /* Call C function make sure parameter true */
>> + li.d $a0, FixedPcdGet64(PcdOvmfFdBaseAddress) # FW base
>> + addi.d $a1, $sp, 0x8
>> + bl SecCoreStartupWithStack
>> +# End of _ModuleEntryPoint
>> +
>> +ASM_PFX(ClearMailBox):
>> + /* Clear mailbox */
>> + li.d $t1, LOONGARCH_IOCSR_MBUF3
>> + iocsrwr.d $zero, $t1
>> + li.d $t1, LOONGARCH_IOCSR_MBUF2
>> + iocsrwr.d $zero, $t1
>> + li.d $t1, LOONGARCH_IOCSR_MBUF1
>> + iocsrwr.d $zero, $t1
>> + li.d $t1, LOONGARCH_IOCSR_MBUF0
>> + iocsrwr.d $zero, $t1
>> + jirl $zero, $ra, 0
>> +# End of ClearMailBox
>> +
>> +ASM_PFX(EnableIPI):
>> + /* Enable IPI interrupt */
>> + li.d $t1, (1 << 12)
>> + csrxchg $t1, $t1, LOONGARCH_CSR_ECFG
>> +
>> + addi.d $t2, $zero, -1
>> + li.d $t1, LOONGARCH_IOCSR_IPI_EN
>> + iocsrwr.w $t2, $t1
>> + jirl $zero, $ra, 0
>> +# End of EeableIPI
>> +
>> +#/**
>> +# Get APIC ID for every CPU.
>> +#
>> +# @param NULL
>> +# @return APICID
>> +#
>> +# UINTN
>> +# EFI_API
>> +# GetApicId (
>> +# VOID
>> +# )
>> +#**/
>> +ASM_PFX(GetApicId):
>> + csrrd $a0, LOONGARCH_CSR_CPUNUM
>> + andi $a0, $a0, 0x3ff
>> + jirl $zero, $ra, 0
>> +# End of GetApicId
>> +
>> +ASM_PFX(ApInitStack):
>> + li.d $t1, SIZE_1KB
>> + csrrd $t0, LOONGARCH_CSR_TMID
>> + mul.d $t1, $t0, $t1
>> + li.d $t2, FixedPcdGet32(PcdCpuMaxLogicalProcessorNumber)
>> + bgeu $t0, $t2, DeadLoop
>> + li.d $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) +
>> FixedPcdGet32(PcdOvmfSecPeiTempRamSize) - SIZE_64KB
>> + sub.d $sp, $t0, $t1
>> + addi.d $sp, $sp, -0x8
>> + jirl $zero, $ra, 0
>> +# End of ApInitStack
>> +
>> +ASM_PFX(SlaveMain):
>> + /* Set AP exception handle in flash */
>> + la.pcrel $a0, ApException
>> + bl SetExceptionBaseAddress
>> +
>> + /* Clean up local mail box and open INT */
>> + bl ClearMailBox
>> + bl EnableIPI
>> + bl EnableInterrupts
> There will be potential problem if ipi interrupt happens after
> EnableInterrupts and before CpuSleep.
Yes, there are some issues, in V6 I will add logic to jump to kernel in
ApException.
>> +
>> +WaitForWake:
>> + /* Wait for wakeup */
>> + bl CpuSleep
>> + li.d $t1, LOONGARCH_IOCSR_MBUF0
>> + iocsrrd.w $t2, $t1
>> + beqz $t2, WaitForWake
>> +
>> + /* Disable global interrupt */
>> + bl DisableInterrupts
>> +
>> + /* Disable IPI interrupt */
>> + li.d $t0, (1 << 12)
>> + csrxchg $zero, $t0, LOONGARCH_CSR_ECFG
>> +
>> + /* Read mail buf and jump to specified entry */
>> + li.d $t1, LOONGARCH_IOCSR_MBUF0
>> + iocsrrd.d $t0, $t1
>> + li.d $t1, LOONGARCH_IOCSR_MBUF3
> What is usage of mail buffer LOONGARCH_IOCSR_MBUF3 here? Do we have
> documented Multi-processor boot protocol?
In the linux kernel, the IPI vector 0 is used for the SMP boot CPU, I
beleve the documentation for multi-processor is coming soon, it is on
the way.
>
> Regards
> Bibo Mao
>> + iocsrrd.d $a1, $t1
>> + bl ClearMailBox
>> + beqz $a1, NoParameterCall
>> +
>> + //
>> + // If the parameters are not NULL, then calling happened in FW ENV.
>> + // Set the EBASE to be the same as BSP.
>> + //
>> + li.d $a0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
>> + bl SetExceptionBaseAddress
>> +
>> + bl ApInitStack
>> + bl GetApicId
>> +NoParameterCall:
>> + move $ra, $t0
>> + jirl $zero, $ra, 0x0
>> +# End of SlaveMain
>> +
>> +.align 12
>> +ASM_PFX(ApException):
>> + csrrd $t0, LOONGARCH_CSR_ESTAT
>> + srli.d $t0, $t0, 12
>> + beqz $t0, DeadLoop
>> +
>> + li.d $t0, LOONGARCH_IOCSR_IPI_STATUS
>> + iocsrrd.w $t1, $t0
>> + li.d $t0, LOONGARCH_IOCSR_IPI_CLEAR
>> + iocsrwr.w $t1, $t0
>> + ertn
>> +# End of ApException
>> +
>> +ASM_PFX(DeadLoop):
>> + b DeadLoop
>> +# End of DeadLoop
>> +.end
>> diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.c
>> b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
>> new file mode 100644
>> index 0000000000..a7f4324811
>> --- /dev/null
>> +++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
>> @@ -0,0 +1,507 @@
>> +/** @file
>> + Main SEC phase code. Transitions to PEI.
>> +
>> + Copyright (c) 2024 Loongson Technology Corporation Limited. All
>> rights reserved.<BR>
>> +
>> + SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <PiPei.h>
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/DebugAgentLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/ExtractGuidedSectionLib.h>
>> +#include <Library/IoLib.h>
>> +#include <Library/PeiServicesLib.h>
>> +#include <Library/PcdLib.h>
>> +#include <Library/PeCoffLib.h>
>> +#include <Library/PeCoffGetEntryPointLib.h>
>> +#include <Library/PeCoffExtraActionLib.h>
>> +#include <Library/PeimEntryPoint.h>
>> +
>> +#include <Ppi/TemporaryRamSupport.h>
>> +
>> +/**
>> + temporary memory to permanent memory and do stack switching.
>> +
>> + @param[in] PeiServices Pointer to the PEI Services Table.
>> + @param[in] TemporaryMemoryBase Temporary Memory Base address.
>> + @param[in] PermanentMemoryBase Permanent Memory Base address.
>> + @param[in] CopySize The size of memory that needs to be migrated.
>> +
>> + @retval EFI_SUCCESS Migration successful.
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +TemporaryRamMigration (
>> + IN CONST EFI_PEI_SERVICES **PeiServices,
>> + IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
>> + IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
>> + IN UINTN CopySize
>> + );
>> +
>> +EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mTemporaryRamSupportPpi = {
>> + TemporaryRamMigration
>> +};
>> +
>> +EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTable[] = {
>> + {
>> + (EFI_PEI_PPI_DESCRIPTOR_PPI |
>> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
>> + &gEfiTemporaryRamSupportPpiGuid,
>> + &mTemporaryRamSupportPpi
>> + },
>> +};
>> +
>> +/**
>> + Locates a section within a series of sections
>> + with the specified section type.
>> +
>> + The Instance parameter indicates which instance of the section
>> + type to return. (0 is first instance, 1 is second...)
>> +
>> + @param[in] Sections The sections to search
>> + @param[in] SizeOfSections Total size of all sections
>> + @param[in] SectionType The section type to locate
>> + @param[in] Instance The section instance number
>> + @param[out] FoundSection The FFS section if found
>> +
>> + @retval EFI_SUCCESS The file and section was found
>> + @retval EFI_NOT_FOUND The file and section was not found
>> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
>> +**/
>> +EFI_STATUS
>> +FindFfsSectionInstance (
>> + IN VOID *Sections,
>> + IN UINTN SizeOfSections,
>> + IN EFI_SECTION_TYPE SectionType,
>> + IN UINTN Instance,
>> + OUT EFI_COMMON_SECTION_HEADER **FoundSection
>> + )
>> +{
>> + EFI_PHYSICAL_ADDRESS CurrentAddress;
>> + UINT32 Size;
>> + EFI_PHYSICAL_ADDRESS EndOfSections;
>> + EFI_COMMON_SECTION_HEADER *Section;
>> + EFI_PHYSICAL_ADDRESS EndOfSection;
>> +
>> + //
>> + // Loop through the FFS file sections within the PEI Core FFS file
>> + //
>> + EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN)Sections;
>> + EndOfSections = EndOfSection + SizeOfSections;
>> + for ( ; ; ) {
>> + if (EndOfSection == EndOfSections) {
>> + break;
>> + }
>> +
>> + CurrentAddress = (EndOfSection + 3) & ~(3ULL);
>> + if (CurrentAddress >= EndOfSections) {
>> + return EFI_VOLUME_CORRUPTED;
>> + }
>> +
>> + Section = (EFI_COMMON_SECTION_HEADER *)(UINTN)CurrentAddress;
>> +
>> + Size = SECTION_SIZE (Section);
>> + if (Size < sizeof (*Section)) {
>> + return EFI_VOLUME_CORRUPTED;
>> + }
>> +
>> + EndOfSection = CurrentAddress + Size;
>> + if (EndOfSection > EndOfSections) {
>> + return EFI_VOLUME_CORRUPTED;
>> + }
>> +
>> + //
>> + // Look for the requested section type
>> + //
>> + if (Section->Type == SectionType) {
>> + if (Instance == 0) {
>> + *FoundSection = Section;
>> + return EFI_SUCCESS;
>> + } else {
>> + Instance--;
>> + }
>> + }
>> + }
>> +
>> + return EFI_NOT_FOUND;
>> +}
>> +
>> +/**
>> + Locates a section within a series of sections
>> + with the specified section type.
>> +
>> + @param[in] Sections The sections to search
>> + @param[in] SizeOfSections Total size of all sections
>> + @param[in] SectionType The section type to locate
>> + @param[out] FoundSection The FFS section if found
>> +
>> + @retval EFI_SUCCESS The file and section was found
>> + @retval EFI_NOT_FOUND The file and section was not found
>> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
>> +**/
>> +EFI_STATUS
>> +FindFfsSectionInSections (
>> + IN VOID *Sections,
>> + IN UINTN SizeOfSections,
>> + IN EFI_SECTION_TYPE SectionType,
>> + OUT EFI_COMMON_SECTION_HEADER **FoundSection
>> + )
>> +{
>> + return FindFfsSectionInstance (
>> + Sections,
>> + SizeOfSections,
>> + SectionType,
>> + 0,
>> + FoundSection
>> + );
>> +}
>> +
>> +/**
>> + Locates a FFS file with the specified file type and a section
>> + within that file with the specified section type.
>> +
>> + @param[in] Fv The firmware volume to search
>> + @param[in] FileType The file type to locate
>> + @param[in] SectionType The section type to locate
>> + @param[out] FoundSection The FFS section if found
>> +
>> + @retval EFI_SUCCESS The file and section was found
>> + @retval EFI_NOT_FOUND The file and section was not found
>> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
>> +**/
>> +EFI_STATUS
>> +FindFfsFileAndSection (
>> + IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
>> + IN EFI_FV_FILETYPE FileType,
>> + IN EFI_SECTION_TYPE SectionType,
>> + OUT EFI_COMMON_SECTION_HEADER **FoundSection
>> + )
>> +{
>> + EFI_STATUS Status;
>> + EFI_PHYSICAL_ADDRESS CurrentAddress;
>> + EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume;
>> + EFI_FFS_FILE_HEADER *File;
>> + UINT32 Size;
>> + EFI_PHYSICAL_ADDRESS EndOfFile;
>> +
>> + if (Fv->Signature != EFI_FVH_SIGNATURE) {
>> + DEBUG ((DEBUG_ERROR, "FV at %p does not have FV header
>> signature\n", Fv));
>> + return EFI_VOLUME_CORRUPTED;
>> + }
>> +
>> + CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Fv;
>> + EndOfFirmwareVolume = CurrentAddress + Fv->FvLength;
>> +
>> + //
>> + // Loop through the FFS files in the Boot Firmware Volume
>> + //
>> + for (EndOfFile = CurrentAddress + Fv->HeaderLength; ; ) {
>> + CurrentAddress = (EndOfFile + 7) & ~(7ULL);
>> + if (CurrentAddress > EndOfFirmwareVolume) {
>> + return EFI_VOLUME_CORRUPTED;
>> + }
>> +
>> + File = (EFI_FFS_FILE_HEADER *)(UINTN)CurrentAddress;
>> + Size = *(UINT32 *)File->Size & 0xffffff;
>> + if (Size < (sizeof (*File) + sizeof (EFI_COMMON_SECTION_HEADER))) {
>> + return EFI_VOLUME_CORRUPTED;
>> + }
>> +
>> + EndOfFile = CurrentAddress + Size;
>> + if (EndOfFile > EndOfFirmwareVolume) {
>> + return EFI_VOLUME_CORRUPTED;
>> + }
>> +
>> + //
>> + // Look for the request file type
>> + //
>> + if (File->Type != FileType) {
>> + continue;
>> + }
>> +
>> + Status = FindFfsSectionInSections (
>> + (VOID *)(File + 1),
>> + (UINTN)EndOfFile - (UINTN)(File + 1),
>> + SectionType,
>> + FoundSection
>> + );
>> + if (!EFI_ERROR (Status) ||
>> + (Status == EFI_VOLUME_CORRUPTED))
>> + {
>> + return Status;
>> + }
>> + }
>> +}
>> +
>> +/**
>> + Locates the PEI Core entry point address
>> +
>> + @param[in] Fv The firmware volume to search
>> + @param[out] PeiCoreEntryPoint The entry point of the PEI Core image
>> +
>> + @retval EFI_SUCCESS The file and section was found
>> + @retval EFI_NOT_FOUND The file and section was not found
>> + @retval EFI_VOLUME_CORRUPTED The firmware volume was corrupted
>> +**/
>> +EFI_STATUS
>> +FindPeiCoreImageBaseInFv (
>> + IN EFI_FIRMWARE_VOLUME_HEADER *Fv,
>> + OUT EFI_PHYSICAL_ADDRESS *PeiCoreImageBase
>> + )
>> +{
>> + EFI_STATUS Status;
>> + EFI_COMMON_SECTION_HEADER *Section;
>> +
>> + Status = FindFfsFileAndSection (
>> + Fv,
>> + EFI_FV_FILETYPE_PEI_CORE,
>> + EFI_SECTION_PE32,
>> + &Section
>> + );
>> + if (EFI_ERROR (Status)) {
>> + Status = FindFfsFileAndSection (
>> + Fv,
>> + EFI_FV_FILETYPE_PEI_CORE,
>> + EFI_SECTION_TE,
>> + &Section
>> + );
>> + if (EFI_ERROR (Status)) {
>> + DEBUG ((DEBUG_ERROR, "Unable to find PEI Core image\n"));
>> + return Status;
>> + }
>> + }
>> +
>> + *PeiCoreImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)(Section + 1);
>> + return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> + Find and return Pei Core entry point.
>> +
>> + It also find SEC and PEI Core file debug information. It will
>> report them if
>> + remote debug is enabled.
>> +**/
>> +VOID
>> +FindAndReportEntryPoints (
>> + IN EFI_FIRMWARE_VOLUME_HEADER **BootFirmwareVolumePtr,
>> + OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint
>> + )
>> +{
>> + EFI_STATUS Status;
>> + EFI_PHYSICAL_ADDRESS PeiCoreImageBase = 0;
>> + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
>> +
>> + Status = FindPeiCoreImageBaseInFv (*BootFirmwareVolumePtr,
>> &PeiCoreImageBase);
>> + ASSERT (Status == EFI_SUCCESS);
>> +
>> + ZeroMem ((VOID *)&ImageContext, sizeof
>> (PE_COFF_LOADER_IMAGE_CONTEXT));
>> +
>> + //
>> + // Report PEI Core debug information when remote debug is enabled
>> + //
>> + ImageContext.ImageAddress =
>> (EFI_PHYSICAL_ADDRESS)(UINTN)PeiCoreImageBase;
>> + ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID
>> *)(UINTN)ImageContext.ImageAddress);
>> + PeCoffLoaderRelocateImageExtraAction (&ImageContext);
>> +
>> + //
>> + // Find PEI Core entry point
>> + //
>> + Status = PeCoffLoaderGetEntryPoint ((VOID
>> *)(UINTN)PeiCoreImageBase, (VOID **)PeiCoreEntryPoint);
>> + if (EFI_ERROR (Status)) {
>> + *PeiCoreEntryPoint = 0;
>> + }
>> +
>> + return;
>> +}
>> +
>> +/**
>> + Find the peicore entry point and jump to the entry point to execute.
>> +
>> + @param[in] Context The first input parameter of
>> InitializeDebugAgent().
>> +**/
>> +VOID
>> +EFIAPI
>> +SecStartupPhase2 (
>> + IN VOID *Context
>> + )
>> +{
>> + EFI_SEC_PEI_HAND_OFF *SecCoreData;
>> + EFI_FIRMWARE_VOLUME_HEADER *BootFv;
>> + EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint;
>> +
>> + SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context;
>> +
>> + //
>> + // Find PEI Core entry point. It will report SEC and Pei Core
>> debug information if remote debug
>> + // is enabled.
>> + //
>> + BootFv = (EFI_FIRMWARE_VOLUME_HEADER
>> *)SecCoreData->BootFirmwareVolumeBase;
>> + FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
>> + SecCoreData->BootFirmwareVolumeBase = BootFv;
>> + SecCoreData->BootFirmwareVolumeSize = (UINTN)BootFv->FvLength;
>> +
>> + DEBUG ((DEBUG_INFO, "Find Pei EntryPoint=%p\n", PeiCoreEntryPoint));
>> +
>> + //
>> + // Transfer the control to the PEI core
>> + //
>> + DEBUG ((DEBUG_INFO, "SecStartupPhase2 %p\n", PeiCoreEntryPoint));
>> +
>> + (*PeiCoreEntryPoint)(SecCoreData, (EFI_PEI_PPI_DESCRIPTOR
>> *)&mPrivateDispatchTable);
>> +
>> + //
>> + // If we get here then the PEI Core returned, which is not
>> recoverable.
>> + //
>> + ASSERT (FALSE);
>> + CpuDeadLoop ();
>> +}
>> +
>> +/**
>> + Entry point to the C language phase of SEC. initialize some
>> temporary memory and set up the stack,
>> + the control is transferred to this function.
>> +
>> + @param[in] BootFv The pointer to the PEI FV in memory.
>> + @param[in] TopOfCurrentStack Top of Current Stack.
>> +**/
>> +VOID
>> +EFIAPI
>> +SecCoreStartupWithStack (
>> + IN EFI_FIRMWARE_VOLUME_HEADER *BootFv,
>> + IN VOID *TopOfCurrentStack
>> + )
>> +{
>> + EFI_SEC_PEI_HAND_OFF SecCoreData;
>> + EFI_FIRMWARE_VOLUME_HEADER *BootPeiFv =
>> (EFI_FIRMWARE_VOLUME_HEADER *)BootFv;
>> +
>> + DEBUG ((DEBUG_INFO, "Entering C environment\n"));
>> +
>> + ProcessLibraryConstructorList (NULL, NULL);
>> +
>> + DEBUG ((
>> + DEBUG_INFO,
>> + "SecCoreStartupWithStack (0x%lx, 0x%lx)\n",
>> + (UINTN)BootFv,
>> + (UINTN)TopOfCurrentStack
>> + ));
>> + DEBUG ((
>> + DEBUG_INFO,
>> + "(0x%lx, 0x%lx)\n",
>> + (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase)),
>> + (UINTN)(FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
>> + ));
>> +
>> + // |-------------| <-- TopOfCurrentStack
>> + // | BSP Stack | 32k
>> + // |-------------|
>> + // | BSP Heap | 32k
>> + // |-------------| <-- SecCoreData.TemporaryRamBase
>> + // | Ap Stack | 384k
>> + // |-------------|
>> + // | Exception | 64k
>> + // |-------------| <-- PcdOvmfSecPeiTempRamBase
>> +
>> + ASSERT (
>> + (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) +
>> + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) ==
>> + (UINTN)TopOfCurrentStack
>> + );
>> +
>> + //
>> + // Initialize SEC hand-off state
>> + //
>> + SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
>> +
>> + SecCoreData.TemporaryRamSize = (UINTN)SIZE_64KB;
>> + SecCoreData.TemporaryRamBase = (VOID *)(FixedPcdGet64
>> (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)
>> - SecCoreData.TemporaryRamSize);
>> +
>> + SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase;
>> + SecCoreData.PeiTemporaryRamSize = SecCoreData.TemporaryRamSize >> 1;
>> +
>> + SecCoreData.StackBase = (UINT8 *)SecCoreData.TemporaryRamBase +
>> SecCoreData.PeiTemporaryRamSize;
>> + SecCoreData.StackSize = SecCoreData.TemporaryRamSize >> 1;
>> +
>> + SecCoreData.BootFirmwareVolumeBase = BootPeiFv;
>> + SecCoreData.BootFirmwareVolumeSize = (UINTN)BootPeiFv->FvLength;
>> +
>> + DEBUG ((
>> + DEBUG_INFO,
>> + "&SecCoreData.BootFirmwareVolumeBase=%lx
>> SecCoreData.BootFirmwareVolumeBase=%lx\n",
>> + (UINT64)&(SecCoreData.BootFirmwareVolumeBase),
>> + (UINT64)(SecCoreData.BootFirmwareVolumeBase)
>> + ));
>> + DEBUG ((
>> + DEBUG_INFO,
>> + "&SecCoreData.BootFirmwareVolumeSize=%lx
>> SecCoreData.BootFirmwareVolumeSize=%lx\n",
>> + (UINT64)&(SecCoreData.BootFirmwareVolumeSize),
>> + (UINT64)(SecCoreData.BootFirmwareVolumeSize)
>> + ));
>> +
>> + //
>> + // Initialize Debug Agent to support source level debug in SEC/PEI
>> phases before memory ready.
>> + //
>> + InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, NULL, NULL);
>> + SecStartupPhase2 (&SecCoreData);
>> +}
>> +
>> +/**
>> + temporary memory to permanent memory and do stack switching.
>> +
>> + @param[in] PeiServices Pointer to the PEI Services Table.
>> + @param[in] TemporaryMemoryBase Temporary Memory Base address.
>> + @param[in] PermanentMemoryBase Permanent Memory Base address.
>> + @param[in] CopySize The size of memory that needs to be migrated.
>> +
>> + @retval EFI_SUCCESS Migration successful.
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +TemporaryRamMigration (
>> + IN CONST EFI_PEI_SERVICES **PeiServices,
>> + IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase,
>> + IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase,
>> + IN UINTN CopySize
>> + )
>> +{
>> + VOID *OldHeap;
>> + VOID *NewHeap;
>> + VOID *OldStack;
>> + VOID *NewStack;
>> + BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
>> +
>> + DEBUG ((
>> + DEBUG_INFO,
>> + "TemporaryRamMigration (0x%Lx, 0x%Lx, 0x%Lx)\n",
>> + TemporaryMemoryBase,
>> + PermanentMemoryBase,
>> + (UINT64)CopySize
>> + ));
>> +
>> + OldHeap = (VOID *)(UINTN)TemporaryMemoryBase;
>> + NewHeap = (VOID *)((UINTN)PermanentMemoryBase + (CopySize >> 1));
>> +
>> + OldStack = (VOID *)((UINTN)TemporaryMemoryBase + (CopySize >> 1));
>> + NewStack = (VOID *)(UINTN)PermanentMemoryBase;
>> +
>> + //
>> + // Migrate Heap
>> + //
>> + CopyMem (NewHeap, OldHeap, CopySize >> 1);
>> +
>> + //
>> + // Migrate Stack
>> + //
>> + CopyMem (NewStack, OldStack, CopySize >> 1);
>> +
>> + // Use SetJump ()/LongJump () to switch to a new stack.
>> + //
>> + if (SetJump (&JumpBuffer) == 0) {
>> + JumpBuffer.SP = JumpBuffer.SP - (UINTN)OldStack + (UINTN)NewStack;
>> + LongJump (&JumpBuffer, (UINTN)-1);
>> + }
>> +
>> + return EFI_SUCCESS;
>> +}
>> diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
>> b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
>> new file mode 100644
>> index 0000000000..af29ba0551
>> --- /dev/null
>> +++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
>> @@ -0,0 +1,53 @@
>> +## @file
>> +# SEC Driver
>> +#
>> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All
>> rights reserved.<BR>
>> +#
>> +# SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +##
>> +
>> +[Defines]
>> + INF_VERSION = 0x00010005
>> + BASE_NAME = SecMain
>> + FILE_GUID = 57d02d4f-5a5d-4bfa-b7d6-ba0a4d2c72ce
>> + MODULE_TYPE = SEC
>> + VERSION_STRING = 1.0
>> +
>> +#
>> +# VALID_ARCHITECTURES = LOONGARCH64
>> +#
>> +
>> +[Sources]
>> + LoongArch64/Start.S
>> + SecMain.c
>> +
>> +[Packages]
>> + MdePkg/MdePkg.dec
>> + MdeModulePkg/MdeModulePkg.dec
>> + OvmfPkg/OvmfPkg.dec
>> + UefiCpuPkg/UefiCpuPkg.dec
>> +
>> +[LibraryClasses]
>> + BaseLib
>> + BaseMemoryLib
>> + CpuExceptionHandlerLib
>> + DebugAgentLib
>> + DebugLib
>> + IoLib
>> + PcdLib
>> + PeCoffLib
>> + PeCoffGetEntryPointLib
>> + PeCoffExtraActionLib
>> +
>> +[Ppis]
>> + gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED
>> +
>> +[FixedPcd]
>> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
>> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>> +
>> + gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
>> + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
>> +
>> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
>>
>
>
>
>
>
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113136): https://edk2.groups.io/g/devel/message/113136
Mute This Topic: https://groups.io/mt/103398640/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
© 2016 - 2026 Red Hat, Inc.