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

xianglai lixianglai at loongson.cn
Fri Nov 11 08:25:14 UTC 2022


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 at loongson.cn>
Cc: Chao Li <lichao at loongson.cn>
Cc: Leif Lindholm <quic_llindhol at quicinc.com>
Cc: Liming Gao <gaoliming at byosoft.com.cn>
Cc: Michael D Kinney <michael.d.kinney at intel.com>
Signed-off-by: xianglai li <lixianglai at 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 (#96264): https://edk2.groups.io/g/devel/message/96264
Mute This Topic: https://groups.io/mt/94954843/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list