[PATCH FYI 4/4] ARM: cobble together FIQ backtracing

Russell King rmk+kernel at arm.linux.org.uk
Fri Sep 5 02:41:43 PDT 2014


Cobble the FIQ backtracing together, some of this patch based on work
which David Thompson has done.

Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 arch/arm/kernel/entry-armv.S | 96 ++++++++++++++++++++++++++++++++++++++------
 arch/arm/kernel/fiq.c        | 11 ++++-
 arch/arm/kernel/setup.c      |  8 +++-
 arch/arm/kernel/smp.c        |  5 +++
 drivers/irqchip/irq-gic.c    | 45 ++++++++++++++++++---
 5 files changed, 144 insertions(+), 21 deletions(-)

diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 36276cdccfbc..bd1ad0076937 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -146,7 +146,7 @@ ENDPROC(__und_invalid)
 #define SPFIX(code...)
 #endif
 
-	.macro	svc_entry, stack_hole=0
+	.macro	svc_entry, stack_hole=0, trace=1
  UNWIND(.fnstart		)
  UNWIND(.save {r0 - pc}		)
 	sub	sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
@@ -182,9 +182,11 @@ ENDPROC(__und_invalid)
 	@
 	stmia	r7, {r2 - r6}
 
+	.if	\trace
 #ifdef CONFIG_TRACE_IRQFLAGS
 	bl	trace_hardirqs_off
 #endif
+	.endif
 	.endm
 
 	.align	5
@@ -314,7 +316,7 @@ ENDPROC(__pabt_svc)
 #error "sizeof(struct pt_regs) must be a multiple of 8"
 #endif
 
-	.macro	usr_entry
+	.macro	usr_entry, trace=1
  UNWIND(.fnstart	)
  UNWIND(.cantunwind	)	@ don't unwind the user space
 	sub	sp, sp, #S_FRAME_SIZE
@@ -351,10 +353,12 @@ ENDPROC(__pabt_svc)
 	@
 	zero_fp
 
+	.if	\trace
 #ifdef CONFIG_IRQSOFF_TRACER
 	bl	trace_hardirqs_off
 #endif
 	ct_user_exit save = 0
+	.endif
 	.endm
 
 	.macro	kuser_cmpxchg_check
@@ -683,6 +687,61 @@ ENTRY(ret_from_exception)
 ENDPROC(__pabt_usr)
 ENDPROC(ret_from_exception)
 
+	.macro  svc_exit_via_fiq, rpsr
+	mov     r0, sp
+	ldmib   r0, {r1 - r14}  @ abort is deadly from here onward (it will
+				@ clobber state restored below)
+	msr     cpsr_c, #FIQ_MODE | PSR_I_BIT | PSR_F_BIT
+	add     r8, r0, #S_PC
+	ldr     r9, [r0, #S_PSR]
+	msr     spsr_cxsf, r9
+	ldr     r0, [r0, #S_R0]
+	ldmia   r8, {pc}^
+	.endm
+
+	.macro	fiq_handler
+	bl	__fiq_handle
+	.endm
+
+	.align 5
+__fiq_usr:
+	usr_entry trace=0
+	kuser_cmpxchg_check
+	mov	r0, sp
+	fiq_handler
+	get_thread_info tsk
+	restore_user_regs fast = 0, offset = 0
+ UNWIND(.fnend)
+ UNWIND(.cantunwind)
+ENDPROC(__fiq_usr)
+
+__fiq_abt:
+	svc_entry trace=0
+	msr     cpsr_c, #ABT_MODE | PSR_I_BIT | PSR_F_BIT
+	mov     r2, lr          @ Save lr_abt
+	mrs     r3, spsr        @ Save spsr_abt, abort is now safe
+	msr     cpsr_c, #SVC_MODE | PSR_I_BIT | PSR_F_BIT
+	mov	r0, sp
+	push    {r2 - r3}
+	fiq_handler
+	pop     {r0 - r1}
+	msr     cpsr_c, #ABT_MODE | PSR_I_BIT | PSR_F_BIT
+	mov     lr, r0          @ Restore lr_abt, abort is unsafe
+	msr     spsr_cxsf, r1   @ Restore spsr_abt
+	msr     cpsr_c, #SVC_MODE | PSR_I_BIT | PSR_F_BIT
+	svc_exit_via_fiq r5
+ UNWIND(.fnend)
+ENDPROC(__fiq_abt)
+
+__fiq_svc:
+	svc_entry trace=0
+	mov	r0, sp
+	fiq_handler
+	svc_exit_via_fiq r5
+ UNWIND(.fnend)
+ENDPROC(__fiq_svc)
+
+
 /*
  * Register switch for ARMv3 and ARMv4 processors
  * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
@@ -1117,18 +1176,29 @@ vector_rst:
 vector_addrexcptn:
 	b	vector_addrexcptn
 
-/*=============================================================================
- * Undefined FIQs
- *-----------------------------------------------------------------------------
- * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
- * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
- * Basically to switch modes, we *HAVE* to clobber one register...  brain
- * damage alert!  I don't think that we can execute any code in here in any
- * other mode than FIQ...  Ok you can switch to another mode, but you can't
- * get out of that mode without clobbering one register.
+/*
+ * FIQs.  We provide a default stub here, which is used by the kernel
+ * when something fails (eg, spinlock lockup or deadlock, rcu stall,
+ * etc.)
  */
-vector_fiq:
-	subs	pc, lr, #4
+	vector_stub	fiq, FIQ_MODE, 4
+
+	.long	__fiq_usr
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_abt
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
+	.long	__fiq_svc
 
 	.globl	vector_fiq_offset
 	.equ	vector_fiq_offset, vector_fiq
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 918875d96d5d..1743049c433b 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -53,6 +53,7 @@
 	})
 
 static unsigned long no_fiq_insn;
+static struct pt_regs def_fiq_regs;
 
 /* Default reacquire function
  * - we always relinquish FIQ control
@@ -60,8 +61,15 @@ static unsigned long no_fiq_insn;
  */
 static int fiq_def_op(void *ref, int relinquish)
 {
-	if (!relinquish)
+	if (!relinquish) {
+		/* Restore default handler and registers */
+		local_fiq_disable();
+		set_fiq_regs(&dfl_fiq_regs);
 		set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
+		local_fiq_enable();
+
+		/* FIXME: notify irq controller to standard enable FIQs */
+	}
 
 	return 0;
 }
@@ -151,5 +159,6 @@ void __init init_FIQ(int start)
 {
 	unsigned offset = FIQ_OFFSET;
 	no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
+	get_fiq_regs(&dfl_fiq_regs);
 	fiq_start = start;
 }
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 84db893dedc2..22e56f6ff446 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -423,6 +423,8 @@ static void __init elf_hwcap_fixup(void)
 		elf_hwcap &= ~HWCAP_SWP;
 }
 
+static unsigned int fiq_stack[4][1024];
+
 /*
  * cpu_init - initialise one CPU.
  *
@@ -470,7 +472,9 @@ void notrace cpu_init(void)
 	"msr	cpsr_c, %5\n\t"
 	"add	r14, %0, %6\n\t"
 	"mov	sp, r14\n\t"
-	"msr	cpsr_c, %7"
+	"msr	cpsr_c, %7\n\t"
+	"mov	sp, %8\n\t"
+	"msr	cpsr_c, %9"
 	    :
 	    : "r" (stk),
 	      PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
@@ -479,6 +483,8 @@ void notrace cpu_init(void)
 	      "I" (offsetof(struct stack, abt[0])),
 	      PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
 	      "I" (offsetof(struct stack, und[0])),
+	      PLC (PSR_F_BIT | PSR_I_BIT | FIQ_MODE),
+	      "r" (&fiq_stack[cpu][1024]),
 	      PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
 	    : "r14");
 #endif
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 94959f977b82..ce30ed9f8e1a 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -774,3 +774,8 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
 	smp_mb__after_atomic();
 	put_cpu();
 }
+
+void __fiq_handle(struct pt_regs *regs)
+{
+	ipi_cpu_backtrace(regs);
+}
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 4b959e606fe8..ec7be8eaab3a 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -371,9 +371,19 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
 	for (i = 32; i < gic_irqs; i += 4)
 		writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
 
+	/*
+	 * Optionally set all global interrupts to be group 1.
+	 */
+	for (i = 32; i < gic_irqs; i += 32)
+		writel_relaxed(0xffffffff, base + GIC_DIST_IGROUP + i * 4 / 32);
+
 	gic_dist_config(base, gic_irqs, NULL);
 
-	writel_relaxed(1, base + GIC_DIST_CTRL);
+	/*
+	 * Set EnableGrp1/EnableGrp0 (bit 1 and 0) or EnableGrp (bit 0 only,
+	 * bit 1 ignored)
+	 */
+	writel_relaxed(3, base + GIC_DIST_CTRL);
 }
 
 static void gic_cpu_init(struct gic_chip_data *gic)
@@ -400,8 +410,17 @@ static void gic_cpu_init(struct gic_chip_data *gic)
 
 	gic_cpu_config(dist_base, NULL);
 
+	/*
+	 * Set all PPI and SGI interrupts to be group 1.
+	 *
+	 * If grouping is not available (not implemented or prohibited by
+	 * security mode) these registers are read-as-zero/write-ignored.
+	 */
+	writel_relaxed(0xfffffeff, dist_base + GIC_DIST_IGROUP + 0);
+	writel_relaxed(0xa0a0a000, dist_base + GIC_DIST_PRI + 8);
+
 	writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
-	writel_relaxed(1, base + GIC_CPU_CTRL);
+	writel_relaxed(0x1f, base + GIC_CPU_CTRL);
 }
 
 void gic_cpu_if_down(void)
@@ -485,7 +504,7 @@ static void gic_dist_restore(unsigned int gic_nr)
 		writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
 			dist_base + GIC_DIST_ENABLE_SET + i * 4);
 
-	writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+	writel_relaxed(3, dist_base + GIC_DIST_CTRL);
 }
 
 static void gic_cpu_save(unsigned int gic_nr)
@@ -542,7 +561,7 @@ static void gic_cpu_restore(unsigned int gic_nr)
 		writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
 
 	writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
-	writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+	writel_relaxed(0x1f, cpu_base + GIC_CPU_CTRL);
 }
 
 static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
@@ -600,10 +619,18 @@ static void __init gic_pm_init(struct gic_chip_data *gic)
 #endif
 
 #ifdef CONFIG_SMP
+static bool sgi_is_nonsecure(int irq, struct gic_chip_data *gic)
+{
+	/* FIXME: this should be done in a more generic way */
+	return irq != 8;
+}
+
 static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
 {
-	int cpu;
+	struct gic_chip_data *gic = &gic_data[0];
 	unsigned long flags, map = 0;
+	unsigned int softirq;
+	int cpu;
 
 	raw_spin_lock_irqsave(&irq_controller_lock, flags);
 
@@ -617,8 +644,14 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
 	 */
 	dmb(ishst);
 
+	softirq = map << 16 | irq;
+
+	/* SATT only has effect if we are running in the secure world */
+	if (sgi_is_nonsecure(irq, gic))
+		softirq |= 0x8000;
+
 	/* this always happens on GIC0 */
-	writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
+	writel_relaxed(softirq, gic_data_dist_base(gic) + GIC_DIST_SOFTINT);
 
 	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
-- 
1.8.3.1




More information about the linux-arm-kernel mailing list