/** @file | |
RISC-V Exception Handler library implementation. | |
Copyright (c) 2016 - 2022, Hewlett Packard Enterprise Development LP. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiPei.h> | |
#include <Library/CpuExceptionHandlerLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/SerialPortLib.h> | |
#include <Library/PrintLib.h> | |
#include <Register/RiscV64/RiscVEncoding.h> | |
#include "CpuExceptionHandlerLib.h" | |
// | |
// Define the maximum message length | |
// | |
#define MAX_DEBUG_MESSAGE_LENGTH 0x100 | |
STATIC EFI_CPU_INTERRUPT_HANDLER mExceptionHandlers[EXCEPT_RISCV_MAX_EXCEPTIONS + 1]; | |
STATIC EFI_CPU_INTERRUPT_HANDLER mIrqHandlers[EXCEPT_RISCV_MAX_IRQS + 1]; | |
STATIC CONST CHAR8 mExceptionOrIrqUnknown[] = "Unknown"; | |
STATIC CONST CHAR8 *mExceptionNameStr[EXCEPT_RISCV_MAX_EXCEPTIONS + 1] = { | |
"EXCEPT_RISCV_INST_MISALIGNED", | |
"EXCEPT_RISCV_INST_ACCESS_FAULT", | |
"EXCEPT_RISCV_ILLEGAL_INST", | |
"EXCEPT_RISCV_BREAKPOINT", | |
"EXCEPT_RISCV_LOAD_ADDRESS_MISALIGNED", | |
"EXCEPT_RISCV_LOAD_ACCESS_FAULT", | |
"EXCEPT_RISCV_STORE_AMO_ADDRESS_MISALIGNED", | |
"EXCEPT_RISCV_STORE_AMO_ACCESS_FAULT", | |
"EXCEPT_RISCV_ENV_CALL_FROM_UMODE", | |
"EXCEPT_RISCV_ENV_CALL_FROM_SMODE", | |
"EXCEPT_RISCV_ENV_CALL_FROM_VS_MODE", | |
"EXCEPT_RISCV_ENV_CALL_FROM_MMODE", | |
"EXCEPT_RISCV_INST_ACCESS_PAGE_FAULT", | |
"EXCEPT_RISCV_LOAD_ACCESS_PAGE_FAULT", | |
"EXCEPT_RISCV_14", | |
"EXCEPT_RISCV_STORE_ACCESS_PAGE_FAULT", | |
"EXCEPT_RISCV_16", | |
"EXCEPT_RISCV_17", | |
"EXCEPT_RISCV_18", | |
"EXCEPT_RISCV_19", | |
"EXCEPT_RISCV_INST_GUEST_PAGE_FAULT", | |
"EXCEPT_RISCV_LOAD_GUEST_PAGE_FAULT", | |
"EXCEPT_RISCV_VIRTUAL_INSTRUCTION", | |
"EXCEPT_RISCV_STORE_GUEST_PAGE_FAULT" | |
}; | |
STATIC CONST CHAR8 *mIrqNameStr[EXCEPT_RISCV_MAX_IRQS + 1] = { | |
"EXCEPT_RISCV_IRQ_0", | |
"EXCEPT_RISCV_IRQ_SOFT_FROM_SMODE", | |
"EXCEPT_RISCV_IRQ_SOFT_FROM_VSMODE", | |
"EXCEPT_RISCV_IRQ_SOFT_FROM_MMODE", | |
"EXCEPT_RISCV_IRQ_4", | |
"EXCEPT_RISCV_IRQ_TIMER_FROM_SMODE", | |
}; | |
/** | |
Prints a message to the serial port. | |
@param Format Format string for the message to print. | |
@param ... Variable argument list whose contents are accessed | |
based on the format string specified by Format. | |
**/ | |
STATIC | |
VOID | |
EFIAPI | |
InternalPrintMessage ( | |
IN CONST CHAR8 *Format, | |
... | |
) | |
{ | |
CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH]; | |
VA_LIST Marker; | |
// | |
// Convert the message to an ASCII String | |
// | |
VA_START (Marker, Format); | |
AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker); | |
VA_END (Marker); | |
// | |
// Send the print string to a Serial Port | |
// | |
SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer)); | |
} | |
/** | |
Get ASCII format string exception name by exception type. | |
@param ExceptionType Exception type. | |
@return ASCII format string exception name. | |
**/ | |
STATIC | |
CONST CHAR8 * | |
GetExceptionNameStr ( | |
IN EFI_EXCEPTION_TYPE ExceptionType | |
) | |
{ | |
if (EXCEPT_RISCV_IS_IRQ (ExceptionType)) { | |
if (EXCEPT_RISCV_IRQ_INDEX (ExceptionType) > EXCEPT_RISCV_MAX_IRQS) { | |
return mExceptionOrIrqUnknown; | |
} | |
return mIrqNameStr[EXCEPT_RISCV_IRQ_INDEX (ExceptionType)]; | |
} | |
if (ExceptionType > EXCEPT_RISCV_MAX_EXCEPTIONS) { | |
return mExceptionOrIrqUnknown; | |
} | |
return mExceptionNameStr[ExceptionType]; | |
} | |
/** | |
Display CPU information. This can be called by 3rd-party handlers | |
set by RegisterCpuInterruptHandler. | |
@param ExceptionType Exception type. | |
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT. | |
**/ | |
VOID | |
EFIAPI | |
DumpCpuContext ( | |
IN EFI_EXCEPTION_TYPE ExceptionType, | |
IN EFI_SYSTEM_CONTEXT SystemContext | |
) | |
{ | |
UINTN Printed; | |
SMODE_TRAP_REGISTERS *Regs; | |
Printed = 0; | |
Regs = (SMODE_TRAP_REGISTERS *)SystemContext.SystemContextRiscV64; | |
InternalPrintMessage ( | |
"!!!! RISCV64 Exception Type - %016x(%a) !!!!\n", | |
ExceptionType, | |
GetExceptionNameStr (ExceptionType) | |
); | |
DEBUG_CODE_BEGIN (); | |
#define REGS() \ | |
REG (t0); REG (t1); REG (t2); REG (t3); REG (t4); REG (t5); REG (t6); \ | |
REG (s0); REG (s1); REG (s2); REG (s3); REG (s4); REG (s5); REG (s6); \ | |
REG (s7); REG (s8); REG (s9); REG (s10); REG (s11); \ | |
REG (a0); REG (a1); REG (a2); REG (a3); REG (a4); REG (a5); REG (a6); \ | |
REG (a7); \ | |
REG (zero); REG (ra); REG (sp); REG (gp); REG (tp); \ | |
REG (sepc); REG (sstatus); REG (stval); | |
#define REG(x) do { \ | |
InternalPrintMessage ("%7a = 0x%017lx%c", #x, Regs->x, \ | |
(++Printed % 2) ? L'\t' : L'\n'); \ | |
} while (0); | |
REGS (); | |
if (Printed % 2 != 0) { | |
InternalPrintMessage ("\n"); | |
} | |
#undef REG | |
#undef REGS | |
DEBUG_CODE_END (); | |
} | |
/** | |
Initializes all CPU exceptions entries and provides the default exception handlers. | |
Caller should try to get an array of interrupt and/or exception vectors that are in use and need to | |
persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. | |
If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. | |
If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. | |
@param[in] VectorInfo Pointer to reserved vector list. | |
@retval EFI_SUCCESS CPU Exception Entries have been successfully initialized | |
with default exception handlers. | |
@retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. | |
@retval EFI_UNSUPPORTED This function is not supported. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InitializeCpuExceptionHandlers ( | |
IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL | |
) | |
{ | |
RiscVSetSupervisorStvec ((UINT64)SupervisorModeTrap); | |
return EFI_SUCCESS; | |
} | |
/** | |
Registers a function to be called from the processor interrupt handler. | |
This function registers and enables the handler specified by InterruptHandler for a processor | |
interrupt or exception type specified by ExceptionType. If InterruptHandler is NULL, then the | |
handler for the processor interrupt or exception type specified by ExceptionType is uninstalled. | |
The installed handler is called once for each processor interrupt or exception. | |
NOTE: This function should be invoked after InitializeCpuExceptionHandlers() or | |
InitializeCpuInterruptHandlers() invoked, otherwise EFI_UNSUPPORTED returned. | |
@param[in] ExceptionType Defines which interrupt or exception to hook. | |
@param[in] 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 ExceptionType was | |
previously installed. | |
@retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for ExceptionType was not | |
previously installed. | |
@retval EFI_UNSUPPORTED The interrupt specified by ExceptionType is not supported, | |
or this function is not supported. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RegisterCpuInterruptHandler ( | |
IN EFI_EXCEPTION_TYPE ExceptionType, | |
IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler | |
) | |
{ | |
DEBUG ((DEBUG_INFO, "%a: Type:%x Handler: %x\n", __func__, ExceptionType, InterruptHandler)); | |
if (EXCEPT_RISCV_IS_IRQ (ExceptionType)) { | |
if (EXCEPT_RISCV_IRQ_INDEX (ExceptionType) > EXCEPT_RISCV_MAX_IRQS) { | |
return EFI_UNSUPPORTED; | |
} | |
if (mIrqHandlers[EXCEPT_RISCV_IRQ_INDEX (ExceptionType)] != NULL) { | |
return EFI_ALREADY_STARTED; | |
} else if (InterruptHandler == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
mIrqHandlers[EXCEPT_RISCV_IRQ_INDEX (ExceptionType)] = InterruptHandler; | |
} else { | |
if (ExceptionType > EXCEPT_RISCV_MAX_EXCEPTIONS) { | |
return EFI_UNSUPPORTED; | |
} | |
if (mExceptionHandlers[ExceptionType] != NULL) { | |
return EFI_ALREADY_STARTED; | |
} else if (InterruptHandler == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
mExceptionHandlers[ExceptionType] = InterruptHandler; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Setup separate stacks for certain exception handlers. | |
If the input Buffer and BufferSize are both NULL, use global variable if possible. | |
@param[in] Buffer Point to buffer used to separate exception stack. | |
@param[in, out] BufferSize On input, it indicates the byte size of Buffer. | |
If the size is not enough, the return status will | |
be EFI_BUFFER_TOO_SMALL, and output BufferSize | |
will be the size it needs. | |
@retval EFI_SUCCESS The stacks are assigned successfully. | |
@retval EFI_UNSUPPORTED This function is not supported. | |
@retval EFI_BUFFER_TOO_SMALL This BufferSize is too small. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InitializeSeparateExceptionStacks ( | |
IN VOID *Buffer, | |
IN OUT UINTN *BufferSize | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Supervisor mode trap handler. | |
@param[in] SmodeTrapReg Registers before trap occurred. | |
**/ | |
VOID | |
RiscVSupervisorModeTrapHandler ( | |
SMODE_TRAP_REGISTERS *SmodeTrapReg | |
) | |
{ | |
EFI_EXCEPTION_TYPE ExceptionType; | |
EFI_SYSTEM_CONTEXT RiscVSystemContext; | |
UINTN IrqIndex; | |
RiscVSystemContext.SystemContextRiscV64 = (EFI_SYSTEM_CONTEXT_RISCV64 *)SmodeTrapReg; | |
ExceptionType = (UINTN)RiscVGetSupervisorTrapCause (); | |
if (EXCEPT_RISCV_IS_IRQ (ExceptionType)) { | |
IrqIndex = EXCEPT_RISCV_IRQ_INDEX (ExceptionType); | |
if ((IrqIndex <= EXCEPT_RISCV_MAX_IRQS) && | |
(mIrqHandlers[IrqIndex] != NULL)) | |
{ | |
mIrqHandlers[IrqIndex](ExceptionType, RiscVSystemContext); | |
return; | |
} | |
} else { | |
if ((ExceptionType <= EXCEPT_RISCV_MAX_EXCEPTIONS) && | |
(mExceptionHandlers[ExceptionType] != 0)) | |
{ | |
mExceptionHandlers[ExceptionType](ExceptionType, RiscVSystemContext); | |
return; | |
} | |
} | |
DumpCpuContext (ExceptionType, RiscVSystemContext); | |
CpuDeadLoop (); | |
} |