The ArmGicAcknowledgeInterrupt () returns the value returned by the Interrupt Acknowledge Register and the InterruptID separately in an out parameter. The function documents the following: 'InterruptId is returned separately from the register value because in the GICv2 the register value contains the CpuId and InterruptId while in the GICv3 the register value is only the InterruptId.' This function skips setting the InterruptId in the out parameter for GICv3. Although the return value from the function is the InterruptId for GICv3, this breaks the function usage model as the caller expects the InterruptId in the out parameter for the function. e.g. The caller may end up using the InterruptID which could potentially be an uninitialised variable value. Therefore, set the InterruptID in the function out parameter for GICv3 as well. Signed-off-by: Sami Mujawar <sami.mujawar@arm.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
448 lines
12 KiB
C
448 lines
12 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2011-2023, Arm Limited. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
*
|
|
**/
|
|
|
|
#include <Base.h>
|
|
#include <Library/ArmGicLib.h>
|
|
#include <Library/ArmLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/PcdLib.h>
|
|
|
|
// In GICv3, there are 2 x 64KB frames:
|
|
// Redistributor control frame + SGI Control & Generation frame
|
|
#define GIC_V3_REDISTRIBUTOR_GRANULARITY (ARM_GICR_CTLR_FRAME_SIZE \
|
|
+ ARM_GICR_SGI_PPI_FRAME_SIZE)
|
|
|
|
// In GICv4, there are 2 additional 64KB frames:
|
|
// VLPI frame + Reserved page frame
|
|
#define GIC_V4_REDISTRIBUTOR_GRANULARITY (GIC_V3_REDISTRIBUTOR_GRANULARITY \
|
|
+ ARM_GICR_SGI_VLPI_FRAME_SIZE \
|
|
+ ARM_GICR_SGI_RESERVED_FRAME_SIZE)
|
|
|
|
#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))
|
|
|
|
/**
|
|
*
|
|
* Return whether the Source interrupt index refers to a shared interrupt (SPI)
|
|
*/
|
|
STATIC
|
|
BOOLEAN
|
|
SourceIsSpi (
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
return Source >= 32 && Source < 1020;
|
|
}
|
|
|
|
/**
|
|
* Return the base address of the GIC redistributor for the current CPU
|
|
*
|
|
* @param Revision GIC Revision. The GIC redistributor might have a different
|
|
* granularity following the GIC revision.
|
|
*
|
|
* @retval Base address of the associated GIC Redistributor
|
|
*/
|
|
STATIC
|
|
UINTN
|
|
GicGetCpuRedistributorBase (
|
|
IN UINTN GicRedistributorBase,
|
|
IN ARM_GIC_ARCH_REVISION Revision
|
|
)
|
|
{
|
|
UINTN MpId;
|
|
UINTN CpuAffinity;
|
|
UINTN Affinity;
|
|
UINTN GicCpuRedistributorBase;
|
|
UINT64 TypeRegister;
|
|
|
|
MpId = ArmReadMpidr ();
|
|
// Define CPU affinity as:
|
|
// Affinity0[0:8], Affinity1[9:15], Affinity2[16:23], Affinity3[24:32]
|
|
// whereas Affinity3 is defined at [32:39] in MPIDR
|
|
CpuAffinity = (MpId & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2)) |
|
|
((MpId & ARM_CORE_AFF3) >> 8);
|
|
|
|
if (Revision < ARM_GIC_ARCH_REVISION_3) {
|
|
ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
|
|
return 0;
|
|
}
|
|
|
|
GicCpuRedistributorBase = GicRedistributorBase;
|
|
|
|
do {
|
|
TypeRegister = MmioRead64 (GicCpuRedistributorBase + ARM_GICR_TYPER);
|
|
Affinity = ARM_GICR_TYPER_GET_AFFINITY (TypeRegister);
|
|
if (Affinity == CpuAffinity) {
|
|
return GicCpuRedistributorBase;
|
|
}
|
|
|
|
// Move to the next GIC Redistributor frame.
|
|
// The GIC specification does not forbid a mixture of redistributors
|
|
// with or without support for virtual LPIs, so we test Virtual LPIs
|
|
// Support (VLPIS) bit for each frame to decide the granularity.
|
|
// Note: The assumption here is that the redistributors are adjacent
|
|
// for all CPUs. However this may not be the case for NUMA systems.
|
|
GicCpuRedistributorBase += (((ARM_GICR_TYPER_VLPIS & TypeRegister) != 0)
|
|
? GIC_V4_REDISTRIBUTOR_GRANULARITY
|
|
: GIC_V3_REDISTRIBUTOR_GRANULARITY);
|
|
} while ((TypeRegister & ARM_GICR_TYPER_LAST) == 0);
|
|
|
|
// The Redistributor has not been found for the current CPU
|
|
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Return the GIC CPU Interrupt Interface ID.
|
|
|
|
@param GicInterruptInterfaceBase Base address of the GIC Interrupt Interface.
|
|
|
|
@retval CPU Interface Identification information.
|
|
**/
|
|
UINT32
|
|
EFIAPI
|
|
ArmGicGetInterfaceIdentification (
|
|
IN UINTN GicInterruptInterfaceBase
|
|
)
|
|
{
|
|
// Read the GIC Identification Register
|
|
return MmioRead32 (GicInterruptInterfaceBase + ARM_GIC_ICCIIDR);
|
|
}
|
|
|
|
UINTN
|
|
EFIAPI
|
|
ArmGicGetMaxNumInterrupts (
|
|
IN UINTN GicDistributorBase
|
|
)
|
|
{
|
|
UINTN ItLines;
|
|
|
|
ItLines = MmioRead32 (GicDistributorBase + ARM_GIC_ICDICTR) & 0x1F;
|
|
|
|
//
|
|
// Interrupt ID 1020-1023 are reserved.
|
|
//
|
|
return (ItLines == 0x1f) ? 1020 : 32 * (ItLines + 1);
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicSendSgiTo (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINT8 TargetListFilter,
|
|
IN UINT8 CPUTargetList,
|
|
IN UINT8 SgiId
|
|
)
|
|
{
|
|
MmioWrite32 (
|
|
GicDistributorBase + ARM_GIC_ICDSGIR,
|
|
((TargetListFilter & 0x3) << 24) |
|
|
((CPUTargetList & 0xFF) << 16) |
|
|
(SgiId & 0xF)
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Acknowledge and return the value of the Interrupt Acknowledge Register
|
|
*
|
|
* InterruptId is returned separately from the register value because in
|
|
* the GICv2 the register value contains the CpuId and InterruptId while
|
|
* in the GICv3 the register value is only the InterruptId.
|
|
*
|
|
* @param GicInterruptInterfaceBase Base Address of the GIC CPU Interface
|
|
* @param InterruptId InterruptId read from the Interrupt
|
|
* Acknowledge Register
|
|
*
|
|
* @retval value returned by the Interrupt Acknowledge Register
|
|
*
|
|
*/
|
|
UINTN
|
|
EFIAPI
|
|
ArmGicAcknowledgeInterrupt (
|
|
IN UINTN GicInterruptInterfaceBase,
|
|
OUT UINTN *InterruptId
|
|
)
|
|
{
|
|
UINTN Value;
|
|
UINTN IntId;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
|
|
ASSERT (InterruptId != NULL);
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if (Revision == ARM_GIC_ARCH_REVISION_2) {
|
|
Value = ArmGicV2AcknowledgeInterrupt (GicInterruptInterfaceBase);
|
|
IntId = Value & ARM_GIC_ICCIAR_ACKINTID;
|
|
} else if (Revision == ARM_GIC_ARCH_REVISION_3) {
|
|
Value = ArmGicV3AcknowledgeInterrupt ();
|
|
IntId = Value;
|
|
} else {
|
|
ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
|
|
// Report Spurious interrupt which is what the above controllers would
|
|
// return if no interrupt was available
|
|
Value = 1023;
|
|
}
|
|
|
|
if (InterruptId != NULL) {
|
|
// InterruptId is required for the caller to know if a valid or spurious
|
|
// interrupt has been read
|
|
*InterruptId = IntId;
|
|
}
|
|
|
|
return Value;
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicEndOfInterrupt (
|
|
IN UINTN GicInterruptInterfaceBase,
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if (Revision == ARM_GIC_ARCH_REVISION_2) {
|
|
ArmGicV2EndOfInterrupt (GicInterruptInterfaceBase, Source);
|
|
} else if (Revision == ARM_GIC_ARCH_REVISION_3) {
|
|
ArmGicV3EndOfInterrupt (Source);
|
|
} else {
|
|
ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicSetInterruptPriority (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINTN GicRedistributorBase,
|
|
IN UINTN Source,
|
|
IN UINTN Priority
|
|
)
|
|
{
|
|
UINT32 RegOffset;
|
|
UINT8 RegShift;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
UINTN GicCpuRedistributorBase;
|
|
|
|
// Calculate register offset and bit position
|
|
RegOffset = (UINT32)(Source / 4);
|
|
RegShift = (UINT8)((Source % 4) * 8);
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if ((Revision == ARM_GIC_ARCH_REVISION_2) ||
|
|
FeaturePcdGet (PcdArmGicV3WithV2Legacy) ||
|
|
SourceIsSpi (Source))
|
|
{
|
|
MmioAndThenOr32 (
|
|
GicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset),
|
|
~(0xff << RegShift),
|
|
Priority << RegShift
|
|
);
|
|
} else {
|
|
GicCpuRedistributorBase = GicGetCpuRedistributorBase (
|
|
GicRedistributorBase,
|
|
Revision
|
|
);
|
|
if (GicCpuRedistributorBase == 0) {
|
|
return;
|
|
}
|
|
|
|
MmioAndThenOr32 (
|
|
IPRIORITY_ADDRESS (GicCpuRedistributorBase, RegOffset),
|
|
~(0xff << RegShift),
|
|
Priority << RegShift
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicEnableInterrupt (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINTN GicRedistributorBase,
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
UINT32 RegOffset;
|
|
UINT8 RegShift;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
UINTN GicCpuRedistributorBase;
|
|
|
|
// Calculate enable register offset and bit position
|
|
RegOffset = (UINT32)(Source / 32);
|
|
RegShift = (UINT8)(Source % 32);
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if ((Revision == ARM_GIC_ARCH_REVISION_2) ||
|
|
FeaturePcdGet (PcdArmGicV3WithV2Legacy) ||
|
|
SourceIsSpi (Source))
|
|
{
|
|
// Write set-enable register
|
|
MmioWrite32 (
|
|
GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset),
|
|
1 << RegShift
|
|
);
|
|
} else {
|
|
GicCpuRedistributorBase = GicGetCpuRedistributorBase (
|
|
GicRedistributorBase,
|
|
Revision
|
|
);
|
|
if (GicCpuRedistributorBase == 0) {
|
|
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
// Write set-enable register
|
|
MmioWrite32 (
|
|
ISENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset),
|
|
1 << RegShift
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicDisableInterrupt (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINTN GicRedistributorBase,
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
UINT32 RegOffset;
|
|
UINT8 RegShift;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
UINTN GicCpuRedistributorBase;
|
|
|
|
// Calculate enable register offset and bit position
|
|
RegOffset = (UINT32)(Source / 32);
|
|
RegShift = (UINT8)(Source % 32);
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if ((Revision == ARM_GIC_ARCH_REVISION_2) ||
|
|
FeaturePcdGet (PcdArmGicV3WithV2Legacy) ||
|
|
SourceIsSpi (Source))
|
|
{
|
|
// Write clear-enable register
|
|
MmioWrite32 (
|
|
GicDistributorBase + ARM_GIC_ICDICER + (4 * RegOffset),
|
|
1 << RegShift
|
|
);
|
|
} else {
|
|
GicCpuRedistributorBase = GicGetCpuRedistributorBase (
|
|
GicRedistributorBase,
|
|
Revision
|
|
);
|
|
if (GicCpuRedistributorBase == 0) {
|
|
return;
|
|
}
|
|
|
|
// Write clear-enable register
|
|
MmioWrite32 (
|
|
ICENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset),
|
|
1 << RegShift
|
|
);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
EFIAPI
|
|
ArmGicIsInterruptEnabled (
|
|
IN UINTN GicDistributorBase,
|
|
IN UINTN GicRedistributorBase,
|
|
IN UINTN Source
|
|
)
|
|
{
|
|
UINT32 RegOffset;
|
|
UINT8 RegShift;
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
UINTN GicCpuRedistributorBase;
|
|
UINT32 Interrupts;
|
|
|
|
// Calculate enable register offset and bit position
|
|
RegOffset = (UINT32)(Source / 32);
|
|
RegShift = (UINT8)(Source % 32);
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if ((Revision == ARM_GIC_ARCH_REVISION_2) ||
|
|
FeaturePcdGet (PcdArmGicV3WithV2Legacy) ||
|
|
SourceIsSpi (Source))
|
|
{
|
|
Interrupts = MmioRead32 (
|
|
GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset)
|
|
);
|
|
} else {
|
|
GicCpuRedistributorBase = GicGetCpuRedistributorBase (
|
|
GicRedistributorBase,
|
|
Revision
|
|
);
|
|
if (GicCpuRedistributorBase == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Read set-enable register
|
|
Interrupts = MmioRead32 (
|
|
ISENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset)
|
|
);
|
|
}
|
|
|
|
return ((Interrupts & (1 << RegShift)) != 0);
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicDisableDistributor (
|
|
IN UINTN GicDistributorBase
|
|
)
|
|
{
|
|
// Disable Gic Distributor
|
|
MmioWrite32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x0);
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicEnableInterruptInterface (
|
|
IN UINTN GicInterruptInterfaceBase
|
|
)
|
|
{
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if (Revision == ARM_GIC_ARCH_REVISION_2) {
|
|
ArmGicV2EnableInterruptInterface (GicInterruptInterfaceBase);
|
|
} else if (Revision == ARM_GIC_ARCH_REVISION_3) {
|
|
ArmGicV3EnableInterruptInterface ();
|
|
} else {
|
|
ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
ArmGicDisableInterruptInterface (
|
|
IN UINTN GicInterruptInterfaceBase
|
|
)
|
|
{
|
|
ARM_GIC_ARCH_REVISION Revision;
|
|
|
|
Revision = ArmGicGetSupportedArchRevision ();
|
|
if (Revision == ARM_GIC_ARCH_REVISION_2) {
|
|
ArmGicV2DisableInterruptInterface (GicInterruptInterfaceBase);
|
|
} else if (Revision == ARM_GIC_ARCH_REVISION_3) {
|
|
ArmGicV3DisableInterruptInterface ();
|
|
} else {
|
|
ASSERT_EFI_ERROR (EFI_UNSUPPORTED);
|
|
}
|
|
}
|