[PATCH 2/3] arm: introduce IRQ stacks

From: Maninder Singh
Date: Thu Oct 08 2020 - 03:17:57 EST


This patch adds code for switching to IRQ stack.
IRQ stack and Kernel SVC stack have below design.

IRQ STACK:-
------------ IRQ stack top
| |
------------
. .
. .
. .
------------
| sp | <- irq_stack_base + 0x8
------------
| fp | <- irq_stack_base + 0x4
------------
|tinfo_ptr | /* pointer to thread info */
irq_stack_ptr --> ------------ IRQ stack base

Kernel SVC stack:-
------------ Kernel stack top
| |
------------
. .
. .
. .
------------
| |
| |
------------
|tinfo_ptr | /* pointer to thread info */
------------ Kernel stack base

Co-developed-by: Vaneet Narang <v.narang@xxxxxxxxxxx>
Signed-off-by: Vaneet Narang <v.narang@xxxxxxxxxxx>
Signed-off-by: Maninder Singh <maninder1.s@xxxxxxxxxxx>
---
arch/arm/include/asm/assembler.h | 8 ++++++++
arch/arm/include/asm/irq.h | 6 ++++++
arch/arm/kernel/entry-armv.S | 41 +++++++++++++++++++++++++++++++++++++++-
arch/arm/kernel/irq.c | 24 +++++++++++++++++++++++
4 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index 8512bdc..82ee6ee 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -212,6 +212,14 @@
#endif
.endm

+ .macro this_cpu_ptr, sym, reg, tmp
+ ldr \reg, =\sym
+#if defined(CONFIG_SMP) && !defined(CONFIG_CPU_V6)
+ mrc p15, 0, \tmp, cr13, c0, 4
+ add \reg, \reg, \tmp
+#endif
+ .endm
+
/*
* Increment/decrement the preempt count.
*/
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index 46d4114..f3299ab 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -22,10 +22,16 @@
#define NO_IRQ ((unsigned int)(-1))
#endif

+#define IRQ_STACK_SIZE THREAD_SIZE
+
#ifndef __ASSEMBLY__
struct irqaction;
struct pt_regs;

+#ifdef CONFIG_IRQ_STACK
+DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
+#endif
+
extern void asm_do_IRQ(unsigned int, struct pt_regs *);
void handle_IRQ(unsigned int, struct pt_regs *);
void init_IRQ(void);
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 55a47df..13a5889 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -32,6 +32,43 @@
#include "entry-header.S"
#include <asm/entry-macro-multi.S>
#include <asm/probes.h>
+#ifdef CONFIG_IRQ_STACK
+#include <asm/irq.h>
+#endif
+
+ .macro irq_stack_entry
+#ifdef CONFIG_IRQ_STACK
+ mov r6, sp /* preserve sp */
+
+ this_cpu_ptr irq_stack_ptr, r7, r8
+ ldr r7, [r7]
+ mov r8, r7
+
+ /*
+ * Compare sp with base of IRQ stack.
+ * if the top ~(#THREAD_SIZE_ORDER + PAGE_SHIFT) bits match,
+ * we are on a irq stack.
+ */
+ eor r8, r8, sp
+ lsrs r8, #THREAD_SIZE_ORDER + PAGE_SHIFT
+ beq 9998f
+
+ /*
+ * store thread info pointer on IRQ stack and
+ * switch to the irq stack.
+ */
+ get_thread_info r8
+ stm r7, {r8, fp, sp}
+ add sp, r7, #IRQ_STACK_SIZE
+9998:
+#endif
+ .endm
+
+ .macro irq_stack_exit
+#ifdef CONFIG_IRQ_STACK
+ mov sp, r6 /* restore sp */
+#endif
+ .endm

/*
* Interrupt handling.
@@ -41,11 +78,13 @@
ldr r1, =handle_arch_irq
mov r0, sp
badr lr, 9997f
+ irq_stack_entry
ldr pc, [r1]
+9997:
+ irq_stack_exit
#else
arch_irq_handler_default
#endif
-9997:
.endm

.macro pabt_helper
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 698b6f6..79872e5 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -43,6 +43,15 @@

unsigned long irq_err_count;

+#ifdef CONFIG_IRQ_STACK
+DEFINE_PER_CPU(unsigned long *, irq_stack_ptr);
+
+/*
+ * irq_stack must be IRQ_STACK_SIZE(THREAD_SIZE) aligned,
+ */
+DEFINE_PER_CPU(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack) __aligned(IRQ_STACK_SIZE);
+#endif
+
int arch_show_interrupts(struct seq_file *p, int prec)
{
#ifdef CONFIG_FIQ
@@ -55,6 +64,20 @@ int arch_show_interrupts(struct seq_file *p, int prec)
return 0;
}

+#ifdef CONFIG_IRQ_STACK
+static void init_irq_stacks(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ per_cpu(irq_stack_ptr, cpu) = per_cpu(irq_stack, cpu);
+}
+#else
+static inline void init_irq_stacks(void)
+{
+}
+#endif
+
/*
* handle_IRQ handles all hardware IRQ's. Decoded IRQs should
* not come via this function. Instead, they should provide their
@@ -79,6 +102,7 @@ void __init init_IRQ(void)
{
int ret;

+ init_irq_stacks();
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init();
else
--
1.9.1