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]
-=-=-=-=-=-=-=-=-=-=-=-
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] -=-=-=-=-=-=-=-=-=-=-=-
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] -=-=-=-=-=-=-=-=-=-=-=-
© 2016 - 2025 Red Hat, Inc.