Thanks,
Chao
--------
The driver produces EFI_CPU_ARCH_PROTOCOL,Initialize the exception entry address.REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054Cc: 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.ccreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.hcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.infcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.ccreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.Screate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.Sdiff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.cnew file mode 100644index 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.hnew file mode 100644index 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.infnew file mode 100644index 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]+ TRUEdiff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.cnew file mode 100644index 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.Snew file mode 100644index 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,0diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.Snew file mode 100644index 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