206 lines
5.5 KiB
C
206 lines
5.5 KiB
C
/*++
|
|
|
|
Copyright (c) 2013-2017, ARM Ltd. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
--*/
|
|
|
|
#include "ArmGicDxe.h"
|
|
|
|
VOID
|
|
EFIAPI
|
|
IrqInterruptHandler (
|
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
|
IN EFI_SYSTEM_CONTEXT SystemContext
|
|
);
|
|
|
|
VOID
|
|
EFIAPI
|
|
ExitBootServicesEvent (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
);
|
|
|
|
// Making this global saves a few bytes in image size
|
|
EFI_HANDLE gHardwareInterruptHandle = NULL;
|
|
|
|
// Notifications
|
|
EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
|
|
|
|
// Maximum Number of Interrupts
|
|
UINTN mGicNumInterrupts = 0;
|
|
|
|
HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers = NULL;
|
|
|
|
|
|
/**
|
|
Calculate GICD_ICFGRn base address and corresponding bit
|
|
field Int_config[1] of the GIC distributor register.
|
|
|
|
@param Source Hardware source of the interrupt.
|
|
@param RegAddress Corresponding GICD_ICFGRn base address.
|
|
@param Config1Bit Bit number of F Int_config[1] bit in the register.
|
|
|
|
@retval EFI_SUCCESS Source interrupt supported.
|
|
@retval EFI_UNSUPPORTED Source interrupt is not supported.
|
|
**/
|
|
EFI_STATUS
|
|
GicGetDistributorIcfgBaseAndBit (
|
|
IN HARDWARE_INTERRUPT_SOURCE Source,
|
|
OUT UINTN *RegAddress,
|
|
OUT UINTN *Config1Bit
|
|
)
|
|
{
|
|
UINTN RegIndex;
|
|
UINTN Field;
|
|
|
|
if (Source >= mGicNumInterrupts) {
|
|
ASSERT(Source < mGicNumInterrupts);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
RegIndex = Source / ARM_GIC_ICDICFR_F_STRIDE; // NOTE: truncation is significant
|
|
Field = Source % ARM_GIC_ICDICFR_F_STRIDE;
|
|
*RegAddress = PcdGet64 (PcdGicDistributorBase)
|
|
+ ARM_GIC_ICDICFR
|
|
+ (ARM_GIC_ICDICFR_BYTES * RegIndex);
|
|
*Config1Bit = ((Field * ARM_GIC_ICDICFR_F_WIDTH)
|
|
+ ARM_GIC_ICDICFR_F_CONFIG1_BIT);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Register Handler for the specified interrupt source.
|
|
|
|
@param This Instance pointer for this protocol
|
|
@param Source Hardware source of the interrupt
|
|
@param Handler Callback for interrupt. NULL to unregister
|
|
|
|
@retval EFI_SUCCESS Source was updated to support Handler.
|
|
@retval EFI_DEVICE_ERROR Hardware could not be programmed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
RegisterInterruptSource (
|
|
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
|
|
IN HARDWARE_INTERRUPT_SOURCE Source,
|
|
IN HARDWARE_INTERRUPT_HANDLER Handler
|
|
)
|
|
{
|
|
if (Source >= mGicNumInterrupts) {
|
|
ASSERT(FALSE);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
gRegisteredInterruptHandlers[Source] = Handler;
|
|
|
|
// If the interrupt handler is unregistered then disable the interrupt
|
|
if (NULL == Handler){
|
|
return This->DisableInterruptSource (This, Source);
|
|
} else {
|
|
return This->EnableInterruptSource (This, Source);
|
|
}
|
|
}
|
|
|
|
STATIC VOID *mCpuArchProtocolNotifyEventRegistration;
|
|
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
CpuArchEventProtocolNotify (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_CPU_ARCH_PROTOCOL *Cpu;
|
|
EFI_STATUS Status;
|
|
|
|
// Get the CPU protocol that this driver requires.
|
|
Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
// Unregister the default exception handler.
|
|
Status = Cpu->RegisterInterruptHandler (Cpu, ARM_ARCH_EXCEPTION_IRQ, NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n",
|
|
__FUNCTION__, Status));
|
|
return;
|
|
}
|
|
|
|
// Register to receive interrupts
|
|
Status = Cpu->RegisterInterruptHandler (Cpu, ARM_ARCH_EXCEPTION_IRQ,
|
|
Context);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n",
|
|
__FUNCTION__, Status));
|
|
}
|
|
|
|
gBS->CloseEvent (Event);
|
|
}
|
|
|
|
EFI_STATUS
|
|
InstallAndRegisterInterruptService (
|
|
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *InterruptProtocol,
|
|
IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *Interrupt2Protocol,
|
|
IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler,
|
|
IN EFI_EVENT_NOTIFY ExitBootServicesEvent
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CONST UINTN RihArraySize =
|
|
(sizeof(HARDWARE_INTERRUPT_HANDLER) * mGicNumInterrupts);
|
|
|
|
// Initialize the array for the Interrupt Handlers
|
|
gRegisteredInterruptHandlers = AllocateZeroPool (RihArraySize);
|
|
if (gRegisteredInterruptHandlers == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&gHardwareInterruptHandle,
|
|
&gHardwareInterruptProtocolGuid,
|
|
InterruptProtocol,
|
|
&gHardwareInterrupt2ProtocolGuid,
|
|
Interrupt2Protocol,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Install the interrupt handler as soon as the CPU arch protocol appears.
|
|
//
|
|
EfiCreateProtocolNotifyEvent (
|
|
&gEfiCpuArchProtocolGuid,
|
|
TPL_CALLBACK,
|
|
CpuArchEventProtocolNotify,
|
|
InterruptHandler,
|
|
&mCpuArchProtocolNotifyEventRegistration);
|
|
|
|
// Register for an ExitBootServicesEvent
|
|
Status = gBS->CreateEvent (
|
|
EVT_SIGNAL_EXIT_BOOT_SERVICES,
|
|
TPL_NOTIFY,
|
|
ExitBootServicesEvent,
|
|
NULL,
|
|
&EfiExitBootServicesEvent
|
|
);
|
|
|
|
return Status;
|
|
}
|