ArmPkg/Drivers/ArmGicDxe: Add Extended SPI support for GICv3

This commit enables extended SPI support for GicV3. GicV3 must decide,
based on the source intid, whether to access the SPI-range registers,
PPI-range registers in the redistributor, or the extended SPI-range
registers.

The protocol interfaces must also support registering an interrupt
handler with an extended SPI intid. To save ~24KB of memory, handler
allocation and access is delegated to GicV2 and GicV3. GicV2 retains the
existing handler mapping scheme using intids literally. GicV3 remaps
extended SPI intids to be immediately after the highest SPI intids.

Tested on qemu with the BSA test suite.

Signed-off-by: Nick Graves <nicholasgraves@google.com>
This commit is contained in:
Nick Graves
2025-03-13 20:31:59 +00:00
committed by mergify[bot]
parent 15a2f3e511
commit d6d2f68e38
5 changed files with 585 additions and 199 deletions

View File

@@ -7,6 +7,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
--*/
#include "ArmGicDxe.h"
#include "Uefi/UefiBaseType.h"
// Making this global saves a few bytes in image size
EFI_HANDLE gHardwareInterruptHandle = NULL;
@@ -14,11 +15,55 @@ EFI_HANDLE gHardwareInterruptHandle = NULL;
// Notifications
EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
// Maximum Number of Interrupts
UINTN mGicNumInterrupts = 0;
EFI_CPU_ARCH_PROTOCOL *gCpuArch;
HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers = NULL;
EFI_CPU_ARCH_PROTOCOL *gCpuArch;
/**
*
* Return whether the Source interrupt index refers to an extended shared
* interrupt.
*
* @param Source The source intid to test
*
* @return True if Source is an extended SPI intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsExtSpi (
IN UINTN Source
)
{
return Source >= ARM_GIC_ARCH_EXT_SPI_MIN && Source <= ARM_GIC_ARCH_EXT_SPI_MAX;
}
/**
* Return whether this is a special interrupt.
*
* @param Source The source intid to test
*
* @return True if Source is a special interrupt intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsSpecialInterrupt (
IN UINTN Source
)
{
return (Source >= 1020) && (Source <= 1023);
}
/**
*
* Return whether the Source interrupt index refers to a shared interrupt (SPI)
*
* @param Source The source intid to test
*
* @return True if Source is a SPI intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsSpi (
IN UINTN Source
)
{
return (Source >= ARM_GIC_ARCH_SPI_MIN) && (Source <= ARM_GIC_ARCH_SPI_MAX);
}
/**
Calculate GICD_ICFGRn base address and corresponding bit
@@ -32,24 +77,26 @@ EFI_CPU_ARCH_PROTOCOL *gCpuArch;
@retval EFI_UNSUPPORTED Source interrupt is not supported.
**/
EFI_STATUS
GicGetDistributorIcfgBaseAndBit (
GicCommonGetDistributorIcfgBaseAndBit (
IN HARDWARE_INTERRUPT_SOURCE Source,
OUT UINTN *RegAddress,
OUT UINTN *Config1Bit
)
{
UINTN RegIndex;
UINTN Field;
UINTN RegIndex;
UINTN Field;
UINTN RegOffset;
HARDWARE_INTERRUPT_SOURCE AdjustedSource;
if (Source >= mGicNumInterrupts) {
ASSERT (Source < mGicNumInterrupts);
return EFI_UNSUPPORTED;
}
// Translate ESPI sources into the SPI range for indexing purposes.
AdjustedSource = Source & ~(ARM_GIC_ARCH_EXT_SPI_MIN);
RegIndex = Source / ARM_GIC_ICDICFR_F_STRIDE; // NOTE: truncation is significant
Field = Source % ARM_GIC_ICDICFR_F_STRIDE;
RegOffset = (GicCommonSourceIsExtSpi (Source)) ? ARM_GIC_ICDICFR_E : ARM_GIC_ICDICFR;
RegIndex = AdjustedSource / ARM_GIC_ICDICFR_F_STRIDE; // NOTE: truncation is significant
Field = AdjustedSource % ARM_GIC_ICDICFR_F_STRIDE;
*RegAddress = (UINTN)PcdGet64 (PcdGicDistributorBase)
+ ARM_GIC_ICDICFR
+ RegOffset
+ (ARM_GIC_ICDICFR_BYTES * RegIndex);
*Config1Bit = ((Field * ARM_GIC_ICDICFR_F_WIDTH)
+ ARM_GIC_ICDICFR_F_CONFIG1_BIT);
@@ -60,9 +107,10 @@ GicGetDistributorIcfgBaseAndBit (
/**
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
@param This Instance pointer for this protocol
@param Source Hardware source of the interrupt
@param Handler Callback for interrupt. NULL to unregister
@param HandlerDest Address of where to store Handler.
@retval EFI_SUCCESS Source was updated to support Handler.
@retval EFI_DEVICE_ERROR Hardware could not be programmed.
@@ -70,58 +118,53 @@ GicGetDistributorIcfgBaseAndBit (
**/
EFI_STATUS
EFIAPI
RegisterInterruptSource (
GicCommonRegisterInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source,
IN HARDWARE_INTERRUPT_HANDLER Handler
IN HARDWARE_INTERRUPT_HANDLER Handler,
IN HARDWARE_INTERRUPT_HANDLER *HandlerDest
)
{
if (Source >= mGicNumInterrupts) {
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
EFI_STATUS Status;
if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
if (HandlerDest == NULL) {
return EFI_INVALID_PARAMETER;
}
if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
if ((Handler == NULL) && (*HandlerDest == NULL)) {
return EFI_INVALID_PARAMETER;
}
if ((Handler != NULL) && (*HandlerDest != 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);
// Removing the handler - disable the interrupt first.
Status = This->DisableInterruptSource (This, Source);
*HandlerDest = Handler;
return Status;
} else {
// Registering a handler - set up the handler before enabling.
*HandlerDest = Handler;
return This->EnableInterruptSource (This, Source);
}
}
EFI_STATUS
InstallAndRegisterInterruptService (
GicCommonInstallAndRegisterInterruptService (
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;
}
EFI_STATUS Status;
// Register to receive interrupts
Status = gCpuArch->RegisterInterruptHandler (gCpuArch, ARM_ARCH_EXCEPTION_IRQ, InterruptHandler);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n", __func__, Status));
FreePool (gRegisteredInterruptHandlers);
return Status;
}

View File

@@ -21,43 +21,81 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include <Protocol/HardwareInterrupt.h>
#include <Protocol/HardwareInterrupt2.h>
extern UINTN mGicNumInterrupts;
extern HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers;
extern EFI_CPU_ARCH_PROTOCOL *gCpuArch;
extern EFI_CPU_ARCH_PROTOCOL *gCpuArch;
// Common API
// GicV2 API
EFI_STATUS
InstallAndRegisterInterruptService (
GicV2DxeInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
// GicV3 API
EFI_STATUS
GicV3DxeInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
// Shared code
EFI_STATUS
EFIAPI
GicCommonRegisterInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source,
IN HARDWARE_INTERRUPT_HANDLER Handler,
IN HARDWARE_INTERRUPT_HANDLER *HandlerDest
);
EFI_STATUS
EFIAPI
GicCommonInstallAndRegisterInterruptService (
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
EFIAPI
RegisterInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source,
IN HARDWARE_INTERRUPT_HANDLER Handler
/**
*
* Return whether the Source interrupt index refers to an extended shared
* interrupt.
*
* @param Source The source intid to test
*
* @return True if Source is an extended SPI intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsExtSpi (
IN UINTN Source
);
// GicV2 API
EFI_STATUS
GicV2DxeInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
/**
* Return whether this is a special interrupt.
*
* @param Source The source intid to test
*
* @return True if Source is a special interrupt intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsSpecialInterrupt (
IN UINTN Source
);
// GicV3 API
EFI_STATUS
GicV3DxeInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
/**
*
* Return whether the Source interrupt index refers to a shared interrupt (SPI)
*
* @param Source The source intid to test
*
* @return True if Source is a SPI intid, false otherwise.
*/
BOOLEAN
GicCommonSourceIsSpi (
IN UINTN Source
);
// Shared code
/**
Calculate GICD_ICFGRn base address and corresponding bit
field Int_config[1] of the GIC distributor register.
@@ -70,7 +108,7 @@ GicV3DxeInitialize (
@retval EFI_UNSUPPORTED Source interrupt is not supported.
**/
EFI_STATUS
GicGetDistributorIcfgBaseAndBit (
GicCommonGetDistributorIcfgBaseAndBit (
IN HARDWARE_INTERRUPT_SOURCE Source,
OUT UINTN *RegAddress,
OUT UINTN *Config1Bit

View File

@@ -19,23 +19,34 @@ Abstract:
#include <Library/ArmGicLib.h>
#include "ArmGicDxe.h"
#include "Protocol/HardwareInterrupt.h"
#include "Uefi/UefiBaseType.h"
#define ARM_GIC_DEFAULT_PRIORITY 0x80
#define GICD_V2_SIZE SIZE_4KB
#define GICC_V2_SIZE SIZE_8KB
// Interrupts from 1020 to 1023 are considered as special interrupts
// (eg: spurious interrupts)
#define ARM_GIC_IS_SPECIAL_INTERRUPTS(Interrupt) \
(((Interrupt) >= 1020) && ((Interrupt) <= 1023))
extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol;
extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol;
STATIC UINTN mGicInterruptInterfaceBase;
STATIC UINTN mGicDistributorBase;
// Maximum Number of Interrupts
STATIC UINTN mGicNumInterrupts;
STATIC HARDWARE_INTERRUPT_HANDLER *mRegisteredInterruptHandlers;
STATIC
BOOLEAN
EFIAPI
GicIsValidSource (
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
return Source < mGicNumInterrupts;
}
STATIC
VOID
ArmGicEnableInterrupt (
@@ -270,7 +281,7 @@ GicV2IrqInterruptHandler (
return;
}
InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
InterruptHandler = mRegisteredInterruptHandlers[GicInterrupt];
if (InterruptHandler != NULL) {
// Call the registered interrupt handler.
InterruptHandler (GicInterrupt, SystemContext);
@@ -280,9 +291,26 @@ GicV2IrqInterruptHandler (
}
}
STATIC
EFI_STATUS
EFIAPI
GicV2RegisterInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source,
IN HARDWARE_INTERRUPT_HANDLER Handler
)
{
return GicCommonRegisterInterruptSource (
This,
Source,
Handler,
&mRegisteredInterruptHandlers[Source]
);
}
// The protocol instance produced by this driver
EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol = {
RegisterInterruptSource,
GicV2RegisterInterruptSource,
GicV2EnableInterruptSource,
GicV2DisableInterruptSource,
GicV2GetInterruptSourceState,
@@ -312,7 +340,11 @@ GicV2GetTriggerType (
UINTN Config1Bit;
EFI_STATUS Status;
Status = GicGetDistributorIcfgBaseAndBit (
if (!GicIsValidSource (Source)) {
return EFI_UNSUPPORTED;
}
Status = GicCommonGetDistributorIcfgBaseAndBit (
Source,
&RegAddress,
&Config1Bit
@@ -368,7 +400,11 @@ GicV2SetTriggerType (
return EFI_UNSUPPORTED;
}
Status = GicGetDistributorIcfgBaseAndBit (
if (!GicIsValidSource (Source)) {
return EFI_UNSUPPORTED;
}
Status = GicCommonGetDistributorIcfgBaseAndBit (
Source,
&RegAddress,
&Config1Bit
@@ -428,7 +464,7 @@ ArmGicEnableDistributor (
}
EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol = {
(HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource,
(HARDWARE_INTERRUPT2_REGISTER)GicV2RegisterInterruptSource,
(HARDWARE_INTERRUPT2_ENABLE)GicV2EnableInterruptSource,
(HARDWARE_INTERRUPT2_DISABLE)GicV2DisableInterruptSource,
(HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV2GetInterruptSourceState,
@@ -493,7 +529,7 @@ GicV2ExitBootServicesEvent (
if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) < mGicNumInterrupts) {
GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt);
}
} while (!ARM_GIC_IS_SPECIAL_INTERRUPTS (GicInterrupt));
} while (!GicCommonSourceIsSpecialInterrupt (GicInterrupt));
// Disable Gic Interface
ArmGicV2DisableInterruptInterface (mGicInterruptInterfaceBase);
@@ -604,12 +640,20 @@ GicV2DxeInitialize (
// Enable gic distributor
ArmGicEnableDistributor (mGicDistributorBase);
Status = InstallAndRegisterInterruptService (
mRegisteredInterruptHandlers = AllocateZeroPool (mGicNumInterrupts * sizeof (HARDWARE_INTERRUPT_HANDLER));
if (mRegisteredInterruptHandlers == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = GicCommonInstallAndRegisterInterruptService (
&gHardwareInterruptV2Protocol,
&gHardwareInterrupt2V2Protocol,
GicV2IrqInterruptHandler,
GicV2ExitBootServicesEvent
);
if (EFI_ERROR (Status)) {
FreePool (mRegisteredInterruptHandlers);
}
return Status;
}

View File

@@ -9,6 +9,9 @@
#include <Library/ArmGicLib.h>
#include "ArmGicDxe.h"
#include "Base.h"
#include "Protocol/HardwareInterrupt.h"
#include "Uefi/UefiBaseType.h"
#define ARM_GIC_DEFAULT_PRIORITY 0x80
@@ -25,33 +28,15 @@
#define GICD_V3_SIZE SIZE_64KB
#define ISENABLER_ADDRESS(base, offset) ((base) +\
ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ISENABLER + 4 * (offset))
#define ICENABLER_ADDRESS(base, offset) ((base) +\
ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ICENABLER + 4 * (offset))
#define IPRIORITY_ADDRESS(base, offset) ((base) +\
ARM_GICR_CTLR_FRAME_SIZE + ARM_GIC_ICDIPR + 4 * (offset))
extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol;
extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol;
STATIC UINTN mGicDistributorBase;
STATIC UINTN mGicRedistributorBase;
/**
*
* Return whether the Source interrupt index refers to a shared interrupt (SPI)
*/
STATIC
BOOLEAN
SourceIsSpi (
IN UINTN Source
)
{
return Source >= 32 && Source < 1020;
}
STATIC HARDWARE_INTERRUPT_HANDLER *mRegisteredInterruptHandlers;
STATIC UINTN mGicMaxSpiIntId;
STATIC UINTN mGicMaxExtSpiIntId;
/**
* Return the base address of the GIC redistributor for the current CPU
@@ -121,6 +106,109 @@ GicGetCpuRedistributorBase (
return 0;
}
typedef enum {
GicOpSetInterruptPriority,
GicOpEnableInterrupt,
GicOpDisableInterrupt,
GicOpIsInterruptEnabled,
GicOpNumOps,
} GIC_REG_OPERATIONS;
typedef struct {
GIC_REG_OPERATIONS Operation; // Self-referential operation.
UINT16 RegisterWidthBits; // Width of the register. Same for all types.
UINT16 FieldWidthBits; // Bits per IntId within the register.
UINTN DistributorSpiOffset; // Offset from the GIC distributor base for SPIs.
UINTN DistributorExtSpiOffset; // Offset from the GIC distributor base for Extended SPIs.
UINTN RedistributorOffset; // Offset from the GIC redistributor base for non-shared interrupts.
} GIC_REG_OPERATION_CONFIG;
// Order of the array is important as mGicOperationConfigMap is indexed by
// GIC_REG_OPERATIONS.
STATIC CONST GIC_REG_OPERATION_CONFIG mGicOperationConfigMap[GicOpNumOps] = {
[GicOpSetInterruptPriority] = {
.Operation = GicOpSetInterruptPriority,
.RegisterWidthBits = 32,
.FieldWidthBits = 8,
.DistributorSpiOffset = ARM_GIC_ICDIPR,
.DistributorExtSpiOffset = ARM_GIC_ICDIPR_E,
.RedistributorOffset = ARM_GIC_ICDIPR,
},
[GicOpEnableInterrupt] = {
.Operation = GicOpEnableInterrupt,
.RegisterWidthBits = 32,
.FieldWidthBits = 1,
.DistributorSpiOffset = ARM_GIC_ICDISER,
.DistributorExtSpiOffset = ARM_GIC_ICDISER_E,
.RedistributorOffset = ARM_GICR_ISENABLER,
},
[GicOpDisableInterrupt] = {
.Operation = GicOpDisableInterrupt,
.RegisterWidthBits = 32,
.FieldWidthBits = 1,
.DistributorSpiOffset = ARM_GIC_ICDICER,
.DistributorExtSpiOffset = ARM_GIC_ICDICER_E,
.RedistributorOffset = ARM_GICR_ICENABLER,
},
[GicOpIsInterruptEnabled] = {
.Operation = GicOpIsInterruptEnabled,
.RegisterWidthBits = 32,
.FieldWidthBits = 1,
.DistributorSpiOffset = ARM_GIC_ICDISER,
.DistributorExtSpiOffset = ARM_GIC_ICDISER_E,
.RedistributorOffset = ARM_GICR_ISENABLER,
},
};
// Struct describing a bitwise address in the GIC.
typedef struct {
UINTN Address; // MMIO address for the register.
UINT32 Shift; // Position in the register of the lowest included bit.
UINT32 Mask; // Mask of the included bits stating at the position of Shift.
} GIC_BITWISE_ADDRESS;
/**
* Gets a bitwise address for the given intid Source and Operation.
*
* A bitwise address contains all of the information to extract the relevant
* field from a MMIO register, such as the position and bitmask for the field.
*
* @param [in] Source Hardware source of the interrupt
* @param [in] Operation GIC_REG_OPERATION requested for this interrupt.
* @param [out] BitwiseAddress Fully-specified address and mask to populate.
**/
STATIC
VOID
EFIAPI
GicGetBitwiseAddress (
IN UINTN Source,
GIC_REG_OPERATIONS Operation,
OUT GIC_BITWISE_ADDRESS *BitwiseAddress
)
{
UINT32 RegOffset;
UINT32 IntIdsPerRegister;
CONST GIC_REG_OPERATION_CONFIG *OpConfig;
ASSERT (BitwiseAddress != NULL);
ASSERT (Operation < GicOpNumOps);
OpConfig = &mGicOperationConfigMap[Operation];
IntIdsPerRegister = (UINT32)(OpConfig->RegisterWidthBits / OpConfig->FieldWidthBits);
BitwiseAddress->Shift = (UINT32)((Source & ~ARM_GIC_ARCH_EXT_SPI_MIN) % IntIdsPerRegister) * OpConfig->FieldWidthBits;
BitwiseAddress->Mask = (UINT32)(((UINT64)1 << OpConfig->FieldWidthBits) - 1);
RegOffset = (UINT32)((Source & ~ARM_GIC_ARCH_EXT_SPI_MIN) / IntIdsPerRegister);
if (GicCommonSourceIsSpi (Source)) {
BitwiseAddress->Address = mGicDistributorBase + OpConfig->DistributorSpiOffset + (4 * RegOffset);
} else if (GicCommonSourceIsExtSpi (Source)) {
BitwiseAddress->Address = mGicDistributorBase + OpConfig->DistributorExtSpiOffset + (4 * RegOffset);
} else {
BitwiseAddress->Address = mGicRedistributorBase + ARM_GICR_CTLR_FRAME_SIZE + OpConfig->RedistributorOffset + (4 * RegOffset);
}
}
STATIC
VOID
ArmGicSetInterruptPriority (
@@ -128,26 +216,16 @@ ArmGicSetInterruptPriority (
IN UINT32 Priority
)
{
UINT32 RegOffset;
UINT8 RegShift;
GIC_BITWISE_ADDRESS BitwiseAddress;
// Calculate register offset and bit position
RegOffset = (UINT32)(Source / 4);
RegShift = (UINT8)((Source % 4) * 8);
GicGetBitwiseAddress (Source, GicOpSetInterruptPriority, &BitwiseAddress);
if (SourceIsSpi (Source)) {
MmioAndThenOr32 (
mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),
~(0xff << RegShift),
Priority << RegShift
);
} else {
MmioAndThenOr32 (
IPRIORITY_ADDRESS (mGicRedistributorBase, RegOffset),
~(0xff << RegShift),
Priority << RegShift
);
}
Priority &= BitwiseAddress.Mask;
MmioAndThenOr32 (
BitwiseAddress.Address,
~(BitwiseAddress.Mask << BitwiseAddress.Shift),
Priority << BitwiseAddress.Shift
);
}
STATIC
@@ -156,26 +234,15 @@ ArmGicEnableInterrupt (
IN UINTN Source
)
{
UINT32 RegOffset;
UINT8 RegShift;
GIC_BITWISE_ADDRESS BitwiseAddress;
// Calculate enable register offset and bit position
RegOffset = (UINT32)(Source / 32);
RegShift = (UINT8)(Source % 32);
GicGetBitwiseAddress (Source, GicOpEnableInterrupt, &BitwiseAddress);
if (SourceIsSpi (Source)) {
// Write set-enable register
MmioWrite32 (
mGicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset),
1 << RegShift
);
} else {
// Write set-enable register
MmioWrite32 (
ISENABLER_ADDRESS (mGicRedistributorBase, RegOffset),
1 << RegShift
);
}
// Write set-enable register.
MmioWrite32 (
BitwiseAddress.Address,
BitwiseAddress.Mask << BitwiseAddress.Shift
);
}
STATIC
@@ -184,26 +251,15 @@ ArmGicDisableInterrupt (
IN UINTN Source
)
{
UINT32 RegOffset;
UINT8 RegShift;
GIC_BITWISE_ADDRESS BitwiseAddress;
// Calculate enable register offset and bit position
RegOffset = (UINT32)(Source / 32);
RegShift = (UINT8)(Source % 32);
GicGetBitwiseAddress (Source, GicOpDisableInterrupt, &BitwiseAddress);
if (SourceIsSpi (Source)) {
// Write clear-enable register
MmioWrite32 (
mGicDistributorBase + ARM_GIC_ICDICER + (4 * RegOffset),
1 << RegShift
);
} else {
// Write clear-enable register
MmioWrite32 (
ICENABLER_ADDRESS (mGicRedistributorBase, RegOffset),
1 << RegShift
);
}
// Write clear-enable register
MmioWrite32 (
BitwiseAddress.Address,
BitwiseAddress.Mask << BitwiseAddress.Shift
);
}
STATIC
@@ -212,26 +268,58 @@ ArmGicIsInterruptEnabled (
IN UINTN Source
)
{
UINT32 RegOffset;
UINT8 RegShift;
UINT32 Interrupts;
UINT32 Interrupts;
GIC_BITWISE_ADDRESS BitwiseAddress;
// Calculate enable register offset and bit position
RegOffset = (UINT32)(Source / 32);
RegShift = (UINT8)(Source % 32);
GicGetBitwiseAddress (Source, GicOpIsInterruptEnabled, &BitwiseAddress);
if (SourceIsSpi (Source)) {
Interrupts = MmioRead32 (
mGicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset)
);
} else {
// Read set-enable register
Interrupts = MmioRead32 (
ISENABLER_ADDRESS (mGicRedistributorBase, RegOffset)
);
// Read set-enable register
Interrupts = MmioRead32 (BitwiseAddress.Address);
return ((Interrupts & (BitwiseAddress.Mask << BitwiseAddress.Shift)) != 0);
}
STATIC
BOOLEAN
EFIAPI
GicIsValidSource (
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (Source <= mGicMaxSpiIntId) {
return TRUE;
}
return ((Interrupts & (1 << RegShift)) != 0);
return (Source >= ARM_GIC_ARCH_EXT_SPI_MIN && Source <= mGicMaxExtSpiIntId);
}
STATIC
EFI_STATUS
EFIAPI
GicV3GetValidIntIdRanges (
IN UINTN GicDistributorBase,
OUT UINTN *GicMaxSpiIntId,
OUT UINTN *GicMaxExtSpiIntId
)
{
UINT32 GicTyperReg;
if ((GicMaxSpiIntId == NULL) || (GicMaxExtSpiIntId == NULL)) {
return EFI_INVALID_PARAMETER;
}
// Read GIC_TYPER to determine if extended SPI is enabled. If so, get the
// maximum extended SPI INTID.
GicTyperReg = MmioRead32 (GicDistributorBase + ARM_GIC_ICDICTR);
*GicMaxSpiIntId = ARM_GIC_ICDICTR_GET_SPI_MAX_INTID (GicTyperReg);
if ((GicTyperReg & ARM_GIC_ICDICTR_EXT_SPI_ENABLED) != 0) {
*GicMaxExtSpiIntId = ARM_GIC_ICDICTR_GET_EXT_SPI_MAX_INTID (GicTyperReg);
} else {
*GicMaxExtSpiIntId = 0;
}
return EFI_SUCCESS;
}
/**
@@ -252,7 +340,7 @@ GicV3EnableInterruptSource (
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (Source >= mGicNumInterrupts) {
if (!GicIsValidSource (Source)) {
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
@@ -280,7 +368,7 @@ GicV3DisableInterruptSource (
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (Source >= mGicNumInterrupts) {
if (!GicIsValidSource (Source)) {
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
@@ -310,7 +398,7 @@ GicV3GetInterruptSourceState (
IN BOOLEAN *InterruptState
)
{
if (Source >= mGicNumInterrupts) {
if (!GicIsValidSource (Source)) {
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
@@ -339,7 +427,7 @@ GicV3EndOfInterrupt (
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (Source >= mGicNumInterrupts) {
if (!GicIsValidSource (Source)) {
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
@@ -348,6 +436,34 @@ GicV3EndOfInterrupt (
return EFI_SUCCESS;
}
/**
Gets the address of the interrupt callback associated with Source.
@param Source IntId of the interrupt.
@return Pointer to the associated handler
NULL if the source is invalid.
**/
STATIC
HARDWARE_INTERRUPT_HANDLER *
EFIAPI
GicV3GetHandlerAddress (
IN HARDWARE_INTERRUPT_SOURCE Source
)
{
if (!GicIsValidSource (Source)) {
return NULL;
}
if (GicCommonSourceIsExtSpi (Source)) {
return &mRegisteredInterruptHandlers[
Source - ARM_GIC_ARCH_EXT_SPI_MIN + mGicMaxSpiIntId + 1
];
}
return &mRegisteredInterruptHandlers[Source];
}
/**
EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
@@ -369,30 +485,55 @@ GicV3IrqInterruptHandler (
)
{
UINTN GicInterrupt;
HARDWARE_INTERRUPT_HANDLER InterruptHandler;
HARDWARE_INTERRUPT_HANDLER *InterruptHandlerPtr;
GicInterrupt = ArmGicV3AcknowledgeInterrupt ();
// Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the
// number of interrupt (ie: Spurious interrupt).
if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) {
// The special interrupt do not need to be acknowledge
if (GicCommonSourceIsSpecialInterrupt (GicInterrupt)) {
// The special interrupts do not need to be acknowledged.
return;
}
InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt];
if (InterruptHandler != NULL) {
InterruptHandlerPtr = GicV3GetHandlerAddress (GicInterrupt);
if (InterruptHandlerPtr == NULL) {
DEBUG ((DEBUG_ERROR, "Interrupt 0x%x out of expected range\n", GicInterrupt));
return;
}
if (*InterruptHandlerPtr != NULL) {
// Call the registered interrupt handler.
InterruptHandler (GicInterrupt, SystemContext);
(*InterruptHandlerPtr)(GicInterrupt, SystemContext);
} else {
DEBUG ((DEBUG_ERROR, "Spurious GIC interrupt: 0x%x\n", (UINT32)GicInterrupt));
GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt);
}
}
EFI_STATUS
EFIAPI
GicV3RegisterInterruptSource (
IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This,
IN HARDWARE_INTERRUPT_SOURCE Source,
IN HARDWARE_INTERRUPT_HANDLER Handler
)
{
HARDWARE_INTERRUPT_HANDLER *HandlerDest = GicV3GetHandlerAddress (Source);
if (HandlerDest == NULL) {
return EFI_UNSUPPORTED;
}
return GicCommonRegisterInterruptSource (
This,
Source,
Handler,
HandlerDest
);
}
// The protocol instance produced by this driver
EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
RegisterInterruptSource,
GicV3RegisterInterruptSource,
GicV3EnableInterruptSource,
GicV3DisableInterruptSource,
GicV3GetInterruptSourceState,
@@ -408,6 +549,8 @@ EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = {
@retval EFI_SUCCESS Source interrupt supported.
@retval EFI_UNSUPPORTED Source interrupt is not supported.
**/
STATIC
EFI_STATUS
@@ -422,7 +565,11 @@ GicV3GetTriggerType (
UINTN Config1Bit;
EFI_STATUS Status;
Status = GicGetDistributorIcfgBaseAndBit (
if (!GicIsValidSource (Source)) {
return EFI_UNSUPPORTED;
}
Status = GicCommonGetDistributorIcfgBaseAndBit (
Source,
&RegAddress,
&Config1Bit
@@ -478,7 +625,11 @@ GicV3SetTriggerType (
return EFI_UNSUPPORTED;
}
Status = GicGetDistributorIcfgBaseAndBit (
if (!GicIsValidSource (Source)) {
return EFI_UNSUPPORTED;
}
Status = GicCommonGetDistributorIcfgBaseAndBit (
Source,
&RegAddress,
&Config1Bit
@@ -544,7 +695,7 @@ ArmGicEnableDistributor (
}
EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol = {
(HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource,
(HARDWARE_INTERRUPT2_REGISTER)GicV3RegisterInterruptSource,
(HARDWARE_INTERRUPT2_ENABLE)GicV3EnableInterruptSource,
(HARDWARE_INTERRUPT2_DISABLE)GicV3DisableInterruptSource,
(HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV3GetInterruptSourceState,
@@ -571,8 +722,13 @@ GicV3ExitBootServicesEvent (
{
UINTN Index;
// Acknowledge all pending interrupts
for (Index = 0; Index < mGicNumInterrupts; Index++) {
// Acknowledge all pending interrupts in SPI range.
for (Index = 0; Index <= mGicMaxSpiIntId; Index++) {
GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
}
// Acknowledge all pending interrupts in extended SPI range.
for (Index = ARM_GIC_ARCH_EXT_SPI_MIN; Index <= mGicMaxExtSpiIntId; Index++) {
GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
}
@@ -605,6 +761,7 @@ GicV3DxeInitialize (
UINT64 MpId;
UINT64 CpuTarget;
UINT64 RegValue;
UINT64 TotalIntIds;
// Make sure the Interrupt Controller Protocol is not already installed in
// the system.
@@ -624,7 +781,6 @@ GicV3DxeInitialize (
}
mGicRedistributorBase = GicGetCpuRedistributorBase (PcdGet64 (PcdGicRedistributorsBase));
mGicNumInterrupts = ArmGicGetMaxNumInterrupts (mGicDistributorBase);
RegValue = ArmGicV3GetControlSystemRegisterEnable ();
if ((RegValue & ICC_SRE_EL2_SRE) == 0) {
@@ -632,11 +788,30 @@ GicV3DxeInitialize (
ASSERT ((ArmGicV3GetControlSystemRegisterEnable () & ICC_SRE_EL2_SRE) != 0);
}
Status = GicV3GetValidIntIdRanges (
mGicDistributorBase,
&mGicMaxSpiIntId,
&mGicMaxExtSpiIntId
);
if (EFI_ERROR (Status)) {
return Status;
}
// We will be driving this GIC in native v3 mode, i.e., with Affinity
// Routing enabled. So ensure that the ARE bit is set.
MmioOr32 (mGicDistributorBase + ARM_GIC_ICDDCR, ARM_GIC_ICDDCR_ARE);
for (Index = 0; Index < mGicNumInterrupts; Index++) {
// Disable all SPI sources.
for (Index = 0; Index <= mGicMaxSpiIntId; Index++) {
GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
// Set Priority
ArmGicSetInterruptPriority (Index, ARM_GIC_DEFAULT_PRIORITY);
}
// Disable all extended SPI sources.
for (Index = ARM_GIC_ARCH_EXT_SPI_MIN; Index <= mGicMaxExtSpiIntId; Index++) {
GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index);
// Set Priority
@@ -665,20 +840,37 @@ GicV3DxeInitialize (
0xffffffff
);
for (Index = 32; Index < mGicNumInterrupts; Index += 32) {
// Configure SPI
for (Index = 32; Index <= mGicMaxSpiIntId; Index += 32) {
MmioWrite32 (
mGicDistributorBase + ARM_GIC_ICDISR + Index / 8,
0xffffffff
);
}
// Configure extended SPI
for (Index = ARM_GIC_ARCH_EXT_SPI_MIN; Index <= mGicMaxExtSpiIntId; Index += 32) {
MmioWrite32 (
mGicDistributorBase + ARM_GIC_ICDISR_E + (Index - ARM_GIC_ARCH_EXT_SPI_MIN) / 8,
0xffffffff
);
}
// Route the SPIs to the primary CPU. SPIs start at the INTID 32
for (Index = 0; Index < (mGicNumInterrupts - 32); Index++) {
for (Index = 0; Index < (mGicMaxSpiIntId + 1 - 32); Index++) {
MmioWrite64 (
mGicDistributorBase + ARM_GICD_IROUTER + (Index * 8),
CpuTarget
);
}
// Route the extended SPIs to the primary CPU. extended SPIs start at the INTID 4096
for (Index = ARM_GIC_ARCH_EXT_SPI_MIN; Index <= mGicMaxExtSpiIntId; Index++) {
MmioWrite64 (
mGicDistributorBase + ARM_GICD_IROUTER_E + ((Index - ARM_GIC_ARCH_EXT_SPI_MIN) * 8),
CpuTarget
);
}
}
// Set binary point reg to 0x7 (no preemption)
@@ -698,12 +890,31 @@ GicV3DxeInitialize (
// Enable gic distributor
ArmGicEnableDistributor (mGicDistributorBase);
Status = InstallAndRegisterInterruptService (
// Allocate contiguous handlers for SPI and extended SPI. extended SPI
// handlers begin immediately after SPI handlers.
TotalIntIds = mGicMaxSpiIntId + 1;
if (mGicMaxExtSpiIntId >= ARM_GIC_ARCH_EXT_SPI_MIN) {
TotalIntIds += (mGicMaxExtSpiIntId - ARM_GIC_ARCH_EXT_SPI_MIN + 1);
}
mRegisteredInterruptHandlers = AllocateZeroPool (
sizeof (HARDWARE_INTERRUPT_HANDLER) *
TotalIntIds
);
if (mRegisteredInterruptHandlers == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = GicCommonInstallAndRegisterInterruptService (
&gHardwareInterruptV3Protocol,
&gHardwareInterrupt2V3Protocol,
GicV3IrqInterruptHandler,
GicV3ExitBootServicesEvent
);
if (EFI_ERROR (Status)) {
FreePool (mRegisteredInterruptHandlers);
}
return Status;
}

View File

@@ -9,11 +9,58 @@
#ifndef ARMGIC_H_
#define ARMGIC_H_
//
// 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 Distributor
#define ARM_GIC_ICDDCR 0x000 // Distributor Control Register
#define ARM_GIC_ICDICTR 0x004 // Interrupt Controller Type Register
#define ARM_GIC_ICDIIDR 0x008 // Implementer Identification Register
// ICDICTR is also called GICD_TYPER.
// Intids per LSB for EXT_SPI_RANGE and ITLINES.
#define ARM_GIC_ICDICTR_INTID_RANGE_RESOLUTION (32)
// Converts an register range of IntIds to the maximum IntId using Base as an
// offset.
#define ARM_GIC_ICDICTR_INTID_RANGE_TO_MAX_INTID(Range, Base) \
(ARM_GIC_ICDICTR_INTID_RANGE_RESOLUTION * ((Range) + 1) - 1 + (Base))
#define ARM_GIC_ICDICTR_ITLINES_MASK (0x1F)
#define ARM_GIC_ICDICTR_ITLINES_SHIFT (0)
// Gets the range for SPI IntIds from TypeReg.
#define ARM_GIC_ICDICTR_GET_SPI_RANGE(TypeReg) \
(((TypeReg) >> ARM_GIC_ICDICTR_ITLINES_SHIFT) & ARM_GIC_ICDICTR_ITLINES_MASK)
// Converts a range of SPI IntIds to the maximum SPI IntId.
#define ARM_GIC_ICDICTR_SPI_RANGE_TO_MAX_INTID(SpiRange) \
(((SpiRange) == ARM_GIC_ICDICTR_ITLINES_MASK) \
? ARM_GIC_ARCH_SPI_MAX \
: ARM_GIC_ICDICTR_INTID_RANGE_TO_MAX_INTID(SpiRange, 0))
// Extracts the maximum SPI IntId from TypeReg.
#define ARM_GIC_ICDICTR_GET_SPI_MAX_INTID(TypeReg) \
ARM_GIC_ICDICTR_SPI_RANGE_TO_MAX_INTID(ARM_GIC_ICDICTR_GET_EXT_SPI_RANGE(TypeReg))
#define ARM_GIC_ICDICTR_EXT_SPI_ENABLED (1 << 8) // Extended SPI enabled bit.
#define ARM_GIC_ICDICTR_EXT_SPI_RANGE_SHIFT (27) // Extended SPI range position.
#define ARM_GIC_ICDICTR_EXT_SPI_RANGE_MASK (0x1F) // Extended SPI range mask.
#define ARM_GIC_ICDICTR_GET_EXT_SPI_RANGE(TypeReg) \
(((TypeReg) >> ARM_GIC_ICDICTR_EXT_SPI_RANGE_SHIFT) & \
ARM_GIC_ICDICTR_EXT_SPI_RANGE_MASK)
// Extracts the maximum EXT SPI IntId from TypeReg.
#define ARM_GIC_ICDICTR_GET_EXT_SPI_MAX_INTID(TypeReg) \
ARM_GIC_ICDICTR_INTID_RANGE_TO_MAX_INTID( \
ARM_GIC_ICDICTR_GET_EXT_SPI_RANGE(TypeReg), ARM_GIC_ARCH_EXT_SPI_MIN)
// Each reg base below repeats for Number of interrupts / 4 (see GIC spec)
#define ARM_GIC_ICDISR 0x080 // Interrupt Security Registers
#define ARM_GIC_ICDISER 0x100 // Interrupt Set-Enable Registers
@@ -35,7 +82,8 @@
#define ARM_GIC_ICDSGIR 0xF00 // Software Generated Interrupt Register
// GICv3 specific registers
#define ARM_GICD_IROUTER 0x6100 // Interrupt Routing Registers
#define ARM_GICD_IROUTER 0x6100 // Interrupt Routing Registers
#define ARM_GICD_IROUTER_E 0x8000 // Interrupt Routing Registers
// GICD_CTLR bits
#define ARM_GIC_ICDDCR_ARE (1 << 4) // Affinity Routing Enable (ARE)
@@ -50,6 +98,16 @@
#define ARM_GIC_ICDICFR_LEVEL_TRIGGERED 0x0 // Level triggered interrupt
#define ARM_GIC_ICDICFR_EDGE_TRIGGERED 0x1 // Edge triggered interrupt
// GICD ESPI registers
// These registers follow the same bit pattern as the SPI registers.
#define ARM_GIC_ICDISR_E 0x1000 // Interrupt Security Registers
#define ARM_GIC_ICDISER_E 0x1200 // Interrupt Set-Enable for ESPI
#define ARM_GIC_ICDICER_E 0x1400 // Interrupt Clear-Enable Registers
#define ARM_GIC_ICDSPR_E 0x1600 // Interrupt Set-Pending Registers
#define ARM_GIC_ICDICPR_E 0x1800 // Interrupt Clear-Pending Registers
#define ARM_GIC_ICDIPR_E 0x2000 // Interrupt Priority Registers
#define ARM_GIC_ICDICFR_E 0x3000 // Interrupt Configuration Registers
// GIC Redistributor
#define ARM_GICR_CTLR_FRAME_SIZE SIZE_64KB
#define ARM_GICR_SGI_PPI_FRAME_SIZE SIZE_64KB
@@ -108,14 +166,6 @@
// 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)