diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec index 04252dab06..ab3c3b365a 100644 --- a/ArmPkg/ArmPkg.dec +++ b/ArmPkg/ArmPkg.dec @@ -303,6 +303,7 @@ gArmTokenSpaceGuid.PcdGicRedistributorsBase|0|UINT64|0x0000000E gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase|0|UINT64|0x0000000D gArmTokenSpaceGuid.PcdGicSgiIntId|0|UINT32|0x00000025 + gArmTokenSpaceGuid.PcdGicIrsConfigFrameBase|0|UINT64|0x00000064 # # Bases, sizes and translation offsets of IO and MMIO spaces, respectively. diff --git a/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.c b/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.c index b0da48e3a3..ecee83fdc5 100644 --- a/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.c +++ b/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.c @@ -74,7 +74,7 @@ InterruptDxeInitialize ( { EFI_STATUS Status; - if (!GicV3Supported ()) { + if (!GicV3Supported () && !ArmHasGicV5SystemRegisters ()) { Status = GicV2DxeInitialize (ImageHandle, SystemTable); } else { Status = GicV3DxeInitialize (ImageHandle, SystemTable); diff --git a/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.h b/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.h index dba0d322c7..610792f8b4 100644 --- a/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.h +++ b/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.h @@ -37,6 +37,12 @@ GicV3DxeInitialize ( IN EFI_SYSTEM_TABLE *SystemTable ); +// GicV5 API +EFI_STATUS +GicV5DxeInitialize ( + VOID + ); + // Shared code EFI_STATUS diff --git a/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.inf b/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.inf index 688993b89d..6d4d784560 100644 --- a/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.inf +++ b/ArmPkg/Drivers/ArmGicDxe/ArmGicDxe.inf @@ -26,9 +26,12 @@ [Sources.ARM] GicV3/Arm/ArmGicV3.S | GCC + GicV5/Arm/ArmGicV5.c [Sources.AARCH64] GicV3/AArch64/ArmGicV3.S + GicV5/AArch64/ArmGicV5.S + GicV5/ArmGicV5Dxe.c [Packages] MdePkg/MdePkg.dec @@ -46,6 +49,7 @@ UefiDriverEntryPoint IoLib PcdLib + TimerLib UefiLib [Protocols] @@ -57,6 +61,7 @@ gArmTokenSpaceGuid.PcdGicDistributorBase gArmTokenSpaceGuid.PcdGicRedistributorsBase gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase + gArmTokenSpaceGuid.PcdGicIrsConfigFrameBase [Depex] gEfiCpuArchProtocolGuid diff --git a/ArmPkg/Drivers/ArmGicDxe/ArmGicV3Dxe.inf b/ArmPkg/Drivers/ArmGicDxe/ArmGicV3Dxe.inf index c177b27460..05a5498977 100644 --- a/ArmPkg/Drivers/ArmGicDxe/ArmGicV3Dxe.inf +++ b/ArmPkg/Drivers/ArmGicDxe/ArmGicV3Dxe.inf @@ -22,6 +22,8 @@ ArmGicDxe.h GicV3/ArmGicV3Dxe.c GicV3/AArch64/ArmGicV3.S + GicV5/ArmGicV5Dxe.c + GicV5/AArch64/ArmGicV5.S [Packages] MdePkg/MdePkg.dec @@ -35,6 +37,7 @@ IoLib PcdLib PrintLib + TimerLib UefiBootServicesTableLib UefiDriverEntryPoint UefiLib @@ -46,6 +49,7 @@ [Pcd.common] gArmTokenSpaceGuid.PcdGicDistributorBase + gArmTokenSpaceGuid.PcdGicIrsConfigFrameBase gArmTokenSpaceGuid.PcdGicRedistributorsBase [Depex] diff --git a/ArmPkg/Drivers/ArmGicDxe/GicV3/ArmGicV3Dxe.c b/ArmPkg/Drivers/ArmGicDxe/GicV3/ArmGicV3Dxe.c index 8aed686e3f..5fd28e3f57 100644 --- a/ArmPkg/Drivers/ArmGicDxe/GicV3/ArmGicV3Dxe.c +++ b/ArmPkg/Drivers/ArmGicDxe/GicV3/ArmGicV3Dxe.c @@ -771,6 +771,10 @@ GicV3DxeInitialize ( Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpuArch); ASSERT_EFI_ERROR (Status); + if (ArmHasGicV5SystemRegisters ()) { + return GicV5DxeInitialize (); + } + mGicDistributorBase = (UINTN)PcdGet64 (PcdGicDistributorBase); Status = gCpuArch->SetMemoryAttributes (gCpuArch, mGicDistributorBase, GICD_V3_SIZE, EFI_MEMORY_UC | EFI_MEMORY_XP); diff --git a/ArmPkg/Drivers/ArmGicDxe/GicV5/AArch64/ArmGicV5.S b/ArmPkg/Drivers/ArmGicDxe/GicV5/AArch64/ArmGicV5.S new file mode 100644 index 0000000000..9263fd8500 --- /dev/null +++ b/ArmPkg/Drivers/ArmGicDxe/GicV5/AArch64/ArmGicV5.S @@ -0,0 +1,182 @@ +# +# Copyright (c) 2025, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# + +#include + +#define ICC_CR0_EL1 S3_1_C12_C0_1 + +#define ICC_ICSR_EL1 S3_0_C12_C10_4 + +#define ICC_PCR_EL1 S3_1_C12_C0_2 + +#define ICC_PPI_ENABLER0_EL1 S3_0_C12_C10_6 +#define ICC_PPI_ENABLER1_EL1 S3_0_C12_C10_7 + +#define ICC_PPI_HMR0_EL1 S3_0_C12_C10_0 +#define ICC_PPI_HMR1_EL1 S3_0_C12_C10_1 + +#define GICR_CDIA S1_0_C12_C3_0 +#define GIC_CDRCFG S1_0_C12_C1_5 +#define GIC_CDDI S1_0_C12_C2_0 +#define GIC_CDDIS S1_0_C12_C1_0 +#define GIC_CDEN S1_0_C12_C1_1 +#define GIC_CDEOI #0, C12, C1, #7 + +#define IRS_CR0 0x100 +#define IRS_IST_BASER 0x148 + +//UINT64 +//EFIAPI +//ArmGicV5GetPpiEnabler0 ( +// VOID +// ); +ASM_FUNC(ArmGicV5GetPpiEnabler0) + mrs x0, ICC_PPI_ENABLER0_EL1 + ret + +//UINT64 +//EFIAPI +//ArmGicV5GetPpiEnabler1 ( +// VOID +// ); +ASM_FUNC(ArmGicV5GetPpiEnabler1) + mrs x0, ICC_PPI_ENABLER1_EL1 + ret + +//VOID +//EFIAPI +//ArmGicV5SetPpiEnabler0 ( +// IN UINT64 InterruptMask +// ); +ASM_FUNC(ArmGicV5SetPpiEnabler0) + msr ICC_PPI_ENABLER0_EL1, x0 + ret + +//VOID +//EFIAPI +//ArmGicV5SetPpiEnabler1 ( +// IN UINT64 InterruptMask +// ); +ASM_FUNC(ArmGicV5SetPpiEnabler1) + msr ICC_PPI_ENABLER1_EL1, x0 + ret + +//UINT64 +//EFIAPI +//ArmGicV5GetPPIHMR0 ( +// VOID +// ); +ASM_FUNC(ArmGicV5GetPPIHMR0) + mrs x0, ICC_PPI_HMR0_EL1 + ret + +//UINT64 +//EFIAPI +//ArmGicV5GetPPIHMR1 ( +// VOID +// ); +ASM_FUNC(ArmGicV5GetPPIHMR1) + mrs x0, ICC_PPI_HMR1_EL1 + ret + +//VOID +//EFIAPI +//ArmGicV5SpiEnable ( +// IN UINT32 SpiId +// ); +ASM_FUNC(ArmGicV5SpiEnable) + orr x0, x0, #0x60000000 + msr GIC_CDEN, x0 + ret + +//VOID +//EFIAPI +//ArmGicV5SpiDisable ( +// IN UINT32 SpiId +// ); +ASM_FUNC(ArmGicV5SpiDisable) + orr x0, x0, #0x60000000 + msr GIC_CDDIS, x0 + ret + +//UINT64 +//EFIAPI +//ArmGicV5ReadInterruptConfig ( +// IN UINT32 InterruptId +// ); +ASM_FUNC(ArmGicV5ReadInterruptConfig) + msr GIC_CDRCFG, x0 + isb + mrs x0, ICC_ICSR_EL1 + ret + + +//VOID +//EFIAPI +//ArmGicV5EnableInterruptInterface ( +// UINT64 IrsConfigFrameBase +// ); +ASM_FUNC(ArmGicV5EnableInterruptInterface) + mov x1, #1 + msr ICC_CR0_EL1, x1 + mov x1, #0 + msr ICC_PCR_EL1, x1 + + mov x1, #0 + str x1, [x0, #IRS_CR0] + isb + + str x1, [x0, #IRS_IST_BASER] + isb + + mov x1, #1 + str x1, [x0, #IRS_CR0] + isb + + ret + +//VOID +//EFIAPI +//ArmGicV5DisableInterruptInterface ( +// UINT64 IrsConfigFrameBase +// ); +ASM_FUNC(ArmGicV5DisableInterruptInterface) + mov x1, #0 + str x1, [x0, #IRS_CR0] + isb + + mov x1, #0 + msr ICC_CR0_EL1, x1 + + ret + +//VOID +//EFIAPI +//ArmGicV5DeactivateInterrupt ( +// IN UINTN InterruptId +// ); +ASM_FUNC(ArmGicV5DeactivateInterrupt) + msr GIC_CDDI, x0 + ret + +//VOID +//EFIAPI +//ArmGicV5EndOfInterrupt ( +// VOID +// ); +ASM_FUNC(ArmGicV5EndOfInterrupt) + sys GIC_CDEOI + ret + +//UINTN +//EFIAPI +//ArmGicV5AcknowledgeInterrupt ( +// VOID +// ); +ASM_FUNC(ArmGicV5AcknowledgeInterrupt) + mrs x0, GICR_CDIA + ret diff --git a/ArmPkg/Drivers/ArmGicDxe/GicV5/Arm/ArmGicV5.c b/ArmPkg/Drivers/ArmGicDxe/GicV5/Arm/ArmGicV5.c new file mode 100644 index 0000000000..ad845d27b6 --- /dev/null +++ b/ArmPkg/Drivers/ArmGicDxe/GicV5/Arm/ArmGicV5.c @@ -0,0 +1,25 @@ +/*++ + +Copyright (c) 2025, ARM Limited. All rights reserved. + +SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + ArmGicV5.c + +Abstract: + + Driver implementing the GICv5 interrupt controller protocol + +--*/ + +#include "ArmGicDxe.h" + +EFI_STATUS +GicV5DxeInitialize ( + VOID + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/ArmPkg/Drivers/ArmGicDxe/GicV5/ArmGicV5.h b/ArmPkg/Drivers/ArmGicDxe/GicV5/ArmGicV5.h new file mode 100644 index 0000000000..294be79d2d --- /dev/null +++ b/ArmPkg/Drivers/ArmGicDxe/GicV5/ArmGicV5.h @@ -0,0 +1,186 @@ +/** @file +* +* Copyright (c) 2025, Arm Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARMGICV5_H_ +#define ARMGICV5_H_ + +#define GICV5_IRS_CONFIG_FRAME_SIZE SIZE_64KB + +#define GICV5_IRS_IDR5 0x14 +#define GICV5_IRS_IDR6 0x18 +#define GICV5_IRS_IDR7 0x1c +#define GICV5_IRS_SPI_SELR 0x108 +#define GICV5_IRS_SPI_CFGR 0x114 +#define GICV5_IRS_SPI_STATUSR 0x118 + +#define GICV5_IRS_SPI_STATUSR_IDLE (1 << 0) +#define GICV5_IRS_SPI_STATUSR_V (1 << 1) + +#define GICV5_PPI_EDGE_TRIGGER 0 +#define GICV5_PPI_LEVEL_TRIGGER 1 + +#define GICV5_INTERRUPT_ID_MASK 0xFFFFFF +#define GICV5_INTERRUPT_TYPE_MASK (7 << 29) + +#define GICV5_INTERRUPT_ID_VALID (1ull << 32) + +#define GICV5_INTERRUPT_TYPE_PPI (1 << 29) +#define GICV5_INTERRUPT_TYPE_SPI (3 << 29) + +#define GICV5_NUM_PPI_INTERRUPTS 128 + +#define GICV5_IRS_IDLE_TIMEOUT_MS 10 + +/** + Read GICv5 ICC_PPI_ENABLER0_EL1 system register. This provides the interrupt + enable mask for PPIs 0-63. + + @return Value of ICC_PPI_ENABLER0_EL1 +**/ +UINT64 +EFIAPI +ArmGicV5GetPpiEnabler0 ( + VOID + ); + +/** + Read GICv5 ICC_PPI_ENABLER1_EL1 system register.. This provides the interrupt + enable mask for PPIs 64-127. + + @return Value of ICC_PPI_ENABLER1_EL1 +**/ +UINT64 +EFIAPI +ArmGicV5GetPpiEnabler1 ( + VOID + ); + +/** + Write to GICv5 ICC_PPI_ENABLER0_EL1 system register. This provides the + interrupt enable mask for PPIs 0-63. + + @param InterruptMask New interrupt mask to write +**/ +VOID +EFIAPI +ArmGicV5SetPpiEnabler0 ( + IN UINT64 InterruptMask + ); + +/** + Write to GICv5 ICC_PPI_ENABLER1_EL1 system register. This provides the + interrupt enable mask for PPIs 64-127. + + @param InterruptMask New interrupt mask to write +**/ +VOID +EFIAPI +ArmGicV5SetPpiEnabler1 ( + IN UINT64 InterruptMask + ); + +/** + Read GICv5 ICC_PPI_HMR0_EL1 system register. This provides the interrupt + trigger type for PPIs 0-63. + + @return Value of ICC_PPI_HMR0_EL1 +**/ +UINT64 +EFIAPI +ArmGicV5GetPPIHMR0 ( + VOID + ); + +/** + Read GICv5 ICC_PPI_HMR1_EL1 system register. This provides the interrupt + trigger type for PPIs 64-127. + + @return Value of ICC_PPI_HMR1_EL1 +**/ +UINT64 +EFIAPI +ArmGicV5GetPPIHMR1 ( + VOID + ); + +/** + + @param SpiId ID of SPI to enable +**/ +VOID +EFIAPI +ArmGicV5SpiEnable ( + IN UINT32 SpiId + ); + +/** + + @param SpiId ID of SPI to disable +**/ +VOID +EFIAPI +ArmGicV5SpiDisable ( + IN UINT32 SpiId + ); + +UINT64 +EFIAPI +ArmGicV5ReadInterruptConfig ( + IN UINT32 InterruptId + ); + +/** + Enable GICv5 interrupt interface. +**/ +VOID +EFIAPI +ArmGicV5EnableInterruptInterface ( + UINT64 IrsConfigFrameBase + ); + +/** + Disable GICv5 interrupt interface. +**/ +VOID +EFIAPI +ArmGicV5DisableInterruptInterface ( + UINT64 IrsConfigFrameBase + ); + +/** + Clear the active state of the given GICv5 interrupt. + + @param InterruptId The INTID of the interrupt to deactivate +**/ +VOID +EFIAPI +ArmGicV5DeactivateInterrupt ( + IN UINTN InterruptId + ); + +/** + Signal End of Interrupt, causing a priority drop of the running priority at + the CPU interface in the current interrupt domain. +**/ +VOID +EFIAPI +ArmGicV5EndOfInterrupt ( + VOID + ); + +/** + Acknowledge the highest priority pending interrupt (HPPI) in the current + interrupt domain. +**/ +UINTN +EFIAPI +ArmGicV5AcknowledgeInterrupt ( + VOID + ); + +#endif // ARMGICV5_H_ diff --git a/ArmPkg/Drivers/ArmGicDxe/GicV5/ArmGicV5Dxe.c b/ArmPkg/Drivers/ArmGicDxe/GicV5/ArmGicV5Dxe.c new file mode 100644 index 0000000000..9a94baaece --- /dev/null +++ b/ArmPkg/Drivers/ArmGicDxe/GicV5/ArmGicV5Dxe.c @@ -0,0 +1,620 @@ +/*++ + +Copyright (c) 2025, ARM Limited. All rights reserved. + +SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + ArmGicV5Dxe.c + +Abstract: + + Driver implementing the GICv5 interrupt controller protocol + +--*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "ArmGicDxe.h" +#include "ArmGicV5.h" + +STATIC UINTN mGicIrsConfigFrameBase; +STATIC UINT32 SpiRange; +STATIC UINTN mGicNumInterrupts; +STATIC HARDWARE_INTERRUPT_HANDLER *mRegisteredInterruptHandlers; + +STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL mHardwareInterruptV5Protocol; + +/** + Wait for IRS SPI writes to complete. + + @retval TRUE Writes have completed successfully. + @retval FALSE Writes have timed out. +**/ +STATIC +BOOLEAN +WaitForIrsSpiIdle ( + ) +{ + UINT32 Timeout = GICV5_IRS_IDLE_TIMEOUT_MS; + + while (!(MmioRead32 (mGicIrsConfigFrameBase + GICV5_IRS_SPI_STATUSR) & GICV5_IRS_SPI_STATUSR_IDLE) && (Timeout > 0)) { + MicroSecondDelay (1); + Timeout--; + } + + if (!(MmioRead32 (mGicIrsConfigFrameBase + GICV5_IRS_SPI_STATUSR) & GICV5_IRS_SPI_STATUSR_IDLE)) { + DEBUG ((DEBUG_ERROR, "WaitForIrsSpiIdle: timed out\n")); + return FALSE; + } + + return TRUE; +} + +/** + Wait for IRS SPI writes to complete, and return valid status. + + @retval TRUE Writes have completed successfully, register status is valid + @retval FALSE Writes have timed out, or register status is invalid. +**/ +STATIC +BOOLEAN +WaitForIrsSpiIdleValid ( + ) +{ + if (!WaitForIrsSpiIdle ()) { + return FALSE; + } + + if (!(MmioRead32 (mGicIrsConfigFrameBase + GICV5_IRS_SPI_STATUSR) & GICV5_IRS_SPI_STATUSR_V)) { + DEBUG ((DEBUG_ERROR, "WaitForIrsSpiIdle: status invalid\n")); + return FALSE; + } + + return TRUE; +} + +/** + Enable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt enabled. + @retval EFI_UNSUPPORTED Invalid interrupt source number. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV5EnableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + UINT32 InterruptType = Source & GICV5_INTERRUPT_TYPE_MASK; + UINT32 InterruptId = Source & GICV5_INTERRUPT_ID_MASK; + + if (InterruptType == GICV5_INTERRUPT_TYPE_SPI) { + ArmGicV5SpiEnable (Source); + } else if (InterruptType == GICV5_INTERRUPT_TYPE_PPI) { + if (InterruptId >= GICV5_NUM_PPI_INTERRUPTS) { + DEBUG ((DEBUG_ERROR, "GicV5EnableInterruptSource: invalid PPI number\n")); + ASSERT (InterruptId < GICV5_NUM_PPI_INTERRUPTS); + return EFI_UNSUPPORTED; + } else if (InterruptId >= 64) { + UINT64 mask = ArmGicV5GetPpiEnabler1 (); + mask |= (1ull << (InterruptId - 64)); + ArmGicV5SetPpiEnabler1 (mask); + } else { + UINT64 mask = ArmGicV5GetPpiEnabler0 (); + mask |= (1ull << InterruptId); + ArmGicV5SetPpiEnabler0 (mask); + } + } else { + DEBUG ((DEBUG_ERROR, "GicV5EnableInterruptSource: invalid interrupt type\n")); + ASSERT (InterruptType == GICV5_INTERRUPT_TYPE_SPI || InterruptType == GICV5_INTERRUPT_TYPE_PPI); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Disable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt enabled. + @retval EFI_UNSUPPORTED Invalid interrupt source number. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV5DisableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + UINT32 InterruptType = Source & GICV5_INTERRUPT_TYPE_MASK; + UINT32 InterruptId = Source & GICV5_INTERRUPT_ID_MASK; + + if (InterruptType == GICV5_INTERRUPT_TYPE_SPI) { + ArmGicV5SpiDisable (Source); + } else if (InterruptType == GICV5_INTERRUPT_TYPE_PPI) { + if (InterruptId >= GICV5_NUM_PPI_INTERRUPTS) { + DEBUG ((DEBUG_ERROR, "GicV5DisableInterruptSource: invalid PPI number\n")); + ASSERT (InterruptId < GICV5_NUM_PPI_INTERRUPTS); + return EFI_UNSUPPORTED; + } else if (InterruptId >= 64) { + UINT64 mask = ArmGicV5GetPpiEnabler1 (); + mask &= ~(1ull << (InterruptId - 64)); + ArmGicV5SetPpiEnabler1 (mask); + } else { + UINT64 mask = ArmGicV5GetPpiEnabler0 (); + mask &= ~(1ull << InterruptId); + ArmGicV5SetPpiEnabler0 (mask); + } + } else { + DEBUG ((DEBUG_ERROR, "GicV5DisableInterruptSource: invalid interrupt type\n")); + ASSERT (InterruptType == GICV5_INTERRUPT_TYPE_SPI || InterruptType == GICV5_INTERRUPT_TYPE_PPI); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Return current state of interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + @param InterruptState TRUE: source enabled, FALSE: source disabled. + + @retval EFI_SUCCESS Source interrupt enabled. + @retval EFI_UNSUPPORTED Invalid interrupt source number. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV5GetInterruptSourceState ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN BOOLEAN *InterruptState + ) +{ + UINT32 InterruptType = Source & GICV5_INTERRUPT_TYPE_MASK; + UINT32 InterruptId = Source & GICV5_INTERRUPT_ID_MASK; + + UINT64 Mask; + UINT64 Status; + + if (InterruptType == GICV5_INTERRUPT_TYPE_SPI) { + Status = ArmGicV5ReadInterruptConfig (Source); + + if (Status & 1) { + ASSERT (!(Status & 1)); + return EFI_UNSUPPORTED; + } + + *InterruptState = (Status & 4) ? 1 : 0; + } else if (InterruptType == GICV5_INTERRUPT_TYPE_PPI) { + if (InterruptId >= GICV5_NUM_PPI_INTERRUPTS) { + DEBUG ((DEBUG_ERROR, "GicV5GetInterruptSourceState: invalid PPI number\n")); + ASSERT (InterruptId < GICV5_NUM_PPI_INTERRUPTS); + return EFI_UNSUPPORTED; + } else if (InterruptId >= 64) { + Mask = ArmGicV5GetPpiEnabler1 (); + } else { + Mask = ArmGicV5GetPpiEnabler0 (); + } + + *InterruptState = (Mask >> (InterruptId & 63)) & 1; + } else { + DEBUG ((DEBUG_ERROR, "GicV5GetInterruptSourceState: invalid interrupt type\n")); + ASSERT (InterruptType == GICV5_INTERRUPT_TYPE_SPI || InterruptType == GICV5_INTERRUPT_TYPE_PPI); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Signal to the hardware that the End Of Interrupt state + has been reached. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt enabled. + @retval EFI_UNSUPPORTED Invalid interrupt source number. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV5EndOfInterrupt ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + UINT32 InterruptType = Source & GICV5_INTERRUPT_TYPE_MASK; + UINT32 InterruptId = Source & GICV5_INTERRUPT_ID_MASK; + + if ((InterruptType == GICV5_INTERRUPT_TYPE_PPI) && (InterruptId >= GICV5_NUM_PPI_INTERRUPTS)) { + DEBUG ((DEBUG_ERROR, "GicV5EndOfInterrupt: invalid PPI number\n")); + ASSERT (!((InterruptType == GICV5_INTERRUPT_TYPE_PPI) && (InterruptId >= GICV5_NUM_PPI_INTERRUPTS))); + return EFI_UNSUPPORTED; + } + + ArmGicV5DeactivateInterrupt (Source); + ArmGicV5EndOfInterrupt (); + + return EFI_SUCCESS; +} + +/** + EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor. This parameter is + processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + + @return None +**/ +STATIC +VOID +EFIAPI +GicV5IrqInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINT64 GicInterrupt; + HARDWARE_INTERRUPT_HANDLER InterruptHandler; + UINT32 IntId; + UINT32 IntType; + + GicInterrupt = ArmGicV5AcknowledgeInterrupt (); + IntId = GicInterrupt & GICV5_INTERRUPT_ID_MASK; + IntType = GicInterrupt & GICV5_INTERRUPT_TYPE_MASK; + + if (!(GicInterrupt & GICV5_INTERRUPT_ID_VALID)) { + DEBUG ((DEBUG_ERROR, "Spurious invalid GIC interrupt\n")); + return; + } + + if (IntType == GICV5_INTERRUPT_TYPE_SPI) { + IntId += GICV5_NUM_PPI_INTERRUPTS; + } else if (IntType != GICV5_INTERRUPT_TYPE_PPI) { + // Only PPI and SPI are currently supported + DEBUG ((DEBUG_ERROR, "Unsupported GIC interrupt type\n")); + GicV5EndOfInterrupt (&mHardwareInterruptV5Protocol, GicInterrupt); + return; + } + + InterruptHandler = mRegisteredInterruptHandlers[IntId]; + if (InterruptHandler != NULL) { + // Call the registered interrupt handler. + InterruptHandler (GicInterrupt, SystemContext); + } else { + DEBUG ((DEBUG_ERROR, "Spurious GIC interrupt: 0x%x\n", (UINT32)IntId)); + GicV5EndOfInterrupt (&mHardwareInterruptV5Protocol, GicInterrupt); + } +} + +STATIC +EFI_STATUS +EFIAPI +GicV5RegisterInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN HARDWARE_INTERRUPT_HANDLER Handler + ) +{ + // The GICv5 driver supports both PPIs and SPIs. To support both in a single + // interrupt handler array, map IDs 0 to NUM_PPI_INTERRUPTS-1 as PPIs, and + // NUM_PPI_INTERRUPTS and above as SPIs. + UINTN Type = Source & GICV5_INTERRUPT_TYPE_MASK; + UINTN IntId; + + if (Type == GICV5_INTERRUPT_TYPE_PPI) { + // PPI + IntId = Source & GICV5_INTERRUPT_ID_MASK; + if (IntId >= GICV5_NUM_PPI_INTERRUPTS) { + // GICv5 defines valid PPI IDs as 0-127 + ASSERT (IntId < GICV5_NUM_PPI_INTERRUPTS); + return EFI_UNSUPPORTED; + } + } else if (Type == GICV5_INTERRUPT_TYPE_SPI) { + // SPI + IntId = (Source & GICV5_INTERRUPT_ID_MASK) + GICV5_NUM_PPI_INTERRUPTS; + } else { + ASSERT (Type == GICV5_INTERRUPT_TYPE_PPI || Type == GICV5_INTERRUPT_TYPE_SPI); + return EFI_UNSUPPORTED; + } + + if (IntId >= mGicNumInterrupts) { + ASSERT (IntId < mGicNumInterrupts); + return EFI_UNSUPPORTED; + } + + return GicCommonRegisterInterruptSource ( + This, + Source, + Handler, + &mRegisteredInterruptHandlers[IntId] + ); +} + +// The protocol instance produced by this driver +STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL mHardwareInterruptV5Protocol = { + GicV5RegisterInterruptSource, + GicV5EnableInterruptSource, + GicV5DisableInterruptSource, + GicV5GetInterruptSourceState, + GicV5EndOfInterrupt +}; + +/** + Select SPI ID to be read or written + + @param InterruptId Returns interrupt trigger type. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_TIMEOUT Timed out waiting for IRS to go idle. +**/ +STATIC +EFI_STATUS +SelectSPIId ( + UINT32 InterruptId + ) +{ + if (!WaitForIrsSpiIdle ()) { + ASSERT (FALSE); + return EFI_TIMEOUT; + } + + MmioWrite32 (mGicIrsConfigFrameBase + GICV5_IRS_SPI_SELR, InterruptId); + + if (!WaitForIrsSpiIdleValid ()) { + ASSERT (FALSE); + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + Get interrupt trigger type of an interrupt + + Following completion of this function, the configuration for the + selected interrupt can be read or written via GICV5_IRS_SPI_CFGR. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt. + @param TriggerType Returns interrupt trigger type. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. + @retval EFI_TIMEOUT Timed out waiting for IRS to go idle. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV5GetTriggerType ( + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + OUT EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE *TriggerType + ) +{ + UINT32 InterruptType = Source & GICV5_INTERRUPT_TYPE_MASK; + UINT32 InterruptId = Source & GICV5_INTERRUPT_ID_MASK; + UINT32 GicTriggerType; + EFI_STATUS Status; + + if (InterruptType == GICV5_INTERRUPT_TYPE_SPI) { + Status = SelectSPIId (InterruptId); + + if (EFI_ERROR (Status)) { + return Status; + } + + GicTriggerType = MmioRead32 (mGicIrsConfigFrameBase + GICV5_IRS_SPI_CFGR) & 1; + } else if (InterruptType == GICV5_INTERRUPT_TYPE_PPI) { + if (InterruptId >= GICV5_NUM_PPI_INTERRUPTS) { + DEBUG ((DEBUG_ERROR, "GicV5GetTriggerType: invalid PPI number\n")); + ASSERT (InterruptId < GICV5_NUM_PPI_INTERRUPTS); + return EFI_UNSUPPORTED; + } else if (InterruptId < 64) { + GicTriggerType = (ArmGicV5GetPPIHMR0 () >> InterruptId) & 1; + } else { + GicTriggerType = (ArmGicV5GetPPIHMR1 () >> (InterruptId - 64)) & 1; + } + } else { + DEBUG ((DEBUG_ERROR, "GicV5GetTriggerType: invalid interrupt type\n")); + ASSERT (InterruptType == GICV5_INTERRUPT_TYPE_SPI || InterruptType == GICV5_INTERRUPT_TYPE_PPI); + return EFI_UNSUPPORTED; + } + + if (GicTriggerType == GICV5_PPI_EDGE_TRIGGER) { + *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING; + } else if (GicTriggerType == GICV5_PPI_LEVEL_TRIGGER) { + *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH; + } + + return EFI_SUCCESS; +} + +/** + Set interrupt trigger type of an interrupt + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt. + @param TriggerType Interrupt trigger type. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. + @retval EFI_TIMEOUT Timed out waiting for IRS to go idle. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV5SetTriggerType ( + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE TriggerType + ) +{ + UINT32 InterruptType = Source & GICV5_INTERRUPT_TYPE_MASK; + UINT32 InterruptId = Source & GICV5_INTERRUPT_ID_MASK; + UINT32 GicTriggerType; + EFI_STATUS Status; + + if (InterruptType == GICV5_INTERRUPT_TYPE_SPI) { + if (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING) { + GicTriggerType = GICV5_PPI_EDGE_TRIGGER; + } else if (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH) { + GicTriggerType = GICV5_PPI_LEVEL_TRIGGER; + } else { + DEBUG ((DEBUG_ERROR, "GicV5SetTriggerType: invalid interrupt type\n")); + ASSERT (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING || TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH); + return EFI_UNSUPPORTED; + } + + Status = SelectSPIId (InterruptId); + if (EFI_ERROR (Status)) { + return Status; + } + + MmioWrite32 (mGicIrsConfigFrameBase + GICV5_IRS_SPI_CFGR, GicTriggerType); + if (!WaitForIrsSpiIdle ()) { + ASSERT (FALSE); + return EFI_TIMEOUT; + } + } else if (InterruptType == GICV5_INTERRUPT_TYPE_PPI) { + if (InterruptId >= GICV5_NUM_PPI_INTERRUPTS) { + DEBUG ((DEBUG_ERROR, "GicV5SetTriggerType: invalid PPI number\n")); + ASSERT (InterruptId < GICV5_NUM_PPI_INTERRUPTS); + return EFI_UNSUPPORTED; + } else if (InterruptId < 64) { + GicTriggerType = (ArmGicV5GetPPIHMR0 () >> InterruptId) & 1; + } else { + GicTriggerType = (ArmGicV5GetPPIHMR1 () >> (InterruptId - 64)) & 1; + } + + if (((GicTriggerType == GICV5_PPI_EDGE_TRIGGER) && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING)) || + ((GicTriggerType == GICV5_PPI_LEVEL_TRIGGER) && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH))) + { + // PPI trigger types are fixed on GICv5. Fail on any attempt to change. + return EFI_UNSUPPORTED; + } + } else { + DEBUG ((DEBUG_ERROR, "GicV5SetTriggerType: invalid interrupt type\n")); + ASSERT (InterruptType == GICV5_INTERRUPT_TYPE_SPI || InterruptType == GICV5_INTERRUPT_TYPE_PPI); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +STATIC EFI_HARDWARE_INTERRUPT2_PROTOCOL mHardwareInterrupt2V5Protocol = { + (HARDWARE_INTERRUPT2_REGISTER)GicV5RegisterInterruptSource, + (HARDWARE_INTERRUPT2_ENABLE)GicV5EnableInterruptSource, + (HARDWARE_INTERRUPT2_DISABLE)GicV5DisableInterruptSource, + (HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV5GetInterruptSourceState, + (HARDWARE_INTERRUPT2_END_OF_INTERRUPT)GicV5EndOfInterrupt, + GicV5GetTriggerType, + GicV5SetTriggerType +}; + +/** + Shutdown our hardware + + DXE Core will disable interrupts and turn off the timer and disable interrupts + after all the event handlers have run. + + @param[in] Event The Event that is being processed + @param[in] Context Event Context +**/ +VOID +EFIAPI +GicV5ExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + + // Acknowledge all pending interrupts + for (Index = 0; Index < GICV5_NUM_PPI_INTERRUPTS; Index++) { + GicV5DisableInterruptSource (&mHardwareInterruptV5Protocol, Index | GICV5_INTERRUPT_TYPE_PPI); + } + + for (Index = 0; Index < SpiRange; Index++) { + GicV5DisableInterruptSource (&mHardwareInterruptV5Protocol, Index | GICV5_INTERRUPT_TYPE_SPI); + } + + // Disable Gic Interface + ArmGicV5DisableInterruptInterface (mGicIrsConfigFrameBase); + + return; +} + +/** + Initialize the state information for the CPU Architectural Protocol + + @retval EFI_SUCCESS Protocol registered + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Hardware problems + @retval EFI_UNSUPPORTED GIC version not supported +**/ +EFI_STATUS +GicV5DxeInitialize ( + VOID + ) +{ + EFI_STATUS Status; + + mGicIrsConfigFrameBase = (UINTN)PcdGet64 (PcdGicIrsConfigFrameBase); + + Status = gCpuArch->SetMemoryAttributes (gCpuArch, mGicIrsConfigFrameBase, GICV5_IRS_CONFIG_FRAME_SIZE, EFI_MEMORY_UC | EFI_MEMORY_XP); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to map GICv5 configuration frame: %r\n", __func__, Status)); + ASSERT_EFI_ERROR (Status); + return Status; + } + + SpiRange = MmioRead32 (mGicIrsConfigFrameBase + GICV5_IRS_IDR5) & 0x00ffffff; + + mGicNumInterrupts = GICV5_NUM_PPI_INTERRUPTS + SpiRange; + + mRegisteredInterruptHandlers = AllocateZeroPool (mGicNumInterrupts * sizeof (HARDWARE_INTERRUPT_HANDLER)); + if (mRegisteredInterruptHandlers == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = GicCommonInstallAndRegisterInterruptService ( + &mHardwareInterruptV5Protocol, + &mHardwareInterrupt2V5Protocol, + GicV5IrqInterruptHandler, + GicV5ExitBootServicesEvent + ); + + if (Status != EFI_SUCCESS) { + FreePool (mRegisteredInterruptHandlers); + return Status; + } + + ArmGicV5EnableInterruptInterface (mGicIrsConfigFrameBase); + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Include/Library/ArmGicLib.h b/ArmPkg/Include/Library/ArmGicLib.h index bf4b28f91f..32a362c4cb 100644 --- a/ArmPkg/Include/Library/ArmGicLib.h +++ b/ArmPkg/Include/Library/ArmGicLib.h @@ -166,6 +166,14 @@ // Bit Mask for #define ARM_GIC_ICCIAR_ACKINTID 0x3FF +// +// GIC SPI and extended SPI ranges +// +#define ARM_GIC_ARCH_SPI_MIN 32 +#define ARM_GIC_ARCH_SPI_MAX 1019 +#define ARM_GIC_ARCH_EXT_SPI_MIN 4096 +#define ARM_GIC_ARCH_EXT_SPI_MAX 5119 + // GIC revision 3 specific declarations #define ICC_SRE_EL2_SRE (1 << 0) @@ -173,4 +181,9 @@ #define ARM_GICD_IROUTER_IRM BIT31 +// GIC revision 5 specific declarations + +#define ARM_GICV5_ARCH_SPI_MIN 0x60000000 +#define ARM_GICV5_ARCH_SPI_MAX 0x60ffffff + #endif // ARMGIC_H_