Commit 3889ba70 authored by Mark Rutland's avatar Mark Rutland Committed by Catalin Marinas
Browse files

arm64: irq: allow FIQs to be handled



On contemporary platforms we don't use FIQ, and treat any stray FIQ as a
fatal event. However, some platforms have an interrupt controller wired
to FIQ, and need to handle FIQ as part of regular operation.

So that we can support both cases dynamically, this patch updates the
FIQ exception handling code to operate the same way as the IRQ handling
code, with its own handle_arch_fiq handler. Where a root FIQ handler is
not registered, an unexpected FIQ exception will trigger the default FIQ
handler, which will panic() as today. Where a root FIQ handler is
registered, handling of the FIQ is deferred to that handler.

As el0_fiq_invalid_compat is supplanted by el0_fiq, the former is
removed. For !CONFIG_COMPAT builds we never expect to take an exception
from AArch32 EL0, so we keep the common el0_fiq_invalid handler.

Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Tested-by: default avatarHector Martin <marcan@marcan.st>
Cc: James Morse <james.morse@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Will Deacon <will@kernel.org>
Acked-by: default avatarWill Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20210315115629.57191-7-mark.rutland@arm.com


Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent f0098155
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ struct pt_regs;

int set_handle_irq(void (*handle_irq)(struct pt_regs *));
#define set_handle_irq	set_handle_irq
int set_handle_fiq(void (*handle_fiq)(struct pt_regs *));

static inline int nr_legacy_irqs(void)
{
+21 −9
Original line number Diff line number Diff line
@@ -588,18 +588,18 @@ SYM_CODE_START(vectors)

	kernel_ventry	1, sync				// Synchronous EL1h
	kernel_ventry	1, irq				// IRQ EL1h
	kernel_ventry	1, fiq_invalid			// FIQ EL1h
	kernel_ventry	1, fiq				// FIQ EL1h
	kernel_ventry	1, error			// Error EL1h

	kernel_ventry	0, sync				// Synchronous 64-bit EL0
	kernel_ventry	0, irq				// IRQ 64-bit EL0
	kernel_ventry	0, fiq_invalid			// FIQ 64-bit EL0
	kernel_ventry	0, fiq				// FIQ 64-bit EL0
	kernel_ventry	0, error			// Error 64-bit EL0

#ifdef CONFIG_COMPAT
	kernel_ventry	0, sync_compat, 32		// Synchronous 32-bit EL0
	kernel_ventry	0, irq_compat, 32		// IRQ 32-bit EL0
	kernel_ventry	0, fiq_invalid_compat, 32	// FIQ 32-bit EL0
	kernel_ventry	0, fiq_compat, 32		// FIQ 32-bit EL0
	kernel_ventry	0, error_compat, 32		// Error 32-bit EL0
#else
	kernel_ventry	0, sync_invalid, 32		// Synchronous 32-bit EL0
@@ -665,12 +665,6 @@ SYM_CODE_START_LOCAL(el0_error_invalid)
	inv_entry 0, BAD_ERROR
SYM_CODE_END(el0_error_invalid)

#ifdef CONFIG_COMPAT
SYM_CODE_START_LOCAL(el0_fiq_invalid_compat)
	inv_entry 0, BAD_FIQ, 32
SYM_CODE_END(el0_fiq_invalid_compat)
#endif

SYM_CODE_START_LOCAL(el1_sync_invalid)
	inv_entry 1, BAD_SYNC
SYM_CODE_END(el1_sync_invalid)
@@ -705,6 +699,12 @@ SYM_CODE_START_LOCAL_NOALIGN(el1_irq)
	kernel_exit 1
SYM_CODE_END(el1_irq)

SYM_CODE_START_LOCAL_NOALIGN(el1_fiq)
	kernel_entry 1
	el1_interrupt_handler handle_arch_fiq
	kernel_exit 1
SYM_CODE_END(el1_fiq)

/*
 * EL0 mode handlers.
 */
@@ -731,6 +731,11 @@ SYM_CODE_START_LOCAL_NOALIGN(el0_irq_compat)
	b	el0_irq_naked
SYM_CODE_END(el0_irq_compat)

SYM_CODE_START_LOCAL_NOALIGN(el0_fiq_compat)
	kernel_entry 0, 32
	b	el0_fiq_naked
SYM_CODE_END(el0_fiq_compat)

SYM_CODE_START_LOCAL_NOALIGN(el0_error_compat)
	kernel_entry 0, 32
	b	el0_error_naked
@@ -745,6 +750,13 @@ el0_irq_naked:
	b	ret_to_user
SYM_CODE_END(el0_irq)

SYM_CODE_START_LOCAL_NOALIGN(el0_fiq)
	kernel_entry 0
el0_fiq_naked:
	el0_interrupt_handler handle_arch_fiq
	b	ret_to_user
SYM_CODE_END(el0_fiq)

SYM_CODE_START_LOCAL(el1_error)
	kernel_entry 1
	mrs	x1, esr_el1
+16 −0
Original line number Diff line number Diff line
@@ -76,7 +76,13 @@ static void default_handle_irq(struct pt_regs *regs)
	panic("IRQ taken without a root IRQ handler\n");
}

static void default_handle_fiq(struct pt_regs *regs)
{
	panic("FIQ taken without a root FIQ handler\n");
}

void (*handle_arch_irq)(struct pt_regs *) __ro_after_init = default_handle_irq;
void (*handle_arch_fiq)(struct pt_regs *) __ro_after_init = default_handle_fiq;

int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
@@ -88,6 +94,16 @@ int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
	return 0;
}

int __init set_handle_fiq(void (*handle_fiq)(struct pt_regs *))
{
	if (handle_arch_fiq != default_handle_fiq)
		return -EBUSY;

	handle_arch_fiq = handle_fiq;
	pr_info("Root FIQ handler: %ps\n", handle_fiq);
	return 0;
}

void __init init_IRQ(void)
{
	init_irq_stacks();