OvmfPkg/LoongArchVirt: Support SEC phase

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>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
diff --git a/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
new file mode 100644
index 0000000..dd74c6b
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
@@ -0,0 +1,184 @@
+#------------------------------------------------------------------------------

+#

+# 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 global interrupt */

+  bl       DisableInterrupts

+

+  /* Disable all local interrupt */

+  li.w     $a0, 0x1FFF

+  bl       DisableLocalInterrupts

+

+  /* Read physical cpu number id */

+  bl       GetApicId

+  li.d     $t0, BOOTCORE_ID  //0

+  bne      $a0, $t0, SlaveMain

+

+  /* Set BSP stack */

+  li.d     $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize)  # stack base

+  move     $sp, $t0

+  addi.d   $sp, $sp, -0x8

+

+  /* Load the exception vector base address */

+  li.d     $s0, FixedPcdGet64(PcdLoongArchExceptionVectorBaseAddress)

+

+  /* 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, $s0

+  bl       CopyMem

+

+  /* Configure BSP reset ebase */

+  move     $a0, $s0

+  bl       SetExceptionBaseAddress

+

+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.w      $t1, BIT12

+  csrxchg   $t1, $t1, LOONGARCH_CSR_ECFG

+

+  li.w      $t2, 0xFFFFFFFFU

+  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

+  b         WaitForWake

+# End of SlaveMain

+

+.align 12

+ASM_PFX(ApException):

+  csrrd     $t0, LOONGARCH_CSR_ESTAT

+  srli.d    $t0, $t0, 12

+  andi      $t0, $t0, 0x1

+  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

+

+  /* Read mail buf and jump to specified entry */

+  li.d      $t1, LOONGARCH_IOCSR_MBUF0

+  iocsrrd.d $t0, $t1

+  beqz      $t0, OutOfException

+  csrwr     $t0, LOONGARCH_CSR_ERA

+  li.d      $t0, LOONGARCH_IOCSR_MBUF3

+  iocsrrd.d $a1, $t0

+  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(PcdLoongArchExceptionVectorBaseAddress)

+  bl        SetExceptionBaseAddress

+

+  bl        ApInitStack

+  bl        GetApicId

+  b         OutOfException

+NoParameterCall:

+  li.w      $t0, BIT2 // IE

+  csrxchg   $zero, $t0, LOONGARCH_CSR_PRMD // Clean PIE

+

+OutOfException:

+  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 0000000..6f541b1
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
@@ -0,0 +1,512 @@
+/** @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/PcdLib.h>

+#include <Library/PeCoffLib.h>

+#include <Library/PeCoffGetEntryPointLib.h>

+#include <Library/PeCoffExtraActionLib.h>

+#include <Library/PeiServicesLib.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.

+**/

+STATIC

+EFI_STATUS

+EFIAPI

+TemporaryRamMigration (

+  IN CONST EFI_PEI_SERVICES  **PeiServices,

+  IN EFI_PHYSICAL_ADDRESS    TemporaryMemoryBase,

+  IN EFI_PHYSICAL_ADDRESS    PermanentMemoryBase,

+  IN UINTN                   CopySize

+  );

+

+STATIC EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI  mTemporaryRamSupportPpi = {

+  TemporaryRamMigration

+};

+

+STATIC 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

+**/

+STATIC

+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

+**/

+STATIC

+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

+**/

+STATIC

+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

+**/

+STATIC

+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.

+**/

+STATIC

+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().

+**/

+STATIC

+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 ();

+

+  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.

+**/

+STATIC

+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 0000000..afa0ff8
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
@@ -0,0 +1,54 @@
+## @file

+#  SEC Driver

+#

+#  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+#

+#  SPDX-License-Identifier: BSD-2-Clause-Patent

+#

+##

+

+[Defines]

+  INF_VERSION                    = 1.30

+  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

+  PeiServicesLib

+

+[Ppis]

+  gEfiTemporaryRamSupportPpiGuid                # PPI ALWAYS_PRODUCED

+

+[FixedPcd]

+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase

+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize

+

+  gUefiCpuPkgTokenSpaceGuid.PcdLoongArchExceptionVectorBaseAddress

+  gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber

+

+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress