/*++ Copyright (c) 2013-2017, ARM Ltd. All rights reserved.
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; }