[edk2-devel] [edk2-platforms][PATCH V5 08/15] Platform/Loongson: Add CPU DXE driver.

xianglai posted 15 patches 1 year, 3 months ago
There is a newer version of this series
[edk2-devel] [edk2-platforms][PATCH V5 08/15] Platform/Loongson: Add CPU DXE driver.
Posted by xianglai 1 year, 3 months ago
The driver produces EFI_CPU_ARCH_PROTOCOL,
Initialize the exception entry address.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Chao Li <lichao@loongson.cn>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c  | 367 ++++++++++++++++++
 .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h  | 199 ++++++++++
 .../Drivers/CpuDxe/CpuDxe.inf                 |  59 +++
 .../Drivers/CpuDxe/LoongArch64/Exception.c    | 335 ++++++++++++++++
 .../Drivers/CpuDxe/LoongArch64/Fpu.S          |  97 +++++
 .../Drivers/CpuDxe/LoongArch64/LoongArch.S    | 321 +++++++++++++++
 6 files changed, 1378 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S

diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
new file mode 100644
index 0000000000..23f824d82b
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
@@ -0,0 +1,367 @@
+/** @file
+  CPU DXE Module to produce CPU ARCH Protocol
+
+  Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Guid/IdleLoopEvent.h>
+#include <Uefi.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/CpuLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MmuLib.h>
+#include "CpuDxe.h"
+
+BOOLEAN mInterruptState   = FALSE;
+
+/*
+  This function flushes the range of addresses from Start to Start+Length
+  from the processor's data cache. If Start is not aligned to a cache line
+  boundary, then the bytes before Start to the preceding cache line boundary
+  are also flushed. If Start+Length is not aligned to a cache line boundary,
+  then the bytes past Start+Length to the end of the next cache line boundary
+  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
+  supported. If the data cache is fully coherent with all DMA operations, then
+  this function can just return EFI_SUCCESS. If the processor does not support
+  flushing a range of the data cache, then the entire data cache can be flushed.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  Start            The beginning physical address to flush from the processor's data
+                           cache.
+  @param  Length           The number of bytes to flush from the processor's data cache. This
+                           function may flush more bytes than Length specifies depending upon
+                           the granularity of the flush operation that the processor supports.
+  @param  FlushType        Specifies the type of flush operation to perform.
+
+  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
+                                the processor's data cache.
+  @retval EFI_UNSUPPORTEDT      The processor does not support the cache flush type specified
+                                by FlushType.
+  @retval EFI_DEVICE_ERROR      The address range from Start to Start+Length could not be flushed
+                                from the processor's data cache.
+**/
+EFI_STATUS
+EFIAPI
+CpuFlushCpuDataCache (
+  IN EFI_CPU_ARCH_PROTOCOL           *This,
+  IN EFI_PHYSICAL_ADDRESS            Start,
+  IN UINT64                          Length,
+  IN EFI_CPU_FLUSH_TYPE              FlushType
+  )
+{
+  switch (FlushType) {
+    case EfiCpuFlushTypeWriteBack:
+      WriteBackDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
+      break;
+    case EfiCpuFlushTypeInvalidate:
+      InvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
+      break;
+    case EfiCpuFlushTypeWriteBackInvalidate:
+      WriteBackInvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
+      break;
+    default:
+      return EFI_INVALID_PARAMETER;
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  This function enables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
+**/
+EFI_STATUS
+EFIAPI
+CpuEnableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL          *This
+  )
+{
+  EnableInterrupts ();
+
+  mInterruptState  = TRUE;
+  return EFI_SUCCESS;
+}
+
+/**
+  This function disables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
+**/
+EFI_STATUS
+EFIAPI
+CpuDisableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL          *This
+  )
+{
+  DisableInterrupts ();
+
+  mInterruptState = FALSE;
+  return EFI_SUCCESS;
+}
+
+/**
+  This function retrieves the processor's current interrupt state a returns it in
+  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
+  are currently disabled, then FALSE is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
+                           interrupts are enabled and FALSE if interrupts are disabled.
+
+  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
+  @retval EFI_INVALID_PARAMETER State is NULL.
+**/
+EFI_STATUS
+EFIAPI
+CpuGetInterruptState (
+  IN  EFI_CPU_ARCH_PROTOCOL         *This,
+  OUT BOOLEAN                       *State
+  )
+{
+  if (State == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *State = mInterruptState;
+  return EFI_SUCCESS;
+}
+
+/**
+  This function generates an INIT on the processor. If this function succeeds, then the
+  processor will be reset, and control will not be returned to the caller. If InitType is
+  not supported by this processor, or the processor cannot programmatically generate an
+  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
+  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  InitType         The type of processor INIT to perform.
+
+  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
+  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
+                                by this processor.
+  @retval EFI_DEVICE_ERROR      The processor INIT failed.
+**/
+EFI_STATUS
+EFIAPI
+CpuInit (
+  IN EFI_CPU_ARCH_PROTOCOL           *This,
+  IN EFI_CPU_INIT_TYPE               InitType
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InterruptType   Interrupt Type.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+  when a processor interrupt occurs. If this parameter is NULL, then the handler
+  will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
+  previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
+  previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
+**/
+EFI_STATUS
+EFIAPI
+CpuRegisterInterruptHandler (
+  IN EFI_CPU_ARCH_PROTOCOL          *This,
+  IN EFI_EXCEPTION_TYPE             InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  )
+{
+  return RegisterInterruptHandler (InterruptType, InterruptHandler);
+}
+
+/**
+  Returns a timer value from one of the CPU's internal timers. There is no
+  inherent time interval between ticks but is a function of the CPU frequency.
+
+  @param  This                - Protocol instance structure.
+  @param  TimerIndex          - Specifies which CPU timer is requested.
+  @param  TimerValue          - Pointer to the returned timer value.
+  @param  TimerPeriod         - A pointer to the amount of time that passes
+                                in femtoseconds (10-15) for each increment
+                                of TimerValue. If TimerValue does not
+                                increment at a predictable rate, then 0 is
+                                returned.  The amount of time that has
+                                passed between two calls to GetTimerValue()
+                                can be calculated with the formula
+                                (TimerValue2 - TimerValue1) * TimerPeriod.
+                                This parameter is optional and may be NULL.
+
+  @retval EFI_SUCCESS           - If the CPU timer count was returned.
+  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
+  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
+  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
+**/
+EFI_STATUS
+EFIAPI
+CpuGetTimerValue (
+  IN  EFI_CPU_ARCH_PROTOCOL          *This,
+  IN  UINT32                         TimerIndex,
+  OUT UINT64                         *TimerValue,
+  OUT UINT64                         *TimerPeriod   OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  This function modifies the attributes for the memory region specified by BaseAddress and
+  Length from their current attributes to the attributes specified by Attributes.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  BaseAddress      The physical address that is the start address of a memory region.
+  @param  Length           The size in bytes of the memory region.
+  @param  Attributes       The bit mask of attributes to set for the memory region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+                                The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+  IN EFI_CPU_ARCH_PROTOCOL    *This,
+  IN EFI_PHYSICAL_ADDRESS      BaseAddress,
+  IN UINT64                    Length,
+  IN UINT64                    EfiAttributes
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       LoongArchAttributes;
+  UINTN       RegionBaseAddress;
+  UINTN       RegionLength;
+  UINTN       RegionLoongArchAttributes;
+
+  if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
+    // Minimum granularity is SIZE_4KB (4KB on ARM)
+    DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
+      BaseAddress,
+      Length,
+      EfiAttributes));
+
+    return EFI_UNSUPPORTED;
+  }
+  // Convert the 'Attribute' into LoongArch Attribute
+  LoongArchAttributes = EfiAttributeToLoongArchAttribute (EfiAttributes);
+
+  // Get the region starting from 'BaseAddress' and its 'Attribute'
+  RegionBaseAddress = BaseAddress;
+  Status = GetLoongArchMemoryRegion (RegionBaseAddress, BaseAddress + Length,
+             &RegionLength, &RegionLoongArchAttributes);
+
+  LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
+  // Data & Instruction Caches are flushed when we set new memory attributes.
+  // So, we only set the attributes if the new region is different.
+  if (EFI_ERROR (Status) || (RegionLoongArchAttributes != LoongArchAttributes) ||
+      ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
+  {
+    return LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Callback function for idle events.
+
+  @param  Event                 Event whose notification function is being invoked.
+  @param  Context               The pointer to the notification function's context,
+                                which is implementation-dependent.
+
+  @param VOID
+**/
+VOID
+EFIAPI
+IdleLoopEventCallback (
+  IN EFI_EVENT                Event,
+  IN VOID                     *Context
+  )
+{
+  CpuSleep ();
+}
+
+//
+// Globals used to initialize the protocol
+//
+EFI_HANDLE            CpuHandle = NULL;
+EFI_CPU_ARCH_PROTOCOL Cpu = {
+  CpuFlushCpuDataCache,
+  CpuEnableInterrupt,
+  CpuDisableInterrupt,
+  CpuGetInterruptState,
+  CpuInit,
+  CpuRegisterInterruptHandler,
+  CpuGetTimerValue,
+  CpuSetMemoryAttributes,
+  0,          // NumberOfTimers
+  4,          // DmaBufferAlignment
+};
+
+/**
+  Initialize the state information for the CPU Architectural Protocol.
+
+  @param ImageHandle     Image handle this driver.
+  @param SystemTable     Pointer to the System Table.
+
+  @retval EFI_SUCCESS           Thread can be successfully created
+  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+  @retval EFI_DEVICE_ERROR      Cannot create the thread
+**/
+EFI_STATUS
+CpuDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  EFI_EVENT    IdleLoopEvent;
+
+  InitializeExceptions (&Cpu);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &CpuHandle,
+                  &gEfiCpuArchProtocolGuid, &Cpu,
+                  NULL
+                  );
+
+  //
+  // Setup a callback for idle events
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  IdleLoopEventCallback,
+                  NULL,
+                  &gIdleLoopEventGuid,
+                  &IdleLoopEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
new file mode 100644
index 0000000000..43cb976aa2
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
@@ -0,0 +1,199 @@
+/** @file
+  CPU DXE Module to produce CPU ARCH Protocol and CPU MP Protocol
+
+  Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef CPU_DXE_H_
+#define CPU_DXE_H_
+
+#include <Protocol/Cpu.h>
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InteruptNum    A number of the processor's current interrupt.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InteruptNum was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InteruptNum is not supported.
+**/
+EFI_STATUS
+RegisterInterruptHandler (
+  IN EFI_EXCEPTION_TYPE             InteruptNum,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  );
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InteruptNum    A number of the processor's current interrupt.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InteruptNum was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InteruptNum is not supported.
+**/
+EFI_STATUS
+RegisterDebuggerInterruptHandler (
+  IN EFI_EXCEPTION_TYPE             InteruptNum,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  );
+
+/**
+  This function modifies the attributes for the memory region specified by BaseAddress and
+  Length from their current attributes to the attributes specified by Attributes.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  BaseAddress      The physical address that is the start address of a memory region.
+  @param  Length           The size in bytes of the memory region.
+  @param  Attributes       The bit mask of attributes to set for the memory region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+                                The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+  IN EFI_CPU_ARCH_PROTOCOL     *This,
+  IN EFI_PHYSICAL_ADDRESS      BaseAddress,
+  IN UINT64                    Length,
+  IN UINT64                    Attributes
+  );
+
+/** Exception module initialization
+  This function sets the exception base address.
+
+  @param  Cpu   A pointer to the CPU architecture protocol structure.
+
+  @retval EFI_SUCCESS           Initialization succeeded
+  @retval EFI_NOT_FOUND          Could not Found resources.
+  @retval EFI_OUT_OF_RESOURCES   No enough resources.
+**/
+EFI_STATUS
+InitializeExceptions (
+  IN EFI_CPU_ARCH_PROTOCOL    *Cpu
+  );
+
+/** Common exception entry
+  Exception handling is the entry point for the C environment,
+  This function does different things depending on the exception type.
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval            VOID.
+**/
+VOID
+EFIAPI
+CommonExceptionEntry (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  );
+
+extern CHAR8 LoongArchException[], LoongArchExceptionEnd[];
+/** Set Exception Base Address
+
+  @param  addr Exception Base Address.
+
+  @retval      The Old Exception Base Address.
+**/
+extern
+UINT64
+SetEbase (
+  EFI_PHYSICAL_ADDRESS addr
+  );
+/*
+  Load the FPU with signalling NANS.  This bit pattern we're using has
+  the property that no matter whether considered as single or as double
+  precision represents signaling NANS.
+
+  @param  fcsr  The value to initialize FCSR0
+
+  @retval      The Old Exception Base Address.
+ */
+extern
+VOID
+InitFpu (
+  UINT32 fcsr
+  );
+
+/*
+  Read Csr EUEN register.
+
+  @param  CsrEuen Pointer to the variable used to store the EUEN register value
+
+  @retval  none
+ */
+extern
+VOID
+LoongArchReadqCsrEuen (
+  UINT64   *CsrEuen
+  );
+
+/*
+  Write Csr EUEN register.
+
+  @param   The value used to write to the EUEN register
+
+  @retval  none
+ */
+extern
+VOID
+LoongArchWriteqCsrEuen (
+  UINT64   CsrEuen
+  );
+
+/*
+  Enables  floating-point unit
+
+  @param  VOID
+
+  @retval  VOID
+ */
+extern
+VOID
+LoongArchEnableFpu (
+  VOID
+  );
+
+/*
+  Disable  floating-point unit
+
+  @param  VOID
+
+  @retval  VOID
+ */
+extern
+VOID
+LoongArchDisableFpu (
+  VOID
+  );
+
+#endif // CPU_DXE_H_
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
new file mode 100644
index 0000000000..96aabfefb8
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
@@ -0,0 +1,59 @@
+## @file
+#  CPU driver installs CPU Architecture Protocol and CPU MP protocol.
+#
+#  Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = CpuDxe
+  FILE_GUID                      = bf954921-25c1-48c0-9bfb-8d0cd7ee92da
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = CpuDxeInitialize
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources.Common]
+  CpuDxe.c
+  CpuDxe.h
+
+[Sources.LOONGARCH64]
+  LoongArch64/Exception.c
+  LoongArch64/LoongArch.S
+  LoongArch64/Fpu.S
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  CacheMaintenanceLib
+  CpuLib
+  DebugLib
+  DxeServicesTableLib
+  HobLib
+  PeCoffGetEntryPointLib
+  UefiDriverEntryPoint
+  UefiLib
+  MmuLib
+
+[Protocols]
+  gEfiCpuArchProtocolGuid
+  gEfiMpServiceProtocolGuid
+
+[Guids]
+  gEfiDebugImageInfoTableGuid
+  gIdleLoopEventGuid
+
+[Depex]
+ TRUE
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
new file mode 100644
index 0000000000..793ae90e4f
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
@@ -0,0 +1,335 @@
+/** @file
+
+  Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - ESTAT     - Exception Status
+    - ECFG      - Exception Configure
+    - ERA       - Exception Return Address
+    - BADV      - Bad Virtual Address
+    - BADI      - Bad Instructions
+    - Epc or EPC or epc   - Exception Program Counter
+    - pc or PC or pc      - Program Counter
+    - CRMD      - Current Mode
+    - PRMD      - Previous Mode
+    - CsrEuen      - Cpu Status Register Extern Unit Enable
+    - fpu or fp or FP   - Float Point Unit
+    - LOONGARCH   - Loongson Arch
+    - Irq   - Interrupt ReQuest
+**/
+
+#include "Library/Cpu.h"
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include "CpuDxe.h"
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/UefiLib.h>
+#include <Guid/DebugImageInfoTable.h>
+
+EFI_EXCEPTION_CALLBACK  gInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1];
+EFI_EXCEPTION_CALLBACK  gDebuggerExceptionHandlers[MAX_LOONGARCH_INTERRUPT + 1];
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InteruptNum    A number of the processor's current interrupt.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InteruptNum was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InteruptNum is not supported.
+**/
+EFI_STATUS
+RegisterInterruptHandler (
+  IN EFI_EXCEPTION_TYPE             InteruptNum,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  )
+{
+  if (InteruptNum > MAX_LOONGARCH_INTERRUPT) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if ((InterruptHandler != NULL)
+    && (gInterruptHandler[InteruptNum] != NULL))
+  {
+    return EFI_ALREADY_STARTED;
+  }
+
+  gInterruptHandler[InteruptNum] = InterruptHandler;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function calls the corresponding exception handler based on the exception type.
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval VOID
+**/
+STATIC VOID
+EFIAPI
+CommonInterruptHandler (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  INT32 Pending;
+  INT32 InterruptNum;
+  /*Interrupt [13-0] NMI IPI TI PCOV hw IP10-IP2 soft IP1-IP0*/
+  Pending = ((SystemContext.SystemContextLoongArch64->ESTAT) &
+             (SystemContext.SystemContextLoongArch64->ECFG) & 0x1fff);
+  for (InterruptNum = 0; InterruptNum < MAX_LOONGARCH_INTERRUPT; InterruptNum++) {
+    if (Pending & (1 << InterruptNum)) {
+      if (gInterruptHandler[InterruptNum] != NULL) {
+        gInterruptHandler[InterruptNum] (InterruptNum, SystemContext);
+      } else {
+        DEBUG ((DEBUG_INFO, "Pending: 0x%0x, InterruptNum: 0x%0x\n", Pending, InterruptNum));
+      }
+    }
+  }
+}
+
+/**
+  Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image
+  it came from. As long as the PE/COFF image contains a debug directory entry a
+  string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF
+  image. Microsoft tools contain a pointer to the PDB file that contains the debug information.
+
+  @param  FaultAddress         Address to find PE/COFF image for.
+  @param  ImageBase            Return load address of found image
+  @param  PeCoffSizeOfHeaders  Return the size of the PE/COFF header for the image that was found
+
+  @retval NULL                 FaultAddress not in a loaded PE/COFF image.
+  @retval                      Path and file name of PE/COFF image.
+**/
+CHAR8 *
+GetImageName (
+  IN  UINTN  FaultAddress,
+  OUT UINTN  *ImageBase,
+  OUT UINTN  *PeCoffSizeOfHeaders
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_DEBUG_IMAGE_INFO_TABLE_HEADER   *DebugTableHeader;
+  EFI_DEBUG_IMAGE_INFO                *DebugTable;
+  UINTN                               Entry;
+  CHAR8                               *Address;
+
+  Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugTableHeader);
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  DebugTable = DebugTableHeader->EfiDebugImageInfoTable;
+  if (DebugTable == NULL) {
+    return NULL;
+  }
+
+  Address = (CHAR8 *)(UINTN)FaultAddress;
+  for (Entry = 0; Entry < DebugTableHeader->TableSize; Entry++, DebugTable++) {
+    if (DebugTable->NormalImage != NULL) {
+      if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
+          (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
+        if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&
+            (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {
+          *ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;
+          *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);
+          return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);
+        }
+      }
+    }
+  }
+  return NULL;
+}
+
+/**
+  pass a file name string that contains the path, return file name.
+
+  @param  FullName   Path and file name
+
+  @retval            file name.
+**/
+STATIC
+CONST CHAR8 *
+BaseName (
+  IN  CONST CHAR8 *FullName
+  )
+{
+  CONST CHAR8 *Str;
+
+  Str = FullName + AsciiStrLen (FullName);
+
+  while (--Str > FullName) {
+    if (*Str == '/' || *Str == '\\') {
+      return Str + 1;
+    }
+  }
+  return Str;
+}
+
+/** Default Exception Handler Function
+  This function is called when an exception occurs that cannot be handled,
+  and this function prints the system context information when the interrupt occurred
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval            VOID.
+**/
+STATIC
+VOID
+EFIAPI
+DefaultHandler (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  CHAR8  *ImageName;
+  UINTN  ImageBase;
+  UINTN  Epc;
+  UINTN  PeCoffSizeOfHeader;
+
+  DEBUG ((DEBUG_ERROR, "CRMD   0x%llx\n",  SystemContext.SystemContextLoongArch64->CRMD));
+  DEBUG ((DEBUG_ERROR, "PRMD   0x%llx\n",  SystemContext.SystemContextLoongArch64->PRMD));
+  DEBUG ((DEBUG_ERROR, "ECFG  0x%llx\n",  SystemContext.SystemContextLoongArch64->ECFG));
+  DEBUG ((DEBUG_ERROR, "ESTAT   0x%llx\n",  SystemContext.SystemContextLoongArch64->ESTAT));
+  DEBUG ((DEBUG_ERROR, "ERA    0x%llx\n",  SystemContext.SystemContextLoongArch64->ERA));
+  DEBUG ((DEBUG_ERROR, "BADV    0x%llx\n",  SystemContext.SystemContextLoongArch64->BADV));
+  DEBUG ((DEBUG_ERROR, "BADI 0x%llx\n",  SystemContext.SystemContextLoongArch64->BADI));
+
+  Epc = SystemContext.SystemContextLoongArch64->ERA;
+  ImageName = GetImageName (Epc, &ImageBase, &PeCoffSizeOfHeader);
+  if (ImageName != NULL) {
+    DEBUG ((DEBUG_ERROR, "PC 0x%012lx (0x%012lx+0x%08x) [ 0] %a\n",
+           Epc, ImageBase,
+           Epc - ImageBase, BaseName (ImageName)));
+  } else {
+    DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Epc));
+  }
+
+  while (1);
+}
+
+/** Common exception entry
+  Exception handling is the entry point for the C environment,
+  This function does different things depending on the exception type.
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval            VOID.
+**/
+VOID
+EFIAPI
+CommonExceptionEntry (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  INT32    ExceptionType;
+  UINT64   CsrEuen;
+  UINT64   FpuStatus;
+
+  ExceptionType = SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC;
+  ExceptionType = ExceptionType >> CSR_ESTAT_EXC_SHIFT;
+
+  LoongArchReadqCsrEuen (&CsrEuen);
+  FpuStatus = CsrEuen & CSR_EUEN_FPEN;
+  switch (ExceptionType) {
+    case EXC_INT:
+      /*
+       * handle interrupt exception
+       */
+      CommonInterruptHandler (SystemContext);
+      if (!FpuStatus) {
+        LoongArchReadqCsrEuen (&CsrEuen);
+        if (CsrEuen & CSR_EUEN_FPEN) {
+          /*
+           * Since Hw FP is enabled during interrupt handler,
+           * disable FP
+           */
+           CsrEuen &= ~CSR_EUEN_FPEN;
+           LoongArchWriteqCsrEuen (CsrEuen);
+        }
+      }
+      break;
+    case EXC_FPDIS:
+      /*
+       * Hardware FP disabled exception,
+       * Enable and init FP registers here
+       */
+      LoongArchEnableFpu ();
+      InitFpu(FPU_CSR_RN);
+      break;
+    default:
+      DefaultHandler(SystemContext);
+      break;
+  }
+}
+
+/** Exception module initialization
+  This function sets the exception base address.
+
+  @param  Cpu   A pointer to the CPU architecture protocol structure.
+
+  @retval EFI_SUCCESS           Initialization succeeded
+  @retval EFI_NOT_FOUND          Could not Found resources.
+  @retval EFI_OUT_OF_RESOURCES   No enough resources.
+**/
+EFI_STATUS
+InitializeExceptions (
+  IN EFI_CPU_ARCH_PROTOCOL    *Cpu
+  )
+{
+  EFI_STATUS           Status;
+  BOOLEAN              IrqEnabled;
+  EFI_PHYSICAL_ADDRESS Address;
+
+  ZeroMem (gInterruptHandler, sizeof (*gInterruptHandler));
+
+  //
+  // Disable interrupts
+  //
+  Cpu->GetInterruptState (Cpu, &IrqEnabled);
+  Cpu->DisableInterrupt (Cpu);
+
+  //
+  // EFI does not use the FIQ, but a debugger might so we must disable
+  // as we take over the exception vectors.
+  //
+  Status = gBS->AllocatePages (
+                 AllocateAnyPages,
+                 EfiRuntimeServicesData,
+                 1,
+                 &Address
+                 );
+  if (EFI_ERROR (Status)) {
+         return Status;
+  }
+
+  DEBUG ((DEBUG_INFO, "Set Exception Base Address\n"));
+  CopyMem ((char *)Address, LoongArchException, (LoongArchExceptionEnd - LoongArchException));
+  InvalidateInstructionCacheRange ((char *)Address, (LoongArchExceptionEnd - LoongArchException));
+
+  SetEbase (Address);
+  DEBUG ((DEBUG_INFO, "LoongArchException address: 0x%p\n", Address));
+  DEBUG ((DEBUG_INFO, "LoongArchExceptionEnd address: 0x%p\n", Address + (LoongArchExceptionEnd - LoongArchException)));
+
+  DEBUG ((DEBUG_INFO, "InitializeExceptions, IrqEnabled = %x\n", IrqEnabled));
+  if (IrqEnabled) {
+    //
+    // Restore interrupt state
+    //
+    Status = Cpu->EnableInterrupt (Cpu);
+  }
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
new file mode 100644
index 0000000000..79a20c66a2
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
@@ -0,0 +1,97 @@
+#------------------------------------------------------------------------------
+#
+# Fpu for LoongArch
+#
+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#  @par Glossary:
+#    - CsrEuen   - Cpu Status Register Extern Unit Enable
+#    - FPEN      - FPU Enable
+#    - fpu or fp or FP   - Float Point Unit
+#-----------------------------------------------------------------------------
+#ifndef __ASSEMBLY__
+#define __ASSEMBLY__
+#endif
+#include "Library/Cpu.h"
+#include "CpuDxe.h"
+
+ASM_GLOBAL ASM_PFX(InitFpu)
+ASM_GLOBAL ASM_PFX(LoongArchEnableFpu)
+ASM_GLOBAL ASM_PFX(LoongArchDisableFpu)
+
+#
+#  Load the FPU with signalling NANS.  This bit pattern we're using has
+#  the property that no matter whether considered as single or as double
+#  precision represents signaling NANS.
+#
+#  The value to initialize FCSR0 to comes in $A0.
+#
+
+ASM_PFX(InitFpu):
+  li.d    T1, CSR_EUEN_FPEN
+  csrxchg T1, T1, LOONGARCH_CSR_EUEN
+
+  movgr2fcsr      FCSR0, A0
+  li.d    T1, -1                          # SNaN
+  movgr2fr.d      $f0, T1
+  movgr2fr.d      $f1, T1
+  movgr2fr.d      $f2, T1
+  movgr2fr.d      $f3, T1
+  movgr2fr.d      $f4, T1
+  movgr2fr.d      $f5, T1
+  movgr2fr.d      $f6, T1
+  movgr2fr.d      $f7, T1
+  movgr2fr.d      $f8, T1
+  movgr2fr.d      $f9, T1
+  movgr2fr.d      $f10, T1
+  movgr2fr.d      $f11, T1
+  movgr2fr.d      $f12, T1
+  movgr2fr.d      $f13, T1
+  movgr2fr.d      $f14, T1
+  movgr2fr.d      $f15, T1
+  movgr2fr.d      $f16, T1
+  movgr2fr.d      $f17, T1
+  movgr2fr.d      $f18, T1
+  movgr2fr.d      $f19, T1
+  movgr2fr.d      $f20, T1
+  movgr2fr.d      $f21, T1
+  movgr2fr.d      $f22, T1
+  movgr2fr.d      $f23, T1
+  movgr2fr.d      $f24, T1
+  movgr2fr.d      $f25, T1
+  movgr2fr.d      $f26, T1
+  movgr2fr.d      $f27, T1
+  movgr2fr.d      $f28, T1
+  movgr2fr.d      $f29, T1
+  movgr2fr.d      $f30, T1
+  movgr2fr.d      $f31, T1
+
+  jirl ZERO, RA, 0
+
+#
+#  Enables  floating-point unit
+#  @param  VOID
+#  @retval  VOID
+#
+
+ASM_PFX(LoongArchEnableFpu):
+    li.d    T0, 1
+    li.d    T1, CSR_EUEN_FPEN_SHIFT
+    sll.d   T0, T0, T1
+    csrxchg T0, T0, LOONGARCH_CSR_EUEN
+    jirl    ZERO, RA,0
+
+#
+#  Disable  floating-point unit
+#  @param  VOID
+#  @retval  VOID
+#
+
+ASM_PFX(LoongArchDisableFpu):
+    li.d    T0, 1
+    li.d    T1, CSR_EUEN_FPEN_SHIFT
+    sll.d   T0, T0, T1
+    csrxchg ZERO, T0, LOONGARCH_CSR_EUEN
+    jirl    ZERO, RA,0
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
new file mode 100644
index 0000000000..e463cf44f2
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
@@ -0,0 +1,321 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch for LoongArch
+#
+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#  @par Glossary:
+#    - CsrEuen      - Cpu Status Register Extern Unit Enable
+#    - fpu    - Float Point Unit
+#    - LOONGARCH   - Loongson Arch
+#    - Ebase - Exception Base Address
+#-----------------------------------------------------------------------------
+
+#ifndef __ASSEMBLY__
+#define __ASSEMBLY__
+#endif
+
+#include "Library/Cpu.h"
+#include "CpuDxe.h"
+
+#define RSIZE   8       /* 64 bit mode register size */
+#define RLOGSIZE 3
+
+ASM_GLOBAL ASM_PFX(Exception_handler)
+ASM_GLOBAL ASM_PFX(LoongArchException)
+ASM_GLOBAL ASM_PFX(SetEbase)
+ASM_GLOBAL ASM_PFX(LoongArchReadqCsrEuen)
+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrEuen)
+
+#
+#   Main exception handler. Not really a leaf routine but not a normal
+#   function either. Save away the entire cpu state end enter exception mode.
+#
+
+ASM_PFX(Exception_handler):
+    csrrd   SP, LOONGARCH_CSR_KS1
+
+    addi.d  T0, $r0, -0x10
+    and     SP, SP, T0
+    addi.d  SP, SP,  -((CSR_NUM + BASE_NUM + FP_BASE_NUM)  * RSIZE)
+
+    st.d   RA, SP, RA_NUM * RSIZE
+    st.d   GP, SP, GP_NUM * RSIZE
+    st.d   A0, SP, A0_NUM * RSIZE
+    st.d   A1, SP, A1_NUM * RSIZE
+    st.d   A2, SP, A2_NUM * RSIZE
+    st.d   A3, SP, A3_NUM * RSIZE
+    st.d   A4, SP, A4_NUM * RSIZE
+    st.d   A5, SP, A5_NUM * RSIZE
+    st.d   A6, SP, A6_NUM * RSIZE
+    st.d   A7, SP, A7_NUM * RSIZE
+    st.d   T1, SP, T1_NUM * RSIZE
+    st.d   T2, SP, T2_NUM * RSIZE
+    st.d   T3, SP, T3_NUM * RSIZE
+    st.d   T4, SP, T4_NUM * RSIZE
+    st.d   T5, SP, T5_NUM * RSIZE
+    st.d   T6, SP, T6_NUM * RSIZE
+    st.d   T7, SP, T7_NUM * RSIZE
+    st.d   T8, SP, T8_NUM * RSIZE
+    st.d   TP, SP, TP_NUM * RSIZE
+    st.d   FP, SP, FP_NUM * RSIZE
+    st.d   S0, SP, S0_NUM * RSIZE
+    st.d   S1, SP, S1_NUM * RSIZE
+    st.d   S2, SP, S2_NUM * RSIZE
+    st.d   S3, SP, S3_NUM * RSIZE
+    st.d   S4, SP, S4_NUM * RSIZE
+    st.d   S5, SP, S5_NUM * RSIZE
+    st.d   S6, SP, S6_NUM * RSIZE
+    st.d   S7, SP, S7_NUM * RSIZE
+    st.d   S8, SP, S8_NUM * RSIZE
+
+    #
+    #  save T0/SP from scratch registers on stack
+    #
+    csrrd  T0, LOONGARCH_CSR_KS0
+    st.d   T0, SP, T0_NUM * RSIZE
+    csrrd  T0, LOONGARCH_CSR_KS1
+    st.d   T0, SP, SP_NUM * RSIZE
+
+    csrrd   T0, LOONGARCH_CSR_CRMD
+    st.d    T0, SP, (LOONGARCH_CSR_CRMD + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_PRMD
+    st.d    T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_ECFG
+    st.d    T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
+    csrrd   T0, LOONGARCH_CSR_ESTAT
+    st.d    T0, SP, (LOONGARCH_CSR_ESTAT + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_EPC
+    st.d    T0, SP, (LOONGARCH_CSR_EPC+ BASE_NUM)    * RSIZE
+    csrrd   T0, LOONGARCH_CSR_BADV
+    st.d    T0, SP, (LOONGARCH_CSR_BADV + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_BADI
+    st.d    T0, SP, (LOONGARCH_CSR_BADI + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_EUEN
+    st.d    T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM)  * RSIZE
+
+    #
+    #  Save FPU context
+    #
+    ori     T1, ZERO, CSR_EUEN_FPEN
+    and     T2, T0, T1
+    beqz    T2, 1f
+
+    fst.d   $f0,  SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f1,  SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f2,  SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f3,  SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f4,  SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f5,  SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f6,  SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f7,  SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f8,  SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f9,  SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
+
+    movfcsr2gr      T3, FCSR0
+    st.d            T3, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
+    movcf2gr        T3, $fcc0
+    or              T2, T3, ZERO
+    movcf2gr        T3, $fcc1
+    bstrins.d       T2, T3, 0xf, 0x8
+    movcf2gr        T3, $fcc2
+    bstrins.d       T2, T3, 0x17, 0x10
+    movcf2gr        T3, $fcc3
+    bstrins.d       T2, T3, 0x1f, 0x18
+    movcf2gr        T3, $fcc4
+    bstrins.d       T2, T3, 0x27, 0x20
+    movcf2gr        T3, $fcc5
+    bstrins.d       T2, T3, 0x2f, 0x28
+    movcf2gr        T3, $fcc6
+    bstrins.d       T2, T3, 0x37, 0x30
+    movcf2gr        T3, $fcc7
+    bstrins.d       T2, T3, 0x3f, 0x38
+    st.d            T2, SP, (FCC_NUM + FP_BASE_INDEX)  * RSIZE
+1:
+    or      A0, SP, ZERO
+    bl      CommonExceptionEntry
+    /*disable interrupt*/
+    li.d     T0, (1 << 2)
+    csrxchg ZERO, T0, LOONGARCH_CSR_CRMD
+
+    ld.d    T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
+    csrwr   T0, LOONGARCH_CSR_PRMD
+    ld.d    T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
+    csrwr   T0, LOONGARCH_CSR_ECFG
+    ld.d    T0, SP, (LOONGARCH_CSR_EPC + BASE_NUM) * RSIZE
+    csrwr   T0, LOONGARCH_CSR_EPC
+
+    ld.d    T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
+    ori     T1, ZERO, CSR_EUEN_FPEN
+    and     T2, T0, T1
+    beqz    T2, 2f
+
+    #
+    #  check previous FP state
+    #  restore FP contect if FP enabled
+    #
+    fld.d   $f0,  SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f1,  SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f2,  SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f3,  SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f4,  SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f5,  SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f6,  SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f7,  SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f8,  SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f9,  SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
+
+    ld.d    T0, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
+    movgr2fcsr      FCSR0, T0
+    ld.d    T0, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
+    bstrpick.d      T1, T0, 7, 0
+    movgr2cf        $fcc0, T1
+    bstrpick.d      T1, T0, 15, 8
+    movgr2cf        $fcc1, T1
+    bstrpick.d      T1, T0, 23, 16
+    movgr2cf        $fcc2, T1
+    bstrpick.d      T1, T0, 31, 24
+    movgr2cf        $fcc3, T1
+    bstrpick.d      T1, T0, 39, 32
+    movgr2cf        $fcc4, T1
+    bstrpick.d      T1, T0, 47, 40
+    movgr2cf        $fcc5, T1
+    bstrpick.d      T1, T0, 55, 48
+    movgr2cf        $fcc6, T1
+    bstrpick.d      T1, T0, 63, 56
+    movgr2cf        $fcc7, T1
+2:
+    ld.d    RA, SP, RA_NUM * RSIZE
+    ld.d    GP, SP, GP_NUM * RSIZE
+    ld.d    A0, SP, A0_NUM * RSIZE
+    ld.d    A1, SP, A1_NUM * RSIZE
+    ld.d    A2, SP, A2_NUM * RSIZE
+    ld.d    A3, SP, A3_NUM * RSIZE
+    ld.d    A4, SP, A4_NUM * RSIZE
+    ld.d    A5, SP, A5_NUM * RSIZE
+    ld.d    A6, SP, A6_NUM * RSIZE
+    ld.d    A7, SP, A7_NUM * RSIZE
+    ld.d    T0, SP, T0_NUM * RSIZE
+    ld.d    T1, SP, T1_NUM * RSIZE
+    ld.d    T2, SP, T2_NUM * RSIZE
+    ld.d    T3, SP, T3_NUM * RSIZE
+    ld.d    T4, SP, T4_NUM * RSIZE
+    ld.d    T5, SP, T5_NUM * RSIZE
+    ld.d    T6, SP, T6_NUM * RSIZE
+    ld.d    T7, SP, T7_NUM * RSIZE
+    ld.d    T8, SP, T8_NUM * RSIZE
+    ld.d    TP, SP, TP_NUM * RSIZE
+    ld.d    FP, SP, FP_NUM * RSIZE
+    ld.d    S0, SP, S0_NUM * RSIZE
+    ld.d    S1, SP, S1_NUM * RSIZE
+    ld.d    S2, SP, S2_NUM * RSIZE
+    ld.d    S3, SP, S3_NUM * RSIZE
+    ld.d    S4, SP, S4_NUM * RSIZE
+    ld.d    S5, SP, S5_NUM * RSIZE
+    ld.d    S6, SP, S6_NUM * RSIZE
+    ld.d    S7, SP, S7_NUM * RSIZE
+    ld.d    S8, SP, S8_NUM * RSIZE
+
+    ld.d    SP, SP, SP_NUM * RSIZE
+    ertn
+
+#
+#   Exception trampoline copied down to RAM after initialization.
+#
+
+ASM_PFX(LoongArchException):
+    csrwr   T0, LOONGARCH_CSR_KS0
+    csrwr   SP, LOONGARCH_CSR_KS1
+    pcaddi   T0, 0
+    ld.d     T0, T0, 16
+    jirl    ZERO, T0, 0
+    nop
+1:
+    .quad  Exception_handler
+.globl  LoongArchExceptionEnd
+LoongArchExceptionEnd:
+
+#
+#   Set Exception Base Address.
+#
+
+ASM_PFX(SetEbase):
+    #
+    #  clear Vint cofigure
+    #  all exceptions share the same interrupt entry
+    #
+    csrrd   T0, LOONGARCH_CSR_ECFG
+    li.d    T1, ~0x70000
+    and     T0, T0, T1
+    csrwr   T0, LOONGARCH_CSR_ECFG
+
+    #  set ebase
+    csrwr   A0, LOONGARCH_CSR_EBASE
+    jirl    ZERO, RA, 0
+
+#
+#  Read Csr EUEN register.
+#  @param   A0 Pointer to the variable used to store the EUEN register value
+#  @retval  none
+#
+
+ASM_PFX(LoongArchReadqCsrEuen):
+    csrrd T0, LOONGARCH_CSR_EUEN
+    stptr.d T0, A0, 0
+    jirl    ZERO, RA,0
+
+#
+#  Write Csr EUEN register.
+#  @param  A0  The value used to write to the EUEN register
+#  @retval  none
+#
+
+ASM_PFX(LoongArchWriteqCsrEuen):
+    csrwr A0, LOONGARCH_CSR_EUEN
+    jirl    ZERO, RA,0
-- 
2.31.1




-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#96278): https://edk2.groups.io/g/devel/message/96278
Mute This Topic: https://groups.io/mt/94955175/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
Re: [edk2-devel] [edk2-platforms][PATCH V5 08/15] Platform/Loongson: Add CPU DXE driver.
Posted by Chao Li 1 year, 3 months ago
Reviewed-by: Chao Li <lichao@loongson.cn>

Thanks,
Chao
--------

On 11月 11 2022, at 5:12 下午, xianglai li <lixianglai@loongson.cn> wrote:
> The driver produces EFI_CPU_ARCH_PROTOCOL,
>
> Initialize the exception entry address.
>
>
> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
>
>
> Cc: Bibo Mao <maobibo@loongson.cn>
> Cc: Chao Li <lichao@loongson.cn>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Liming Gao <gaoliming@byosoft.com.cn>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Signed-off-by: xianglai li <lixianglai@loongson.cn>
> ---
> .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c | 367 ++++++++++++++++++
> .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h | 199 ++++++++++
> .../Drivers/CpuDxe/CpuDxe.inf | 59 +++
> .../Drivers/CpuDxe/LoongArch64/Exception.c | 335 ++++++++++++++++
> .../Drivers/CpuDxe/LoongArch64/Fpu.S | 97 +++++
> .../Drivers/CpuDxe/LoongArch64/LoongArch.S | 321 +++++++++++++++
> 6 files changed, 1378 insertions(+)
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
>
>
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> new file mode 100644
> index 0000000000..23f824d82b
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> @@ -0,0 +1,367 @@
> +/** @file
> + CPU DXE Module to produce CPU ARCH Protocol
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Guid/IdleLoopEvent.h>
> +#include <Uefi.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/MmuLib.h>
> +#include "CpuDxe.h"
> +
> +BOOLEAN mInterruptState = FALSE;
> +
> +/*
> + This function flushes the range of addresses from Start to Start+Length
> + from the processor's data cache. If Start is not aligned to a cache line
> + boundary, then the bytes before Start to the preceding cache line boundary
> + are also flushed. If Start+Length is not aligned to a cache line boundary,
> + then the bytes past Start+Length to the end of the next cache line boundary
> + are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
> + supported. If the data cache is fully coherent with all DMA operations, then
> + this function can just return EFI_SUCCESS. If the processor does not support
> + flushing a range of the data cache, then the entire data cache can be flushed.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param Start The beginning physical address to flush from the processor's data
> + cache.
> + @param Length The number of bytes to flush from the processor's data cache. This
> + function may flush more bytes than Length specifies depending upon
> + the granularity of the flush operation that the processor supports.
> + @param FlushType Specifies the type of flush operation to perform.
> +
> + @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from
> + the processor's data cache.
> + @retval EFI_UNSUPPORTEDT The processor does not support the cache flush type specified
> + by FlushType.
> + @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed
> + from the processor's data cache.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuFlushCpuDataCache (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS Start,
> + IN UINT64 Length,
> + IN EFI_CPU_FLUSH_TYPE FlushType
> + )
> +{
> + switch (FlushType) {
> + case EfiCpuFlushTypeWriteBack:
> + WriteBackDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + case EfiCpuFlushTypeInvalidate:
> + InvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + case EfiCpuFlushTypeWriteBackInvalidate:
> + WriteBackInvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + default:
> + return EFI_INVALID_PARAMETER;
> + }
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function enables interrupt processing by the processor.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> +
> + @retval EFI_SUCCESS Interrupts are enabled on the processor.
> + @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuEnableInterrupt (
> + IN EFI_CPU_ARCH_PROTOCOL *This
> + )
> +{
> + EnableInterrupts ();
> +
> + mInterruptState = TRUE;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function disables interrupt processing by the processor.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> +
> + @retval EFI_SUCCESS Interrupts are disabled on the processor.
> + @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuDisableInterrupt (
> + IN EFI_CPU_ARCH_PROTOCOL *This
> + )
> +{
> + DisableInterrupts ();
> +
> + mInterruptState = FALSE;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function retrieves the processor's current interrupt state a returns it in
> + State. If interrupts are currently enabled, then TRUE is returned. If interrupts
> + are currently disabled, then FALSE is returned.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param State A pointer to the processor's current interrupt state. Set to TRUE if
> + interrupts are enabled and FALSE if interrupts are disabled.
> +
> + @retval EFI_SUCCESS The processor's current interrupt state was returned in State.
> + @retval EFI_INVALID_PARAMETER State is NULL.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetInterruptState (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + OUT BOOLEAN *State
> + )
> +{
> + if (State == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + *State = mInterruptState;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function generates an INIT on the processor. If this function succeeds, then the
> + processor will be reset, and control will not be returned to the caller. If InitType is
> + not supported by this processor, or the processor cannot programmatically generate an
> + INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
> + occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param InitType The type of processor INIT to perform.
> +
> + @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen.
> + @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported
> + by this processor.
> + @retval EFI_DEVICE_ERROR The processor INIT failed.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuInit (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_CPU_INIT_TYPE InitType
> + )
> +{
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InterruptType Interrupt Type.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuRegisterInterruptHandler (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_EXCEPTION_TYPE InterruptType,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + )
> +{
> + return RegisterInterruptHandler (InterruptType, InterruptHandler);
> +}
> +
> +/**
> + Returns a timer value from one of the CPU's internal timers. There is no
> + inherent time interval between ticks but is a function of the CPU frequency.
> +
> + @param This - Protocol instance structure.
> + @param TimerIndex - Specifies which CPU timer is requested.
> + @param TimerValue - Pointer to the returned timer value.
> + @param TimerPeriod - A pointer to the amount of time that passes
> + in femtoseconds (10-15) for each increment
> + of TimerValue. If TimerValue does not
> + increment at a predictable rate, then 0 is
> + returned. The amount of time that has
> + passed between two calls to GetTimerValue()
> + can be calculated with the formula
> + (TimerValue2 - TimerValue1) * TimerPeriod.
> + This parameter is optional and may be NULL.
> +
> + @retval EFI_SUCCESS - If the CPU timer count was returned.
> + @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers.
> + @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer.
> + @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetTimerValue (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN UINT32 TimerIndex,
> + OUT UINT64 *TimerValue,
> + OUT UINT64 *TimerPeriod OPTIONAL
> + )
> +{
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + This function modifies the attributes for the memory region specified by BaseAddress and
> + Length from their current attributes to the attributes specified by Attributes.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param BaseAddress The physical address that is the start address of a memory region.
> + @param Length The size in bytes of the memory region.
> + @param Attributes The bit mask of attributes to set for the memory region.
> +
> + @retval EFI_SUCCESS The attributes were set for the memory region.
> + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
> + BaseAddress and Length cannot be modified.
> + @retval EFI_INVALID_PARAMETER Length is zero.
> + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
> + the memory resource range.
> + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
> + resource range specified by BaseAddress and Length.
> + The bit mask of attributes is not support for the memory resource
> + range specified by BaseAddress and Length.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS BaseAddress,
> + IN UINT64 Length,
> + IN UINT64 EfiAttributes
> + )
> +{
> + EFI_STATUS Status;
> + UINTN LoongArchAttributes;
> + UINTN RegionBaseAddress;
> + UINTN RegionLength;
> + UINTN RegionLoongArchAttributes;
> +
> + if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
> + // Minimum granularity is SIZE_4KB (4KB on ARM)
> + DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
> + BaseAddress,
> + Length,
> + EfiAttributes));
> +
> + return EFI_UNSUPPORTED;
> + }
> + // Convert the 'Attribute' into LoongArch Attribute
> + LoongArchAttributes = EfiAttributeToLoongArchAttribute (EfiAttributes);
> +
> + // Get the region starting from 'BaseAddress' and its 'Attribute'
> + RegionBaseAddress = BaseAddress;
> + Status = GetLoongArchMemoryRegion (RegionBaseAddress, BaseAddress + Length,
> + &RegionLength, &RegionLoongArchAttributes);
> +
> + LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
> + // Data & Instruction Caches are flushed when we set new memory attributes.
> + // So, we only set the attributes if the new region is different.
> + if (EFI_ERROR (Status) || (RegionLoongArchAttributes != LoongArchAttributes) ||
> + ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
> + {
> + return LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
> + }
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Callback function for idle events.
> +
> + @param Event Event whose notification function is being invoked.
> + @param Context The pointer to the notification function's context,
> + which is implementation-dependent.
> +
> + @param VOID
> +**/
> +VOID
> +EFIAPI
> +IdleLoopEventCallback (
> + IN EFI_EVENT Event,
> + IN VOID *Context
> + )
> +{
> + CpuSleep ();
> +}
> +
> +//
> +// Globals used to initialize the protocol
> +//
> +EFI_HANDLE CpuHandle = NULL;
> +EFI_CPU_ARCH_PROTOCOL Cpu = {
> + CpuFlushCpuDataCache,
> + CpuEnableInterrupt,
> + CpuDisableInterrupt,
> + CpuGetInterruptState,
> + CpuInit,
> + CpuRegisterInterruptHandler,
> + CpuGetTimerValue,
> + CpuSetMemoryAttributes,
> + 0, // NumberOfTimers
> + 4, // DmaBufferAlignment
> +};
> +
> +/**
> + Initialize the state information for the CPU Architectural Protocol.
> +
> + @param ImageHandle Image handle this driver.
> + @param SystemTable Pointer to the System Table.
> +
> + @retval EFI_SUCCESS Thread can be successfully created
> + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
> + @retval EFI_DEVICE_ERROR Cannot create the thread
> +**/
> +EFI_STATUS
> +CpuDxeInitialize (
> + IN EFI_HANDLE ImageHandle,
> + IN EFI_SYSTEM_TABLE *SystemTable
> + )
> +{
> + EFI_STATUS Status;
> + EFI_EVENT IdleLoopEvent;
> +
> + InitializeExceptions (&Cpu);
> +
> + Status = gBS->InstallMultipleProtocolInterfaces (
> + &CpuHandle,
> + &gEfiCpuArchProtocolGuid, &Cpu,
> + NULL
> + );
> +
> + //
> + // Setup a callback for idle events
> + //
> + Status = gBS->CreateEventEx (
> + EVT_NOTIFY_SIGNAL,
> + TPL_NOTIFY,
> + IdleLoopEventCallback,
> + NULL,
> + &gIdleLoopEventGuid,
> + &IdleLoopEvent
> + );
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> new file mode 100644
> index 0000000000..43cb976aa2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> @@ -0,0 +1,199 @@
> +/** @file
> + CPU DXE Module to produce CPU ARCH Protocol and CPU MP Protocol
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef CPU_DXE_H_
> +#define CPU_DXE_H_
> +
> +#include <Protocol/Cpu.h>
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + );
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterDebuggerInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + );
> +
> +/**
> + This function modifies the attributes for the memory region specified by BaseAddress and
> + Length from their current attributes to the attributes specified by Attributes.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param BaseAddress The physical address that is the start address of a memory region.
> + @param Length The size in bytes of the memory region.
> + @param Attributes The bit mask of attributes to set for the memory region.
> +
> + @retval EFI_SUCCESS The attributes were set for the memory region.
> + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
> + BaseAddress and Length cannot be modified.
> + @retval EFI_INVALID_PARAMETER Length is zero.
> + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
> + the memory resource range.
> + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
> + resource range specified by BaseAddress and Length.
> + The bit mask of attributes is not support for the memory resource
> + range specified by BaseAddress and Length.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS BaseAddress,
> + IN UINT64 Length,
> + IN UINT64 Attributes
> + );
> +
> +/** Exception module initialization
> + This function sets the exception base address.
> +
> + @param Cpu A pointer to the CPU architecture protocol structure.
> +
> + @retval EFI_SUCCESS Initialization succeeded
> + @retval EFI_NOT_FOUND Could not Found resources.
> + @retval EFI_OUT_OF_RESOURCES No enough resources.
> +**/
> +EFI_STATUS
> +InitializeExceptions (
> + IN EFI_CPU_ARCH_PROTOCOL *Cpu
> + );
> +
> +/** Common exception entry
> + Exception handling is the entry point for the C environment,
> + This function does different things depending on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionEntry (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + );
> +
> +extern CHAR8 LoongArchException[], LoongArchExceptionEnd[];
> +/** Set Exception Base Address
> +
> + @param addr Exception Base Address.
> +
> + @retval The Old Exception Base Address.
> +**/
> +extern
> +UINT64
> +SetEbase (
> + EFI_PHYSICAL_ADDRESS addr
> + );
> +/*
> + Load the FPU with signalling NANS. This bit pattern we're using has
> + the property that no matter whether considered as single or as double
> + precision represents signaling NANS.
> +
> + @param fcsr The value to initialize FCSR0
> +
> + @retval The Old Exception Base Address.
> + */
> +extern
> +VOID
> +InitFpu (
> + UINT32 fcsr
> + );
> +
> +/*
> + Read Csr EUEN register.
> +
> + @param CsrEuen Pointer to the variable used to store the EUEN register value
> +
> + @retval none
> + */
> +extern
> +VOID
> +LoongArchReadqCsrEuen (
> + UINT64 *CsrEuen
> + );
> +
> +/*
> + Write Csr EUEN register.
> +
> + @param The value used to write to the EUEN register
> +
> + @retval none
> + */
> +extern
> +VOID
> +LoongArchWriteqCsrEuen (
> + UINT64 CsrEuen
> + );
> +
> +/*
> + Enables floating-point unit
> +
> + @param VOID
> +
> + @retval VOID
> + */
> +extern
> +VOID
> +LoongArchEnableFpu (
> + VOID
> + );
> +
> +/*
> + Disable floating-point unit
> +
> + @param VOID
> +
> + @retval VOID
> + */
> +extern
> +VOID
> +LoongArchDisableFpu (
> + VOID
> + );
> +
> +#endif // CPU_DXE_H_
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> new file mode 100644
> index 0000000000..96aabfefb8
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> @@ -0,0 +1,59 @@
> +## @file
> +# CPU driver installs CPU Architecture Protocol and CPU MP protocol.
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010005
> + BASE_NAME = CpuDxe
> + FILE_GUID = bf954921-25c1-48c0-9bfb-8d0cd7ee92da
> + MODULE_TYPE = DXE_DRIVER
> + VERSION_STRING = 1.0
> + ENTRY_POINT = CpuDxeInitialize
> +
> +#
> +# VALID_ARCHITECTURES = LOONGARCH64
> +#
> +
> +[Sources.Common]
> + CpuDxe.c
> + CpuDxe.h
> +
> +[Sources.LOONGARCH64]
> + LoongArch64/Exception.c
> + LoongArch64/LoongArch.S
> + LoongArch64/Fpu.S
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + EmbeddedPkg/EmbeddedPkg.dec
> + Platform/Loongson/LoongArchQemuPkg/Loongson.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + CacheMaintenanceLib
> + CpuLib
> + DebugLib
> + DxeServicesTableLib
> + HobLib
> + PeCoffGetEntryPointLib
> + UefiDriverEntryPoint
> + UefiLib
> + MmuLib
> +
> +[Protocols]
> + gEfiCpuArchProtocolGuid
> + gEfiMpServiceProtocolGuid
> +
> +[Guids]
> + gEfiDebugImageInfoTableGuid
> + gIdleLoopEventGuid
> +
> +[Depex]
> + TRUE
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> new file mode 100644
> index 0000000000..793ae90e4f
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> @@ -0,0 +1,335 @@
> +/** @file
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> + @par Glossary:
> + - ESTAT - Exception Status
> + - ECFG - Exception Configure
> + - ERA - Exception Return Address
> + - BADV - Bad Virtual Address
> + - BADI - Bad Instructions
> + - Epc or EPC or epc - Exception Program Counter
> + - pc or PC or pc - Program Counter
> + - CRMD - Current Mode
> + - PRMD - Previous Mode
> + - CsrEuen - Cpu Status Register Extern Unit Enable
> + - fpu or fp or FP - Float Point Unit
> + - LOONGARCH - Loongson Arch
> + - Irq - Interrupt ReQuest
> +**/
> +
> +#include "Library/Cpu.h"
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/DebugLib.h>
> +#include "CpuDxe.h"
> +#include <Library/PeCoffGetEntryPointLib.h>
> +#include <Library/UefiLib.h>
> +#include <Guid/DebugImageInfoTable.h>
> +
> +EFI_EXCEPTION_CALLBACK gInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1];
> +EFI_EXCEPTION_CALLBACK gDebuggerExceptionHandlers[MAX_LOONGARCH_INTERRUPT + 1];
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + )
> +{
> + if (InteruptNum > MAX_LOONGARCH_INTERRUPT) {
> + return EFI_UNSUPPORTED;
> + }
> +
> + if ((InterruptHandler != NULL)
> + && (gInterruptHandler[InteruptNum] != NULL))
> + {
> + return EFI_ALREADY_STARTED;
> + }
> +
> + gInterruptHandler[InteruptNum] = InterruptHandler;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function calls the corresponding exception handler based on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID
> +**/
> +STATIC VOID
> +EFIAPI
> +CommonInterruptHandler (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + INT32 Pending;
> + INT32 InterruptNum;
> + /*Interrupt [13-0] NMI IPI TI PCOV hw IP10-IP2 soft IP1-IP0*/
> + Pending = ((SystemContext.SystemContextLoongArch64->ESTAT) &
> + (SystemContext.SystemContextLoongArch64->ECFG) & 0x1fff);
> + for (InterruptNum = 0; InterruptNum < MAX_LOONGARCH_INTERRUPT; InterruptNum++) {
> + if (Pending & (1 << InterruptNum)) {
> + if (gInterruptHandler[InterruptNum] != NULL) {
> + gInterruptHandler[InterruptNum] (InterruptNum, SystemContext);
> + } else {
> + DEBUG ((DEBUG_INFO, "Pending: 0x%0x, InterruptNum: 0x%0x\n", Pending, InterruptNum));
> + }
> + }
> + }
> +}
> +
> +/**
> + Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image
> + it came from. As long as the PE/COFF image contains a debug directory entry a
> + string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF
> + image. Microsoft tools contain a pointer to the PDB file that contains the debug information.
> +
> + @param FaultAddress Address to find PE/COFF image for.
> + @param ImageBase Return load address of found image
> + @param PeCoffSizeOfHeaders Return the size of the PE/COFF header for the image that was found
> +
> + @retval NULL FaultAddress not in a loaded PE/COFF image.
> + @retval Path and file name of PE/COFF image.
> +**/
> +CHAR8 *
> +GetImageName (
> + IN UINTN FaultAddress,
> + OUT UINTN *ImageBase,
> + OUT UINTN *PeCoffSizeOfHeaders
> + )
> +{
> + EFI_STATUS Status;
> + EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *DebugTableHeader;
> + EFI_DEBUG_IMAGE_INFO *DebugTable;
> + UINTN Entry;
> + CHAR8 *Address;
> +
> + Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugTableHeader);
> + if (EFI_ERROR (Status)) {
> + return NULL;
> + }
> +
> + DebugTable = DebugTableHeader->EfiDebugImageInfoTable;
> + if (DebugTable == NULL) {
> + return NULL;
> + }
> +
> + Address = (CHAR8 *)(UINTN)FaultAddress;
> + for (Entry = 0; Entry < DebugTableHeader->TableSize; Entry++, DebugTable++) {
> + if (DebugTable->NormalImage != NULL) {
> + if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
> + (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
> + if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&
> + (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {
> + *ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;
> + *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);
> + return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);
> + }
> + }
> + }
> + }
> + return NULL;
> +}
> +
> +/**
> + pass a file name string that contains the path, return file name.
> +
> + @param FullName Path and file name
> +
> + @retval file name.
> +**/
> +STATIC
> +CONST CHAR8 *
> +BaseName (
> + IN CONST CHAR8 *FullName
> + )
> +{
> + CONST CHAR8 *Str;
> +
> + Str = FullName + AsciiStrLen (FullName);
> +
> + while (--Str > FullName) {
> + if (*Str == '/' || *Str == '\\') {
> + return Str + 1;
> + }
> + }
> + return Str;
> +}
> +
> +/** Default Exception Handler Function
> + This function is called when an exception occurs that cannot be handled,
> + and this function prints the system context information when the interrupt occurred
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +DefaultHandler (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + CHAR8 *ImageName;
> + UINTN ImageBase;
> + UINTN Epc;
> + UINTN PeCoffSizeOfHeader;
> +
> + DEBUG ((DEBUG_ERROR, "CRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->CRMD));
> + DEBUG ((DEBUG_ERROR, "PRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->PRMD));
> + DEBUG ((DEBUG_ERROR, "ECFG 0x%llx\n", SystemContext.SystemContextLoongArch64->ECFG));
> + DEBUG ((DEBUG_ERROR, "ESTAT 0x%llx\n", SystemContext.SystemContextLoongArch64->ESTAT));
> + DEBUG ((DEBUG_ERROR, "ERA 0x%llx\n", SystemContext.SystemContextLoongArch64->ERA));
> + DEBUG ((DEBUG_ERROR, "BADV 0x%llx\n", SystemContext.SystemContextLoongArch64->BADV));
> + DEBUG ((DEBUG_ERROR, "BADI 0x%llx\n", SystemContext.SystemContextLoongArch64->BADI));
> +
> + Epc = SystemContext.SystemContextLoongArch64->ERA;
> + ImageName = GetImageName (Epc, &ImageBase, &PeCoffSizeOfHeader);
> + if (ImageName != NULL) {
> + DEBUG ((DEBUG_ERROR, "PC 0x%012lx (0x%012lx+0x%08x) [ 0] %a\n",
> + Epc, ImageBase,
> + Epc - ImageBase, BaseName (ImageName)));
> + } else {
> + DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Epc));
> + }
> +
> + while (1);
> +}
> +
> +/** Common exception entry
> + Exception handling is the entry point for the C environment,
> + This function does different things depending on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionEntry (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + INT32 ExceptionType;
> + UINT64 CsrEuen;
> + UINT64 FpuStatus;
> +
> + ExceptionType = SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC;
> + ExceptionType = ExceptionType >> CSR_ESTAT_EXC_SHIFT;
> +
> + LoongArchReadqCsrEuen (&CsrEuen);
> + FpuStatus = CsrEuen & CSR_EUEN_FPEN;
> + switch (ExceptionType) {
> + case EXC_INT:
> + /*
> + * handle interrupt exception
> + */
> + CommonInterruptHandler (SystemContext);
> + if (!FpuStatus) {
> + LoongArchReadqCsrEuen (&CsrEuen);
> + if (CsrEuen & CSR_EUEN_FPEN) {
> + /*
> + * Since Hw FP is enabled during interrupt handler,
> + * disable FP
> + */
> + CsrEuen &= ~CSR_EUEN_FPEN;
> + LoongArchWriteqCsrEuen (CsrEuen);
> + }
> + }
> + break;
> + case EXC_FPDIS:
> + /*
> + * Hardware FP disabled exception,
> + * Enable and init FP registers here
> + */
> + LoongArchEnableFpu ();
> + InitFpu(FPU_CSR_RN);
> + break;
> + default:
> + DefaultHandler(SystemContext);
> + break;
> + }
> +}
> +
> +/** Exception module initialization
> + This function sets the exception base address.
> +
> + @param Cpu A pointer to the CPU architecture protocol structure.
> +
> + @retval EFI_SUCCESS Initialization succeeded
> + @retval EFI_NOT_FOUND Could not Found resources.
> + @retval EFI_OUT_OF_RESOURCES No enough resources.
> +**/
> +EFI_STATUS
> +InitializeExceptions (
> + IN EFI_CPU_ARCH_PROTOCOL *Cpu
> + )
> +{
> + EFI_STATUS Status;
> + BOOLEAN IrqEnabled;
> + EFI_PHYSICAL_ADDRESS Address;
> +
> + ZeroMem (gInterruptHandler, sizeof (*gInterruptHandler));
> +
> + //
> + // Disable interrupts
> + //
> + Cpu->GetInterruptState (Cpu, &IrqEnabled);
> + Cpu->DisableInterrupt (Cpu);
> +
> + //
> + // EFI does not use the FIQ, but a debugger might so we must disable
> + // as we take over the exception vectors.
> + //
> + Status = gBS->AllocatePages (
> + AllocateAnyPages,
> + EfiRuntimeServicesData,
> + 1,
> + &Address
> + );
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + DEBUG ((DEBUG_INFO, "Set Exception Base Address\n"));
> + CopyMem ((char *)Address, LoongArchException, (LoongArchExceptionEnd - LoongArchException));
> + InvalidateInstructionCacheRange ((char *)Address, (LoongArchExceptionEnd - LoongArchException));
> +
> + SetEbase (Address);
> + DEBUG ((DEBUG_INFO, "LoongArchException address: 0x%p\n", Address));
> + DEBUG ((DEBUG_INFO, "LoongArchExceptionEnd address: 0x%p\n", Address + (LoongArchExceptionEnd - LoongArchException)));
> +
> + DEBUG ((DEBUG_INFO, "InitializeExceptions, IrqEnabled = %x\n", IrqEnabled));
> + if (IrqEnabled) {
> + //
> + // Restore interrupt state
> + //
> + Status = Cpu->EnableInterrupt (Cpu);
> + }
> + return Status;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> new file mode 100644
> index 0000000000..79a20c66a2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> @@ -0,0 +1,97 @@
> +#------------------------------------------------------------------------------
> +#
> +# Fpu for LoongArch
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +# @par Glossary:
> +# - CsrEuen - Cpu Status Register Extern Unit Enable
> +# - FPEN - FPU Enable
> +# - fpu or fp or FP - Float Point Unit
> +#-----------------------------------------------------------------------------
> +#ifndef __ASSEMBLY__
> +#define __ASSEMBLY__
> +#endif
> +#include "Library/Cpu.h"
> +#include "CpuDxe.h"
> +
> +ASM_GLOBAL ASM_PFX(InitFpu)
> +ASM_GLOBAL ASM_PFX(LoongArchEnableFpu)
> +ASM_GLOBAL ASM_PFX(LoongArchDisableFpu)
> +
> +#
> +# Load the FPU with signalling NANS. This bit pattern we're using has
> +# the property that no matter whether considered as single or as double
> +# precision represents signaling NANS.
> +#
> +# The value to initialize FCSR0 to comes in $A0.
> +#
> +
> +ASM_PFX(InitFpu):
> + li.d T1, CSR_EUEN_FPEN
> + csrxchg T1, T1, LOONGARCH_CSR_EUEN
> +
> + movgr2fcsr FCSR0, A0
> + li.d T1, -1 # SNaN
> + movgr2fr.d $f0, T1
> + movgr2fr.d $f1, T1
> + movgr2fr.d $f2, T1
> + movgr2fr.d $f3, T1
> + movgr2fr.d $f4, T1
> + movgr2fr.d $f5, T1
> + movgr2fr.d $f6, T1
> + movgr2fr.d $f7, T1
> + movgr2fr.d $f8, T1
> + movgr2fr.d $f9, T1
> + movgr2fr.d $f10, T1
> + movgr2fr.d $f11, T1
> + movgr2fr.d $f12, T1
> + movgr2fr.d $f13, T1
> + movgr2fr.d $f14, T1
> + movgr2fr.d $f15, T1
> + movgr2fr.d $f16, T1
> + movgr2fr.d $f17, T1
> + movgr2fr.d $f18, T1
> + movgr2fr.d $f19, T1
> + movgr2fr.d $f20, T1
> + movgr2fr.d $f21, T1
> + movgr2fr.d $f22, T1
> + movgr2fr.d $f23, T1
> + movgr2fr.d $f24, T1
> + movgr2fr.d $f25, T1
> + movgr2fr.d $f26, T1
> + movgr2fr.d $f27, T1
> + movgr2fr.d $f28, T1
> + movgr2fr.d $f29, T1
> + movgr2fr.d $f30, T1
> + movgr2fr.d $f31, T1
> +
> + jirl ZERO, RA, 0
> +
> +#
> +# Enables floating-point unit
> +# @param VOID
> +# @retval VOID
> +#
> +
> +ASM_PFX(LoongArchEnableFpu):
> + li.d T0, 1
> + li.d T1, CSR_EUEN_FPEN_SHIFT
> + sll.d T0, T0, T1
> + csrxchg T0, T0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> +
> +#
> +# Disable floating-point unit
> +# @param VOID
> +# @retval VOID
> +#
> +
> +ASM_PFX(LoongArchDisableFpu):
> + li.d T0, 1
> + li.d T1, CSR_EUEN_FPEN_SHIFT
> + sll.d T0, T0, T1
> + csrxchg ZERO, T0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
> new file mode 100644
> index 0000000000..e463cf44f2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
> @@ -0,0 +1,321 @@
> +#------------------------------------------------------------------------------
> +#
> +# LoongArch for LoongArch
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +# @par Glossary:
> +# - CsrEuen - Cpu Status Register Extern Unit Enable
> +# - fpu - Float Point Unit
> +# - LOONGARCH - Loongson Arch
> +# - Ebase - Exception Base Address
> +#-----------------------------------------------------------------------------
> +
> +#ifndef __ASSEMBLY__
> +#define __ASSEMBLY__
> +#endif
> +
> +#include "Library/Cpu.h"
> +#include "CpuDxe.h"
> +
> +#define RSIZE 8 /* 64 bit mode register size */
> +#define RLOGSIZE 3
> +
> +ASM_GLOBAL ASM_PFX(Exception_handler)
> +ASM_GLOBAL ASM_PFX(LoongArchException)
> +ASM_GLOBAL ASM_PFX(SetEbase)
> +ASM_GLOBAL ASM_PFX(LoongArchReadqCsrEuen)
> +ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrEuen)
> +
> +#
> +# Main exception handler. Not really a leaf routine but not a normal
> +# function either. Save away the entire cpu state end enter exception mode.
> +#
> +
> +ASM_PFX(Exception_handler):
> + csrrd SP, LOONGARCH_CSR_KS1
> +
> + addi.d T0, $r0, -0x10
> + and SP, SP, T0
> + addi.d SP, SP, -((CSR_NUM + BASE_NUM + FP_BASE_NUM) * RSIZE)
> +
> + st.d RA, SP, RA_NUM * RSIZE
> + st.d GP, SP, GP_NUM * RSIZE
> + st.d A0, SP, A0_NUM * RSIZE
> + st.d A1, SP, A1_NUM * RSIZE
> + st.d A2, SP, A2_NUM * RSIZE
> + st.d A3, SP, A3_NUM * RSIZE
> + st.d A4, SP, A4_NUM * RSIZE
> + st.d A5, SP, A5_NUM * RSIZE
> + st.d A6, SP, A6_NUM * RSIZE
> + st.d A7, SP, A7_NUM * RSIZE
> + st.d T1, SP, T1_NUM * RSIZE
> + st.d T2, SP, T2_NUM * RSIZE
> + st.d T3, SP, T3_NUM * RSIZE
> + st.d T4, SP, T4_NUM * RSIZE
> + st.d T5, SP, T5_NUM * RSIZE
> + st.d T6, SP, T6_NUM * RSIZE
> + st.d T7, SP, T7_NUM * RSIZE
> + st.d T8, SP, T8_NUM * RSIZE
> + st.d TP, SP, TP_NUM * RSIZE
> + st.d FP, SP, FP_NUM * RSIZE
> + st.d S0, SP, S0_NUM * RSIZE
> + st.d S1, SP, S1_NUM * RSIZE
> + st.d S2, SP, S2_NUM * RSIZE
> + st.d S3, SP, S3_NUM * RSIZE
> + st.d S4, SP, S4_NUM * RSIZE
> + st.d S5, SP, S5_NUM * RSIZE
> + st.d S6, SP, S6_NUM * RSIZE
> + st.d S7, SP, S7_NUM * RSIZE
> + st.d S8, SP, S8_NUM * RSIZE
> +
> + #
> + # save T0/SP from scratch registers on stack
> + #
> + csrrd T0, LOONGARCH_CSR_KS0
> + st.d T0, SP, T0_NUM * RSIZE
> + csrrd T0, LOONGARCH_CSR_KS1
> + st.d T0, SP, SP_NUM * RSIZE
> +
> + csrrd T0, LOONGARCH_CSR_CRMD
> + st.d T0, SP, (LOONGARCH_CSR_CRMD + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_PRMD
> + st.d T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_ECFG
> + st.d T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_ESTAT
> + st.d T0, SP, (LOONGARCH_CSR_ESTAT + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_EPC
> + st.d T0, SP, (LOONGARCH_CSR_EPC+ BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_BADV
> + st.d T0, SP, (LOONGARCH_CSR_BADV + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_BADI
> + st.d T0, SP, (LOONGARCH_CSR_BADI + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_EUEN
> + st.d T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
> +
> + #
> + # Save FPU context
> + #
> + ori T1, ZERO, CSR_EUEN_FPEN
> + and T2, T0, T1
> + beqz T2, 1f
> +
> + fst.d $f0, SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f1, SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f2, SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f3, SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f4, SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f5, SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f6, SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f7, SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f8, SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f9, SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
> +
> + movfcsr2gr T3, FCSR0
> + st.d T3, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
> + movcf2gr T3, $fcc0
> + or T2, T3, ZERO
> + movcf2gr T3, $fcc1
> + bstrins.d T2, T3, 0xf, 0x8
> + movcf2gr T3, $fcc2
> + bstrins.d T2, T3, 0x17, 0x10
> + movcf2gr T3, $fcc3
> + bstrins.d T2, T3, 0x1f, 0x18
> + movcf2gr T3, $fcc4
> + bstrins.d T2, T3, 0x27, 0x20
> + movcf2gr T3, $fcc5
> + bstrins.d T2, T3, 0x2f, 0x28
> + movcf2gr T3, $fcc6
> + bstrins.d T2, T3, 0x37, 0x30
> + movcf2gr T3, $fcc7
> + bstrins.d T2, T3, 0x3f, 0x38
> + st.d T2, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
> +1:
> + or A0, SP, ZERO
> + bl CommonExceptionEntry
> + /*disable interrupt*/
> + li.d T0, (1 << 2)
> + csrxchg ZERO, T0, LOONGARCH_CSR_CRMD
> +
> + ld.d T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_PRMD
> + ld.d T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_ECFG
> + ld.d T0, SP, (LOONGARCH_CSR_EPC + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_EPC
> +
> + ld.d T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
> + ori T1, ZERO, CSR_EUEN_FPEN
> + and T2, T0, T1
> + beqz T2, 2f
> +
> + #
> + # check previous FP state
> + # restore FP contect if FP enabled
> + #
> + fld.d $f0, SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f1, SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f2, SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f3, SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f4, SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f5, SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f6, SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f7, SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f8, SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f9, SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
> +
> + ld.d T0, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
> + movgr2fcsr FCSR0, T0
> + ld.d T0, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
> + bstrpick.d T1, T0, 7, 0
> + movgr2cf $fcc0, T1
> + bstrpick.d T1, T0, 15, 8
> + movgr2cf $fcc1, T1
> + bstrpick.d T1, T0, 23, 16
> + movgr2cf $fcc2, T1
> + bstrpick.d T1, T0, 31, 24
> + movgr2cf $fcc3, T1
> + bstrpick.d T1, T0, 39, 32
> + movgr2cf $fcc4, T1
> + bstrpick.d T1, T0, 47, 40
> + movgr2cf $fcc5, T1
> + bstrpick.d T1, T0, 55, 48
> + movgr2cf $fcc6, T1
> + bstrpick.d T1, T0, 63, 56
> + movgr2cf $fcc7, T1
> +2:
> + ld.d RA, SP, RA_NUM * RSIZE
> + ld.d GP, SP, GP_NUM * RSIZE
> + ld.d A0, SP, A0_NUM * RSIZE
> + ld.d A1, SP, A1_NUM * RSIZE
> + ld.d A2, SP, A2_NUM * RSIZE
> + ld.d A3, SP, A3_NUM * RSIZE
> + ld.d A4, SP, A4_NUM * RSIZE
> + ld.d A5, SP, A5_NUM * RSIZE
> + ld.d A6, SP, A6_NUM * RSIZE
> + ld.d A7, SP, A7_NUM * RSIZE
> + ld.d T0, SP, T0_NUM * RSIZE
> + ld.d T1, SP, T1_NUM * RSIZE
> + ld.d T2, SP, T2_NUM * RSIZE
> + ld.d T3, SP, T3_NUM * RSIZE
> + ld.d T4, SP, T4_NUM * RSIZE
> + ld.d T5, SP, T5_NUM * RSIZE
> + ld.d T6, SP, T6_NUM * RSIZE
> + ld.d T7, SP, T7_NUM * RSIZE
> + ld.d T8, SP, T8_NUM * RSIZE
> + ld.d TP, SP, TP_NUM * RSIZE
> + ld.d FP, SP, FP_NUM * RSIZE
> + ld.d S0, SP, S0_NUM * RSIZE
> + ld.d S1, SP, S1_NUM * RSIZE
> + ld.d S2, SP, S2_NUM * RSIZE
> + ld.d S3, SP, S3_NUM * RSIZE
> + ld.d S4, SP, S4_NUM * RSIZE
> + ld.d S5, SP, S5_NUM * RSIZE
> + ld.d S6, SP, S6_NUM * RSIZE
> + ld.d S7, SP, S7_NUM * RSIZE
> + ld.d S8, SP, S8_NUM * RSIZE
> +
> + ld.d SP, SP, SP_NUM * RSIZE
> + ertn
> +
> +#
> +# Exception trampoline copied down to RAM after initialization.
> +#
> +
> +ASM_PFX(LoongArchException):
> + csrwr T0, LOONGARCH_CSR_KS0
> + csrwr SP, LOONGARCH_CSR_KS1
> + pcaddi T0, 0
> + ld.d T0, T0, 16
> + jirl ZERO, T0, 0
> + nop
> +1:
> + .quad Exception_handler
> +.globl LoongArchExceptionEnd
> +LoongArchExceptionEnd:
> +
> +#
> +# Set Exception Base Address.
> +#
> +
> +ASM_PFX(SetEbase):
> + #
> + # clear Vint cofigure
> + # all exceptions share the same interrupt entry
> + #
> + csrrd T0, LOONGARCH_CSR_ECFG
> + li.d T1, ~0x70000
> + and T0, T0, T1
> + csrwr T0, LOONGARCH_CSR_ECFG
> +
> + # set ebase
> + csrwr A0, LOONGARCH_CSR_EBASE
> + jirl ZERO, RA, 0
> +
> +#
> +# Read Csr EUEN register.
> +# @param A0 Pointer to the variable used to store the EUEN register value
> +# @retval none
> +#
> +
> +ASM_PFX(LoongArchReadqCsrEuen):
> + csrrd T0, LOONGARCH_CSR_EUEN
> + stptr.d T0, A0, 0
> + jirl ZERO, RA,0
> +
> +#
> +# Write Csr EUEN register.
> +# @param A0 The value used to write to the EUEN register
> +# @retval none
> +#
> +
> +ASM_PFX(LoongArchWriteqCsrEuen):
> + csrwr A0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> --
> 2.31.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#96294): https://edk2.groups.io/g/devel/message/96294
Mute This Topic: https://groups.io/mt/94955175/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [edk2-platforms][PATCH V5 08/15] Platform/Loongson: Add CPU DXE driver.
Posted by Chao Li 1 year, 3 months ago
Reviewed-by: Chao Li <lichao@loongson.cn>

Thanks,
Chao
--------

On 11月 11 2022, at 5:12 下午, xianglai li <lixianglai@loongson.cn> wrote:
> The driver produces EFI_CPU_ARCH_PROTOCOL,
>
> Initialize the exception entry address.
>
>
> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
>
>
> Cc: Bibo Mao <maobibo@loongson.cn>
> Cc: Chao Li <lichao@loongson.cn>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Liming Gao <gaoliming@byosoft.com.cn>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Signed-off-by: xianglai li <lixianglai@loongson.cn>
> ---
> .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c | 367 ++++++++++++++++++
> .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h | 199 ++++++++++
> .../Drivers/CpuDxe/CpuDxe.inf | 59 +++
> .../Drivers/CpuDxe/LoongArch64/Exception.c | 335 ++++++++++++++++
> .../Drivers/CpuDxe/LoongArch64/Fpu.S | 97 +++++
> .../Drivers/CpuDxe/LoongArch64/LoongArch.S | 321 +++++++++++++++
> 6 files changed, 1378 insertions(+)
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
>
>
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> new file mode 100644
> index 0000000000..23f824d82b
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> @@ -0,0 +1,367 @@
> +/** @file
> + CPU DXE Module to produce CPU ARCH Protocol
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Guid/IdleLoopEvent.h>
> +#include <Uefi.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/MmuLib.h>
> +#include "CpuDxe.h"
> +
> +BOOLEAN mInterruptState = FALSE;
> +
> +/*
> + This function flushes the range of addresses from Start to Start+Length
> + from the processor's data cache. If Start is not aligned to a cache line
> + boundary, then the bytes before Start to the preceding cache line boundary
> + are also flushed. If Start+Length is not aligned to a cache line boundary,
> + then the bytes past Start+Length to the end of the next cache line boundary
> + are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
> + supported. If the data cache is fully coherent with all DMA operations, then
> + this function can just return EFI_SUCCESS. If the processor does not support
> + flushing a range of the data cache, then the entire data cache can be flushed.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param Start The beginning physical address to flush from the processor's data
> + cache.
> + @param Length The number of bytes to flush from the processor's data cache. This
> + function may flush more bytes than Length specifies depending upon
> + the granularity of the flush operation that the processor supports.
> + @param FlushType Specifies the type of flush operation to perform.
> +
> + @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from
> + the processor's data cache.
> + @retval EFI_UNSUPPORTEDT The processor does not support the cache flush type specified
> + by FlushType.
> + @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed
> + from the processor's data cache.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuFlushCpuDataCache (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS Start,
> + IN UINT64 Length,
> + IN EFI_CPU_FLUSH_TYPE FlushType
> + )
> +{
> + switch (FlushType) {
> + case EfiCpuFlushTypeWriteBack:
> + WriteBackDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + case EfiCpuFlushTypeInvalidate:
> + InvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + case EfiCpuFlushTypeWriteBackInvalidate:
> + WriteBackInvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + default:
> + return EFI_INVALID_PARAMETER;
> + }
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function enables interrupt processing by the processor.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> +
> + @retval EFI_SUCCESS Interrupts are enabled on the processor.
> + @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuEnableInterrupt (
> + IN EFI_CPU_ARCH_PROTOCOL *This
> + )
> +{
> + EnableInterrupts ();
> +
> + mInterruptState = TRUE;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function disables interrupt processing by the processor.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> +
> + @retval EFI_SUCCESS Interrupts are disabled on the processor.
> + @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuDisableInterrupt (
> + IN EFI_CPU_ARCH_PROTOCOL *This
> + )
> +{
> + DisableInterrupts ();
> +
> + mInterruptState = FALSE;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function retrieves the processor's current interrupt state a returns it in
> + State. If interrupts are currently enabled, then TRUE is returned. If interrupts
> + are currently disabled, then FALSE is returned.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param State A pointer to the processor's current interrupt state. Set to TRUE if
> + interrupts are enabled and FALSE if interrupts are disabled.
> +
> + @retval EFI_SUCCESS The processor's current interrupt state was returned in State.
> + @retval EFI_INVALID_PARAMETER State is NULL.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetInterruptState (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + OUT BOOLEAN *State
> + )
> +{
> + if (State == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + *State = mInterruptState;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function generates an INIT on the processor. If this function succeeds, then the
> + processor will be reset, and control will not be returned to the caller. If InitType is
> + not supported by this processor, or the processor cannot programmatically generate an
> + INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
> + occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param InitType The type of processor INIT to perform.
> +
> + @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen.
> + @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported
> + by this processor.
> + @retval EFI_DEVICE_ERROR The processor INIT failed.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuInit (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_CPU_INIT_TYPE InitType
> + )
> +{
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InterruptType Interrupt Type.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuRegisterInterruptHandler (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_EXCEPTION_TYPE InterruptType,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + )
> +{
> + return RegisterInterruptHandler (InterruptType, InterruptHandler);
> +}
> +
> +/**
> + Returns a timer value from one of the CPU's internal timers. There is no
> + inherent time interval between ticks but is a function of the CPU frequency.
> +
> + @param This - Protocol instance structure.
> + @param TimerIndex - Specifies which CPU timer is requested.
> + @param TimerValue - Pointer to the returned timer value.
> + @param TimerPeriod - A pointer to the amount of time that passes
> + in femtoseconds (10-15) for each increment
> + of TimerValue. If TimerValue does not
> + increment at a predictable rate, then 0 is
> + returned. The amount of time that has
> + passed between two calls to GetTimerValue()
> + can be calculated with the formula
> + (TimerValue2 - TimerValue1) * TimerPeriod.
> + This parameter is optional and may be NULL.
> +
> + @retval EFI_SUCCESS - If the CPU timer count was returned.
> + @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers.
> + @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer.
> + @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetTimerValue (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN UINT32 TimerIndex,
> + OUT UINT64 *TimerValue,
> + OUT UINT64 *TimerPeriod OPTIONAL
> + )
> +{
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + This function modifies the attributes for the memory region specified by BaseAddress and
> + Length from their current attributes to the attributes specified by Attributes.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param BaseAddress The physical address that is the start address of a memory region.
> + @param Length The size in bytes of the memory region.
> + @param Attributes The bit mask of attributes to set for the memory region.
> +
> + @retval EFI_SUCCESS The attributes were set for the memory region.
> + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
> + BaseAddress and Length cannot be modified.
> + @retval EFI_INVALID_PARAMETER Length is zero.
> + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
> + the memory resource range.
> + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
> + resource range specified by BaseAddress and Length.
> + The bit mask of attributes is not support for the memory resource
> + range specified by BaseAddress and Length.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS BaseAddress,
> + IN UINT64 Length,
> + IN UINT64 EfiAttributes
> + )
> +{
> + EFI_STATUS Status;
> + UINTN LoongArchAttributes;
> + UINTN RegionBaseAddress;
> + UINTN RegionLength;
> + UINTN RegionLoongArchAttributes;
> +
> + if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
> + // Minimum granularity is SIZE_4KB (4KB on ARM)
> + DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
> + BaseAddress,
> + Length,
> + EfiAttributes));
> +
> + return EFI_UNSUPPORTED;
> + }
> + // Convert the 'Attribute' into LoongArch Attribute
> + LoongArchAttributes = EfiAttributeToLoongArchAttribute (EfiAttributes);
> +
> + // Get the region starting from 'BaseAddress' and its 'Attribute'
> + RegionBaseAddress = BaseAddress;
> + Status = GetLoongArchMemoryRegion (RegionBaseAddress, BaseAddress + Length,
> + &RegionLength, &RegionLoongArchAttributes);
> +
> + LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
> + // Data & Instruction Caches are flushed when we set new memory attributes.
> + // So, we only set the attributes if the new region is different.
> + if (EFI_ERROR (Status) || (RegionLoongArchAttributes != LoongArchAttributes) ||
> + ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
> + {
> + return LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
> + }
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Callback function for idle events.
> +
> + @param Event Event whose notification function is being invoked.
> + @param Context The pointer to the notification function's context,
> + which is implementation-dependent.
> +
> + @param VOID
> +**/
> +VOID
> +EFIAPI
> +IdleLoopEventCallback (
> + IN EFI_EVENT Event,
> + IN VOID *Context
> + )
> +{
> + CpuSleep ();
> +}
> +
> +//
> +// Globals used to initialize the protocol
> +//
> +EFI_HANDLE CpuHandle = NULL;
> +EFI_CPU_ARCH_PROTOCOL Cpu = {
> + CpuFlushCpuDataCache,
> + CpuEnableInterrupt,
> + CpuDisableInterrupt,
> + CpuGetInterruptState,
> + CpuInit,
> + CpuRegisterInterruptHandler,
> + CpuGetTimerValue,
> + CpuSetMemoryAttributes,
> + 0, // NumberOfTimers
> + 4, // DmaBufferAlignment
> +};
> +
> +/**
> + Initialize the state information for the CPU Architectural Protocol.
> +
> + @param ImageHandle Image handle this driver.
> + @param SystemTable Pointer to the System Table.
> +
> + @retval EFI_SUCCESS Thread can be successfully created
> + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
> + @retval EFI_DEVICE_ERROR Cannot create the thread
> +**/
> +EFI_STATUS
> +CpuDxeInitialize (
> + IN EFI_HANDLE ImageHandle,
> + IN EFI_SYSTEM_TABLE *SystemTable
> + )
> +{
> + EFI_STATUS Status;
> + EFI_EVENT IdleLoopEvent;
> +
> + InitializeExceptions (&Cpu);
> +
> + Status = gBS->InstallMultipleProtocolInterfaces (
> + &CpuHandle,
> + &gEfiCpuArchProtocolGuid, &Cpu,
> + NULL
> + );
> +
> + //
> + // Setup a callback for idle events
> + //
> + Status = gBS->CreateEventEx (
> + EVT_NOTIFY_SIGNAL,
> + TPL_NOTIFY,
> + IdleLoopEventCallback,
> + NULL,
> + &gIdleLoopEventGuid,
> + &IdleLoopEvent
> + );
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> new file mode 100644
> index 0000000000..43cb976aa2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> @@ -0,0 +1,199 @@
> +/** @file
> + CPU DXE Module to produce CPU ARCH Protocol and CPU MP Protocol
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef CPU_DXE_H_
> +#define CPU_DXE_H_
> +
> +#include <Protocol/Cpu.h>
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + );
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterDebuggerInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + );
> +
> +/**
> + This function modifies the attributes for the memory region specified by BaseAddress and
> + Length from their current attributes to the attributes specified by Attributes.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param BaseAddress The physical address that is the start address of a memory region.
> + @param Length The size in bytes of the memory region.
> + @param Attributes The bit mask of attributes to set for the memory region.
> +
> + @retval EFI_SUCCESS The attributes were set for the memory region.
> + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
> + BaseAddress and Length cannot be modified.
> + @retval EFI_INVALID_PARAMETER Length is zero.
> + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
> + the memory resource range.
> + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
> + resource range specified by BaseAddress and Length.
> + The bit mask of attributes is not support for the memory resource
> + range specified by BaseAddress and Length.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS BaseAddress,
> + IN UINT64 Length,
> + IN UINT64 Attributes
> + );
> +
> +/** Exception module initialization
> + This function sets the exception base address.
> +
> + @param Cpu A pointer to the CPU architecture protocol structure.
> +
> + @retval EFI_SUCCESS Initialization succeeded
> + @retval EFI_NOT_FOUND Could not Found resources.
> + @retval EFI_OUT_OF_RESOURCES No enough resources.
> +**/
> +EFI_STATUS
> +InitializeExceptions (
> + IN EFI_CPU_ARCH_PROTOCOL *Cpu
> + );
> +
> +/** Common exception entry
> + Exception handling is the entry point for the C environment,
> + This function does different things depending on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionEntry (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + );
> +
> +extern CHAR8 LoongArchException[], LoongArchExceptionEnd[];
> +/** Set Exception Base Address
> +
> + @param addr Exception Base Address.
> +
> + @retval The Old Exception Base Address.
> +**/
> +extern
> +UINT64
> +SetEbase (
> + EFI_PHYSICAL_ADDRESS addr
> + );
> +/*
> + Load the FPU with signalling NANS. This bit pattern we're using has
> + the property that no matter whether considered as single or as double
> + precision represents signaling NANS.
> +
> + @param fcsr The value to initialize FCSR0
> +
> + @retval The Old Exception Base Address.
> + */
> +extern
> +VOID
> +InitFpu (
> + UINT32 fcsr
> + );
> +
> +/*
> + Read Csr EUEN register.
> +
> + @param CsrEuen Pointer to the variable used to store the EUEN register value
> +
> + @retval none
> + */
> +extern
> +VOID
> +LoongArchReadqCsrEuen (
> + UINT64 *CsrEuen
> + );
> +
> +/*
> + Write Csr EUEN register.
> +
> + @param The value used to write to the EUEN register
> +
> + @retval none
> + */
> +extern
> +VOID
> +LoongArchWriteqCsrEuen (
> + UINT64 CsrEuen
> + );
> +
> +/*
> + Enables floating-point unit
> +
> + @param VOID
> +
> + @retval VOID
> + */
> +extern
> +VOID
> +LoongArchEnableFpu (
> + VOID
> + );
> +
> +/*
> + Disable floating-point unit
> +
> + @param VOID
> +
> + @retval VOID
> + */
> +extern
> +VOID
> +LoongArchDisableFpu (
> + VOID
> + );
> +
> +#endif // CPU_DXE_H_
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> new file mode 100644
> index 0000000000..96aabfefb8
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> @@ -0,0 +1,59 @@
> +## @file
> +# CPU driver installs CPU Architecture Protocol and CPU MP protocol.
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010005
> + BASE_NAME = CpuDxe
> + FILE_GUID = bf954921-25c1-48c0-9bfb-8d0cd7ee92da
> + MODULE_TYPE = DXE_DRIVER
> + VERSION_STRING = 1.0
> + ENTRY_POINT = CpuDxeInitialize
> +
> +#
> +# VALID_ARCHITECTURES = LOONGARCH64
> +#
> +
> +[Sources.Common]
> + CpuDxe.c
> + CpuDxe.h
> +
> +[Sources.LOONGARCH64]
> + LoongArch64/Exception.c
> + LoongArch64/LoongArch.S
> + LoongArch64/Fpu.S
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + EmbeddedPkg/EmbeddedPkg.dec
> + Platform/Loongson/LoongArchQemuPkg/Loongson.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + CacheMaintenanceLib
> + CpuLib
> + DebugLib
> + DxeServicesTableLib
> + HobLib
> + PeCoffGetEntryPointLib
> + UefiDriverEntryPoint
> + UefiLib
> + MmuLib
> +
> +[Protocols]
> + gEfiCpuArchProtocolGuid
> + gEfiMpServiceProtocolGuid
> +
> +[Guids]
> + gEfiDebugImageInfoTableGuid
> + gIdleLoopEventGuid
> +
> +[Depex]
> + TRUE
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> new file mode 100644
> index 0000000000..793ae90e4f
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> @@ -0,0 +1,335 @@
> +/** @file
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> + @par Glossary:
> + - ESTAT - Exception Status
> + - ECFG - Exception Configure
> + - ERA - Exception Return Address
> + - BADV - Bad Virtual Address
> + - BADI - Bad Instructions
> + - Epc or EPC or epc - Exception Program Counter
> + - pc or PC or pc - Program Counter
> + - CRMD - Current Mode
> + - PRMD - Previous Mode
> + - CsrEuen - Cpu Status Register Extern Unit Enable
> + - fpu or fp or FP - Float Point Unit
> + - LOONGARCH - Loongson Arch
> + - Irq - Interrupt ReQuest
> +**/
> +
> +#include "Library/Cpu.h"
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/DebugLib.h>
> +#include "CpuDxe.h"
> +#include <Library/PeCoffGetEntryPointLib.h>
> +#include <Library/UefiLib.h>
> +#include <Guid/DebugImageInfoTable.h>
> +
> +EFI_EXCEPTION_CALLBACK gInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1];
> +EFI_EXCEPTION_CALLBACK gDebuggerExceptionHandlers[MAX_LOONGARCH_INTERRUPT + 1];
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + )
> +{
> + if (InteruptNum > MAX_LOONGARCH_INTERRUPT) {
> + return EFI_UNSUPPORTED;
> + }
> +
> + if ((InterruptHandler != NULL)
> + && (gInterruptHandler[InteruptNum] != NULL))
> + {
> + return EFI_ALREADY_STARTED;
> + }
> +
> + gInterruptHandler[InteruptNum] = InterruptHandler;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function calls the corresponding exception handler based on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID
> +**/
> +STATIC VOID
> +EFIAPI
> +CommonInterruptHandler (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + INT32 Pending;
> + INT32 InterruptNum;
> + /*Interrupt [13-0] NMI IPI TI PCOV hw IP10-IP2 soft IP1-IP0*/
> + Pending = ((SystemContext.SystemContextLoongArch64->ESTAT) &
> + (SystemContext.SystemContextLoongArch64->ECFG) & 0x1fff);
> + for (InterruptNum = 0; InterruptNum < MAX_LOONGARCH_INTERRUPT; InterruptNum++) {
> + if (Pending & (1 << InterruptNum)) {
> + if (gInterruptHandler[InterruptNum] != NULL) {
> + gInterruptHandler[InterruptNum] (InterruptNum, SystemContext);
> + } else {
> + DEBUG ((DEBUG_INFO, "Pending: 0x%0x, InterruptNum: 0x%0x\n", Pending, InterruptNum));
> + }
> + }
> + }
> +}
> +
> +/**
> + Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image
> + it came from. As long as the PE/COFF image contains a debug directory entry a
> + string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF
> + image. Microsoft tools contain a pointer to the PDB file that contains the debug information.
> +
> + @param FaultAddress Address to find PE/COFF image for.
> + @param ImageBase Return load address of found image
> + @param PeCoffSizeOfHeaders Return the size of the PE/COFF header for the image that was found
> +
> + @retval NULL FaultAddress not in a loaded PE/COFF image.
> + @retval Path and file name of PE/COFF image.
> +**/
> +CHAR8 *
> +GetImageName (
> + IN UINTN FaultAddress,
> + OUT UINTN *ImageBase,
> + OUT UINTN *PeCoffSizeOfHeaders
> + )
> +{
> + EFI_STATUS Status;
> + EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *DebugTableHeader;
> + EFI_DEBUG_IMAGE_INFO *DebugTable;
> + UINTN Entry;
> + CHAR8 *Address;
> +
> + Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugTableHeader);
> + if (EFI_ERROR (Status)) {
> + return NULL;
> + }
> +
> + DebugTable = DebugTableHeader->EfiDebugImageInfoTable;
> + if (DebugTable == NULL) {
> + return NULL;
> + }
> +
> + Address = (CHAR8 *)(UINTN)FaultAddress;
> + for (Entry = 0; Entry < DebugTableHeader->TableSize; Entry++, DebugTable++) {
> + if (DebugTable->NormalImage != NULL) {
> + if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
> + (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
> + if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&
> + (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {
> + *ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;
> + *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);
> + return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);
> + }
> + }
> + }
> + }
> + return NULL;
> +}
> +
> +/**
> + pass a file name string that contains the path, return file name.
> +
> + @param FullName Path and file name
> +
> + @retval file name.
> +**/
> +STATIC
> +CONST CHAR8 *
> +BaseName (
> + IN CONST CHAR8 *FullName
> + )
> +{
> + CONST CHAR8 *Str;
> +
> + Str = FullName + AsciiStrLen (FullName);
> +
> + while (--Str > FullName) {
> + if (*Str == '/' || *Str == '\\') {
> + return Str + 1;
> + }
> + }
> + return Str;
> +}
> +
> +/** Default Exception Handler Function
> + This function is called when an exception occurs that cannot be handled,
> + and this function prints the system context information when the interrupt occurred
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +DefaultHandler (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + CHAR8 *ImageName;
> + UINTN ImageBase;
> + UINTN Epc;
> + UINTN PeCoffSizeOfHeader;
> +
> + DEBUG ((DEBUG_ERROR, "CRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->CRMD));
> + DEBUG ((DEBUG_ERROR, "PRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->PRMD));
> + DEBUG ((DEBUG_ERROR, "ECFG 0x%llx\n", SystemContext.SystemContextLoongArch64->ECFG));
> + DEBUG ((DEBUG_ERROR, "ESTAT 0x%llx\n", SystemContext.SystemContextLoongArch64->ESTAT));
> + DEBUG ((DEBUG_ERROR, "ERA 0x%llx\n", SystemContext.SystemContextLoongArch64->ERA));
> + DEBUG ((DEBUG_ERROR, "BADV 0x%llx\n", SystemContext.SystemContextLoongArch64->BADV));
> + DEBUG ((DEBUG_ERROR, "BADI 0x%llx\n", SystemContext.SystemContextLoongArch64->BADI));
> +
> + Epc = SystemContext.SystemContextLoongArch64->ERA;
> + ImageName = GetImageName (Epc, &ImageBase, &PeCoffSizeOfHeader);
> + if (ImageName != NULL) {
> + DEBUG ((DEBUG_ERROR, "PC 0x%012lx (0x%012lx+0x%08x) [ 0] %a\n",
> + Epc, ImageBase,
> + Epc - ImageBase, BaseName (ImageName)));
> + } else {
> + DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Epc));
> + }
> +
> + while (1);
> +}
> +
> +/** Common exception entry
> + Exception handling is the entry point for the C environment,
> + This function does different things depending on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionEntry (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + INT32 ExceptionType;
> + UINT64 CsrEuen;
> + UINT64 FpuStatus;
> +
> + ExceptionType = SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC;
> + ExceptionType = ExceptionType >> CSR_ESTAT_EXC_SHIFT;
> +
> + LoongArchReadqCsrEuen (&CsrEuen);
> + FpuStatus = CsrEuen & CSR_EUEN_FPEN;
> + switch (ExceptionType) {
> + case EXC_INT:
> + /*
> + * handle interrupt exception
> + */
> + CommonInterruptHandler (SystemContext);
> + if (!FpuStatus) {
> + LoongArchReadqCsrEuen (&CsrEuen);
> + if (CsrEuen & CSR_EUEN_FPEN) {
> + /*
> + * Since Hw FP is enabled during interrupt handler,
> + * disable FP
> + */
> + CsrEuen &= ~CSR_EUEN_FPEN;
> + LoongArchWriteqCsrEuen (CsrEuen);
> + }
> + }
> + break;
> + case EXC_FPDIS:
> + /*
> + * Hardware FP disabled exception,
> + * Enable and init FP registers here
> + */
> + LoongArchEnableFpu ();
> + InitFpu(FPU_CSR_RN);
> + break;
> + default:
> + DefaultHandler(SystemContext);
> + break;
> + }
> +}
> +
> +/** Exception module initialization
> + This function sets the exception base address.
> +
> + @param Cpu A pointer to the CPU architecture protocol structure.
> +
> + @retval EFI_SUCCESS Initialization succeeded
> + @retval EFI_NOT_FOUND Could not Found resources.
> + @retval EFI_OUT_OF_RESOURCES No enough resources.
> +**/
> +EFI_STATUS
> +InitializeExceptions (
> + IN EFI_CPU_ARCH_PROTOCOL *Cpu
> + )
> +{
> + EFI_STATUS Status;
> + BOOLEAN IrqEnabled;
> + EFI_PHYSICAL_ADDRESS Address;
> +
> + ZeroMem (gInterruptHandler, sizeof (*gInterruptHandler));
> +
> + //
> + // Disable interrupts
> + //
> + Cpu->GetInterruptState (Cpu, &IrqEnabled);
> + Cpu->DisableInterrupt (Cpu);
> +
> + //
> + // EFI does not use the FIQ, but a debugger might so we must disable
> + // as we take over the exception vectors.
> + //
> + Status = gBS->AllocatePages (
> + AllocateAnyPages,
> + EfiRuntimeServicesData,
> + 1,
> + &Address
> + );
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + DEBUG ((DEBUG_INFO, "Set Exception Base Address\n"));
> + CopyMem ((char *)Address, LoongArchException, (LoongArchExceptionEnd - LoongArchException));
> + InvalidateInstructionCacheRange ((char *)Address, (LoongArchExceptionEnd - LoongArchException));
> +
> + SetEbase (Address);
> + DEBUG ((DEBUG_INFO, "LoongArchException address: 0x%p\n", Address));
> + DEBUG ((DEBUG_INFO, "LoongArchExceptionEnd address: 0x%p\n", Address + (LoongArchExceptionEnd - LoongArchException)));
> +
> + DEBUG ((DEBUG_INFO, "InitializeExceptions, IrqEnabled = %x\n", IrqEnabled));
> + if (IrqEnabled) {
> + //
> + // Restore interrupt state
> + //
> + Status = Cpu->EnableInterrupt (Cpu);
> + }
> + return Status;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> new file mode 100644
> index 0000000000..79a20c66a2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> @@ -0,0 +1,97 @@
> +#------------------------------------------------------------------------------
> +#
> +# Fpu for LoongArch
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +# @par Glossary:
> +# - CsrEuen - Cpu Status Register Extern Unit Enable
> +# - FPEN - FPU Enable
> +# - fpu or fp or FP - Float Point Unit
> +#-----------------------------------------------------------------------------
> +#ifndef __ASSEMBLY__
> +#define __ASSEMBLY__
> +#endif
> +#include "Library/Cpu.h"
> +#include "CpuDxe.h"
> +
> +ASM_GLOBAL ASM_PFX(InitFpu)
> +ASM_GLOBAL ASM_PFX(LoongArchEnableFpu)
> +ASM_GLOBAL ASM_PFX(LoongArchDisableFpu)
> +
> +#
> +# Load the FPU with signalling NANS. This bit pattern we're using has
> +# the property that no matter whether considered as single or as double
> +# precision represents signaling NANS.
> +#
> +# The value to initialize FCSR0 to comes in $A0.
> +#
> +
> +ASM_PFX(InitFpu):
> + li.d T1, CSR_EUEN_FPEN
> + csrxchg T1, T1, LOONGARCH_CSR_EUEN
> +
> + movgr2fcsr FCSR0, A0
> + li.d T1, -1 # SNaN
> + movgr2fr.d $f0, T1
> + movgr2fr.d $f1, T1
> + movgr2fr.d $f2, T1
> + movgr2fr.d $f3, T1
> + movgr2fr.d $f4, T1
> + movgr2fr.d $f5, T1
> + movgr2fr.d $f6, T1
> + movgr2fr.d $f7, T1
> + movgr2fr.d $f8, T1
> + movgr2fr.d $f9, T1
> + movgr2fr.d $f10, T1
> + movgr2fr.d $f11, T1
> + movgr2fr.d $f12, T1
> + movgr2fr.d $f13, T1
> + movgr2fr.d $f14, T1
> + movgr2fr.d $f15, T1
> + movgr2fr.d $f16, T1
> + movgr2fr.d $f17, T1
> + movgr2fr.d $f18, T1
> + movgr2fr.d $f19, T1
> + movgr2fr.d $f20, T1
> + movgr2fr.d $f21, T1
> + movgr2fr.d $f22, T1
> + movgr2fr.d $f23, T1
> + movgr2fr.d $f24, T1
> + movgr2fr.d $f25, T1
> + movgr2fr.d $f26, T1
> + movgr2fr.d $f27, T1
> + movgr2fr.d $f28, T1
> + movgr2fr.d $f29, T1
> + movgr2fr.d $f30, T1
> + movgr2fr.d $f31, T1
> +
> + jirl ZERO, RA, 0
> +
> +#
> +# Enables floating-point unit
> +# @param VOID
> +# @retval VOID
> +#
> +
> +ASM_PFX(LoongArchEnableFpu):
> + li.d T0, 1
> + li.d T1, CSR_EUEN_FPEN_SHIFT
> + sll.d T0, T0, T1
> + csrxchg T0, T0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> +
> +#
> +# Disable floating-point unit
> +# @param VOID
> +# @retval VOID
> +#
> +
> +ASM_PFX(LoongArchDisableFpu):
> + li.d T0, 1
> + li.d T1, CSR_EUEN_FPEN_SHIFT
> + sll.d T0, T0, T1
> + csrxchg ZERO, T0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
> new file mode 100644
> index 0000000000..e463cf44f2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
> @@ -0,0 +1,321 @@
> +#------------------------------------------------------------------------------
> +#
> +# LoongArch for LoongArch
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +# @par Glossary:
> +# - CsrEuen - Cpu Status Register Extern Unit Enable
> +# - fpu - Float Point Unit
> +# - LOONGARCH - Loongson Arch
> +# - Ebase - Exception Base Address
> +#-----------------------------------------------------------------------------
> +
> +#ifndef __ASSEMBLY__
> +#define __ASSEMBLY__
> +#endif
> +
> +#include "Library/Cpu.h"
> +#include "CpuDxe.h"
> +
> +#define RSIZE 8 /* 64 bit mode register size */
> +#define RLOGSIZE 3
> +
> +ASM_GLOBAL ASM_PFX(Exception_handler)
> +ASM_GLOBAL ASM_PFX(LoongArchException)
> +ASM_GLOBAL ASM_PFX(SetEbase)
> +ASM_GLOBAL ASM_PFX(LoongArchReadqCsrEuen)
> +ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrEuen)
> +
> +#
> +# Main exception handler. Not really a leaf routine but not a normal
> +# function either. Save away the entire cpu state end enter exception mode.
> +#
> +
> +ASM_PFX(Exception_handler):
> + csrrd SP, LOONGARCH_CSR_KS1
> +
> + addi.d T0, $r0, -0x10
> + and SP, SP, T0
> + addi.d SP, SP, -((CSR_NUM + BASE_NUM + FP_BASE_NUM) * RSIZE)
> +
> + st.d RA, SP, RA_NUM * RSIZE
> + st.d GP, SP, GP_NUM * RSIZE
> + st.d A0, SP, A0_NUM * RSIZE
> + st.d A1, SP, A1_NUM * RSIZE
> + st.d A2, SP, A2_NUM * RSIZE
> + st.d A3, SP, A3_NUM * RSIZE
> + st.d A4, SP, A4_NUM * RSIZE
> + st.d A5, SP, A5_NUM * RSIZE
> + st.d A6, SP, A6_NUM * RSIZE
> + st.d A7, SP, A7_NUM * RSIZE
> + st.d T1, SP, T1_NUM * RSIZE
> + st.d T2, SP, T2_NUM * RSIZE
> + st.d T3, SP, T3_NUM * RSIZE
> + st.d T4, SP, T4_NUM * RSIZE
> + st.d T5, SP, T5_NUM * RSIZE
> + st.d T6, SP, T6_NUM * RSIZE
> + st.d T7, SP, T7_NUM * RSIZE
> + st.d T8, SP, T8_NUM * RSIZE
> + st.d TP, SP, TP_NUM * RSIZE
> + st.d FP, SP, FP_NUM * RSIZE
> + st.d S0, SP, S0_NUM * RSIZE
> + st.d S1, SP, S1_NUM * RSIZE
> + st.d S2, SP, S2_NUM * RSIZE
> + st.d S3, SP, S3_NUM * RSIZE
> + st.d S4, SP, S4_NUM * RSIZE
> + st.d S5, SP, S5_NUM * RSIZE
> + st.d S6, SP, S6_NUM * RSIZE
> + st.d S7, SP, S7_NUM * RSIZE
> + st.d S8, SP, S8_NUM * RSIZE
> +
> + #
> + # save T0/SP from scratch registers on stack
> + #
> + csrrd T0, LOONGARCH_CSR_KS0
> + st.d T0, SP, T0_NUM * RSIZE
> + csrrd T0, LOONGARCH_CSR_KS1
> + st.d T0, SP, SP_NUM * RSIZE
> +
> + csrrd T0, LOONGARCH_CSR_CRMD
> + st.d T0, SP, (LOONGARCH_CSR_CRMD + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_PRMD
> + st.d T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_ECFG
> + st.d T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_ESTAT
> + st.d T0, SP, (LOONGARCH_CSR_ESTAT + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_EPC
> + st.d T0, SP, (LOONGARCH_CSR_EPC+ BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_BADV
> + st.d T0, SP, (LOONGARCH_CSR_BADV + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_BADI
> + st.d T0, SP, (LOONGARCH_CSR_BADI + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_EUEN
> + st.d T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
> +
> + #
> + # Save FPU context
> + #
> + ori T1, ZERO, CSR_EUEN_FPEN
> + and T2, T0, T1
> + beqz T2, 1f
> +
> + fst.d $f0, SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f1, SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f2, SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f3, SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f4, SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f5, SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f6, SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f7, SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f8, SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f9, SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
> +
> + movfcsr2gr T3, FCSR0
> + st.d T3, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
> + movcf2gr T3, $fcc0
> + or T2, T3, ZERO
> + movcf2gr T3, $fcc1
> + bstrins.d T2, T3, 0xf, 0x8
> + movcf2gr T3, $fcc2
> + bstrins.d T2, T3, 0x17, 0x10
> + movcf2gr T3, $fcc3
> + bstrins.d T2, T3, 0x1f, 0x18
> + movcf2gr T3, $fcc4
> + bstrins.d T2, T3, 0x27, 0x20
> + movcf2gr T3, $fcc5
> + bstrins.d T2, T3, 0x2f, 0x28
> + movcf2gr T3, $fcc6
> + bstrins.d T2, T3, 0x37, 0x30
> + movcf2gr T3, $fcc7
> + bstrins.d T2, T3, 0x3f, 0x38
> + st.d T2, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
> +1:
> + or A0, SP, ZERO
> + bl CommonExceptionEntry
> + /*disable interrupt*/
> + li.d T0, (1 << 2)
> + csrxchg ZERO, T0, LOONGARCH_CSR_CRMD
> +
> + ld.d T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_PRMD
> + ld.d T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_ECFG
> + ld.d T0, SP, (LOONGARCH_CSR_EPC + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_EPC
> +
> + ld.d T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
> + ori T1, ZERO, CSR_EUEN_FPEN
> + and T2, T0, T1
> + beqz T2, 2f
> +
> + #
> + # check previous FP state
> + # restore FP contect if FP enabled
> + #
> + fld.d $f0, SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f1, SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f2, SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f3, SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f4, SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f5, SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f6, SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f7, SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f8, SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f9, SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
> +
> + ld.d T0, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
> + movgr2fcsr FCSR0, T0
> + ld.d T0, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
> + bstrpick.d T1, T0, 7, 0
> + movgr2cf $fcc0, T1
> + bstrpick.d T1, T0, 15, 8
> + movgr2cf $fcc1, T1
> + bstrpick.d T1, T0, 23, 16
> + movgr2cf $fcc2, T1
> + bstrpick.d T1, T0, 31, 24
> + movgr2cf $fcc3, T1
> + bstrpick.d T1, T0, 39, 32
> + movgr2cf $fcc4, T1
> + bstrpick.d T1, T0, 47, 40
> + movgr2cf $fcc5, T1
> + bstrpick.d T1, T0, 55, 48
> + movgr2cf $fcc6, T1
> + bstrpick.d T1, T0, 63, 56
> + movgr2cf $fcc7, T1
> +2:
> + ld.d RA, SP, RA_NUM * RSIZE
> + ld.d GP, SP, GP_NUM * RSIZE
> + ld.d A0, SP, A0_NUM * RSIZE
> + ld.d A1, SP, A1_NUM * RSIZE
> + ld.d A2, SP, A2_NUM * RSIZE
> + ld.d A3, SP, A3_NUM * RSIZE
> + ld.d A4, SP, A4_NUM * RSIZE
> + ld.d A5, SP, A5_NUM * RSIZE
> + ld.d A6, SP, A6_NUM * RSIZE
> + ld.d A7, SP, A7_NUM * RSIZE
> + ld.d T0, SP, T0_NUM * RSIZE
> + ld.d T1, SP, T1_NUM * RSIZE
> + ld.d T2, SP, T2_NUM * RSIZE
> + ld.d T3, SP, T3_NUM * RSIZE
> + ld.d T4, SP, T4_NUM * RSIZE
> + ld.d T5, SP, T5_NUM * RSIZE
> + ld.d T6, SP, T6_NUM * RSIZE
> + ld.d T7, SP, T7_NUM * RSIZE
> + ld.d T8, SP, T8_NUM * RSIZE
> + ld.d TP, SP, TP_NUM * RSIZE
> + ld.d FP, SP, FP_NUM * RSIZE
> + ld.d S0, SP, S0_NUM * RSIZE
> + ld.d S1, SP, S1_NUM * RSIZE
> + ld.d S2, SP, S2_NUM * RSIZE
> + ld.d S3, SP, S3_NUM * RSIZE
> + ld.d S4, SP, S4_NUM * RSIZE
> + ld.d S5, SP, S5_NUM * RSIZE
> + ld.d S6, SP, S6_NUM * RSIZE
> + ld.d S7, SP, S7_NUM * RSIZE
> + ld.d S8, SP, S8_NUM * RSIZE
> +
> + ld.d SP, SP, SP_NUM * RSIZE
> + ertn
> +
> +#
> +# Exception trampoline copied down to RAM after initialization.
> +#
> +
> +ASM_PFX(LoongArchException):
> + csrwr T0, LOONGARCH_CSR_KS0
> + csrwr SP, LOONGARCH_CSR_KS1
> + pcaddi T0, 0
> + ld.d T0, T0, 16
> + jirl ZERO, T0, 0
> + nop
> +1:
> + .quad Exception_handler
> +.globl LoongArchExceptionEnd
> +LoongArchExceptionEnd:
> +
> +#
> +# Set Exception Base Address.
> +#
> +
> +ASM_PFX(SetEbase):
> + #
> + # clear Vint cofigure
> + # all exceptions share the same interrupt entry
> + #
> + csrrd T0, LOONGARCH_CSR_ECFG
> + li.d T1, ~0x70000
> + and T0, T0, T1
> + csrwr T0, LOONGARCH_CSR_ECFG
> +
> + # set ebase
> + csrwr A0, LOONGARCH_CSR_EBASE
> + jirl ZERO, RA, 0
> +
> +#
> +# Read Csr EUEN register.
> +# @param A0 Pointer to the variable used to store the EUEN register value
> +# @retval none
> +#
> +
> +ASM_PFX(LoongArchReadqCsrEuen):
> + csrrd T0, LOONGARCH_CSR_EUEN
> + stptr.d T0, A0, 0
> + jirl ZERO, RA,0
> +
> +#
> +# Write Csr EUEN register.
> +# @param A0 The value used to write to the EUEN register
> +# @retval none
> +#
> +
> +ASM_PFX(LoongArchWriteqCsrEuen):
> + csrwr A0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> --
> 2.31.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#96293): https://edk2.groups.io/g/devel/message/96293
Mute This Topic: https://groups.io/mt/94955175/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-