Reviewed-by: Chao Li Thanks, Chao -------- On 11月 11 2022, at 5:12 δΈ‹εˆ, xianglai li 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 > Cc: Chao Li > Cc: Leif Lindholm > Cc: Liming Gao > Cc: Michael D Kinney > Signed-off-by: xianglai li > --- > .../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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef CPU_DXE_H_ > +#define CPU_DXE_H_ > + > +#include > + > +/** > + 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.
> +# > +# 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.
> + > + 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 > +#include > +#include > +#include > +#include > +#include "CpuDxe.h" > +#include > +#include > +#include > + > +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.
> +# > +# 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.
> +# > +# 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