| /*++ | |
| Copyright (c) 2013-2023, Arm Ltd. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| --*/ | |
| #include "ArmGicDxe.h" | |
| // 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 = (UINTN)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", | |
| __func__, | |
| 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", | |
| __func__, | |
| 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; | |
| } |