[edk2-devel] [edk2-platforms][PATCH V5 08/15] Platform/Loongson: Add CPU DXE driver.

Chao Li lichao at loongson.cn
Fri Nov 11 09:46:04 UTC 2022


Reviewed-by: Chao Li <lichao at loongson.cn>

Thanks,
Chao
--------

On 11月 11 2022, at 5:12 下午, xianglai li <lixianglai at 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 at loongson.cn>
> Cc: Chao Li <lichao at loongson.cn>
> Cc: Leif Lindholm <quic_llindhol at quicinc.com>
> Cc: Liming Gao <gaoliming at byosoft.com.cn>
> Cc: Michael D Kinney <michael.d.kinney at intel.com>
> Signed-off-by: xianglai li <lixianglai at loongson.cn>
> ---
> .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c | 367 ++++++++++++++++++
> .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h | 199 ++++++++++
> .../Drivers/CpuDxe/CpuDxe.inf | 59 +++
> .../Drivers/CpuDxe/LoongArch64/Exception.c | 335 ++++++++++++++++
> .../Drivers/CpuDxe/LoongArch64/Fpu.S | 97 +++++
> .../Drivers/CpuDxe/LoongArch64/LoongArch.S | 321 +++++++++++++++
> 6 files changed, 1378 insertions(+)
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
>
>
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> new file mode 100644
> index 0000000000..23f824d82b
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
> @@ -0,0 +1,367 @@
> +/** @file
> + CPU DXE Module to produce CPU ARCH Protocol
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Guid/IdleLoopEvent.h>
> +#include <Uefi.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/MmuLib.h>
> +#include "CpuDxe.h"
> +
> +BOOLEAN mInterruptState = FALSE;
> +
> +/*
> + This function flushes the range of addresses from Start to Start+Length
> + from the processor's data cache. If Start is not aligned to a cache line
> + boundary, then the bytes before Start to the preceding cache line boundary
> + are also flushed. If Start+Length is not aligned to a cache line boundary,
> + then the bytes past Start+Length to the end of the next cache line boundary
> + are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
> + supported. If the data cache is fully coherent with all DMA operations, then
> + this function can just return EFI_SUCCESS. If the processor does not support
> + flushing a range of the data cache, then the entire data cache can be flushed.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param Start The beginning physical address to flush from the processor's data
> + cache.
> + @param Length The number of bytes to flush from the processor's data cache. This
> + function may flush more bytes than Length specifies depending upon
> + the granularity of the flush operation that the processor supports.
> + @param FlushType Specifies the type of flush operation to perform.
> +
> + @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from
> + the processor's data cache.
> + @retval EFI_UNSUPPORTEDT The processor does not support the cache flush type specified
> + by FlushType.
> + @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed
> + from the processor's data cache.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuFlushCpuDataCache (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS Start,
> + IN UINT64 Length,
> + IN EFI_CPU_FLUSH_TYPE FlushType
> + )
> +{
> + switch (FlushType) {
> + case EfiCpuFlushTypeWriteBack:
> + WriteBackDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + case EfiCpuFlushTypeInvalidate:
> + InvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + case EfiCpuFlushTypeWriteBackInvalidate:
> + WriteBackInvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
> + break;
> + default:
> + return EFI_INVALID_PARAMETER;
> + }
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function enables interrupt processing by the processor.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> +
> + @retval EFI_SUCCESS Interrupts are enabled on the processor.
> + @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuEnableInterrupt (
> + IN EFI_CPU_ARCH_PROTOCOL *This
> + )
> +{
> + EnableInterrupts ();
> +
> + mInterruptState = TRUE;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function disables interrupt processing by the processor.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> +
> + @retval EFI_SUCCESS Interrupts are disabled on the processor.
> + @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuDisableInterrupt (
> + IN EFI_CPU_ARCH_PROTOCOL *This
> + )
> +{
> + DisableInterrupts ();
> +
> + mInterruptState = FALSE;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function retrieves the processor's current interrupt state a returns it in
> + State. If interrupts are currently enabled, then TRUE is returned. If interrupts
> + are currently disabled, then FALSE is returned.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param State A pointer to the processor's current interrupt state. Set to TRUE if
> + interrupts are enabled and FALSE if interrupts are disabled.
> +
> + @retval EFI_SUCCESS The processor's current interrupt state was returned in State.
> + @retval EFI_INVALID_PARAMETER State is NULL.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetInterruptState (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + OUT BOOLEAN *State
> + )
> +{
> + if (State == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + *State = mInterruptState;
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function generates an INIT on the processor. If this function succeeds, then the
> + processor will be reset, and control will not be returned to the caller. If InitType is
> + not supported by this processor, or the processor cannot programmatically generate an
> + INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
> + occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param InitType The type of processor INIT to perform.
> +
> + @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen.
> + @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported
> + by this processor.
> + @retval EFI_DEVICE_ERROR The processor INIT failed.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuInit (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_CPU_INIT_TYPE InitType
> + )
> +{
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InterruptType Interrupt Type.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuRegisterInterruptHandler (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_EXCEPTION_TYPE InterruptType,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + )
> +{
> + return RegisterInterruptHandler (InterruptType, InterruptHandler);
> +}
> +
> +/**
> + Returns a timer value from one of the CPU's internal timers. There is no
> + inherent time interval between ticks but is a function of the CPU frequency.
> +
> + @param This - Protocol instance structure.
> + @param TimerIndex - Specifies which CPU timer is requested.
> + @param TimerValue - Pointer to the returned timer value.
> + @param TimerPeriod - A pointer to the amount of time that passes
> + in femtoseconds (10-15) for each increment
> + of TimerValue. If TimerValue does not
> + increment at a predictable rate, then 0 is
> + returned. The amount of time that has
> + passed between two calls to GetTimerValue()
> + can be calculated with the formula
> + (TimerValue2 - TimerValue1) * TimerPeriod.
> + This parameter is optional and may be NULL.
> +
> + @retval EFI_SUCCESS - If the CPU timer count was returned.
> + @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers.
> + @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer.
> + @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetTimerValue (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN UINT32 TimerIndex,
> + OUT UINT64 *TimerValue,
> + OUT UINT64 *TimerPeriod OPTIONAL
> + )
> +{
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + This function modifies the attributes for the memory region specified by BaseAddress and
> + Length from their current attributes to the attributes specified by Attributes.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param BaseAddress The physical address that is the start address of a memory region.
> + @param Length The size in bytes of the memory region.
> + @param Attributes The bit mask of attributes to set for the memory region.
> +
> + @retval EFI_SUCCESS The attributes were set for the memory region.
> + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
> + BaseAddress and Length cannot be modified.
> + @retval EFI_INVALID_PARAMETER Length is zero.
> + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
> + the memory resource range.
> + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
> + resource range specified by BaseAddress and Length.
> + The bit mask of attributes is not support for the memory resource
> + range specified by BaseAddress and Length.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS BaseAddress,
> + IN UINT64 Length,
> + IN UINT64 EfiAttributes
> + )
> +{
> + EFI_STATUS Status;
> + UINTN LoongArchAttributes;
> + UINTN RegionBaseAddress;
> + UINTN RegionLength;
> + UINTN RegionLoongArchAttributes;
> +
> + if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
> + // Minimum granularity is SIZE_4KB (4KB on ARM)
> + DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
> + BaseAddress,
> + Length,
> + EfiAttributes));
> +
> + return EFI_UNSUPPORTED;
> + }
> + // Convert the 'Attribute' into LoongArch Attribute
> + LoongArchAttributes = EfiAttributeToLoongArchAttribute (EfiAttributes);
> +
> + // Get the region starting from 'BaseAddress' and its 'Attribute'
> + RegionBaseAddress = BaseAddress;
> + Status = GetLoongArchMemoryRegion (RegionBaseAddress, BaseAddress + Length,
> + &RegionLength, &RegionLoongArchAttributes);
> +
> + LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
> + // Data & Instruction Caches are flushed when we set new memory attributes.
> + // So, we only set the attributes if the new region is different.
> + if (EFI_ERROR (Status) || (RegionLoongArchAttributes != LoongArchAttributes) ||
> + ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
> + {
> + return LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
> + }
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Callback function for idle events.
> +
> + @param Event Event whose notification function is being invoked.
> + @param Context The pointer to the notification function's context,
> + which is implementation-dependent.
> +
> + @param VOID
> +**/
> +VOID
> +EFIAPI
> +IdleLoopEventCallback (
> + IN EFI_EVENT Event,
> + IN VOID *Context
> + )
> +{
> + CpuSleep ();
> +}
> +
> +//
> +// Globals used to initialize the protocol
> +//
> +EFI_HANDLE CpuHandle = NULL;
> +EFI_CPU_ARCH_PROTOCOL Cpu = {
> + CpuFlushCpuDataCache,
> + CpuEnableInterrupt,
> + CpuDisableInterrupt,
> + CpuGetInterruptState,
> + CpuInit,
> + CpuRegisterInterruptHandler,
> + CpuGetTimerValue,
> + CpuSetMemoryAttributes,
> + 0, // NumberOfTimers
> + 4, // DmaBufferAlignment
> +};
> +
> +/**
> + Initialize the state information for the CPU Architectural Protocol.
> +
> + @param ImageHandle Image handle this driver.
> + @param SystemTable Pointer to the System Table.
> +
> + @retval EFI_SUCCESS Thread can be successfully created
> + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
> + @retval EFI_DEVICE_ERROR Cannot create the thread
> +**/
> +EFI_STATUS
> +CpuDxeInitialize (
> + IN EFI_HANDLE ImageHandle,
> + IN EFI_SYSTEM_TABLE *SystemTable
> + )
> +{
> + EFI_STATUS Status;
> + EFI_EVENT IdleLoopEvent;
> +
> + InitializeExceptions (&Cpu);
> +
> + Status = gBS->InstallMultipleProtocolInterfaces (
> + &CpuHandle,
> + &gEfiCpuArchProtocolGuid, &Cpu,
> + NULL
> + );
> +
> + //
> + // Setup a callback for idle events
> + //
> + Status = gBS->CreateEventEx (
> + EVT_NOTIFY_SIGNAL,
> + TPL_NOTIFY,
> + IdleLoopEventCallback,
> + NULL,
> + &gIdleLoopEventGuid,
> + &IdleLoopEvent
> + );
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> new file mode 100644
> index 0000000000..43cb976aa2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
> @@ -0,0 +1,199 @@
> +/** @file
> + CPU DXE Module to produce CPU ARCH Protocol and CPU MP Protocol
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef CPU_DXE_H_
> +#define CPU_DXE_H_
> +
> +#include <Protocol/Cpu.h>
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + );
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterDebuggerInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + );
> +
> +/**
> + This function modifies the attributes for the memory region specified by BaseAddress and
> + Length from their current attributes to the attributes specified by Attributes.
> +
> + @param This The EFI_CPU_ARCH_PROTOCOL instance.
> + @param BaseAddress The physical address that is the start address of a memory region.
> + @param Length The size in bytes of the memory region.
> + @param Attributes The bit mask of attributes to set for the memory region.
> +
> + @retval EFI_SUCCESS The attributes were set for the memory region.
> + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
> + BaseAddress and Length cannot be modified.
> + @retval EFI_INVALID_PARAMETER Length is zero.
> + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
> + the memory resource range.
> + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
> + resource range specified by BaseAddress and Length.
> + The bit mask of attributes is not support for the memory resource
> + range specified by BaseAddress and Length.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> + IN EFI_CPU_ARCH_PROTOCOL *This,
> + IN EFI_PHYSICAL_ADDRESS BaseAddress,
> + IN UINT64 Length,
> + IN UINT64 Attributes
> + );
> +
> +/** Exception module initialization
> + This function sets the exception base address.
> +
> + @param Cpu A pointer to the CPU architecture protocol structure.
> +
> + @retval EFI_SUCCESS Initialization succeeded
> + @retval EFI_NOT_FOUND Could not Found resources.
> + @retval EFI_OUT_OF_RESOURCES No enough resources.
> +**/
> +EFI_STATUS
> +InitializeExceptions (
> + IN EFI_CPU_ARCH_PROTOCOL *Cpu
> + );
> +
> +/** Common exception entry
> + Exception handling is the entry point for the C environment,
> + This function does different things depending on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionEntry (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + );
> +
> +extern CHAR8 LoongArchException[], LoongArchExceptionEnd[];
> +/** Set Exception Base Address
> +
> + @param addr Exception Base Address.
> +
> + @retval The Old Exception Base Address.
> +**/
> +extern
> +UINT64
> +SetEbase (
> + EFI_PHYSICAL_ADDRESS addr
> + );
> +/*
> + Load the FPU with signalling NANS. This bit pattern we're using has
> + the property that no matter whether considered as single or as double
> + precision represents signaling NANS.
> +
> + @param fcsr The value to initialize FCSR0
> +
> + @retval The Old Exception Base Address.
> + */
> +extern
> +VOID
> +InitFpu (
> + UINT32 fcsr
> + );
> +
> +/*
> + Read Csr EUEN register.
> +
> + @param CsrEuen Pointer to the variable used to store the EUEN register value
> +
> + @retval none
> + */
> +extern
> +VOID
> +LoongArchReadqCsrEuen (
> + UINT64 *CsrEuen
> + );
> +
> +/*
> + Write Csr EUEN register.
> +
> + @param The value used to write to the EUEN register
> +
> + @retval none
> + */
> +extern
> +VOID
> +LoongArchWriteqCsrEuen (
> + UINT64 CsrEuen
> + );
> +
> +/*
> + Enables floating-point unit
> +
> + @param VOID
> +
> + @retval VOID
> + */
> +extern
> +VOID
> +LoongArchEnableFpu (
> + VOID
> + );
> +
> +/*
> + Disable floating-point unit
> +
> + @param VOID
> +
> + @retval VOID
> + */
> +extern
> +VOID
> +LoongArchDisableFpu (
> + VOID
> + );
> +
> +#endif // CPU_DXE_H_
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> new file mode 100644
> index 0000000000..96aabfefb8
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
> @@ -0,0 +1,59 @@
> +## @file
> +# CPU driver installs CPU Architecture Protocol and CPU MP protocol.
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010005
> + BASE_NAME = CpuDxe
> + FILE_GUID = bf954921-25c1-48c0-9bfb-8d0cd7ee92da
> + MODULE_TYPE = DXE_DRIVER
> + VERSION_STRING = 1.0
> + ENTRY_POINT = CpuDxeInitialize
> +
> +#
> +# VALID_ARCHITECTURES = LOONGARCH64
> +#
> +
> +[Sources.Common]
> + CpuDxe.c
> + CpuDxe.h
> +
> +[Sources.LOONGARCH64]
> + LoongArch64/Exception.c
> + LoongArch64/LoongArch.S
> + LoongArch64/Fpu.S
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + EmbeddedPkg/EmbeddedPkg.dec
> + Platform/Loongson/LoongArchQemuPkg/Loongson.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + CacheMaintenanceLib
> + CpuLib
> + DebugLib
> + DxeServicesTableLib
> + HobLib
> + PeCoffGetEntryPointLib
> + UefiDriverEntryPoint
> + UefiLib
> + MmuLib
> +
> +[Protocols]
> + gEfiCpuArchProtocolGuid
> + gEfiMpServiceProtocolGuid
> +
> +[Guids]
> + gEfiDebugImageInfoTableGuid
> + gIdleLoopEventGuid
> +
> +[Depex]
> + TRUE
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> new file mode 100644
> index 0000000000..793ae90e4f
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
> @@ -0,0 +1,335 @@
> +/** @file
> +
> + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> + @par Glossary:
> + - ESTAT - Exception Status
> + - ECFG - Exception Configure
> + - ERA - Exception Return Address
> + - BADV - Bad Virtual Address
> + - BADI - Bad Instructions
> + - Epc or EPC or epc - Exception Program Counter
> + - pc or PC or pc - Program Counter
> + - CRMD - Current Mode
> + - PRMD - Previous Mode
> + - CsrEuen - Cpu Status Register Extern Unit Enable
> + - fpu or fp or FP - Float Point Unit
> + - LOONGARCH - Loongson Arch
> + - Irq - Interrupt ReQuest
> +**/
> +
> +#include "Library/Cpu.h"
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/DebugLib.h>
> +#include "CpuDxe.h"
> +#include <Library/PeCoffGetEntryPointLib.h>
> +#include <Library/UefiLib.h>
> +#include <Guid/DebugImageInfoTable.h>
> +
> +EFI_EXCEPTION_CALLBACK gInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1];
> +EFI_EXCEPTION_CALLBACK gDebuggerExceptionHandlers[MAX_LOONGARCH_INTERRUPT + 1];
> +
> +/**
> + This function registers and enables the handler specified by InterruptHandler for a processor
> + interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
> + handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
> + The installed handler is called once for each processor interrupt or exception.
> +
> + @param InteruptNum A number of the processor's current interrupt.
> + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> + when a processor interrupt occurs. If this parameter is NULL, then the handler
> + will be uninstalled.
> +
> + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
> + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
> + previously installed.
> + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
> + previously installed.
> + @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> + IN EFI_EXCEPTION_TYPE InteruptNum,
> + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
> + )
> +{
> + if (InteruptNum > MAX_LOONGARCH_INTERRUPT) {
> + return EFI_UNSUPPORTED;
> + }
> +
> + if ((InterruptHandler != NULL)
> + && (gInterruptHandler[InteruptNum] != NULL))
> + {
> + return EFI_ALREADY_STARTED;
> + }
> +
> + gInterruptHandler[InteruptNum] = InterruptHandler;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function calls the corresponding exception handler based on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID
> +**/
> +STATIC VOID
> +EFIAPI
> +CommonInterruptHandler (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + INT32 Pending;
> + INT32 InterruptNum;
> + /*Interrupt [13-0] NMI IPI TI PCOV hw IP10-IP2 soft IP1-IP0*/
> + Pending = ((SystemContext.SystemContextLoongArch64->ESTAT) &
> + (SystemContext.SystemContextLoongArch64->ECFG) & 0x1fff);
> + for (InterruptNum = 0; InterruptNum < MAX_LOONGARCH_INTERRUPT; InterruptNum++) {
> + if (Pending & (1 << InterruptNum)) {
> + if (gInterruptHandler[InterruptNum] != NULL) {
> + gInterruptHandler[InterruptNum] (InterruptNum, SystemContext);
> + } else {
> + DEBUG ((DEBUG_INFO, "Pending: 0x%0x, InterruptNum: 0x%0x\n", Pending, InterruptNum));
> + }
> + }
> + }
> +}
> +
> +/**
> + Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image
> + it came from. As long as the PE/COFF image contains a debug directory entry a
> + string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF
> + image. Microsoft tools contain a pointer to the PDB file that contains the debug information.
> +
> + @param FaultAddress Address to find PE/COFF image for.
> + @param ImageBase Return load address of found image
> + @param PeCoffSizeOfHeaders Return the size of the PE/COFF header for the image that was found
> +
> + @retval NULL FaultAddress not in a loaded PE/COFF image.
> + @retval Path and file name of PE/COFF image.
> +**/
> +CHAR8 *
> +GetImageName (
> + IN UINTN FaultAddress,
> + OUT UINTN *ImageBase,
> + OUT UINTN *PeCoffSizeOfHeaders
> + )
> +{
> + EFI_STATUS Status;
> + EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *DebugTableHeader;
> + EFI_DEBUG_IMAGE_INFO *DebugTable;
> + UINTN Entry;
> + CHAR8 *Address;
> +
> + Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugTableHeader);
> + if (EFI_ERROR (Status)) {
> + return NULL;
> + }
> +
> + DebugTable = DebugTableHeader->EfiDebugImageInfoTable;
> + if (DebugTable == NULL) {
> + return NULL;
> + }
> +
> + Address = (CHAR8 *)(UINTN)FaultAddress;
> + for (Entry = 0; Entry < DebugTableHeader->TableSize; Entry++, DebugTable++) {
> + if (DebugTable->NormalImage != NULL) {
> + if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
> + (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
> + if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&
> + (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {
> + *ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;
> + *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);
> + return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);
> + }
> + }
> + }
> + }
> + return NULL;
> +}
> +
> +/**
> + pass a file name string that contains the path, return file name.
> +
> + @param FullName Path and file name
> +
> + @retval file name.
> +**/
> +STATIC
> +CONST CHAR8 *
> +BaseName (
> + IN CONST CHAR8 *FullName
> + )
> +{
> + CONST CHAR8 *Str;
> +
> + Str = FullName + AsciiStrLen (FullName);
> +
> + while (--Str > FullName) {
> + if (*Str == '/' || *Str == '\\') {
> + return Str + 1;
> + }
> + }
> + return Str;
> +}
> +
> +/** Default Exception Handler Function
> + This function is called when an exception occurs that cannot be handled,
> + and this function prints the system context information when the interrupt occurred
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +DefaultHandler (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + CHAR8 *ImageName;
> + UINTN ImageBase;
> + UINTN Epc;
> + UINTN PeCoffSizeOfHeader;
> +
> + DEBUG ((DEBUG_ERROR, "CRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->CRMD));
> + DEBUG ((DEBUG_ERROR, "PRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->PRMD));
> + DEBUG ((DEBUG_ERROR, "ECFG 0x%llx\n", SystemContext.SystemContextLoongArch64->ECFG));
> + DEBUG ((DEBUG_ERROR, "ESTAT 0x%llx\n", SystemContext.SystemContextLoongArch64->ESTAT));
> + DEBUG ((DEBUG_ERROR, "ERA 0x%llx\n", SystemContext.SystemContextLoongArch64->ERA));
> + DEBUG ((DEBUG_ERROR, "BADV 0x%llx\n", SystemContext.SystemContextLoongArch64->BADV));
> + DEBUG ((DEBUG_ERROR, "BADI 0x%llx\n", SystemContext.SystemContextLoongArch64->BADI));
> +
> + Epc = SystemContext.SystemContextLoongArch64->ERA;
> + ImageName = GetImageName (Epc, &ImageBase, &PeCoffSizeOfHeader);
> + if (ImageName != NULL) {
> + DEBUG ((DEBUG_ERROR, "PC 0x%012lx (0x%012lx+0x%08x) [ 0] %a\n",
> + Epc, ImageBase,
> + Epc - ImageBase, BaseName (ImageName)));
> + } else {
> + DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Epc));
> + }
> +
> + while (1);
> +}
> +
> +/** Common exception entry
> + Exception handling is the entry point for the C environment,
> + This function does different things depending on the exception type.
> +
> + @param SystemContext The system context at the time of the exception.
> +
> + @retval VOID.
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionEntry (
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + INT32 ExceptionType;
> + UINT64 CsrEuen;
> + UINT64 FpuStatus;
> +
> + ExceptionType = SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC;
> + ExceptionType = ExceptionType >> CSR_ESTAT_EXC_SHIFT;
> +
> + LoongArchReadqCsrEuen (&CsrEuen);
> + FpuStatus = CsrEuen & CSR_EUEN_FPEN;
> + switch (ExceptionType) {
> + case EXC_INT:
> + /*
> + * handle interrupt exception
> + */
> + CommonInterruptHandler (SystemContext);
> + if (!FpuStatus) {
> + LoongArchReadqCsrEuen (&CsrEuen);
> + if (CsrEuen & CSR_EUEN_FPEN) {
> + /*
> + * Since Hw FP is enabled during interrupt handler,
> + * disable FP
> + */
> + CsrEuen &= ~CSR_EUEN_FPEN;
> + LoongArchWriteqCsrEuen (CsrEuen);
> + }
> + }
> + break;
> + case EXC_FPDIS:
> + /*
> + * Hardware FP disabled exception,
> + * Enable and init FP registers here
> + */
> + LoongArchEnableFpu ();
> + InitFpu(FPU_CSR_RN);
> + break;
> + default:
> + DefaultHandler(SystemContext);
> + break;
> + }
> +}
> +
> +/** Exception module initialization
> + This function sets the exception base address.
> +
> + @param Cpu A pointer to the CPU architecture protocol structure.
> +
> + @retval EFI_SUCCESS Initialization succeeded
> + @retval EFI_NOT_FOUND Could not Found resources.
> + @retval EFI_OUT_OF_RESOURCES No enough resources.
> +**/
> +EFI_STATUS
> +InitializeExceptions (
> + IN EFI_CPU_ARCH_PROTOCOL *Cpu
> + )
> +{
> + EFI_STATUS Status;
> + BOOLEAN IrqEnabled;
> + EFI_PHYSICAL_ADDRESS Address;
> +
> + ZeroMem (gInterruptHandler, sizeof (*gInterruptHandler));
> +
> + //
> + // Disable interrupts
> + //
> + Cpu->GetInterruptState (Cpu, &IrqEnabled);
> + Cpu->DisableInterrupt (Cpu);
> +
> + //
> + // EFI does not use the FIQ, but a debugger might so we must disable
> + // as we take over the exception vectors.
> + //
> + Status = gBS->AllocatePages (
> + AllocateAnyPages,
> + EfiRuntimeServicesData,
> + 1,
> + &Address
> + );
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + DEBUG ((DEBUG_INFO, "Set Exception Base Address\n"));
> + CopyMem ((char *)Address, LoongArchException, (LoongArchExceptionEnd - LoongArchException));
> + InvalidateInstructionCacheRange ((char *)Address, (LoongArchExceptionEnd - LoongArchException));
> +
> + SetEbase (Address);
> + DEBUG ((DEBUG_INFO, "LoongArchException address: 0x%p\n", Address));
> + DEBUG ((DEBUG_INFO, "LoongArchExceptionEnd address: 0x%p\n", Address + (LoongArchExceptionEnd - LoongArchException)));
> +
> + DEBUG ((DEBUG_INFO, "InitializeExceptions, IrqEnabled = %x\n", IrqEnabled));
> + if (IrqEnabled) {
> + //
> + // Restore interrupt state
> + //
> + Status = Cpu->EnableInterrupt (Cpu);
> + }
> + return Status;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> new file mode 100644
> index 0000000000..79a20c66a2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
> @@ -0,0 +1,97 @@
> +#------------------------------------------------------------------------------
> +#
> +# Fpu for LoongArch
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +# @par Glossary:
> +# - CsrEuen - Cpu Status Register Extern Unit Enable
> +# - FPEN - FPU Enable
> +# - fpu or fp or FP - Float Point Unit
> +#-----------------------------------------------------------------------------
> +#ifndef __ASSEMBLY__
> +#define __ASSEMBLY__
> +#endif
> +#include "Library/Cpu.h"
> +#include "CpuDxe.h"
> +
> +ASM_GLOBAL ASM_PFX(InitFpu)
> +ASM_GLOBAL ASM_PFX(LoongArchEnableFpu)
> +ASM_GLOBAL ASM_PFX(LoongArchDisableFpu)
> +
> +#
> +# Load the FPU with signalling NANS. This bit pattern we're using has
> +# the property that no matter whether considered as single or as double
> +# precision represents signaling NANS.
> +#
> +# The value to initialize FCSR0 to comes in $A0.
> +#
> +
> +ASM_PFX(InitFpu):
> + li.d T1, CSR_EUEN_FPEN
> + csrxchg T1, T1, LOONGARCH_CSR_EUEN
> +
> + movgr2fcsr FCSR0, A0
> + li.d T1, -1 # SNaN
> + movgr2fr.d $f0, T1
> + movgr2fr.d $f1, T1
> + movgr2fr.d $f2, T1
> + movgr2fr.d $f3, T1
> + movgr2fr.d $f4, T1
> + movgr2fr.d $f5, T1
> + movgr2fr.d $f6, T1
> + movgr2fr.d $f7, T1
> + movgr2fr.d $f8, T1
> + movgr2fr.d $f9, T1
> + movgr2fr.d $f10, T1
> + movgr2fr.d $f11, T1
> + movgr2fr.d $f12, T1
> + movgr2fr.d $f13, T1
> + movgr2fr.d $f14, T1
> + movgr2fr.d $f15, T1
> + movgr2fr.d $f16, T1
> + movgr2fr.d $f17, T1
> + movgr2fr.d $f18, T1
> + movgr2fr.d $f19, T1
> + movgr2fr.d $f20, T1
> + movgr2fr.d $f21, T1
> + movgr2fr.d $f22, T1
> + movgr2fr.d $f23, T1
> + movgr2fr.d $f24, T1
> + movgr2fr.d $f25, T1
> + movgr2fr.d $f26, T1
> + movgr2fr.d $f27, T1
> + movgr2fr.d $f28, T1
> + movgr2fr.d $f29, T1
> + movgr2fr.d $f30, T1
> + movgr2fr.d $f31, T1
> +
> + jirl ZERO, RA, 0
> +
> +#
> +# Enables floating-point unit
> +# @param VOID
> +# @retval VOID
> +#
> +
> +ASM_PFX(LoongArchEnableFpu):
> + li.d T0, 1
> + li.d T1, CSR_EUEN_FPEN_SHIFT
> + sll.d T0, T0, T1
> + csrxchg T0, T0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> +
> +#
> +# Disable floating-point unit
> +# @param VOID
> +# @retval VOID
> +#
> +
> +ASM_PFX(LoongArchDisableFpu):
> + li.d T0, 1
> + li.d T1, CSR_EUEN_FPEN_SHIFT
> + sll.d T0, T0, T1
> + csrxchg ZERO, T0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
> new file mode 100644
> index 0000000000..e463cf44f2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
> @@ -0,0 +1,321 @@
> +#------------------------------------------------------------------------------
> +#
> +# LoongArch for LoongArch
> +#
> +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +# @par Glossary:
> +# - CsrEuen - Cpu Status Register Extern Unit Enable
> +# - fpu - Float Point Unit
> +# - LOONGARCH - Loongson Arch
> +# - Ebase - Exception Base Address
> +#-----------------------------------------------------------------------------
> +
> +#ifndef __ASSEMBLY__
> +#define __ASSEMBLY__
> +#endif
> +
> +#include "Library/Cpu.h"
> +#include "CpuDxe.h"
> +
> +#define RSIZE 8 /* 64 bit mode register size */
> +#define RLOGSIZE 3
> +
> +ASM_GLOBAL ASM_PFX(Exception_handler)
> +ASM_GLOBAL ASM_PFX(LoongArchException)
> +ASM_GLOBAL ASM_PFX(SetEbase)
> +ASM_GLOBAL ASM_PFX(LoongArchReadqCsrEuen)
> +ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrEuen)
> +
> +#
> +# Main exception handler. Not really a leaf routine but not a normal
> +# function either. Save away the entire cpu state end enter exception mode.
> +#
> +
> +ASM_PFX(Exception_handler):
> + csrrd SP, LOONGARCH_CSR_KS1
> +
> + addi.d T0, $r0, -0x10
> + and SP, SP, T0
> + addi.d SP, SP, -((CSR_NUM + BASE_NUM + FP_BASE_NUM) * RSIZE)
> +
> + st.d RA, SP, RA_NUM * RSIZE
> + st.d GP, SP, GP_NUM * RSIZE
> + st.d A0, SP, A0_NUM * RSIZE
> + st.d A1, SP, A1_NUM * RSIZE
> + st.d A2, SP, A2_NUM * RSIZE
> + st.d A3, SP, A3_NUM * RSIZE
> + st.d A4, SP, A4_NUM * RSIZE
> + st.d A5, SP, A5_NUM * RSIZE
> + st.d A6, SP, A6_NUM * RSIZE
> + st.d A7, SP, A7_NUM * RSIZE
> + st.d T1, SP, T1_NUM * RSIZE
> + st.d T2, SP, T2_NUM * RSIZE
> + st.d T3, SP, T3_NUM * RSIZE
> + st.d T4, SP, T4_NUM * RSIZE
> + st.d T5, SP, T5_NUM * RSIZE
> + st.d T6, SP, T6_NUM * RSIZE
> + st.d T7, SP, T7_NUM * RSIZE
> + st.d T8, SP, T8_NUM * RSIZE
> + st.d TP, SP, TP_NUM * RSIZE
> + st.d FP, SP, FP_NUM * RSIZE
> + st.d S0, SP, S0_NUM * RSIZE
> + st.d S1, SP, S1_NUM * RSIZE
> + st.d S2, SP, S2_NUM * RSIZE
> + st.d S3, SP, S3_NUM * RSIZE
> + st.d S4, SP, S4_NUM * RSIZE
> + st.d S5, SP, S5_NUM * RSIZE
> + st.d S6, SP, S6_NUM * RSIZE
> + st.d S7, SP, S7_NUM * RSIZE
> + st.d S8, SP, S8_NUM * RSIZE
> +
> + #
> + # save T0/SP from scratch registers on stack
> + #
> + csrrd T0, LOONGARCH_CSR_KS0
> + st.d T0, SP, T0_NUM * RSIZE
> + csrrd T0, LOONGARCH_CSR_KS1
> + st.d T0, SP, SP_NUM * RSIZE
> +
> + csrrd T0, LOONGARCH_CSR_CRMD
> + st.d T0, SP, (LOONGARCH_CSR_CRMD + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_PRMD
> + st.d T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_ECFG
> + st.d T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_ESTAT
> + st.d T0, SP, (LOONGARCH_CSR_ESTAT + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_EPC
> + st.d T0, SP, (LOONGARCH_CSR_EPC+ BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_BADV
> + st.d T0, SP, (LOONGARCH_CSR_BADV + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_BADI
> + st.d T0, SP, (LOONGARCH_CSR_BADI + BASE_NUM) * RSIZE
> + csrrd T0, LOONGARCH_CSR_EUEN
> + st.d T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
> +
> + #
> + # Save FPU context
> + #
> + ori T1, ZERO, CSR_EUEN_FPEN
> + and T2, T0, T1
> + beqz T2, 1f
> +
> + fst.d $f0, SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f1, SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f2, SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f3, SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f4, SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f5, SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f6, SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f7, SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f8, SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f9, SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
> + fst.d $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
> +
> + movfcsr2gr T3, FCSR0
> + st.d T3, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
> + movcf2gr T3, $fcc0
> + or T2, T3, ZERO
> + movcf2gr T3, $fcc1
> + bstrins.d T2, T3, 0xf, 0x8
> + movcf2gr T3, $fcc2
> + bstrins.d T2, T3, 0x17, 0x10
> + movcf2gr T3, $fcc3
> + bstrins.d T2, T3, 0x1f, 0x18
> + movcf2gr T3, $fcc4
> + bstrins.d T2, T3, 0x27, 0x20
> + movcf2gr T3, $fcc5
> + bstrins.d T2, T3, 0x2f, 0x28
> + movcf2gr T3, $fcc6
> + bstrins.d T2, T3, 0x37, 0x30
> + movcf2gr T3, $fcc7
> + bstrins.d T2, T3, 0x3f, 0x38
> + st.d T2, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
> +1:
> + or A0, SP, ZERO
> + bl CommonExceptionEntry
> + /*disable interrupt*/
> + li.d T0, (1 << 2)
> + csrxchg ZERO, T0, LOONGARCH_CSR_CRMD
> +
> + ld.d T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_PRMD
> + ld.d T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_ECFG
> + ld.d T0, SP, (LOONGARCH_CSR_EPC + BASE_NUM) * RSIZE
> + csrwr T0, LOONGARCH_CSR_EPC
> +
> + ld.d T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
> + ori T1, ZERO, CSR_EUEN_FPEN
> + and T2, T0, T1
> + beqz T2, 2f
> +
> + #
> + # check previous FP state
> + # restore FP contect if FP enabled
> + #
> + fld.d $f0, SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f1, SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f2, SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f3, SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f4, SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f5, SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f6, SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f7, SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f8, SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f9, SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
> + fld.d $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
> +
> + ld.d T0, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
> + movgr2fcsr FCSR0, T0
> + ld.d T0, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
> + bstrpick.d T1, T0, 7, 0
> + movgr2cf $fcc0, T1
> + bstrpick.d T1, T0, 15, 8
> + movgr2cf $fcc1, T1
> + bstrpick.d T1, T0, 23, 16
> + movgr2cf $fcc2, T1
> + bstrpick.d T1, T0, 31, 24
> + movgr2cf $fcc3, T1
> + bstrpick.d T1, T0, 39, 32
> + movgr2cf $fcc4, T1
> + bstrpick.d T1, T0, 47, 40
> + movgr2cf $fcc5, T1
> + bstrpick.d T1, T0, 55, 48
> + movgr2cf $fcc6, T1
> + bstrpick.d T1, T0, 63, 56
> + movgr2cf $fcc7, T1
> +2:
> + ld.d RA, SP, RA_NUM * RSIZE
> + ld.d GP, SP, GP_NUM * RSIZE
> + ld.d A0, SP, A0_NUM * RSIZE
> + ld.d A1, SP, A1_NUM * RSIZE
> + ld.d A2, SP, A2_NUM * RSIZE
> + ld.d A3, SP, A3_NUM * RSIZE
> + ld.d A4, SP, A4_NUM * RSIZE
> + ld.d A5, SP, A5_NUM * RSIZE
> + ld.d A6, SP, A6_NUM * RSIZE
> + ld.d A7, SP, A7_NUM * RSIZE
> + ld.d T0, SP, T0_NUM * RSIZE
> + ld.d T1, SP, T1_NUM * RSIZE
> + ld.d T2, SP, T2_NUM * RSIZE
> + ld.d T3, SP, T3_NUM * RSIZE
> + ld.d T4, SP, T4_NUM * RSIZE
> + ld.d T5, SP, T5_NUM * RSIZE
> + ld.d T6, SP, T6_NUM * RSIZE
> + ld.d T7, SP, T7_NUM * RSIZE
> + ld.d T8, SP, T8_NUM * RSIZE
> + ld.d TP, SP, TP_NUM * RSIZE
> + ld.d FP, SP, FP_NUM * RSIZE
> + ld.d S0, SP, S0_NUM * RSIZE
> + ld.d S1, SP, S1_NUM * RSIZE
> + ld.d S2, SP, S2_NUM * RSIZE
> + ld.d S3, SP, S3_NUM * RSIZE
> + ld.d S4, SP, S4_NUM * RSIZE
> + ld.d S5, SP, S5_NUM * RSIZE
> + ld.d S6, SP, S6_NUM * RSIZE
> + ld.d S7, SP, S7_NUM * RSIZE
> + ld.d S8, SP, S8_NUM * RSIZE
> +
> + ld.d SP, SP, SP_NUM * RSIZE
> + ertn
> +
> +#
> +# Exception trampoline copied down to RAM after initialization.
> +#
> +
> +ASM_PFX(LoongArchException):
> + csrwr T0, LOONGARCH_CSR_KS0
> + csrwr SP, LOONGARCH_CSR_KS1
> + pcaddi T0, 0
> + ld.d T0, T0, 16
> + jirl ZERO, T0, 0
> + nop
> +1:
> + .quad Exception_handler
> +.globl LoongArchExceptionEnd
> +LoongArchExceptionEnd:
> +
> +#
> +# Set Exception Base Address.
> +#
> +
> +ASM_PFX(SetEbase):
> + #
> + # clear Vint cofigure
> + # all exceptions share the same interrupt entry
> + #
> + csrrd T0, LOONGARCH_CSR_ECFG
> + li.d T1, ~0x70000
> + and T0, T0, T1
> + csrwr T0, LOONGARCH_CSR_ECFG
> +
> + # set ebase
> + csrwr A0, LOONGARCH_CSR_EBASE
> + jirl ZERO, RA, 0
> +
> +#
> +# Read Csr EUEN register.
> +# @param A0 Pointer to the variable used to store the EUEN register value
> +# @retval none
> +#
> +
> +ASM_PFX(LoongArchReadqCsrEuen):
> + csrrd T0, LOONGARCH_CSR_EUEN
> + stptr.d T0, A0, 0
> + jirl ZERO, RA,0
> +
> +#
> +# Write Csr EUEN register.
> +# @param A0 The value used to write to the EUEN register
> +# @retval none
> +#
> +
> +ASM_PFX(LoongArchWriteqCsrEuen):
> + csrwr A0, LOONGARCH_CSR_EUEN
> + jirl ZERO, RA,0
> --
> 2.31.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#96294): https://edk2.groups.io/g/devel/message/96294
Mute This Topic: https://groups.io/mt/94955175/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/edk2-devel-archive/attachments/20221111/0182f8c5/attachment-0001.htm>


More information about the edk2-devel-archive mailing list