Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Mark Rutland and committed by
Catalin Marinas
18fdb634 d447bf09

+96 -105
-15
arch/arm64/include/asm/arch_gicv3.h
··· 175 175 176 176 static inline void gic_pmr_mask_irqs(void) 177 177 { 178 - BUILD_BUG_ON(GICD_INT_DEF_PRI < (__GIC_PRIO_IRQOFF | 179 - GIC_PRIO_PSR_I_SET)); 180 - BUILD_BUG_ON(GICD_INT_DEF_PRI >= GIC_PRIO_IRQON); 181 - /* 182 - * Need to make sure IRQON allows IRQs when SCR_EL3.FIQ is cleared 183 - * and non-secure PMR accesses are not subject to the shifts that 184 - * are applied to IRQ priorities 185 - */ 186 - BUILD_BUG_ON((0x80 | (GICD_INT_DEF_PRI >> 1)) >= GIC_PRIO_IRQON); 187 - /* 188 - * Same situation as above, but now we make sure that we can mask 189 - * regular interrupts. 190 - */ 191 - BUILD_BUG_ON((0x80 | (GICD_INT_DEF_PRI >> 1)) < (__GIC_PRIO_IRQOFF_NS | 192 - GIC_PRIO_PSR_I_SET)); 193 178 gic_write_pmr(GIC_PRIO_IRQOFF); 194 179 } 195 180
+5 -28
arch/arm64/include/asm/ptrace.h
··· 21 21 #define INIT_PSTATE_EL2 \ 22 22 (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL2h) 23 23 24 - /* 25 - * PMR values used to mask/unmask interrupts. 26 - * 27 - * GIC priority masking works as follows: if an IRQ's priority is a higher value 28 - * than the value held in PMR, that IRQ is masked. Lowering the value of PMR 29 - * means masking more IRQs (or at least that the same IRQs remain masked). 30 - * 31 - * To mask interrupts, we clear the most significant bit of PMR. 32 - * 33 - * Some code sections either automatically switch back to PSR.I or explicitly 34 - * require to not use priority masking. If bit GIC_PRIO_PSR_I_SET is included 35 - * in the priority mask, it indicates that PSR.I should be set and 36 - * interrupt disabling temporarily does not rely on IRQ priorities. 37 - */ 38 - #define GIC_PRIO_IRQON 0xe0 39 - #define __GIC_PRIO_IRQOFF (GIC_PRIO_IRQON & ~0x80) 40 - #define __GIC_PRIO_IRQOFF_NS 0xa0 41 - #define GIC_PRIO_PSR_I_SET (1 << 4) 24 + #include <linux/irqchip/arm-gic-v3-prio.h> 42 25 43 - #define GIC_PRIO_IRQOFF \ 44 - ({ \ 45 - extern struct static_key_false gic_nonsecure_priorities;\ 46 - u8 __prio = __GIC_PRIO_IRQOFF; \ 47 - \ 48 - if (static_branch_unlikely(&gic_nonsecure_priorities)) \ 49 - __prio = __GIC_PRIO_IRQOFF_NS; \ 50 - \ 51 - __prio; \ 52 - }) 26 + #define GIC_PRIO_IRQON GICV3_PRIO_UNMASKED 27 + #define GIC_PRIO_IRQOFF GICV3_PRIO_IRQ 28 + 29 + #define GIC_PRIO_PSR_I_SET GICV3_PRIO_PSR_I_SET 53 30 54 31 /* Additional SPSR bits not exposed in the UABI */ 55 32 #define PSR_MODE_THREAD_BIT (1 << 0)
-5
arch/arm64/kernel/image-vars.h
··· 105 105 KVM_NVHE_ALIAS(vgic_v2_cpuif_trap); 106 106 KVM_NVHE_ALIAS(vgic_v3_cpuif_trap); 107 107 108 - #ifdef CONFIG_ARM64_PSEUDO_NMI 109 - /* Static key checked in GIC_PRIO_IRQOFF. */ 110 - KVM_NVHE_ALIAS(gic_nonsecure_priorities); 111 - #endif 112 - 113 108 /* EL2 exception handling */ 114 109 KVM_NVHE_ALIAS(__start___kvm_ex_table); 115 110 KVM_NVHE_ALIAS(__stop___kvm_ex_table);
+39 -57
drivers/irqchip/irq-gic-v3.c
··· 25 25 #include <linux/irqchip.h> 26 26 #include <linux/irqchip/arm-gic-common.h> 27 27 #include <linux/irqchip/arm-gic-v3.h> 28 + #include <linux/irqchip/arm-gic-v3-prio.h> 28 29 #include <linux/irqchip/irq-partition-percpu.h> 29 30 #include <linux/bitfield.h> 30 31 #include <linux/bits.h> ··· 38 37 39 38 #include "irq-gic-common.h" 40 39 41 - static u8 dist_prio_irq __ro_after_init = GICD_INT_DEF_PRI; 42 - static u8 dist_prio_nmi __ro_after_init = GICD_INT_DEF_PRI & ~0x80; 40 + static u8 dist_prio_irq __ro_after_init = GICV3_PRIO_IRQ; 41 + static u8 dist_prio_nmi __ro_after_init = GICV3_PRIO_NMI; 43 42 44 43 #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) 45 44 #define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1) ··· 111 110 */ 112 111 static DEFINE_STATIC_KEY_FALSE(supports_pseudo_nmis); 113 112 114 - DEFINE_STATIC_KEY_FALSE(gic_nonsecure_priorities); 115 - EXPORT_SYMBOL(gic_nonsecure_priorities); 116 - 117 - /* 118 - * When the Non-secure world has access to group 0 interrupts (as a 119 - * consequence of SCR_EL3.FIQ == 0), reading the ICC_RPR_EL1 register will 120 - * return the Distributor's view of the interrupt priority. 121 - * 122 - * When GIC security is enabled (GICD_CTLR.DS == 0), the interrupt priority 123 - * written by software is moved to the Non-secure range by the Distributor. 124 - * 125 - * If both are true (which is when gic_nonsecure_priorities gets enabled), 126 - * we need to shift down the priority programmed by software to match it 127 - * against the value returned by ICC_RPR_EL1. 128 - */ 129 - #define GICD_INT_RPR_PRI(priority) \ 130 - ({ \ 131 - u32 __priority = (priority); \ 132 - if (static_branch_unlikely(&gic_nonsecure_priorities)) \ 133 - __priority = 0x80 | (__priority >> 1); \ 134 - \ 135 - __priority; \ 136 - }) 137 - 138 113 static u32 gic_get_pribits(void) 139 114 { 140 115 u32 pribits; ··· 161 184 { 162 185 cpus_have_security_disabled = gic_dist_security_disabled(); 163 186 cpus_have_group0 = gic_has_group0(); 187 + 188 + /* 189 + * How priority values are used by the GIC depends on two things: 190 + * the security state of the GIC (controlled by the GICD_CTRL.DS bit) 191 + * and if Group 0 interrupts can be delivered to Linux in the non-secure 192 + * world as FIQs (controlled by the SCR_EL3.FIQ bit). These affect the 193 + * way priorities are presented in ICC_PMR_EL1 and in the distributor: 194 + * 195 + * GICD_CTRL.DS | SCR_EL3.FIQ | ICC_PMR_EL1 | Distributor 196 + * ------------------------------------------------------- 197 + * 1 | - | unchanged | unchanged 198 + * ------------------------------------------------------- 199 + * 0 | 1 | non-secure | non-secure 200 + * ------------------------------------------------------- 201 + * 0 | 0 | unchanged | non-secure 202 + * 203 + * In the non-secure view reads and writes are modified: 204 + * 205 + * - A value written is right-shifted by one and the MSB is set, 206 + * forcing the priority into the non-secure range. 207 + * 208 + * - A value read is left-shifted by one. 209 + * 210 + * In the first two cases, where ICC_PMR_EL1 and the interrupt priority 211 + * are both either modified or unchanged, we can use the same set of 212 + * priorities. 213 + * 214 + * In the last case, where only the interrupt priorities are modified to 215 + * be in the non-secure range, we program the non-secure values into 216 + * the distributor to match the PMR values we want. 217 + */ 218 + if (cpus_have_group0 & !cpus_have_security_disabled) { 219 + dist_prio_irq = __gicv3_prio_to_ns(dist_prio_irq); 220 + dist_prio_nmi = __gicv3_prio_to_ns(dist_prio_nmi); 221 + } 164 222 165 223 pr_info("GICD_CTRL.DS=%d, SCR_EL3.FIQ=%d\n", 166 224 cpus_have_security_disabled, ··· 823 811 if (!gic_supports_nmi()) 824 812 return false; 825 813 826 - return unlikely(gic_read_rpr() == GICD_INT_RPR_PRI(dist_prio_nmi)); 814 + return unlikely(gic_read_rpr() == GICV3_PRIO_NMI); 827 815 } 828 816 829 817 static bool gic_irqnr_is_special(u32 irqnr) ··· 1971 1959 1972 1960 pr_info("Pseudo-NMIs enabled using %s ICC_PMR_EL1 synchronisation\n", 1973 1961 gic_has_relaxed_pmr_sync() ? "relaxed" : "forced"); 1974 - 1975 - /* 1976 - * How priority values are used by the GIC depends on two things: 1977 - * the security state of the GIC (controlled by the GICD_CTRL.DS bit) 1978 - * and if Group 0 interrupts can be delivered to Linux in the non-secure 1979 - * world as FIQs (controlled by the SCR_EL3.FIQ bit). These affect the 1980 - * ICC_PMR_EL1 register and the priority that software assigns to 1981 - * interrupts: 1982 - * 1983 - * GICD_CTRL.DS | SCR_EL3.FIQ | ICC_PMR_EL1 | Group 1 priority 1984 - * ----------------------------------------------------------- 1985 - * 1 | - | unchanged | unchanged 1986 - * ----------------------------------------------------------- 1987 - * 0 | 1 | non-secure | non-secure 1988 - * ----------------------------------------------------------- 1989 - * 0 | 0 | unchanged | non-secure 1990 - * 1991 - * where non-secure means that the value is right-shifted by one and the 1992 - * MSB bit set, to make it fit in the non-secure priority range. 1993 - * 1994 - * In the first two cases, where ICC_PMR_EL1 and the interrupt priority 1995 - * are both either modified or unchanged, we can use the same set of 1996 - * priorities. 1997 - * 1998 - * In the last case, where only the interrupt priorities are modified to 1999 - * be in the non-secure range, we use a different PMR value to mask IRQs 2000 - * and the rest of the values that we use remain unchanged. 2001 - */ 2002 - if (gic_has_group0() && !gic_dist_security_disabled()) 2003 - static_branch_enable(&gic_nonsecure_priorities); 2004 1962 2005 1963 static_branch_enable(&supports_pseudo_nmis); 2006 1964
+52
include/linux/irqchip/arm-gic-v3-prio.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + #ifndef __LINUX_IRQCHIP_ARM_GIC_V3_PRIO_H 4 + #define __LINUX_IRQCHIP_ARM_GIC_V3_PRIO_H 5 + 6 + /* 7 + * GIC priorities from the view of the PMR/RPR. 8 + * 9 + * These values are chosen to be valid in either the absolute priority space or 10 + * the NS view of the priority space. The value programmed into the distributor 11 + * and ITS will be chosen at boot time such that these values appear in the 12 + * PMR/RPR. 13 + * 14 + * GICV3_PRIO_UNMASKED is the PMR view of the priority to use to permit both 15 + * IRQs and pseudo-NMIs. 16 + * 17 + * GICV3_PRIO_IRQ is the PMR view of the priority of regular interrupts. This 18 + * can be written to the PMR to mask regular IRQs. 19 + * 20 + * GICV3_PRIO_NMI is the PMR view of the priority of pseudo-NMIs. This can be 21 + * written to the PMR to mask pseudo-NMIs. 22 + * 23 + * On arm64 some code sections either automatically switch back to PSR.I or 24 + * explicitly require to not use priority masking. If bit GICV3_PRIO_PSR_I_SET 25 + * is included in the priority mask, it indicates that PSR.I should be set and 26 + * interrupt disabling temporarily does not rely on IRQ priorities. 27 + */ 28 + #define GICV3_PRIO_UNMASKED 0xe0 29 + #define GICV3_PRIO_IRQ 0xc0 30 + #define GICV3_PRIO_NMI 0x80 31 + 32 + #define GICV3_PRIO_PSR_I_SET (1 << 4) 33 + 34 + #ifndef __ASSEMBLER__ 35 + 36 + #define __gicv3_prio_to_ns(p) (0xff & ((p) << 1)) 37 + #define __gicv3_ns_to_prio(ns) (0x80 | ((ns) >> 1)) 38 + 39 + #define __gicv3_prio_valid_ns(p) \ 40 + (__gicv3_ns_to_prio(__gicv3_prio_to_ns(p)) == (p)) 41 + 42 + static_assert(__gicv3_prio_valid_ns(GICV3_PRIO_NMI)); 43 + static_assert(__gicv3_prio_valid_ns(GICV3_PRIO_IRQ)); 44 + 45 + static_assert(GICV3_PRIO_NMI < GICV3_PRIO_IRQ); 46 + static_assert(GICV3_PRIO_IRQ < GICV3_PRIO_UNMASKED); 47 + 48 + static_assert(GICV3_PRIO_IRQ < (GICV3_PRIO_IRQ | GICV3_PRIO_PSR_I_SET)); 49 + 50 + #endif /* __ASSEMBLER */ 51 + 52 + #endif /* __LINUX_IRQCHIP_ARM_GIC_V3_PRIO_H */