kernel-hacking-2024-linux-s.../include/linux/irqchip/arm-gic-v3-prio.h
Mark Rutland 18fdb6348c arm64: irqchip/gic-v3: Select priorities at boot time
The distributor and PMR/RPR can present different views of the interrupt
priority space dependent upon the values of GICD_CTLR.DS and
SCR_EL3.FIQ. Currently we treat the distributor's view of the priority
space as canonical, and when the two differ we change the way we handle
values in the PMR/RPR, using the `gic_nonsecure_priorities` static key
to decide what to do.

This approach works, but it's sub-optimal. When using pseudo-NMI we
manipulate the distributor rarely, and we manipulate the PMR/RPR
registers very frequently in code spread out throughout the kernel (e.g.
local_irq_{save,restore}()). It would be nicer if we could use fixed
values for the PMR/RPR, and dynamically choose the values programmed
into the distributor.

This patch changes the GICv3 driver and arm64 code accordingly. PMR
values are chosen at compile time, and the GICv3 driver determines the
appropriate values to program into the distributor at boot time. This
removes the need for the `gic_nonsecure_priorities` static key and
results in smaller and better generated code for saving/restoring the
irqflags.

Before this patch, local_irq_disable() compiles to:

| 0000000000000000 <outlined_local_irq_disable>:
|    0:   d503201f        nop
|    4:   d50343df        msr     daifset, #0x3
|    8:   d65f03c0        ret
|    c:   d503201f        nop
|   10:   d2800c00        mov     x0, #0x60                       // #96
|   14:   d5184600        msr     icc_pmr_el1, x0
|   18:   d65f03c0        ret
|   1c:   d2801400        mov     x0, #0xa0                       // #160
|   20:   17fffffd        b       14 <outlined_local_irq_disable+0x14>

After this patch, local_irq_disable() compiles to:

| 0000000000000000 <outlined_local_irq_disable>:
|    0:   d503201f        nop
|    4:   d50343df        msr     daifset, #0x3
|    8:   d65f03c0        ret
|    c:   d2801800        mov     x0, #0xc0                       // #192
|   10:   d5184600        msr     icc_pmr_el1, x0
|   14:   d65f03c0        ret

... with 3 fewer instructions per call.

For defconfig + CONFIG_PSEUDO_NMI=y, this results in a minor saving of
~4K of text, and will make it easier to make further improvements to the
way we manipulate irqflags and DAIF bits.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Alexandru Elisei <alexandru.elisei@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Tested-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20240617111841.2529370-6-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
2024-06-24 18:16:45 +01:00

52 lines
1.8 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __LINUX_IRQCHIP_ARM_GIC_V3_PRIO_H
#define __LINUX_IRQCHIP_ARM_GIC_V3_PRIO_H
/*
* GIC priorities from the view of the PMR/RPR.
*
* These values are chosen to be valid in either the absolute priority space or
* the NS view of the priority space. The value programmed into the distributor
* and ITS will be chosen at boot time such that these values appear in the
* PMR/RPR.
*
* GICV3_PRIO_UNMASKED is the PMR view of the priority to use to permit both
* IRQs and pseudo-NMIs.
*
* GICV3_PRIO_IRQ is the PMR view of the priority of regular interrupts. This
* can be written to the PMR to mask regular IRQs.
*
* GICV3_PRIO_NMI is the PMR view of the priority of pseudo-NMIs. This can be
* written to the PMR to mask pseudo-NMIs.
*
* On arm64 some code sections either automatically switch back to PSR.I or
* explicitly require to not use priority masking. If bit GICV3_PRIO_PSR_I_SET
* is included in the priority mask, it indicates that PSR.I should be set and
* interrupt disabling temporarily does not rely on IRQ priorities.
*/
#define GICV3_PRIO_UNMASKED 0xe0
#define GICV3_PRIO_IRQ 0xc0
#define GICV3_PRIO_NMI 0x80
#define GICV3_PRIO_PSR_I_SET (1 << 4)
#ifndef __ASSEMBLER__
#define __gicv3_prio_to_ns(p) (0xff & ((p) << 1))
#define __gicv3_ns_to_prio(ns) (0x80 | ((ns) >> 1))
#define __gicv3_prio_valid_ns(p) \
(__gicv3_ns_to_prio(__gicv3_prio_to_ns(p)) == (p))
static_assert(__gicv3_prio_valid_ns(GICV3_PRIO_NMI));
static_assert(__gicv3_prio_valid_ns(GICV3_PRIO_IRQ));
static_assert(GICV3_PRIO_NMI < GICV3_PRIO_IRQ);
static_assert(GICV3_PRIO_IRQ < GICV3_PRIO_UNMASKED);
static_assert(GICV3_PRIO_IRQ < (GICV3_PRIO_IRQ | GICV3_PRIO_PSR_I_SET));
#endif /* __ASSEMBLER */
#endif /* __LINUX_IRQCHIP_ARM_GIC_V3_PRIO_H */