The driver produces EFI_CPU_ARCH_PROTOCOL, Initialize the exception entry address.
Signed-off-by: xianglai li <lixiang...@loongson.cn> --- .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c | 382 ++++++++++++++++++ .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h | 153 +++++++ .../Drivers/CpuDxe/CpuDxe.inf | 56 +++ .../Drivers/CpuDxe/LoongArch64/Exception.c | 337 +++++++++++++++ .../Drivers/CpuDxe/LoongArch64/Fpu.S | 67 +++ .../Drivers/CpuDxe/LoongArch64/LoongArch.S | 288 +++++++++++++ 6 files changed, 1283 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..bff2bd0c0a --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c @@ -0,0 +1,382 @@ +/** @file + CPU DXE Module to produce CPU ARCH Protocol + + Copyright (c) 2021 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..062f366ba2 --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h @@ -0,0 +1,153 @@ +/** @file + CPU DXE Module to produce CPU ARCH Protocol and CPU MP Protocol + + Copyright (c) 2021 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 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 A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @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 +RegisterInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + 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 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 A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @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 +RegisterDebuggerInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + 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 + ); + +#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..a5e93efdeb --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf @@ -0,0 +1,56 @@ +## @file +# CPU driver installs CPU Architecture Protocol and CPU MP protocol. +# +# Copyright (c) 2021 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 + +[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..42cd91feb7 --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c @@ -0,0 +1,337 @@ +/** @file + + Copyright (c) 2021 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 <string.h> +#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 gExceptionHandlers[MAX_LOONGARCH_EXCEPTION + 1]; +EFI_EXCEPTION_CALLBACK gDebuggerExceptionHandlers[MAX_LOONGARCH_EXCEPTION + 1]; + +/** + 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 A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @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 +RegisterInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + if (InterruptType > MAX_LOONGARCH_EXCEPTION) { + return EFI_UNSUPPORTED; + } + + if ((InterruptHandler != NULL) + && (gExceptionHandlers[InterruptType] != NULL)) + { + return EFI_ALREADY_STARTED; + } + + gExceptionHandlers[InterruptType] = InterruptHandler; + + return EFI_SUCCESS; +} +/** + This function calls the corresponding exception handler based on the exception type. + + @param ExceptionType Exception Type. + @param SystemContext The system context at the time of the exception. + + @retval VOID +**/ +STATIC VOID +EFIAPI +InterruptHandler ( + IN INT32 ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + INT32 Pending; + /*irq [13-0] NMI IPI TI PCOV hw IP10-IP2 soft IP1-IP0*/ + Pending = ((SystemContext.SystemContextLoongArch64->ESTAT) & + (SystemContext.SystemContextLoongArch64->ECFG) & 0x1fff); + if (Pending & (1 << 11/*TI*/)) { + gExceptionHandlers[ExceptionType] (ExceptionType, SystemContext); + } else { + DEBUG ((DEBUG_INFO, "Pending: 0x%0x, ExceptionType: 0x%0x\n", Pending, ExceptionType)); + } +} + +/** + 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, 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, FpuStatus; + + ExceptionType = SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC; + ExceptionType = ExceptionType >> CSR_ESTAT_EXC_SHIFT; + + LOONGARCH_CSR_READQ (CsrEuen, LOONGARCH_CSR_EUEN); + FpuStatus = CsrEuen & CSR_EUEN_FPEN; + switch (ExceptionType) { + case EXC_INT: + /* + * handle interrupt exception + */ + InterruptHandler (ExceptionType, SystemContext); + if (!FpuStatus) { + LOONGARCH_CSR_READQ (CsrEuen, LOONGARCH_CSR_EUEN); + if (CsrEuen & CSR_EUEN_FPEN) { + /* + * Since Hw FP is enabled during interrupt handler, + * disable FP + */ + CsrEuen &= ~CSR_EUEN_FPEN; + LOONGARCH_CSR_WRITEQ (CsrEuen, LOONGARCH_CSR_EUEN); + } + } + break; + case EXC_FPDIS: + /* + * Hardware FP disabled exception, + * Enable and init FP registers here + */ + LOONGARCH_ENABLR_FPU (); + 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 (gExceptionHandlers, sizeof (*gExceptionHandlers)); + + // + // 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..f6cab5c9e7 --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S @@ -0,0 +1,67 @@ +#------------------------------------------------------------------------------ +# +# Fpu for LoongArch +# +# Copyright (c) 2021 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" +#include "LoongArchAsmMacro.h" + +/* + 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_FUNC(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 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..09adef4610 --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S @@ -0,0 +1,288 @@ +#------------------------------------------------------------------------------ +# +# LoongArch for LoongArch +# +# Copyright (c) 2021 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" +#include "LoongArchAsmMacro.h" + +#define RSIZE 8 /* 64 bit mode register size */ +#define RLOGSIZE 3 + + +/* + 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_FUNC(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_FUNC(LoongArchException) + csrwr T0, LOONGARCH_CSR_KS0 + csrwr SP, LOONGARCH_CSR_KS1 + la T0, Exception_handler + jirl ZERO, T0, 0 +.globl LoongArchExceptionEnd +LoongArchExceptionEnd: + +/* + Set Exception Base Address. + */ +ASM_FUNC(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 -- 2.31.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#88001): https://edk2.groups.io/g/devel/message/88001 Mute This Topic: https://groups.io/mt/90014671/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-