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