2014-06-24 15:18:32

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v6 0/4] arm: KGDB NMI/FIQ support

This patchset makes it possible to use kgdb's NMI infrastructure on ARM
platforms.

The patches have been previously circulated as part of a large patchset
mixing together ARM architecture code and driver changes
(http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901 ). This
patchset is dramatically cut down to include only the arch/arm code. The
driver code (irqchip and tty/serial) will follow when/if the arch code
is accepted.

The first two patches modify the FIQ infrastructure to allow interrupt
controller drivers to register callbacks (the fiq_chip structure) to
manage FIQ routings and to signal FIQ EOI. This makes it possible to
use FIQ in multi-platform kernels and with recent ARM interrupt
controllers.

The remaining two patches provide architecture support for KGDB's NMI
feature (and rely upon the preceding changes to the FIQ code).

Tested on qemu (versatile), STiH416 (B2020 board) and Freescale i.MX6
quad (wandboard).

Changes since v5:

- Separated anything not strictly impacting arch/arm.
- Fixed a spurious add/remove of code within the series (there was
broken code in intermediate patches)

For previous changes see:
http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901


Anton Vorontsov (2):
ARM: Move some macros from entry-armv to entry-header
ARM: Add KGDB/KDB FIQ debugger generic code

Daniel Thompson (2):
arm: fiq: Add callbacks to manage FIQ routings
arm: fiq: Allow EOI to be communicated to the intc

arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 18 +++++
arch/arm/include/asm/fiq.h | 13 ++++
arch/arm/include/asm/kgdb.h | 7 ++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/entry-armv.S | 151 +----------------------------------
arch/arm/kernel/entry-header.S | 164 +++++++++++++++++++++++++++++++++++++++
arch/arm/kernel/fiq.c | 112 +++++++++++++++++++++++++-
arch/arm/kernel/kgdb_fiq.c | 124 +++++++++++++++++++++++++++++
arch/arm/kernel/kgdb_fiq_entry.S | 87 +++++++++++++++++++++
10 files changed, 527 insertions(+), 152 deletions(-)
create mode 100644 arch/arm/kernel/kgdb_fiq.c
create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S

--
1.9.3


2014-06-24 15:18:40

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v6 1/4] arm: fiq: Add callbacks to manage FIQ routings

Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
virq into a FIQ virq. This is too inflexible for multi-platform kernels
and makes runtime error checking impossible.

We solve this by introducing a flexible mapping that allows interrupt
controllers that support FIQ to register those mappings. This, in turn,
makes it much possible for drivers in DT kernels to install FIQ handlers
without knowing anything about the interrupt controller.

Signed-off-by: Daniel Thompson <[email protected]>
Cc: Russell King <[email protected]>
Cc: Fabio Estevam <[email protected]>
Cc: Nicolas Pitre <[email protected]>
---
arch/arm/include/asm/fiq.h | 7 +++
arch/arm/kernel/fiq.c | 103 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index d493d0b..a7806ef 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -18,6 +18,11 @@

#include <asm/ptrace.h>

+struct fiq_chip {
+ void (*fiq_enable)(struct irq_data *data);
+ void (*fiq_disable)(struct irq_data *data);
+};
+
struct fiq_handler {
struct fiq_handler *next;
/* Name
@@ -38,6 +43,8 @@ extern void release_fiq(struct fiq_handler *f);
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern bool has_fiq(int fiq);
+extern void fiq_register_mapping(int irq, struct fiq_chip *chip);

/* helpers defined in fiqasm.S: */
extern void __set_fiq_regs(unsigned long const *regs);
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 918875d..567f8fd 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -40,6 +40,9 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/seq_file.h>
+#include <linux/irq.h>
+#include <linux/radix-tree.h>
+#include <linux/slab.h>

#include <asm/cacheflush.h>
#include <asm/cp15.h>
@@ -52,7 +55,15 @@
(unsigned)&vector_fiq_offset; \
})

+struct fiq_data {
+ struct fiq_chip *fiq_chip;
+ struct irq_data *irq_data;
+};
+
static unsigned long no_fiq_insn;
+static int fiq_start = -1;
+static RADIX_TREE(fiq_data_tree, GFP_KERNEL);
+static DEFINE_MUTEX(fiq_data_mutex);

/* Default reacquire function
* - we always relinquish FIQ control
@@ -127,18 +138,65 @@ void release_fiq(struct fiq_handler *f)
while (current_fiq->fiq_op(current_fiq->dev_id, 0));
}

-static int fiq_start;
+static struct fiq_data *lookup_fiq_data(int fiq)
+{
+ struct fiq_data *data;
+
+ rcu_read_lock();
+ data = radix_tree_lookup(&fiq_data_tree, fiq);
+ rcu_read_unlock();
+
+ return data;
+}

void enable_fiq(int fiq)
{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data) {
+ if (data->fiq_chip->fiq_enable)
+ data->fiq_chip->fiq_enable(data->irq_data);
+ enable_irq(fiq);
+ return;
+ }
+
+ if (WARN_ON(fiq_start == -1))
+ return;
+
enable_irq(fiq + fiq_start);
}

void disable_fiq(int fiq)
{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data) {
+ if (data->fiq_chip->fiq_disable)
+ data->fiq_chip->fiq_disable(data->irq_data);
+ disable_irq(fiq);
+ return;
+ }
+
+ if (WARN_ON(fiq_start == -1))
+ return;
+
disable_irq(fiq + fiq_start);
}

+bool has_fiq(int fiq)
+{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data)
+ return true;
+
+ if (fiq_start == -1)
+ return false;
+
+ return fiq > fiq_start;
+}
+EXPORT_SYMBOL(has_fiq);
+
EXPORT_SYMBOL(set_fiq_handler);
EXPORT_SYMBOL(__set_fiq_regs); /* defined in fiqasm.S */
EXPORT_SYMBOL(__get_fiq_regs); /* defined in fiqasm.S */
@@ -147,9 +205,50 @@ EXPORT_SYMBOL(release_fiq);
EXPORT_SYMBOL(enable_fiq);
EXPORT_SYMBOL(disable_fiq);

+/*
+ * Add a mapping from a Linux irq to the fiq data.
+ */
+void fiq_register_mapping(int irq, struct fiq_chip *chip)
+{
+ struct fiq_data *fiq_data = NULL;
+ int res;
+
+ /* fiq_register_mapping can't be mixed with init_FIQ */
+ BUG_ON(fiq_start != -1);
+
+ fiq_data = kmalloc(sizeof(*fiq_data), GFP_KERNEL);
+ if (!fiq_data)
+ goto err;
+
+ fiq_data->fiq_chip = chip;
+ fiq_data->irq_data = irq_get_irq_data(irq);
+ BUG_ON(!fiq_data->irq_data);
+
+ mutex_lock(&fiq_data_mutex);
+ res = radix_tree_insert(&fiq_data_tree, irq, fiq_data);
+ mutex_unlock(&fiq_data_mutex);
+ if (res)
+ goto err;
+
+ return;
+
+err:
+ kfree(fiq_data);
+ pr_err("fiq: Cannot register mapping %d\n", irq);
+}
+
+/*
+ * Set the offset between normal IRQs and their FIQ shadows.
+ */
void __init init_FIQ(int start)
{
+ fiq_start = start;
+}
+
+static int __init init_default_fiq_handler(void)
+{
unsigned offset = FIQ_OFFSET;
no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
- fiq_start = start;
+ return 0;
}
+pure_initcall(init_default_fiq_handler);
--
1.9.3

2014-06-24 15:18:54

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v6 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

From: Anton Vorontsov <[email protected]>

The FIQ debugger may be used to debug situations when the kernel stuck
in uninterruptable sections, e.g. the kernel infinitely loops or
deadlocked in an interrupt or with interrupts disabled.

By default KGDB FIQ is disabled in runtime, but can be enabled with
kgdb_fiq.enable=1 kernel command line option.

Signed-off-by: Anton Vorontsov <[email protected]>
Signed-off-by: John Stultz <[email protected]>
Signed-off-by: Daniel Thompson <[email protected]>
Cc: Russell King <[email protected]>
Cc: Ben Dooks <[email protected]>
Cc: Dave Martin <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 18 ++++++
arch/arm/include/asm/kgdb.h | 7 +++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/kgdb_fiq.c | 124 +++++++++++++++++++++++++++++++++++++++
arch/arm/kernel/kgdb_fiq_entry.S | 87 +++++++++++++++++++++++++++
6 files changed, 239 insertions(+)
create mode 100644 arch/arm/kernel/kgdb_fiq.c
create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 245058b..f385b27 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -297,6 +297,7 @@ choice
config ARCH_MULTIPLATFORM
bool "Allow multiple platforms to be selected"
depends on MMU
+ select ARCH_MIGHT_HAVE_KGDB_FIQ
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_HAS_SG_CHAIN
select ARM_PATCH_PHYS_VIRT
@@ -346,6 +347,7 @@ config ARCH_REALVIEW

config ARCH_VERSATILE
bool "ARM Ltd. Versatile family"
+ select ARCH_MIGHT_HAVE_KGDB_FIQ
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_AMBA
select ARM_TIMER_SP804
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 26536f7..c7342b6 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -2,6 +2,24 @@ menu "Kernel hacking"

source "lib/Kconfig.debug"

+config ARCH_MIGHT_HAVE_KGDB_FIQ
+ bool
+
+config KGDB_FIQ
+ bool "KGDB FIQ support"
+ depends on KGDB_KDB && ARCH_MIGHT_HAVE_KGDB_FIQ && !THUMB2_KERNEL
+ select FIQ
+ help
+ The FIQ debugger may be used to debug situations when the
+ kernel stuck in uninterruptable sections, e.g. the kernel
+ infinitely loops or deadlocked in an interrupt or with
+ interrupts disabled.
+
+ By default KGDB FIQ is disabled at runtime, but can be
+ enabled with kgdb_fiq.enable=1 kernel command line option.
+
+ If unsure, say N.
+
config ARM_PTDUMP
bool "Export kernel pagetable layout to userspace via debugfs"
depends on DEBUG_KERNEL
diff --git a/arch/arm/include/asm/kgdb.h b/arch/arm/include/asm/kgdb.h
index 0a9d5dd..5de21f01 100644
--- a/arch/arm/include/asm/kgdb.h
+++ b/arch/arm/include/asm/kgdb.h
@@ -11,7 +11,9 @@
#define __ARM_KGDB_H__

#include <linux/ptrace.h>
+#include <linux/linkage.h>
#include <asm/opcodes.h>
+#include <asm/exception.h>

/*
* GDB assumes that we're a user process being debugged, so
@@ -48,6 +50,11 @@ static inline void arch_kgdb_breakpoint(void)
extern void kgdb_handle_bus_error(void);
extern int kgdb_fault_expected;

+extern char kgdb_fiq_handler;
+extern char kgdb_fiq_handler_end;
+asmlinkage void __exception_irq_entry kgdb_fiq_do_handle(struct pt_regs *regs);
+extern int kgdb_register_fiq(unsigned int fiq);
+
#endif /* !__ASSEMBLY__ */

/*
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 38ddd9f..30ee8f3 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -68,6 +68,7 @@ endif
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
obj-$(CONFIG_KGDB) += kgdb.o
+obj-$(CONFIG_KGDB_FIQ) += kgdb_fiq_entry.o kgdb_fiq.o
obj-$(CONFIG_ARM_UNWIND) += unwind.o
obj-$(CONFIG_HAVE_TCM) += tcm.o
obj-$(CONFIG_OF) += devtree.o
diff --git a/arch/arm/kernel/kgdb_fiq.c b/arch/arm/kernel/kgdb_fiq.c
new file mode 100644
index 0000000..dbf4873
--- /dev/null
+++ b/arch/arm/kernel/kgdb_fiq.c
@@ -0,0 +1,124 @@
+/*
+ * KGDB FIQ
+ *
+ * Copyright 2010 Google, Inc.
+ * Arve Hjønnevåg <[email protected]>
+ * Colin Cross <[email protected]>
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/hardirq.h>
+#include <linux/atomic.h>
+#include <linux/kdb.h>
+#include <linux/kgdb.h>
+#include <asm/fiq.h>
+#include <asm/exception.h>
+
+static int kgdb_fiq_enabled;
+module_param_named(enable, kgdb_fiq_enabled, int, 0600);
+MODULE_PARM_DESC(enable, "set to 1 to enable FIQ KGDB");
+
+static unsigned int kgdb_fiq;
+
+asmlinkage void __exception_irq_entry kgdb_fiq_do_handle(struct pt_regs *regs)
+{
+ if (kgdb_nmi_poll_knock()) {
+ nmi_enter();
+ kgdb_handle_exception(1, 0, 0, regs);
+ nmi_exit();
+ }
+
+ eoi_fiq(kgdb_fiq);
+}
+
+static struct fiq_handler kgdb_fiq_desc = {
+ .name = "kgdb",
+};
+
+static long kgdb_fiq_setup_stack(void *info)
+{
+ struct pt_regs regs;
+
+ regs.ARM_sp = __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER) +
+ THREAD_START_SP;
+ WARN_ON(!regs.ARM_sp);
+
+ set_fiq_regs(&regs);
+ return 0;
+}
+
+/**
+ * kgdb_fiq_enable_nmi - Manage NMI-triggered entry to KGDB
+ * @on: Flag to either enable or disable an NMI
+ *
+ * This function manages NMIs that usually cause KGDB to enter. That is, not
+ * all NMIs should be enabled or disabled, but only those that issue
+ * kgdb_handle_exception().
+ *
+ * The call counts disable requests, and thus allows to nest disables. But
+ * trying to enable already enabled NMI is an error.
+ */
+static void kgdb_fiq_enable_nmi(bool on)
+{
+ static atomic_t cnt;
+ int ret;
+
+ ret = atomic_add_return(on ? 1 : -1, &cnt);
+ if (ret > 1 && on) {
+ /*
+ * There should be only one instance that calls this function
+ * in "enable, disable" order. All other users must call
+ * disable first, then enable. If not, something is wrong.
+ */
+ WARN_ON(1);
+ return;
+ }
+
+ if (ret > 0)
+ enable_fiq(kgdb_fiq);
+ else
+ disable_fiq(kgdb_fiq);
+}
+
+int kgdb_register_fiq(unsigned int fiq)
+{
+ int err;
+ int cpu;
+
+ if (!kgdb_fiq_enabled)
+ return -ENODEV;
+
+ if (!has_fiq(fiq)) {
+ pr_warn(
+ "%s: Cannot register %u (no FIQ with this number)\n",
+ __func__, fiq);
+ return -ENODEV;
+ }
+
+ kgdb_fiq = fiq;
+
+ err = claim_fiq(&kgdb_fiq_desc);
+ if (err) {
+ pr_warn("%s: unable to claim fiq", __func__);
+ return err;
+ }
+
+ for_each_possible_cpu(cpu)
+ work_on_cpu(cpu, kgdb_fiq_setup_stack, NULL);
+
+ set_fiq_handler(&kgdb_fiq_handler,
+ &kgdb_fiq_handler_end - &kgdb_fiq_handler);
+
+ arch_kgdb_ops.enable_nmi = kgdb_fiq_enable_nmi;
+ return 0;
+}
diff --git a/arch/arm/kernel/kgdb_fiq_entry.S b/arch/arm/kernel/kgdb_fiq_entry.S
new file mode 100644
index 0000000..d6becca
--- /dev/null
+++ b/arch/arm/kernel/kgdb_fiq_entry.S
@@ -0,0 +1,87 @@
+/*
+ * KGDB FIQ entry
+ *
+ * Copyright 1996,1997,1998 Russell King.
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/memory.h>
+#include <asm/unwind.h>
+#include "entry-header.S"
+
+ .text
+
+@ This is needed for usr_entry/alignment_trap
+.LCcralign:
+ .long cr_alignment
+.LCdohandle:
+ .long kgdb_fiq_do_handle
+
+ .macro fiq_handler
+ ldr r1, =.LCdohandle
+ mov r0, sp
+ adr lr, BSYM(9997f)
+ ldr pc, [r1]
+9997:
+ .endm
+
+ .align 5
+__fiq_svc:
+ svc_entry
+ fiq_handler
+ mov r0, sp
+ ldmib r0, {r1 - r14}
+ 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}^
+
+ UNWIND(.fnend )
+ENDPROC(__fiq_svc)
+ .ltorg
+
+ .align 5
+__fiq_usr:
+ usr_entry
+ kuser_cmpxchg_check
+ fiq_handler
+ get_thread_info tsk
+ mov why, #0
+ b ret_to_user_from_irq
+ UNWIND(.fnend )
+ENDPROC(__fiq_usr)
+ .ltorg
+
+ .global kgdb_fiq_handler
+kgdb_fiq_handler:
+
+ vector_stub fiq, FIQ_MODE, 4
+
+ .long __fiq_usr @ 0 (USR_26 / USR_32)
+ .long __fiq_svc @ 1 (FIQ_26 / FIQ_32)
+ .long __fiq_svc @ 2 (IRQ_26 / IRQ_32)
+ .long __fiq_svc @ 3 (SVC_26 / SVC_32)
+ .long __fiq_svc @ 4
+ .long __fiq_svc @ 5
+ .long __fiq_svc @ 6
+ .long __fiq_svc @ 7
+ .long __fiq_svc @ 8
+ .long __fiq_svc @ 9
+ .long __fiq_svc @ a
+ .long __fiq_svc @ b
+ .long __fiq_svc @ c
+ .long __fiq_svc @ d
+ .long __fiq_svc @ e
+ .long __fiq_svc @ f
+
+ .global kgdb_fiq_handler_end
+kgdb_fiq_handler_end:
--
1.9.3

2014-06-24 15:18:38

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v6 2/4] arm: fiq: Allow EOI to be communicated to the intc

Modern ARM systems require an EOI to be sent to the interrupt controller
on completion of both IRQ and FIQ. The FIQ code currently does not provide
any API to perform this. This patch provides this API, implemented by
adding a callback to the fiq_chip structure.

Signed-off-by: Daniel Thompson <[email protected]>
Cc: Russell King <[email protected]>
Cc: Fabio Estevam <[email protected]>
Cc: Nicolas Pitre <[email protected]>
---
arch/arm/include/asm/fiq.h | 6 ++++++
arch/arm/kernel/fiq.c | 9 +++++++++
2 files changed, 15 insertions(+)

diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index a7806ef..e5d9458 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -21,6 +21,11 @@
struct fiq_chip {
void (*fiq_enable)(struct irq_data *data);
void (*fiq_disable)(struct irq_data *data);
+
+ /* .fiq_eoi() will be called from the FIQ handler. For this
+ * reason it must not use spin locks (or any other locks).
+ */
+ void (*fiq_eoi)(struct irq_data *data);
};

struct fiq_handler {
@@ -43,6 +48,7 @@ extern void release_fiq(struct fiq_handler *f);
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern void eoi_fiq(int fiq);
extern bool has_fiq(int fiq);
extern void fiq_register_mapping(int irq, struct fiq_chip *chip);

diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 567f8fd..edde332 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -183,6 +183,15 @@ void disable_fiq(int fiq)
disable_irq(fiq + fiq_start);
}

+void eoi_fiq(int fiq)
+{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data && data->fiq_chip->fiq_eoi)
+ data->fiq_chip->fiq_eoi(data->irq_data);
+}
+EXPORT_SYMBOL(eoi_fiq);
+
bool has_fiq(int fiq)
{
struct fiq_data *data = lookup_fiq_data(fiq);
--
1.9.3

2014-06-24 15:19:27

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v6 3/4] ARM: Move some macros from entry-armv to entry-header

From: Anton Vorontsov <[email protected]>

Just move the macros into header file as we would want to use them for
KGDB FIQ entry code.

The following macros were moved:

- svc_entry
- usr_entry
- kuser_cmpxchg_check
- vector_stub

To make kuser_cmpxchg_check actually work across different files, we
also have to make kuser_cmpxchg64_fixup global.

Signed-off-by: Anton Vorontsov <[email protected]>
Signed-off-by: John Stultz <[email protected]>
Signed-off-by: Daniel Thompson <[email protected]>
Cc: Russell King <[email protected]>
Cc: Nicolas Pitre <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---
arch/arm/kernel/entry-armv.S | 151 +------------------------------------
arch/arm/kernel/entry-header.S | 164 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 165 insertions(+), 150 deletions(-)

diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 52a949a..4172cd6 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -140,53 +140,6 @@ ENDPROC(__und_invalid)
* SVC mode handlers
*/

-#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
-#define SPFIX(code...) code
-#else
-#define SPFIX(code...)
-#endif
-
- .macro svc_entry, stack_hole=0
- UNWIND(.fnstart )
- UNWIND(.save {r0 - pc} )
- sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
-#ifdef CONFIG_THUMB2_KERNEL
- SPFIX( str r0, [sp] ) @ temporarily saved
- SPFIX( mov r0, sp )
- SPFIX( tst r0, #4 ) @ test original stack alignment
- SPFIX( ldr r0, [sp] ) @ restored
-#else
- SPFIX( tst sp, #4 )
-#endif
- SPFIX( subeq sp, sp, #4 )
- stmia sp, {r1 - r12}
-
- ldmia r0, {r3 - r5}
- add r7, sp, #S_SP - 4 @ here for interlock avoidance
- mov r6, #-1 @ "" "" "" ""
- add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
- SPFIX( addeq r2, r2, #4 )
- str r3, [sp, #-4]! @ save the "real" r0 copied
- @ from the exception stack
-
- mov r3, lr
-
- @
- @ We are now ready to fill in the remaining blanks on the stack:
- @
- @ r2 - sp_svc
- @ r3 - lr_svc
- @ r4 - lr_<exception>, already fixed up for correct return/restart
- @ r5 - spsr_<exception>
- @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
- @
- stmia r7, {r2 - r6}
-
-#ifdef CONFIG_TRACE_IRQFLAGS
- bl trace_hardirqs_off
-#endif
- .endm
-
.align 5
__dabt_svc:
svc_entry
@@ -306,73 +259,8 @@ ENDPROC(__pabt_svc)

/*
* User mode handlers
- *
- * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
*/

-#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
-#error "sizeof(struct pt_regs) must be a multiple of 8"
-#endif
-
- .macro usr_entry
- UNWIND(.fnstart )
- UNWIND(.cantunwind ) @ don't unwind the user space
- sub sp, sp, #S_FRAME_SIZE
- ARM( stmib sp, {r1 - r12} )
- THUMB( stmia sp, {r0 - r12} )
-
- ldmia r0, {r3 - r5}
- add r0, sp, #S_PC @ here for interlock avoidance
- mov r6, #-1 @ "" "" "" ""
-
- str r3, [sp] @ save the "real" r0 copied
- @ from the exception stack
-
- @
- @ We are now ready to fill in the remaining blanks on the stack:
- @
- @ r4 - lr_<exception>, already fixed up for correct return/restart
- @ r5 - spsr_<exception>
- @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
- @
- @ Also, separately save sp_usr and lr_usr
- @
- stmia r0, {r4 - r6}
- ARM( stmdb r0, {sp, lr}^ )
- THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
-
- @
- @ Enable the alignment trap while in kernel mode
- @
- alignment_trap r0, .LCcralign
-
- @
- @ Clear FP to mark the first stack frame
- @
- zero_fp
-
-#ifdef CONFIG_IRQSOFF_TRACER
- bl trace_hardirqs_off
-#endif
- ct_user_exit save = 0
- .endm
-
- .macro kuser_cmpxchg_check
-#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
- !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
-#ifndef CONFIG_MMU
-#warning "NPTL on non MMU needs fixing"
-#else
- @ Make sure our user space atomic helper is restarted
- @ if it was interrupted in a critical region. Here we
- @ perform a quick test inline since it should be false
- @ 99.9999% of the time. The rest is done out of line.
- cmp r4, #TASK_SIZE
- blhs kuser_cmpxchg64_fixup
-#endif
-#endif
- .endm
-
.align 5
__dabt_usr:
usr_entry
@@ -823,6 +711,7 @@ __kuser_cmpxchg64: @ 0xffff0f60
ldmfd sp!, {r4, r5, r6, pc}

.text
+ .global kuser_cmpxchg64_fixup
kuser_cmpxchg64_fixup:
@ Called from kuser_cmpxchg_fixup.
@ r4 = address of interrupted insn (must be preserved).
@@ -964,44 +853,6 @@ __kuser_helper_end:
* SP points to a minimal amount of processor-private memory, the address
* of which is copied into r0 for the mode specific abort handler.
*/
- .macro vector_stub, name, mode, correction=0
- .align 5
-
-vector_\name:
- .if \correction
- sub lr, lr, #\correction
- .endif
-
- @
- @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
- @ (parent CPSR)
- @
- stmia sp, {r0, lr} @ save r0, lr
- mrs lr, spsr
- str lr, [sp, #8] @ save spsr
-
- @
- @ Prepare for SVC32 mode. IRQs remain disabled.
- @
- mrs r0, cpsr
- eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
- msr spsr_cxsf, r0
-
- @
- @ the branch table must immediately follow this code
- @
- and lr, lr, #0x0f
- THUMB( adr r0, 1f )
- THUMB( ldr lr, [r0, lr, lsl #2] )
- mov r0, sp
- ARM( ldr lr, [pc, lr, lsl #2] )
- movs pc, lr @ branch to handler in SVC mode
-ENDPROC(vector_\name)
-
- .align 2
- @ handler addresses follow this label
-1:
- .endm

.section .stubs, "ax", %progbits
__stubs_start:
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index 5d702f8..eb2c426 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S
@@ -356,3 +356,167 @@ scno .req r7 @ syscall number
tbl .req r8 @ syscall table pointer
why .req r8 @ Linux syscall (!= 0)
tsk .req r9 @ current thread_info
+
+/*
+ * SVC mode handler macros
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
+#define SPFIX(code...) code
+#else
+#define SPFIX(code...)
+#endif
+
+ .macro svc_entry, stack_hole=0
+ UNWIND(.fnstart )
+ UNWIND(.save {r0 - pc} )
+ sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+#ifdef CONFIG_THUMB2_KERNEL
+ SPFIX( str r0, [sp] ) @ temporarily saved
+ SPFIX( mov r0, sp )
+ SPFIX( tst r0, #4 ) @ test original stack alignment
+ SPFIX( ldr r0, [sp] ) @ restored
+#else
+ SPFIX( tst sp, #4 )
+#endif
+ SPFIX( subeq sp, sp, #4 )
+ stmia sp, {r1 - r12}
+
+ ldmia r0, {r3 - r5}
+ add r7, sp, #S_SP - 4 @ here for interlock avoidance
+ mov r6, #-1 @ "" "" "" ""
+ add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+ SPFIX( addeq r2, r2, #4 )
+ str r3, [sp, #-4]! @ save the "real" r0 copied
+ @ from the exception stack
+
+ mov r3, lr
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r2 - sp_svc
+ @ r3 - lr_svc
+ @ r4 - lr_<exception>, already fixed up for correct return/restart
+ @ r5 - spsr_<exception>
+ @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ stmia r7, {r2 - r6}
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
+ .endm
+
+/*
+ * User mode handler macros
+ *
+ * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
+#error "sizeof(struct pt_regs) must be a multiple of 8"
+#endif
+
+ .macro usr_entry
+ UNWIND(.fnstart )
+ UNWIND(.cantunwind ) @ don't unwind the user space
+ sub sp, sp, #S_FRAME_SIZE
+ ARM( stmib sp, {r1 - r12} )
+ THUMB( stmia sp, {r0 - r12} )
+
+ ldmia r0, {r3 - r5}
+ add r0, sp, #S_PC @ here for interlock avoidance
+ mov r6, #-1 @ "" "" "" ""
+
+ str r3, [sp] @ save the "real" r0 copied
+ @ from the exception stack
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r4 - lr_<exception>, already fixed up for correct return/restart
+ @ r5 - spsr_<exception>
+ @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ @ Also, separately save sp_usr and lr_usr
+ @
+ stmia r0, {r4 - r6}
+ ARM( stmdb r0, {sp, lr}^ )
+ THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
+
+ @
+ @ Enable the alignment trap while in kernel mode
+ @
+ alignment_trap r0, .LCcralign
+
+ @
+ @ Clear FP to mark the first stack frame
+ @
+ zero_fp
+
+#ifdef CONFIG_IRQSOFF_TRACER
+ bl trace_hardirqs_off
+#endif
+ ct_user_exit save = 0
+ .endm
+
+ .macro kuser_cmpxchg_check
+#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
+ !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
+#ifndef CONFIG_MMU
+#warning "NPTL on non MMU needs fixing"
+#else
+ @ Make sure our user space atomic helper is restarted
+ @ if it was interrupted in a critical region. Here we
+ @ perform a quick test inline since it should be false
+ @ 99.9999% of the time. The rest is done out of line.
+ cmp r4, #TASK_SIZE
+ blhs kuser_cmpxchg64_fixup
+#endif
+#endif
+ .endm
+
+/*
+ * Vector stubs macro.
+ */
+ .macro vector_stub, name, mode, correction=0
+ .align 5
+
+vector_\name:
+ .if \correction
+ sub lr, lr, #\correction
+ .endif
+
+ @
+ @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
+ @ (parent CPSR)
+ @
+ stmia sp, {r0, lr} @ save r0, lr
+ mrs lr, spsr
+ str lr, [sp, #8] @ save spsr
+
+ @
+ @ Prepare for SVC32 mode. IRQs remain disabled.
+ @
+ mrs r0, cpsr
+ eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
+ msr spsr_cxsf, r0
+
+ @
+ @ the branch table must immediately follow this code
+ @
+ and lr, lr, #0x0f
+ THUMB( adr r0, 1f )
+ THUMB( ldr lr, [r0, lr, lsl #2] )
+ mov r0, sp
+ ARM( ldr lr, [pc, lr, lsl #2] )
+ movs pc, lr @ branch to handler in SVC mode
+ENDPROC(vector_\name)
+
+ .align 2
+ @ handler addresses follow this label
+1:
+ .endm
+
+
--
1.9.3

2014-06-24 15:44:13

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v6 1/4] arm: fiq: Add callbacks to manage FIQ routings

On Tue, 24 Jun 2014, Daniel Thompson wrote:

> Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
> virq into a FIQ virq. This is too inflexible for multi-platform kernels
> and makes runtime error checking impossible.
>
> We solve this by introducing a flexible mapping that allows interrupt
> controllers that support FIQ to register those mappings. This, in turn,
> makes it much possible for drivers in DT kernels to install FIQ handlers
> without knowing anything about the interrupt controller.
>
> Signed-off-by: Daniel Thompson <[email protected]>
> Cc: Russell King <[email protected]>
> Cc: Fabio Estevam <[email protected]>
> Cc: Nicolas Pitre <[email protected]>
> ---
> arch/arm/include/asm/fiq.h | 7 +++
> arch/arm/kernel/fiq.c | 103 ++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 108 insertions(+), 2 deletions(-)

[...]

> +bool has_fiq(int fiq)
> +{
> + struct fiq_data *data = lookup_fiq_data(fiq);
> +
> + if (data)
> + return true;
> +
> + if (fiq_start == -1)
> + return false;
> +
> + return fiq > fiq_start;

Shouldn't this be fiq >= fiq_start ?

Other than that...

Acked-by: Nicolas Pitre <[email protected]>


Nicolas

2014-06-24 15:46:36

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v6 2/4] arm: fiq: Allow EOI to be communicated to the intc

On Tue, 24 Jun 2014, Daniel Thompson wrote:

> Modern ARM systems require an EOI to be sent to the interrupt controller
> on completion of both IRQ and FIQ. The FIQ code currently does not provide
> any API to perform this. This patch provides this API, implemented by
> adding a callback to the fiq_chip structure.
>
> Signed-off-by: Daniel Thompson <[email protected]>
> Cc: Russell King <[email protected]>
> Cc: Fabio Estevam <[email protected]>
> Cc: Nicolas Pitre <[email protected]>

Acked-by: Nicolas Pitre <[email protected]>

> ---
> arch/arm/include/asm/fiq.h | 6 ++++++
> arch/arm/kernel/fiq.c | 9 +++++++++
> 2 files changed, 15 insertions(+)
>
> diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
> index a7806ef..e5d9458 100644
> --- a/arch/arm/include/asm/fiq.h
> +++ b/arch/arm/include/asm/fiq.h
> @@ -21,6 +21,11 @@
> struct fiq_chip {
> void (*fiq_enable)(struct irq_data *data);
> void (*fiq_disable)(struct irq_data *data);
> +
> + /* .fiq_eoi() will be called from the FIQ handler. For this
> + * reason it must not use spin locks (or any other locks).
> + */
> + void (*fiq_eoi)(struct irq_data *data);
> };
>
> struct fiq_handler {
> @@ -43,6 +48,7 @@ extern void release_fiq(struct fiq_handler *f);
> extern void set_fiq_handler(void *start, unsigned int length);
> extern void enable_fiq(int fiq);
> extern void disable_fiq(int fiq);
> +extern void eoi_fiq(int fiq);
> extern bool has_fiq(int fiq);
> extern void fiq_register_mapping(int irq, struct fiq_chip *chip);
>
> diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
> index 567f8fd..edde332 100644
> --- a/arch/arm/kernel/fiq.c
> +++ b/arch/arm/kernel/fiq.c
> @@ -183,6 +183,15 @@ void disable_fiq(int fiq)
> disable_irq(fiq + fiq_start);
> }
>
> +void eoi_fiq(int fiq)
> +{
> + struct fiq_data *data = lookup_fiq_data(fiq);
> +
> + if (data && data->fiq_chip->fiq_eoi)
> + data->fiq_chip->fiq_eoi(data->irq_data);
> +}
> +EXPORT_SYMBOL(eoi_fiq);
> +
> bool has_fiq(int fiq)
> {
> struct fiq_data *data = lookup_fiq_data(fiq);
> --
> 1.9.3
>

2014-06-24 15:54:00

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v6 3/4] ARM: Move some macros from entry-armv to entry-header

On Tue, 24 Jun 2014, Daniel Thompson wrote:

> From: Anton Vorontsov <[email protected]>
>
> Just move the macros into header file as we would want to use them for
> KGDB FIQ entry code.
>
> The following macros were moved:
>
> - svc_entry
> - usr_entry
> - kuser_cmpxchg_check
> - vector_stub
>
> To make kuser_cmpxchg_check actually work across different files, we
> also have to make kuser_cmpxchg64_fixup global.
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> Signed-off-by: John Stultz <[email protected]>
> Signed-off-by: Daniel Thompson <[email protected]>
> Cc: Russell King <[email protected]>
> Cc: Nicolas Pitre <[email protected]>
> Cc: Catalin Marinas <[email protected]>
> Cc: Frederic Weisbecker <[email protected]>

Acked-by: Nicolas Pitre <[email protected]>


> ---
> arch/arm/kernel/entry-armv.S | 151 +------------------------------------
> arch/arm/kernel/entry-header.S | 164 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 165 insertions(+), 150 deletions(-)
>
> diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
> index 52a949a..4172cd6 100644
> --- a/arch/arm/kernel/entry-armv.S
> +++ b/arch/arm/kernel/entry-armv.S
> @@ -140,53 +140,6 @@ ENDPROC(__und_invalid)
> * SVC mode handlers
> */
>
> -#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
> -#define SPFIX(code...) code
> -#else
> -#define SPFIX(code...)
> -#endif
> -
> - .macro svc_entry, stack_hole=0
> - UNWIND(.fnstart )
> - UNWIND(.save {r0 - pc} )
> - sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
> -#ifdef CONFIG_THUMB2_KERNEL
> - SPFIX( str r0, [sp] ) @ temporarily saved
> - SPFIX( mov r0, sp )
> - SPFIX( tst r0, #4 ) @ test original stack alignment
> - SPFIX( ldr r0, [sp] ) @ restored
> -#else
> - SPFIX( tst sp, #4 )
> -#endif
> - SPFIX( subeq sp, sp, #4 )
> - stmia sp, {r1 - r12}
> -
> - ldmia r0, {r3 - r5}
> - add r7, sp, #S_SP - 4 @ here for interlock avoidance
> - mov r6, #-1 @ "" "" "" ""
> - add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
> - SPFIX( addeq r2, r2, #4 )
> - str r3, [sp, #-4]! @ save the "real" r0 copied
> - @ from the exception stack
> -
> - mov r3, lr
> -
> - @
> - @ We are now ready to fill in the remaining blanks on the stack:
> - @
> - @ r2 - sp_svc
> - @ r3 - lr_svc
> - @ r4 - lr_<exception>, already fixed up for correct return/restart
> - @ r5 - spsr_<exception>
> - @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
> - @
> - stmia r7, {r2 - r6}
> -
> -#ifdef CONFIG_TRACE_IRQFLAGS
> - bl trace_hardirqs_off
> -#endif
> - .endm
> -
> .align 5
> __dabt_svc:
> svc_entry
> @@ -306,73 +259,8 @@ ENDPROC(__pabt_svc)
>
> /*
> * User mode handlers
> - *
> - * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
> */
>
> -#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
> -#error "sizeof(struct pt_regs) must be a multiple of 8"
> -#endif
> -
> - .macro usr_entry
> - UNWIND(.fnstart )
> - UNWIND(.cantunwind ) @ don't unwind the user space
> - sub sp, sp, #S_FRAME_SIZE
> - ARM( stmib sp, {r1 - r12} )
> - THUMB( stmia sp, {r0 - r12} )
> -
> - ldmia r0, {r3 - r5}
> - add r0, sp, #S_PC @ here for interlock avoidance
> - mov r6, #-1 @ "" "" "" ""
> -
> - str r3, [sp] @ save the "real" r0 copied
> - @ from the exception stack
> -
> - @
> - @ We are now ready to fill in the remaining blanks on the stack:
> - @
> - @ r4 - lr_<exception>, already fixed up for correct return/restart
> - @ r5 - spsr_<exception>
> - @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
> - @
> - @ Also, separately save sp_usr and lr_usr
> - @
> - stmia r0, {r4 - r6}
> - ARM( stmdb r0, {sp, lr}^ )
> - THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
> -
> - @
> - @ Enable the alignment trap while in kernel mode
> - @
> - alignment_trap r0, .LCcralign
> -
> - @
> - @ Clear FP to mark the first stack frame
> - @
> - zero_fp
> -
> -#ifdef CONFIG_IRQSOFF_TRACER
> - bl trace_hardirqs_off
> -#endif
> - ct_user_exit save = 0
> - .endm
> -
> - .macro kuser_cmpxchg_check
> -#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
> - !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
> -#ifndef CONFIG_MMU
> -#warning "NPTL on non MMU needs fixing"
> -#else
> - @ Make sure our user space atomic helper is restarted
> - @ if it was interrupted in a critical region. Here we
> - @ perform a quick test inline since it should be false
> - @ 99.9999% of the time. The rest is done out of line.
> - cmp r4, #TASK_SIZE
> - blhs kuser_cmpxchg64_fixup
> -#endif
> -#endif
> - .endm
> -
> .align 5
> __dabt_usr:
> usr_entry
> @@ -823,6 +711,7 @@ __kuser_cmpxchg64: @ 0xffff0f60
> ldmfd sp!, {r4, r5, r6, pc}
>
> .text
> + .global kuser_cmpxchg64_fixup
> kuser_cmpxchg64_fixup:
> @ Called from kuser_cmpxchg_fixup.
> @ r4 = address of interrupted insn (must be preserved).
> @@ -964,44 +853,6 @@ __kuser_helper_end:
> * SP points to a minimal amount of processor-private memory, the address
> * of which is copied into r0 for the mode specific abort handler.
> */
> - .macro vector_stub, name, mode, correction=0
> - .align 5
> -
> -vector_\name:
> - .if \correction
> - sub lr, lr, #\correction
> - .endif
> -
> - @
> - @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
> - @ (parent CPSR)
> - @
> - stmia sp, {r0, lr} @ save r0, lr
> - mrs lr, spsr
> - str lr, [sp, #8] @ save spsr
> -
> - @
> - @ Prepare for SVC32 mode. IRQs remain disabled.
> - @
> - mrs r0, cpsr
> - eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
> - msr spsr_cxsf, r0
> -
> - @
> - @ the branch table must immediately follow this code
> - @
> - and lr, lr, #0x0f
> - THUMB( adr r0, 1f )
> - THUMB( ldr lr, [r0, lr, lsl #2] )
> - mov r0, sp
> - ARM( ldr lr, [pc, lr, lsl #2] )
> - movs pc, lr @ branch to handler in SVC mode
> -ENDPROC(vector_\name)
> -
> - .align 2
> - @ handler addresses follow this label
> -1:
> - .endm
>
> .section .stubs, "ax", %progbits
> __stubs_start:
> diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
> index 5d702f8..eb2c426 100644
> --- a/arch/arm/kernel/entry-header.S
> +++ b/arch/arm/kernel/entry-header.S
> @@ -356,3 +356,167 @@ scno .req r7 @ syscall number
> tbl .req r8 @ syscall table pointer
> why .req r8 @ Linux syscall (!= 0)
> tsk .req r9 @ current thread_info
> +
> +/*
> + * SVC mode handler macros
> + */
> +
> +#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
> +#define SPFIX(code...) code
> +#else
> +#define SPFIX(code...)
> +#endif
> +
> + .macro svc_entry, stack_hole=0
> + UNWIND(.fnstart )
> + UNWIND(.save {r0 - pc} )
> + sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
> +#ifdef CONFIG_THUMB2_KERNEL
> + SPFIX( str r0, [sp] ) @ temporarily saved
> + SPFIX( mov r0, sp )
> + SPFIX( tst r0, #4 ) @ test original stack alignment
> + SPFIX( ldr r0, [sp] ) @ restored
> +#else
> + SPFIX( tst sp, #4 )
> +#endif
> + SPFIX( subeq sp, sp, #4 )
> + stmia sp, {r1 - r12}
> +
> + ldmia r0, {r3 - r5}
> + add r7, sp, #S_SP - 4 @ here for interlock avoidance
> + mov r6, #-1 @ "" "" "" ""
> + add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
> + SPFIX( addeq r2, r2, #4 )
> + str r3, [sp, #-4]! @ save the "real" r0 copied
> + @ from the exception stack
> +
> + mov r3, lr
> +
> + @
> + @ We are now ready to fill in the remaining blanks on the stack:
> + @
> + @ r2 - sp_svc
> + @ r3 - lr_svc
> + @ r4 - lr_<exception>, already fixed up for correct return/restart
> + @ r5 - spsr_<exception>
> + @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
> + @
> + stmia r7, {r2 - r6}
> +
> +#ifdef CONFIG_TRACE_IRQFLAGS
> + bl trace_hardirqs_off
> +#endif
> + .endm
> +
> +/*
> + * User mode handler macros
> + *
> + * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
> + */
> +
> +#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
> +#error "sizeof(struct pt_regs) must be a multiple of 8"
> +#endif
> +
> + .macro usr_entry
> + UNWIND(.fnstart )
> + UNWIND(.cantunwind ) @ don't unwind the user space
> + sub sp, sp, #S_FRAME_SIZE
> + ARM( stmib sp, {r1 - r12} )
> + THUMB( stmia sp, {r0 - r12} )
> +
> + ldmia r0, {r3 - r5}
> + add r0, sp, #S_PC @ here for interlock avoidance
> + mov r6, #-1 @ "" "" "" ""
> +
> + str r3, [sp] @ save the "real" r0 copied
> + @ from the exception stack
> +
> + @
> + @ We are now ready to fill in the remaining blanks on the stack:
> + @
> + @ r4 - lr_<exception>, already fixed up for correct return/restart
> + @ r5 - spsr_<exception>
> + @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
> + @
> + @ Also, separately save sp_usr and lr_usr
> + @
> + stmia r0, {r4 - r6}
> + ARM( stmdb r0, {sp, lr}^ )
> + THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
> +
> + @
> + @ Enable the alignment trap while in kernel mode
> + @
> + alignment_trap r0, .LCcralign
> +
> + @
> + @ Clear FP to mark the first stack frame
> + @
> + zero_fp
> +
> +#ifdef CONFIG_IRQSOFF_TRACER
> + bl trace_hardirqs_off
> +#endif
> + ct_user_exit save = 0
> + .endm
> +
> + .macro kuser_cmpxchg_check
> +#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
> + !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
> +#ifndef CONFIG_MMU
> +#warning "NPTL on non MMU needs fixing"
> +#else
> + @ Make sure our user space atomic helper is restarted
> + @ if it was interrupted in a critical region. Here we
> + @ perform a quick test inline since it should be false
> + @ 99.9999% of the time. The rest is done out of line.
> + cmp r4, #TASK_SIZE
> + blhs kuser_cmpxchg64_fixup
> +#endif
> +#endif
> + .endm
> +
> +/*
> + * Vector stubs macro.
> + */
> + .macro vector_stub, name, mode, correction=0
> + .align 5
> +
> +vector_\name:
> + .if \correction
> + sub lr, lr, #\correction
> + .endif
> +
> + @
> + @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
> + @ (parent CPSR)
> + @
> + stmia sp, {r0, lr} @ save r0, lr
> + mrs lr, spsr
> + str lr, [sp, #8] @ save spsr
> +
> + @
> + @ Prepare for SVC32 mode. IRQs remain disabled.
> + @
> + mrs r0, cpsr
> + eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
> + msr spsr_cxsf, r0
> +
> + @
> + @ the branch table must immediately follow this code
> + @
> + and lr, lr, #0x0f
> + THUMB( adr r0, 1f )
> + THUMB( ldr lr, [r0, lr, lsl #2] )
> + mov r0, sp
> + ARM( ldr lr, [pc, lr, lsl #2] )
> + movs pc, lr @ branch to handler in SVC mode
> +ENDPROC(vector_\name)
> +
> + .align 2
> + @ handler addresses follow this label
> +1:
> + .endm
> +
> +
> --
> 1.9.3
>

2014-06-24 15:58:27

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v6 1/4] arm: fiq: Add callbacks to manage FIQ routings

On 24/06/14 16:44, Nicolas Pitre wrote:
> On Tue, 24 Jun 2014, Daniel Thompson wrote:
>
>> Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
>> virq into a FIQ virq. This is too inflexible for multi-platform kernels
>> and makes runtime error checking impossible.
>>
>> We solve this by introducing a flexible mapping that allows interrupt
>> controllers that support FIQ to register those mappings. This, in turn,
>> makes it much possible for drivers in DT kernels to install FIQ handlers
>> without knowing anything about the interrupt controller.
>>
>> Signed-off-by: Daniel Thompson <[email protected]>
>> Cc: Russell King <[email protected]>
>> Cc: Fabio Estevam <[email protected]>
>> Cc: Nicolas Pitre <[email protected]>
>> ---
>> arch/arm/include/asm/fiq.h | 7 +++
>> arch/arm/kernel/fiq.c | 103 ++++++++++++++++++++++++++++++++++++++++++++-
>> 2 files changed, 108 insertions(+), 2 deletions(-)
>
> [...]
>
>> +bool has_fiq(int fiq)
>> +{
>> + struct fiq_data *data = lookup_fiq_data(fiq);
>> +
>> + if (data)
>> + return true;
>> +
>> + if (fiq_start == -1)
>> + return false;
>> +
>> + return fiq > fiq_start;
>
> Shouldn't this be fiq >= fiq_start ?

Absolutely! Will fix that shortly.


Thanks

Daniel.

2014-06-24 16:09:05

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH v6 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

On Tue, Jun 24, 2014 at 04:18:17PM +0100, Daniel Thompson wrote:
> + .align 5
> +__fiq_svc:
> + svc_entry

Remember that the registers you have on the stack here are r0-r12, plus
the SVC banked sp and lr registers. These may not be the registers
from the mode you took the FIQ (eg, if it was IRQ, or abort mode.)

Also bear in mind that svc_entry calls trace_hardirqs_off - is this
appropriate and safe for the FIQ to call?

> + fiq_handler
> + mov r0, sp
> + ldmib r0, {r1 - r14}

So this restores r1 to r12, and the SVC mode sp and lr registers.
Nothing touches the SVC SPSR, so we hope that retains its value
throughout the FIQ processing. Note that the stack pointer at this
point will be above state which we have not yet read, so we better
not take any exceptions from this instruction (not even an imprecise
abort).

> + msr cpsr_c, #FIQ_MODE | PSR_I_BIT | PSR_F_BIT

Here we switch to FIQ mode. What about the PSR_A_BIT which prevents
imprecise aborts on ARMv6+ ?

Nevertheless, I think it's safe because the A bit will be set by the
CPU when taking the FIQ exception, and it should remain set since
cpsr_c won't modify it.

> + add r8, r0, #S_PC
> + ldr r9, [r0, #S_PSR]
> + msr spsr_cxsf, r9

Here we update the FIQ SPSR with the calling mode's CPSR, ready to
return...

> + ldr r0, [r0, #S_R0]

Load the calling mode's R0 value.

> + ldmia r8, {pc}^

And return (restoring CPSR from SPSR_fiq).

This looks pretty good except for the niggles...

--
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.

2014-06-24 16:22:57

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v6 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

On Tue, 24 Jun 2014, Daniel Thompson wrote:

> From: Anton Vorontsov <[email protected]>
>
> The FIQ debugger may be used to debug situations when the kernel stuck
> in uninterruptable sections, e.g. the kernel infinitely loops or
> deadlocked in an interrupt or with interrupts disabled.
>
> By default KGDB FIQ is disabled in runtime, but can be enabled with
> kgdb_fiq.enable=1 kernel command line option.
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> Signed-off-by: John Stultz <[email protected]>
> Signed-off-by: Daniel Thompson <[email protected]>
> Cc: Russell King <[email protected]>
> Cc: Ben Dooks <[email protected]>
> Cc: Dave Martin <[email protected]>
> ---
> arch/arm/Kconfig | 2 +
> arch/arm/Kconfig.debug | 18 ++++++
> arch/arm/include/asm/kgdb.h | 7 +++
> arch/arm/kernel/Makefile | 1 +
> arch/arm/kernel/kgdb_fiq.c | 124 +++++++++++++++++++++++++++++++++++++++
> arch/arm/kernel/kgdb_fiq_entry.S | 87 +++++++++++++++++++++++++++
> 6 files changed, 239 insertions(+)
> create mode 100644 arch/arm/kernel/kgdb_fiq.c
> create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S

[...]

> +static long kgdb_fiq_setup_stack(void *info)
> +{
> + struct pt_regs regs;
> +
> + regs.ARM_sp = __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER) +
> + THREAD_START_SP;
> + WARN_ON(!regs.ARM_sp);

Isn't this rather fatal if you can't allocate any stack? Why not using
BUG_ON(), or better yet propagate a proper error code back?

> +
> + set_fiq_regs(&regs);
> + return 0;
> +}
> +
> +/**
> + * kgdb_fiq_enable_nmi - Manage NMI-triggered entry to KGDB
> + * @on: Flag to either enable or disable an NMI
> + *
> + * This function manages NMIs that usually cause KGDB to enter. That is, not
> + * all NMIs should be enabled or disabled, but only those that issue
> + * kgdb_handle_exception().
> + *
> + * The call counts disable requests, and thus allows to nest disables. But
> + * trying to enable already enabled NMI is an error.
> + */
> +static void kgdb_fiq_enable_nmi(bool on)
> +{
> + static atomic_t cnt;
> + int ret;
> +
> + ret = atomic_add_return(on ? 1 : -1, &cnt);
> + if (ret > 1 && on) {
> + /*
> + * There should be only one instance that calls this function
> + * in "enable, disable" order. All other users must call
> + * disable first, then enable. If not, something is wrong.
> + */
> + WARN_ON(1);
> + return;
> + }

Minor style suggestion:

/*
* There should be only one instance that calls this function
* in "enable, disable" order. All other users must call
* disable first, then enable. If not, something is wrong.
*/
if (WARN_ON(ret > 1 && on))
return;

Other than that...

Acked-by: Nicolas Pitre <[email protected]>


Nicolas

2014-06-26 09:54:40

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v6 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

On 24/06/14 17:08, Russell King - ARM Linux wrote:
> On Tue, Jun 24, 2014 at 04:18:17PM +0100, Daniel Thompson wrote:
>> + .align 5
>> +__fiq_svc:
>> + svc_entry
>
> Remember that the registers you have on the stack here are r0-r12, plus
> the SVC banked sp and lr registers. These may not be the registers
> from the mode you took the FIQ (eg, if it was IRQ, or abort mode.)

We probably ought to save/restore lr_abt and spsr_abt but I think sp_abt
and the state for irq and und can be neglected.

The stack pointers are constant anyway and I think it reasonable to
assume the FIQ handler doesn't unmask interrupts or attempt to execute
undefined instructions.


> Also bear in mind that svc_entry calls trace_hardirqs_off - is this
> appropriate and safe for the FIQ to call?

I personally think it appropriate and it looked safe on the lockdep side
of things. However I will look a bit deeper at this since I don't
remember how far I chased things back.

Naturally it is a problem that we don't currently call trace_hardirq_on.
I'll fix this.


>> + fiq_handler
>> + mov r0, sp
>> + ldmib r0, {r1 - r14}
>
> So this restores r1 to r12, and the SVC mode sp and lr registers.
> Nothing touches the SVC SPSR, so we hope that retains its value
> throughout the FIQ processing.

Are you worried about something changing it?

I haven't thought of any good reason for it to change. The FIQ handler
can't safely execute a SVC instruction.


> Note that the stack pointer at this
> point will be above state which we have not yet read, so we better
> not take any exceptions from this instruction (not even an imprecise
> abort).

Can a comment cover this? We shouldn't get an abort reading the SVC
stack and imprecise abort is blocked.

Note we could copy these values back onto the FIQ stack before switching
modes if is there's a possibility of an abort we cannot avoid, however
I'm not know if this is worthwhile.


>> + msr cpsr_c, #FIQ_MODE | PSR_I_BIT | PSR_F_BIT
>
> Here we switch to FIQ mode. What about the PSR_A_BIT which prevents
> imprecise aborts on ARMv6+ ?
>
> Nevertheless, I think it's safe because the A bit will be set by the
> CPU when taking the FIQ exception, and it should remain set since
> cpsr_c won't modify it.

Agreed.

Note that while double checking this I realized that this code will drop
the value of PSR_ISETSTATE (T bit) that the vector_stub macro set for
us. I'll fix this.


>> + add r8, r0, #S_PC
>> + ldr r9, [r0, #S_PSR]
>> + msr spsr_cxsf, r9
>
> Here we update the FIQ SPSR with the calling mode's CPSR, ready to
> return...
>
>> + ldr r0, [r0, #S_R0]
>
> Load the calling mode's R0 value.
>
>> + ldmia r8, {pc}^
>
> And return (restoring CPSR from SPSR_fiq).
>
> This looks pretty good except for the niggles...

Thanks.

I've picked out the following actions from the above:

1. Wrap a save and restore lr_abt and spsr_abt around the FIQ handler
2. Add a paired up trace_hardirqs_on() (and review more deeply).
3. Add comments explaining hazards w.r.t. data abort,
4. Correctly manage T bit during transition back to FIQ mode.

Do I miss anything?


Daniel.

2014-06-26 12:48:56

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v6 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

On 24/06/14 17:22, Nicolas Pitre wrote:
> On Tue, 24 Jun 2014, Daniel Thompson wrote:
>
>> From: Anton Vorontsov <[email protected]>
>>
>> The FIQ debugger may be used to debug situations when the kernel stuck
>> in uninterruptable sections, e.g. the kernel infinitely loops or
>> deadlocked in an interrupt or with interrupts disabled.
>>
>> By default KGDB FIQ is disabled in runtime, but can be enabled with
>> kgdb_fiq.enable=1 kernel command line option.
>>
>> Signed-off-by: Anton Vorontsov <[email protected]>
>> Signed-off-by: John Stultz <[email protected]>
>> Signed-off-by: Daniel Thompson <[email protected]>
>> Cc: Russell King <[email protected]>
>> Cc: Ben Dooks <[email protected]>
>> Cc: Dave Martin <[email protected]>
>> ---
>> arch/arm/Kconfig | 2 +
>> arch/arm/Kconfig.debug | 18 ++++++
>> arch/arm/include/asm/kgdb.h | 7 +++
>> arch/arm/kernel/Makefile | 1 +
>> arch/arm/kernel/kgdb_fiq.c | 124 +++++++++++++++++++++++++++++++++++++++
>> arch/arm/kernel/kgdb_fiq_entry.S | 87 +++++++++++++++++++++++++++
>> 6 files changed, 239 insertions(+)
>> create mode 100644 arch/arm/kernel/kgdb_fiq.c
>> create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S
>
> [...]
>
>> +static long kgdb_fiq_setup_stack(void *info)
>> +{
>> + struct pt_regs regs;
>> +
>> + regs.ARM_sp = __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER) +
>> + THREAD_START_SP;
>> + WARN_ON(!regs.ARM_sp);
>
> Isn't this rather fatal if you can't allocate any stack? Why not using
> BUG_ON(), or better yet propagate a proper error code back?

Thanks for raising this.

I think we can get rid of the allocation altogether. This stack is *way*
oversized (it only needs to be 12 bytes).


>> +
>> + set_fiq_regs(&regs);
>> + return 0;
>> +}
>> +
>> +/**
>> + * kgdb_fiq_enable_nmi - Manage NMI-triggered entry to KGDB
>> + * @on: Flag to either enable or disable an NMI
>> + *
>> + * This function manages NMIs that usually cause KGDB to enter. That is, not
>> + * all NMIs should be enabled or disabled, but only those that issue
>> + * kgdb_handle_exception().
>> + *
>> + * The call counts disable requests, and thus allows to nest disables. But
>> + * trying to enable already enabled NMI is an error.
>> + */
>> +static void kgdb_fiq_enable_nmi(bool on)
>> +{
>> + static atomic_t cnt;
>> + int ret;
>> +
>> + ret = atomic_add_return(on ? 1 : -1, &cnt);
>> + if (ret > 1 && on) {
>> + /*
>> + * There should be only one instance that calls this function
>> + * in "enable, disable" order. All other users must call
>> + * disable first, then enable. If not, something is wrong.
>> + */
>> + WARN_ON(1);
>> + return;
>> + }
>
> Minor style suggestion:
>
> /*
> * There should be only one instance that calls this function
> * in "enable, disable" order. All other users must call
> * disable first, then enable. If not, something is wrong.
> */
> if (WARN_ON(ret > 1 && on))
> return;

Will adopt this style.


> Other than that...
>
> Acked-by: Nicolas Pitre <[email protected]>

Thanks for review.

2014-06-30 08:53:22

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v7 0/4] arm: KGDB NMI/FIQ support

This patchset makes it possible to use kgdb's NMI infrastructure on ARM
platforms.

The patches have been previously circulated as part of a large patchset
mixing together ARM architecture code and driver changes
(http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901 ). This
patchset is dramatically cut down to include only the arch/arm code. The
driver code (irqchip and tty/serial) will follow when/if the arch code
is accepted.

The first two patches modify the FIQ infrastructure to allow interrupt
controller drivers to register callbacks (the fiq_chip structure) to
manage FIQ routings and to signal FIQ EOI. This makes it possible to
use FIQ in multi-platform kernels and with recent ARM interrupt
controllers.

The remaining two patches provide architecture support for KGDB's NMI
feature (and rely upon the preceding changes to the FIQ code).

Tested on qemu (versatile), STiH416 (B2020 board) and Freescale i.MX6
quad (wandboard).

Changes since v6:

- Corrected off-by-one comparison in has_fiq() (Nicolas Pitre)
- Rewrote the FIQ stack initialization (Nicolas Pitre). This fixes a
serious data corruption bug due to bad stack mismanagement.
- Introduced __fiq_abt to ensure lr_abt and spsr_abt are saved and
restored if we fast-interrupt an abort (Russell King).
- Significantly improved the commenting of the exception handlers.
- Added a call to trace_hardirqs_on() if we clear the I bit as we
exit the exception handler.

Changes since v5:

- Separated anything not strictly impacting arch/arm.
- Fixed a spurious add/remove of code within the series (there was
broken code in intermediate patches)

For previous changes see:
http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901


Anton Vorontsov (2):
ARM: Move some macros from entry-armv to entry-header
ARM: Add KGDB/KDB FIQ debugger generic code

Daniel Thompson (2):
arm: fiq: Add callbacks to manage FIQ routings
arm: fiq: Allow EOI to be communicated to the intc

arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 18 +++++
arch/arm/include/asm/fiq.h | 14 ++++
arch/arm/include/asm/kgdb.h | 7 ++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/entry-armv.S | 151 +----------------------------------
arch/arm/kernel/entry-header.S | 164 +++++++++++++++++++++++++++++++++++++++
arch/arm/kernel/fiq.c | 112 +++++++++++++++++++++++++-
arch/arm/kernel/kgdb_fiq.c | 127 ++++++++++++++++++++++++++++++
arch/arm/kernel/kgdb_fiq_entry.S | 130 +++++++++++++++++++++++++++++++
10 files changed, 574 insertions(+), 152 deletions(-)
create mode 100644 arch/arm/kernel/kgdb_fiq.c
create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S

--
1.9.3

2014-06-30 08:58:18

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v7 1/4] arm: fiq: Add callbacks to manage FIQ routings

Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
virq into a FIQ virq. This is too inflexible for multi-platform kernels
and makes runtime error checking impossible.

We solve this by introducing a flexible mapping that allows interrupt
controllers that support FIQ to register those mappings. This, in turn,
makes it much possible for drivers in DT kernels to install FIQ handlers
without knowing anything about the interrupt controller.

Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Fabio Estevam <[email protected]>
---
arch/arm/include/asm/fiq.h | 8 ++++
arch/arm/kernel/fiq.c | 103 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index d493d0b..ed44528 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -16,8 +16,14 @@
#ifndef __ASM_FIQ_H
#define __ASM_FIQ_H

+#include <linux/irq.h>
#include <asm/ptrace.h>

+struct fiq_chip {
+ void (*fiq_enable)(struct irq_data *data);
+ void (*fiq_disable)(struct irq_data *data);
+};
+
struct fiq_handler {
struct fiq_handler *next;
/* Name
@@ -38,6 +44,8 @@ extern void release_fiq(struct fiq_handler *f);
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern bool has_fiq(int fiq);
+extern void fiq_register_mapping(int irq, struct fiq_chip *chip);

/* helpers defined in fiqasm.S: */
extern void __set_fiq_regs(unsigned long const *regs);
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 918875d..5d831cf 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -40,6 +40,9 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/seq_file.h>
+#include <linux/irq.h>
+#include <linux/radix-tree.h>
+#include <linux/slab.h>

#include <asm/cacheflush.h>
#include <asm/cp15.h>
@@ -52,7 +55,15 @@
(unsigned)&vector_fiq_offset; \
})

+struct fiq_data {
+ struct fiq_chip *fiq_chip;
+ struct irq_data *irq_data;
+};
+
static unsigned long no_fiq_insn;
+static int fiq_start = -1;
+static RADIX_TREE(fiq_data_tree, GFP_KERNEL);
+static DEFINE_MUTEX(fiq_data_mutex);

/* Default reacquire function
* - we always relinquish FIQ control
@@ -127,18 +138,65 @@ void release_fiq(struct fiq_handler *f)
while (current_fiq->fiq_op(current_fiq->dev_id, 0));
}

-static int fiq_start;
+static struct fiq_data *lookup_fiq_data(int fiq)
+{
+ struct fiq_data *data;
+
+ rcu_read_lock();
+ data = radix_tree_lookup(&fiq_data_tree, fiq);
+ rcu_read_unlock();
+
+ return data;
+}

void enable_fiq(int fiq)
{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data) {
+ if (data->fiq_chip->fiq_enable)
+ data->fiq_chip->fiq_enable(data->irq_data);
+ enable_irq(fiq);
+ return;
+ }
+
+ if (WARN_ON(fiq_start == -1))
+ return;
+
enable_irq(fiq + fiq_start);
}

void disable_fiq(int fiq)
{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data) {
+ if (data->fiq_chip->fiq_disable)
+ data->fiq_chip->fiq_disable(data->irq_data);
+ disable_irq(fiq);
+ return;
+ }
+
+ if (WARN_ON(fiq_start == -1))
+ return;
+
disable_irq(fiq + fiq_start);
}

+bool has_fiq(int fiq)
+{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data)
+ return true;
+
+ if (fiq_start == -1)
+ return false;
+
+ return fiq >= fiq_start;
+}
+EXPORT_SYMBOL(has_fiq);
+
EXPORT_SYMBOL(set_fiq_handler);
EXPORT_SYMBOL(__set_fiq_regs); /* defined in fiqasm.S */
EXPORT_SYMBOL(__get_fiq_regs); /* defined in fiqasm.S */
@@ -147,9 +205,50 @@ EXPORT_SYMBOL(release_fiq);
EXPORT_SYMBOL(enable_fiq);
EXPORT_SYMBOL(disable_fiq);

+/*
+ * Add a mapping from a Linux irq to the fiq data.
+ */
+void fiq_register_mapping(int irq, struct fiq_chip *chip)
+{
+ struct fiq_data *fiq_data = NULL;
+ int res;
+
+ /* fiq_register_mapping can't be mixed with init_FIQ */
+ BUG_ON(fiq_start != -1);
+
+ fiq_data = kmalloc(sizeof(*fiq_data), GFP_KERNEL);
+ if (!fiq_data)
+ goto err;
+
+ fiq_data->fiq_chip = chip;
+ fiq_data->irq_data = irq_get_irq_data(irq);
+ BUG_ON(!fiq_data->irq_data);
+
+ mutex_lock(&fiq_data_mutex);
+ res = radix_tree_insert(&fiq_data_tree, irq, fiq_data);
+ mutex_unlock(&fiq_data_mutex);
+ if (res)
+ goto err;
+
+ return;
+
+err:
+ kfree(fiq_data);
+ pr_err("fiq: Cannot register mapping %d\n", irq);
+}
+
+/*
+ * Set the offset between normal IRQs and their FIQ shadows.
+ */
void __init init_FIQ(int start)
{
+ fiq_start = start;
+}
+
+static int __init init_default_fiq_handler(void)
+{
unsigned offset = FIQ_OFFSET;
no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
- fiq_start = start;
+ return 0;
}
+pure_initcall(init_default_fiq_handler);
--
1.9.3

2014-06-30 09:01:33

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v7 2/4] arm: fiq: Allow EOI to be communicated to the intc

Modern ARM systems require an EOI to be sent to the interrupt controller
on completion of both IRQ and FIQ. The FIQ code currently does not provide
any API to perform this. This patch provides this API, implemented by
adding a callback to the fiq_chip structure.

Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Fabio Estevam <[email protected]>
---
arch/arm/include/asm/fiq.h | 6 ++++++
arch/arm/kernel/fiq.c | 9 +++++++++
2 files changed, 15 insertions(+)

diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index ed44528..fbc0df1 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -22,6 +22,11 @@
struct fiq_chip {
void (*fiq_enable)(struct irq_data *data);
void (*fiq_disable)(struct irq_data *data);
+
+ /* .fiq_eoi() will be called from the FIQ handler. For this
+ * reason it must not use spin locks (or any other locks).
+ */
+ void (*fiq_eoi)(struct irq_data *data);
};

struct fiq_handler {
@@ -44,6 +49,7 @@ extern void release_fiq(struct fiq_handler *f);
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern void eoi_fiq(int fiq);
extern bool has_fiq(int fiq);
extern void fiq_register_mapping(int irq, struct fiq_chip *chip);

diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 5d831cf..c35fd6a 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -183,6 +183,15 @@ void disable_fiq(int fiq)
disable_irq(fiq + fiq_start);
}

+void eoi_fiq(int fiq)
+{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data && data->fiq_chip->fiq_eoi)
+ data->fiq_chip->fiq_eoi(data->irq_data);
+}
+EXPORT_SYMBOL(eoi_fiq);
+
bool has_fiq(int fiq)
{
struct fiq_data *data = lookup_fiq_data(fiq);
--
1.9.3

2014-06-30 09:01:45

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v7 3/4] ARM: Move some macros from entry-armv to entry-header

From: Anton Vorontsov <[email protected]>

Just move the macros into header file as we would want to use them for
KGDB FIQ entry code.

The following macros were moved:

- svc_entry
- usr_entry
- kuser_cmpxchg_check
- vector_stub

To make kuser_cmpxchg_check actually work across different files, we
also have to make kuser_cmpxchg64_fixup global.

Signed-off-by: Anton Vorontsov <[email protected]>
Signed-off-by: John Stultz <[email protected]>
Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---
arch/arm/kernel/entry-armv.S | 151 +------------------------------------
arch/arm/kernel/entry-header.S | 164 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 165 insertions(+), 150 deletions(-)

diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 52a949a..4172cd6 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -140,53 +140,6 @@ ENDPROC(__und_invalid)
* SVC mode handlers
*/

-#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
-#define SPFIX(code...) code
-#else
-#define SPFIX(code...)
-#endif
-
- .macro svc_entry, stack_hole=0
- UNWIND(.fnstart )
- UNWIND(.save {r0 - pc} )
- sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
-#ifdef CONFIG_THUMB2_KERNEL
- SPFIX( str r0, [sp] ) @ temporarily saved
- SPFIX( mov r0, sp )
- SPFIX( tst r0, #4 ) @ test original stack alignment
- SPFIX( ldr r0, [sp] ) @ restored
-#else
- SPFIX( tst sp, #4 )
-#endif
- SPFIX( subeq sp, sp, #4 )
- stmia sp, {r1 - r12}
-
- ldmia r0, {r3 - r5}
- add r7, sp, #S_SP - 4 @ here for interlock avoidance
- mov r6, #-1 @ "" "" "" ""
- add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
- SPFIX( addeq r2, r2, #4 )
- str r3, [sp, #-4]! @ save the "real" r0 copied
- @ from the exception stack
-
- mov r3, lr
-
- @
- @ We are now ready to fill in the remaining blanks on the stack:
- @
- @ r2 - sp_svc
- @ r3 - lr_svc
- @ r4 - lr_<exception>, already fixed up for correct return/restart
- @ r5 - spsr_<exception>
- @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
- @
- stmia r7, {r2 - r6}
-
-#ifdef CONFIG_TRACE_IRQFLAGS
- bl trace_hardirqs_off
-#endif
- .endm
-
.align 5
__dabt_svc:
svc_entry
@@ -306,73 +259,8 @@ ENDPROC(__pabt_svc)

/*
* User mode handlers
- *
- * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
*/

-#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
-#error "sizeof(struct pt_regs) must be a multiple of 8"
-#endif
-
- .macro usr_entry
- UNWIND(.fnstart )
- UNWIND(.cantunwind ) @ don't unwind the user space
- sub sp, sp, #S_FRAME_SIZE
- ARM( stmib sp, {r1 - r12} )
- THUMB( stmia sp, {r0 - r12} )
-
- ldmia r0, {r3 - r5}
- add r0, sp, #S_PC @ here for interlock avoidance
- mov r6, #-1 @ "" "" "" ""
-
- str r3, [sp] @ save the "real" r0 copied
- @ from the exception stack
-
- @
- @ We are now ready to fill in the remaining blanks on the stack:
- @
- @ r4 - lr_<exception>, already fixed up for correct return/restart
- @ r5 - spsr_<exception>
- @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
- @
- @ Also, separately save sp_usr and lr_usr
- @
- stmia r0, {r4 - r6}
- ARM( stmdb r0, {sp, lr}^ )
- THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
-
- @
- @ Enable the alignment trap while in kernel mode
- @
- alignment_trap r0, .LCcralign
-
- @
- @ Clear FP to mark the first stack frame
- @
- zero_fp
-
-#ifdef CONFIG_IRQSOFF_TRACER
- bl trace_hardirqs_off
-#endif
- ct_user_exit save = 0
- .endm
-
- .macro kuser_cmpxchg_check
-#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
- !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
-#ifndef CONFIG_MMU
-#warning "NPTL on non MMU needs fixing"
-#else
- @ Make sure our user space atomic helper is restarted
- @ if it was interrupted in a critical region. Here we
- @ perform a quick test inline since it should be false
- @ 99.9999% of the time. The rest is done out of line.
- cmp r4, #TASK_SIZE
- blhs kuser_cmpxchg64_fixup
-#endif
-#endif
- .endm
-
.align 5
__dabt_usr:
usr_entry
@@ -823,6 +711,7 @@ __kuser_cmpxchg64: @ 0xffff0f60
ldmfd sp!, {r4, r5, r6, pc}

.text
+ .global kuser_cmpxchg64_fixup
kuser_cmpxchg64_fixup:
@ Called from kuser_cmpxchg_fixup.
@ r4 = address of interrupted insn (must be preserved).
@@ -964,44 +853,6 @@ __kuser_helper_end:
* SP points to a minimal amount of processor-private memory, the address
* of which is copied into r0 for the mode specific abort handler.
*/
- .macro vector_stub, name, mode, correction=0
- .align 5
-
-vector_\name:
- .if \correction
- sub lr, lr, #\correction
- .endif
-
- @
- @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
- @ (parent CPSR)
- @
- stmia sp, {r0, lr} @ save r0, lr
- mrs lr, spsr
- str lr, [sp, #8] @ save spsr
-
- @
- @ Prepare for SVC32 mode. IRQs remain disabled.
- @
- mrs r0, cpsr
- eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
- msr spsr_cxsf, r0
-
- @
- @ the branch table must immediately follow this code
- @
- and lr, lr, #0x0f
- THUMB( adr r0, 1f )
- THUMB( ldr lr, [r0, lr, lsl #2] )
- mov r0, sp
- ARM( ldr lr, [pc, lr, lsl #2] )
- movs pc, lr @ branch to handler in SVC mode
-ENDPROC(vector_\name)
-
- .align 2
- @ handler addresses follow this label
-1:
- .endm

.section .stubs, "ax", %progbits
__stubs_start:
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index 5d702f8..eb2c426 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S
@@ -356,3 +356,167 @@ scno .req r7 @ syscall number
tbl .req r8 @ syscall table pointer
why .req r8 @ Linux syscall (!= 0)
tsk .req r9 @ current thread_info
+
+/*
+ * SVC mode handler macros
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
+#define SPFIX(code...) code
+#else
+#define SPFIX(code...)
+#endif
+
+ .macro svc_entry, stack_hole=0
+ UNWIND(.fnstart )
+ UNWIND(.save {r0 - pc} )
+ sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+#ifdef CONFIG_THUMB2_KERNEL
+ SPFIX( str r0, [sp] ) @ temporarily saved
+ SPFIX( mov r0, sp )
+ SPFIX( tst r0, #4 ) @ test original stack alignment
+ SPFIX( ldr r0, [sp] ) @ restored
+#else
+ SPFIX( tst sp, #4 )
+#endif
+ SPFIX( subeq sp, sp, #4 )
+ stmia sp, {r1 - r12}
+
+ ldmia r0, {r3 - r5}
+ add r7, sp, #S_SP - 4 @ here for interlock avoidance
+ mov r6, #-1 @ "" "" "" ""
+ add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+ SPFIX( addeq r2, r2, #4 )
+ str r3, [sp, #-4]! @ save the "real" r0 copied
+ @ from the exception stack
+
+ mov r3, lr
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r2 - sp_svc
+ @ r3 - lr_svc
+ @ r4 - lr_<exception>, already fixed up for correct return/restart
+ @ r5 - spsr_<exception>
+ @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ stmia r7, {r2 - r6}
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
+ .endm
+
+/*
+ * User mode handler macros
+ *
+ * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
+#error "sizeof(struct pt_regs) must be a multiple of 8"
+#endif
+
+ .macro usr_entry
+ UNWIND(.fnstart )
+ UNWIND(.cantunwind ) @ don't unwind the user space
+ sub sp, sp, #S_FRAME_SIZE
+ ARM( stmib sp, {r1 - r12} )
+ THUMB( stmia sp, {r0 - r12} )
+
+ ldmia r0, {r3 - r5}
+ add r0, sp, #S_PC @ here for interlock avoidance
+ mov r6, #-1 @ "" "" "" ""
+
+ str r3, [sp] @ save the "real" r0 copied
+ @ from the exception stack
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r4 - lr_<exception>, already fixed up for correct return/restart
+ @ r5 - spsr_<exception>
+ @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ @ Also, separately save sp_usr and lr_usr
+ @
+ stmia r0, {r4 - r6}
+ ARM( stmdb r0, {sp, lr}^ )
+ THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
+
+ @
+ @ Enable the alignment trap while in kernel mode
+ @
+ alignment_trap r0, .LCcralign
+
+ @
+ @ Clear FP to mark the first stack frame
+ @
+ zero_fp
+
+#ifdef CONFIG_IRQSOFF_TRACER
+ bl trace_hardirqs_off
+#endif
+ ct_user_exit save = 0
+ .endm
+
+ .macro kuser_cmpxchg_check
+#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
+ !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
+#ifndef CONFIG_MMU
+#warning "NPTL on non MMU needs fixing"
+#else
+ @ Make sure our user space atomic helper is restarted
+ @ if it was interrupted in a critical region. Here we
+ @ perform a quick test inline since it should be false
+ @ 99.9999% of the time. The rest is done out of line.
+ cmp r4, #TASK_SIZE
+ blhs kuser_cmpxchg64_fixup
+#endif
+#endif
+ .endm
+
+/*
+ * Vector stubs macro.
+ */
+ .macro vector_stub, name, mode, correction=0
+ .align 5
+
+vector_\name:
+ .if \correction
+ sub lr, lr, #\correction
+ .endif
+
+ @
+ @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
+ @ (parent CPSR)
+ @
+ stmia sp, {r0, lr} @ save r0, lr
+ mrs lr, spsr
+ str lr, [sp, #8] @ save spsr
+
+ @
+ @ Prepare for SVC32 mode. IRQs remain disabled.
+ @
+ mrs r0, cpsr
+ eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
+ msr spsr_cxsf, r0
+
+ @
+ @ the branch table must immediately follow this code
+ @
+ and lr, lr, #0x0f
+ THUMB( adr r0, 1f )
+ THUMB( ldr lr, [r0, lr, lsl #2] )
+ mov r0, sp
+ ARM( ldr lr, [pc, lr, lsl #2] )
+ movs pc, lr @ branch to handler in SVC mode
+ENDPROC(vector_\name)
+
+ .align 2
+ @ handler addresses follow this label
+1:
+ .endm
+
+
--
1.9.3

2014-06-30 09:01:57

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v7 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

From: Anton Vorontsov <[email protected]>

The FIQ debugger may be used to debug situations when the kernel stuck
in uninterruptable sections, e.g. the kernel infinitely loops or
deadlocked in an interrupt or with interrupts disabled.

By default KGDB FIQ is disabled in runtime, but can be enabled with
kgdb_fiq.enable=1 kernel command line option.

Signed-off-by: Anton Vorontsov <[email protected]>
Signed-off-by: John Stultz <[email protected]>
[[email protected]: Added __fiq_abt, rewrote stack init]
Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Ben Dooks <[email protected]>
Cc: Dave Martin <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 18 ++++++
arch/arm/include/asm/kgdb.h | 7 +++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/kgdb_fiq.c | 127 ++++++++++++++++++++++++++++++++++++++
arch/arm/kernel/kgdb_fiq_entry.S | 130 +++++++++++++++++++++++++++++++++++++++
6 files changed, 285 insertions(+)
create mode 100644 arch/arm/kernel/kgdb_fiq.c
create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 245058b..f385b27 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -297,6 +297,7 @@ choice
config ARCH_MULTIPLATFORM
bool "Allow multiple platforms to be selected"
depends on MMU
+ select ARCH_MIGHT_HAVE_KGDB_FIQ
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_HAS_SG_CHAIN
select ARM_PATCH_PHYS_VIRT
@@ -346,6 +347,7 @@ config ARCH_REALVIEW

config ARCH_VERSATILE
bool "ARM Ltd. Versatile family"
+ select ARCH_MIGHT_HAVE_KGDB_FIQ
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_AMBA
select ARM_TIMER_SP804
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 26536f7..c7342b6 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -2,6 +2,24 @@ menu "Kernel hacking"

source "lib/Kconfig.debug"

+config ARCH_MIGHT_HAVE_KGDB_FIQ
+ bool
+
+config KGDB_FIQ
+ bool "KGDB FIQ support"
+ depends on KGDB_KDB && ARCH_MIGHT_HAVE_KGDB_FIQ && !THUMB2_KERNEL
+ select FIQ
+ help
+ The FIQ debugger may be used to debug situations when the
+ kernel stuck in uninterruptable sections, e.g. the kernel
+ infinitely loops or deadlocked in an interrupt or with
+ interrupts disabled.
+
+ By default KGDB FIQ is disabled at runtime, but can be
+ enabled with kgdb_fiq.enable=1 kernel command line option.
+
+ If unsure, say N.
+
config ARM_PTDUMP
bool "Export kernel pagetable layout to userspace via debugfs"
depends on DEBUG_KERNEL
diff --git a/arch/arm/include/asm/kgdb.h b/arch/arm/include/asm/kgdb.h
index 0a9d5dd..5de21f01 100644
--- a/arch/arm/include/asm/kgdb.h
+++ b/arch/arm/include/asm/kgdb.h
@@ -11,7 +11,9 @@
#define __ARM_KGDB_H__

#include <linux/ptrace.h>
+#include <linux/linkage.h>
#include <asm/opcodes.h>
+#include <asm/exception.h>

/*
* GDB assumes that we're a user process being debugged, so
@@ -48,6 +50,11 @@ static inline void arch_kgdb_breakpoint(void)
extern void kgdb_handle_bus_error(void);
extern int kgdb_fault_expected;

+extern char kgdb_fiq_handler;
+extern char kgdb_fiq_handler_end;
+asmlinkage void __exception_irq_entry kgdb_fiq_do_handle(struct pt_regs *regs);
+extern int kgdb_register_fiq(unsigned int fiq);
+
#endif /* !__ASSEMBLY__ */

/*
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 38ddd9f..30ee8f3 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -68,6 +68,7 @@ endif
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
obj-$(CONFIG_KGDB) += kgdb.o
+obj-$(CONFIG_KGDB_FIQ) += kgdb_fiq_entry.o kgdb_fiq.o
obj-$(CONFIG_ARM_UNWIND) += unwind.o
obj-$(CONFIG_HAVE_TCM) += tcm.o
obj-$(CONFIG_OF) += devtree.o
diff --git a/arch/arm/kernel/kgdb_fiq.c b/arch/arm/kernel/kgdb_fiq.c
new file mode 100644
index 0000000..99d23cc
--- /dev/null
+++ b/arch/arm/kernel/kgdb_fiq.c
@@ -0,0 +1,127 @@
+/*
+ * KGDB FIQ
+ *
+ * Copyright 2010 Google, Inc.
+ * Arve Hjønnevåg <[email protected]>
+ * Colin Cross <[email protected]>
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/hardirq.h>
+#include <linux/atomic.h>
+#include <linux/kdb.h>
+#include <linux/kgdb.h>
+#include <asm/fiq.h>
+#include <asm/exception.h>
+
+static int kgdb_fiq_enabled;
+module_param_named(enable, kgdb_fiq_enabled, int, 0600);
+MODULE_PARM_DESC(enable, "set to 1 to enable FIQ KGDB");
+
+static unsigned int kgdb_fiq;
+
+struct fiq_stack {
+ u32 fiq[3];
+} ____cacheline_aligned;
+
+static struct fiq_stack stacks[NR_CPUS];
+
+asmlinkage void __exception_irq_entry kgdb_fiq_do_handle(struct pt_regs *regs)
+{
+ if (kgdb_nmi_poll_knock()) {
+ nmi_enter();
+ kgdb_handle_exception(1, 0, 0, regs);
+ nmi_exit();
+ }
+
+ eoi_fiq(kgdb_fiq);
+}
+
+static struct fiq_handler kgdb_fiq_desc = {
+ .name = "kgdb",
+};
+
+static long kgdb_fiq_setup_stack(void *info)
+{
+ struct pt_regs regs;
+
+ regs.ARM_sp = (unsigned long) stacks[smp_processor_id()].fiq;
+ set_fiq_regs(&regs);
+
+ return 0;
+}
+
+/**
+ * kgdb_fiq_enable_nmi - Manage NMI-triggered entry to KGDB
+ * @on: Flag to either enable or disable an NMI
+ *
+ * This function manages NMIs that usually cause KGDB to enter. That is, not
+ * all NMIs should be enabled or disabled, but only those that issue
+ * kgdb_handle_exception().
+ *
+ * The call counts disable requests, and thus allows to nest disables. But
+ * trying to enable already enabled NMI is an error.
+ */
+static void kgdb_fiq_enable_nmi(bool on)
+{
+ static atomic_t cnt;
+ int ret;
+
+ ret = atomic_add_return(on ? 1 : -1, &cnt);
+
+ /*
+ * There should be only one instance that calls this function
+ * in "enable, disable" order. All other users must call
+ * disable first, then enable. If not, something is wrong.
+ */
+ if (WARN_ON(ret > 1 && on))
+ return;
+
+ if (ret > 0)
+ enable_fiq(kgdb_fiq);
+ else
+ disable_fiq(kgdb_fiq);
+}
+
+int kgdb_register_fiq(unsigned int fiq)
+{
+ int err;
+ int cpu;
+
+ if (!kgdb_fiq_enabled)
+ return -ENODEV;
+
+ if (!has_fiq(fiq)) {
+ pr_warn(
+ "%s: Cannot register %u (no FIQ with this number)\n",
+ __func__, fiq);
+ return -ENODEV;
+ }
+
+ kgdb_fiq = fiq;
+
+ err = claim_fiq(&kgdb_fiq_desc);
+ if (err) {
+ pr_warn("%s: unable to claim fiq", __func__);
+ return err;
+ }
+
+ for_each_possible_cpu(cpu)
+ work_on_cpu(cpu, kgdb_fiq_setup_stack, NULL);
+
+ set_fiq_handler(&kgdb_fiq_handler,
+ &kgdb_fiq_handler_end - &kgdb_fiq_handler);
+
+ arch_kgdb_ops.enable_nmi = kgdb_fiq_enable_nmi;
+ return 0;
+}
diff --git a/arch/arm/kernel/kgdb_fiq_entry.S b/arch/arm/kernel/kgdb_fiq_entry.S
new file mode 100644
index 0000000..50499c0
--- /dev/null
+++ b/arch/arm/kernel/kgdb_fiq_entry.S
@@ -0,0 +1,130 @@
+/*
+ * KGDB FIQ entry
+ *
+ * Copyright 1996,1997,1998 Russell King.
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/memory.h>
+#include <asm/unwind.h>
+#include "entry-header.S"
+
+ .text
+
+@ This is needed for usr_entry/alignment_trap
+.LCcralign:
+ .long cr_alignment
+.LCdohandle:
+ .long kgdb_fiq_do_handle
+
+ .macro fiq_handler
+ ldr r1, =.LCdohandle
+ mov r0, sp
+ adr lr, BSYM(9997f)
+ ldr pc, [r1]
+9997:
+ .endm
+
+/*
+ * svc_exit_via_fiq
+ *
+ * This macro acts in a similar manner to svc_exit but switches to FIQ
+ * mode to restore the final part of the register state.
+ *
+ * We cannot use the normal svc_exit procedure because that would
+ * clobber spsr_svc (FIQ could be delivered during the first few instructions
+ * of vector_swi meaning its contents have not been saved anywhere).
+ */
+ .macro svc_exit_via_fiq, rpsr
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ tst \rpsr, #PSR_I_BIT
+ bleq trace_hardirqs_on
+#endif
+
+ 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
+
+ .align 5
+__fiq_svc:
+ svc_entry
+ fiq_handler
+ svc_exit_via_fiq r5
+ UNWIND(.fnend )
+ENDPROC(__fiq_svc)
+ .ltorg
+
+ .align 5
+__fiq_abt:
+ svc_entry
+
+ msr cpsr_c, #ABT_MODE | PSR_I_BIT | PSR_F_BIT
+ mov r0, lr @ Save lr_abt
+ mrs r1, spsr @ Save spsr_abt, abort is now safe
+ msr cpsr_c, #SVC_MODE | PSR_I_BIT | PSR_F_BIT
+ push {r0 - r1}
+
+ 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_svc)
+ .ltorg
+
+ .align 5
+__fiq_usr:
+ usr_entry
+ kuser_cmpxchg_check
+ fiq_handler
+ get_thread_info tsk
+ mov why, #0
+ b ret_to_user_from_irq
+ UNWIND(.fnend )
+ENDPROC(__fiq_usr)
+ .ltorg
+
+ .global kgdb_fiq_handler
+kgdb_fiq_handler:
+
+ vector_stub fiq, FIQ_MODE, 4
+
+ .long __fiq_usr @ 0 (USR_26 / USR_32)
+ .long __fiq_svc @ 1 (FIQ_26 / FIQ_32)
+ .long __fiq_svc @ 2 (IRQ_26 / IRQ_32)
+ .long __fiq_svc @ 3 (SVC_26 / SVC_32)
+ .long __fiq_svc @ 4
+ .long __fiq_svc @ 5
+ .long __fiq_svc @ 6
+ .long __fiq_abt @ 7
+ .long __fiq_svc @ 8
+ .long __fiq_svc @ 9
+ .long __fiq_svc @ a
+ .long __fiq_svc @ b
+ .long __fiq_svc @ c
+ .long __fiq_svc @ d
+ .long __fiq_svc @ e
+ .long __fiq_svc @ f
+
+ .global kgdb_fiq_handler_end
+kgdb_fiq_handler_end:
--
1.9.3

2014-06-30 13:54:32

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v6 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

On 26/06/14 10:54, Daniel Thompson wrote:
>> Also bear in mind that svc_entry calls trace_hardirqs_off - is this
>> appropriate and safe for the FIQ to call?
>
> I personally think it appropriate and it looked safe on the lockdep side
> of things. However I will look a bit deeper at this since I don't
> remember how far I chased things back.

I've reviewed as far as I can.

Regarding safety I can't find anything much to upset the FIQ handler. I
think it might occasionally trigger the trace code's recursion avoidance
causing the trace event to be dropped but that's about it.

I admit I came very close to removing the trace_hardirqs calls from the
FIQ code but in the end I've left it. The hardirqs *are* off during FIQ
execution.


>>> + msr cpsr_c, #FIQ_MODE | PSR_I_BIT | PSR_F_BIT
>>
>> Here we switch to FIQ mode. What about the PSR_A_BIT which prevents
>> imprecise aborts on ARMv6+ ?
>>
>> Nevertheless, I think it's safe because the A bit will be set by the
>> CPU when taking the FIQ exception, and it should remain set since
>> cpsr_c won't modify it.
>
> Agreed.
>
> Note that while double checking this I realized that this code will drop
> the value of PSR_ISETSTATE (T bit) that the vector_stub macro set for
> us. I'll fix this.

I was wrong about this. CPSR T bit is part of execution state can cannot
be modified by msr.


> I've picked out the following actions from the above:
>
> 1. Wrap a save and restore lr_abt and spsr_abt around the FIQ handler

Done.

> 2. Add a paired up trace_hardirqs_on() (and review more deeply).

Done.

> 3. Add comments explaining hazards w.r.t. data abort,

Done.

> 4. Correctly manage T bit during transition back to FIQ mode.

Not applicable.

> Do I miss anything?

I hope not!


Daniel.

2014-07-10 08:05:41

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

This patchset makes it possible to use kgdb's NMI infrastructure on ARM
platforms.

The patches have been previously circulated as part of a large patchset
mixing together ARM architecture code and driver changes
(http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901 ). This
patchset is dramatically cut down to include only the arch/arm code. The
driver code (irqchip and tty/serial) will follow when/if the arch code
is accepted.

The first two patches modify the FIQ infrastructure to allow interrupt
controller drivers to register callbacks (the fiq_chip structure) to
manage FIQ routings and to ACK and EOI the FIQ. This makes it possible
to use FIQ in multi-platform kernels and with recent ARM interrupt
controllers.

The remaining two patches provide architecture support for KGDB's NMI
feature (and rely upon the preceding changes to the FIQ code).

Tested on qemu (versatile), STiH416 (B2020 board) and Freescale i.MX6
quad (wandboard).

Changes since v7:

- Introduced ack_fiq() to complement eoi_fiq(). Without this it is
not possible to meet the GIC specification (previous versions worked
when tested but are unpredictable according to the specification).
ack_fiq() also makes is possible for drivers for devices with multiple
interrupt lines to discover the interrupt source correctly.

Changes since v6:

- Corrected off-by-one comparison in has_fiq() (Nicolas Pitre)
- Rewrote the FIQ stack initialization (Nicolas Pitre). This fixes a
serious data corruption bug due to bad stack mismanagement.
- Introduced __fiq_abt to ensure lr_abt and spsr_abt are saved and
restored if we fast-interrupt an abort (Russell King).
- Significantly improved the commenting of the exception handlers.
- Added a call to trace_hardirqs_on() if we clear the I bit as we
exit the exception handler.

Changes since v5:

- Separated anything not strictly impacting arch/arm.
- Fixed a spurious add/remove of code within the series (there was
broken code in intermediate patches)

For previous changes see:
http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901

Anton Vorontsov (2):
ARM: Move some macros from entry-armv to entry-header
ARM: Add KGDB/KDB FIQ debugger generic code

Daniel Thompson (2):
arm: fiq: Add callbacks to manage FIQ routings
arm: fiq: Allow ACK and EOI to be passed to the intc

arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 18 +++++
arch/arm/include/asm/fiq.h | 17 ++++
arch/arm/include/asm/kgdb.h | 7 ++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/entry-armv.S | 151 +----------------------------------
arch/arm/kernel/entry-header.S | 164 +++++++++++++++++++++++++++++++++++++++
arch/arm/kernel/fiq.c | 122 ++++++++++++++++++++++++++++-
arch/arm/kernel/kgdb_fiq.c | 132 +++++++++++++++++++++++++++++++
arch/arm/kernel/kgdb_fiq_entry.S | 130 +++++++++++++++++++++++++++++++
10 files changed, 592 insertions(+), 152 deletions(-)
create mode 100644 arch/arm/kernel/kgdb_fiq.c
create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S

--
1.9.3

2014-07-10 08:05:53

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v8 1/4] arm: fiq: Add callbacks to manage FIQ routings

Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
virq into a FIQ virq. This is too inflexible for multi-platform kernels
and makes runtime error checking impossible.

We solve this by introducing a flexible mapping that allows interrupt
controllers that support FIQ to register those mappings. This, in turn,
makes it much possible for drivers in DT kernels to install FIQ handlers
without knowing anything about the interrupt controller.

Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Fabio Estevam <[email protected]>
---
arch/arm/include/asm/fiq.h | 8 ++++
arch/arm/kernel/fiq.c | 103 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index d493d0b..ed44528 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -16,8 +16,14 @@
#ifndef __ASM_FIQ_H
#define __ASM_FIQ_H

+#include <linux/irq.h>
#include <asm/ptrace.h>

+struct fiq_chip {
+ void (*fiq_enable)(struct irq_data *data);
+ void (*fiq_disable)(struct irq_data *data);
+};
+
struct fiq_handler {
struct fiq_handler *next;
/* Name
@@ -38,6 +44,8 @@ extern void release_fiq(struct fiq_handler *f);
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern bool has_fiq(int fiq);
+extern void fiq_register_mapping(int irq, struct fiq_chip *chip);

/* helpers defined in fiqasm.S: */
extern void __set_fiq_regs(unsigned long const *regs);
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 918875d..5d831cf 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -40,6 +40,9 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/seq_file.h>
+#include <linux/irq.h>
+#include <linux/radix-tree.h>
+#include <linux/slab.h>

#include <asm/cacheflush.h>
#include <asm/cp15.h>
@@ -52,7 +55,15 @@
(unsigned)&vector_fiq_offset; \
})

+struct fiq_data {
+ struct fiq_chip *fiq_chip;
+ struct irq_data *irq_data;
+};
+
static unsigned long no_fiq_insn;
+static int fiq_start = -1;
+static RADIX_TREE(fiq_data_tree, GFP_KERNEL);
+static DEFINE_MUTEX(fiq_data_mutex);

/* Default reacquire function
* - we always relinquish FIQ control
@@ -127,18 +138,65 @@ void release_fiq(struct fiq_handler *f)
while (current_fiq->fiq_op(current_fiq->dev_id, 0));
}

-static int fiq_start;
+static struct fiq_data *lookup_fiq_data(int fiq)
+{
+ struct fiq_data *data;
+
+ rcu_read_lock();
+ data = radix_tree_lookup(&fiq_data_tree, fiq);
+ rcu_read_unlock();
+
+ return data;
+}

void enable_fiq(int fiq)
{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data) {
+ if (data->fiq_chip->fiq_enable)
+ data->fiq_chip->fiq_enable(data->irq_data);
+ enable_irq(fiq);
+ return;
+ }
+
+ if (WARN_ON(fiq_start == -1))
+ return;
+
enable_irq(fiq + fiq_start);
}

void disable_fiq(int fiq)
{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data) {
+ if (data->fiq_chip->fiq_disable)
+ data->fiq_chip->fiq_disable(data->irq_data);
+ disable_irq(fiq);
+ return;
+ }
+
+ if (WARN_ON(fiq_start == -1))
+ return;
+
disable_irq(fiq + fiq_start);
}

+bool has_fiq(int fiq)
+{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data)
+ return true;
+
+ if (fiq_start == -1)
+ return false;
+
+ return fiq >= fiq_start;
+}
+EXPORT_SYMBOL(has_fiq);
+
EXPORT_SYMBOL(set_fiq_handler);
EXPORT_SYMBOL(__set_fiq_regs); /* defined in fiqasm.S */
EXPORT_SYMBOL(__get_fiq_regs); /* defined in fiqasm.S */
@@ -147,9 +205,50 @@ EXPORT_SYMBOL(release_fiq);
EXPORT_SYMBOL(enable_fiq);
EXPORT_SYMBOL(disable_fiq);

+/*
+ * Add a mapping from a Linux irq to the fiq data.
+ */
+void fiq_register_mapping(int irq, struct fiq_chip *chip)
+{
+ struct fiq_data *fiq_data = NULL;
+ int res;
+
+ /* fiq_register_mapping can't be mixed with init_FIQ */
+ BUG_ON(fiq_start != -1);
+
+ fiq_data = kmalloc(sizeof(*fiq_data), GFP_KERNEL);
+ if (!fiq_data)
+ goto err;
+
+ fiq_data->fiq_chip = chip;
+ fiq_data->irq_data = irq_get_irq_data(irq);
+ BUG_ON(!fiq_data->irq_data);
+
+ mutex_lock(&fiq_data_mutex);
+ res = radix_tree_insert(&fiq_data_tree, irq, fiq_data);
+ mutex_unlock(&fiq_data_mutex);
+ if (res)
+ goto err;
+
+ return;
+
+err:
+ kfree(fiq_data);
+ pr_err("fiq: Cannot register mapping %d\n", irq);
+}
+
+/*
+ * Set the offset between normal IRQs and their FIQ shadows.
+ */
void __init init_FIQ(int start)
{
+ fiq_start = start;
+}
+
+static int __init init_default_fiq_handler(void)
+{
unsigned offset = FIQ_OFFSET;
no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
- fiq_start = start;
+ return 0;
}
+pure_initcall(init_default_fiq_handler);
--
1.9.3

2014-07-10 08:06:07

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v8 3/4] ARM: Move some macros from entry-armv to entry-header

From: Anton Vorontsov <[email protected]>

Just move the macros into header file as we would want to use them for
KGDB FIQ entry code.

The following macros were moved:

- svc_entry
- usr_entry
- kuser_cmpxchg_check
- vector_stub

To make kuser_cmpxchg_check actually work across different files, we
also have to make kuser_cmpxchg64_fixup global.

Signed-off-by: Anton Vorontsov <[email protected]>
Signed-off-by: John Stultz <[email protected]>
Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---
arch/arm/kernel/entry-armv.S | 151 +------------------------------------
arch/arm/kernel/entry-header.S | 164 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 165 insertions(+), 150 deletions(-)

diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 52a949a..4172cd6 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -140,53 +140,6 @@ ENDPROC(__und_invalid)
* SVC mode handlers
*/

-#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
-#define SPFIX(code...) code
-#else
-#define SPFIX(code...)
-#endif
-
- .macro svc_entry, stack_hole=0
- UNWIND(.fnstart )
- UNWIND(.save {r0 - pc} )
- sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
-#ifdef CONFIG_THUMB2_KERNEL
- SPFIX( str r0, [sp] ) @ temporarily saved
- SPFIX( mov r0, sp )
- SPFIX( tst r0, #4 ) @ test original stack alignment
- SPFIX( ldr r0, [sp] ) @ restored
-#else
- SPFIX( tst sp, #4 )
-#endif
- SPFIX( subeq sp, sp, #4 )
- stmia sp, {r1 - r12}
-
- ldmia r0, {r3 - r5}
- add r7, sp, #S_SP - 4 @ here for interlock avoidance
- mov r6, #-1 @ "" "" "" ""
- add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
- SPFIX( addeq r2, r2, #4 )
- str r3, [sp, #-4]! @ save the "real" r0 copied
- @ from the exception stack
-
- mov r3, lr
-
- @
- @ We are now ready to fill in the remaining blanks on the stack:
- @
- @ r2 - sp_svc
- @ r3 - lr_svc
- @ r4 - lr_<exception>, already fixed up for correct return/restart
- @ r5 - spsr_<exception>
- @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
- @
- stmia r7, {r2 - r6}
-
-#ifdef CONFIG_TRACE_IRQFLAGS
- bl trace_hardirqs_off
-#endif
- .endm
-
.align 5
__dabt_svc:
svc_entry
@@ -306,73 +259,8 @@ ENDPROC(__pabt_svc)

/*
* User mode handlers
- *
- * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
*/

-#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
-#error "sizeof(struct pt_regs) must be a multiple of 8"
-#endif
-
- .macro usr_entry
- UNWIND(.fnstart )
- UNWIND(.cantunwind ) @ don't unwind the user space
- sub sp, sp, #S_FRAME_SIZE
- ARM( stmib sp, {r1 - r12} )
- THUMB( stmia sp, {r0 - r12} )
-
- ldmia r0, {r3 - r5}
- add r0, sp, #S_PC @ here for interlock avoidance
- mov r6, #-1 @ "" "" "" ""
-
- str r3, [sp] @ save the "real" r0 copied
- @ from the exception stack
-
- @
- @ We are now ready to fill in the remaining blanks on the stack:
- @
- @ r4 - lr_<exception>, already fixed up for correct return/restart
- @ r5 - spsr_<exception>
- @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
- @
- @ Also, separately save sp_usr and lr_usr
- @
- stmia r0, {r4 - r6}
- ARM( stmdb r0, {sp, lr}^ )
- THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
-
- @
- @ Enable the alignment trap while in kernel mode
- @
- alignment_trap r0, .LCcralign
-
- @
- @ Clear FP to mark the first stack frame
- @
- zero_fp
-
-#ifdef CONFIG_IRQSOFF_TRACER
- bl trace_hardirqs_off
-#endif
- ct_user_exit save = 0
- .endm
-
- .macro kuser_cmpxchg_check
-#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
- !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
-#ifndef CONFIG_MMU
-#warning "NPTL on non MMU needs fixing"
-#else
- @ Make sure our user space atomic helper is restarted
- @ if it was interrupted in a critical region. Here we
- @ perform a quick test inline since it should be false
- @ 99.9999% of the time. The rest is done out of line.
- cmp r4, #TASK_SIZE
- blhs kuser_cmpxchg64_fixup
-#endif
-#endif
- .endm
-
.align 5
__dabt_usr:
usr_entry
@@ -823,6 +711,7 @@ __kuser_cmpxchg64: @ 0xffff0f60
ldmfd sp!, {r4, r5, r6, pc}

.text
+ .global kuser_cmpxchg64_fixup
kuser_cmpxchg64_fixup:
@ Called from kuser_cmpxchg_fixup.
@ r4 = address of interrupted insn (must be preserved).
@@ -964,44 +853,6 @@ __kuser_helper_end:
* SP points to a minimal amount of processor-private memory, the address
* of which is copied into r0 for the mode specific abort handler.
*/
- .macro vector_stub, name, mode, correction=0
- .align 5
-
-vector_\name:
- .if \correction
- sub lr, lr, #\correction
- .endif
-
- @
- @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
- @ (parent CPSR)
- @
- stmia sp, {r0, lr} @ save r0, lr
- mrs lr, spsr
- str lr, [sp, #8] @ save spsr
-
- @
- @ Prepare for SVC32 mode. IRQs remain disabled.
- @
- mrs r0, cpsr
- eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
- msr spsr_cxsf, r0
-
- @
- @ the branch table must immediately follow this code
- @
- and lr, lr, #0x0f
- THUMB( adr r0, 1f )
- THUMB( ldr lr, [r0, lr, lsl #2] )
- mov r0, sp
- ARM( ldr lr, [pc, lr, lsl #2] )
- movs pc, lr @ branch to handler in SVC mode
-ENDPROC(vector_\name)
-
- .align 2
- @ handler addresses follow this label
-1:
- .endm

.section .stubs, "ax", %progbits
__stubs_start:
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index 5d702f8..eb2c426 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S
@@ -356,3 +356,167 @@ scno .req r7 @ syscall number
tbl .req r8 @ syscall table pointer
why .req r8 @ Linux syscall (!= 0)
tsk .req r9 @ current thread_info
+
+/*
+ * SVC mode handler macros
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
+#define SPFIX(code...) code
+#else
+#define SPFIX(code...)
+#endif
+
+ .macro svc_entry, stack_hole=0
+ UNWIND(.fnstart )
+ UNWIND(.save {r0 - pc} )
+ sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+#ifdef CONFIG_THUMB2_KERNEL
+ SPFIX( str r0, [sp] ) @ temporarily saved
+ SPFIX( mov r0, sp )
+ SPFIX( tst r0, #4 ) @ test original stack alignment
+ SPFIX( ldr r0, [sp] ) @ restored
+#else
+ SPFIX( tst sp, #4 )
+#endif
+ SPFIX( subeq sp, sp, #4 )
+ stmia sp, {r1 - r12}
+
+ ldmia r0, {r3 - r5}
+ add r7, sp, #S_SP - 4 @ here for interlock avoidance
+ mov r6, #-1 @ "" "" "" ""
+ add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+ SPFIX( addeq r2, r2, #4 )
+ str r3, [sp, #-4]! @ save the "real" r0 copied
+ @ from the exception stack
+
+ mov r3, lr
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r2 - sp_svc
+ @ r3 - lr_svc
+ @ r4 - lr_<exception>, already fixed up for correct return/restart
+ @ r5 - spsr_<exception>
+ @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ stmia r7, {r2 - r6}
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
+ .endm
+
+/*
+ * User mode handler macros
+ *
+ * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
+#error "sizeof(struct pt_regs) must be a multiple of 8"
+#endif
+
+ .macro usr_entry
+ UNWIND(.fnstart )
+ UNWIND(.cantunwind ) @ don't unwind the user space
+ sub sp, sp, #S_FRAME_SIZE
+ ARM( stmib sp, {r1 - r12} )
+ THUMB( stmia sp, {r0 - r12} )
+
+ ldmia r0, {r3 - r5}
+ add r0, sp, #S_PC @ here for interlock avoidance
+ mov r6, #-1 @ "" "" "" ""
+
+ str r3, [sp] @ save the "real" r0 copied
+ @ from the exception stack
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r4 - lr_<exception>, already fixed up for correct return/restart
+ @ r5 - spsr_<exception>
+ @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ @ Also, separately save sp_usr and lr_usr
+ @
+ stmia r0, {r4 - r6}
+ ARM( stmdb r0, {sp, lr}^ )
+ THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
+
+ @
+ @ Enable the alignment trap while in kernel mode
+ @
+ alignment_trap r0, .LCcralign
+
+ @
+ @ Clear FP to mark the first stack frame
+ @
+ zero_fp
+
+#ifdef CONFIG_IRQSOFF_TRACER
+ bl trace_hardirqs_off
+#endif
+ ct_user_exit save = 0
+ .endm
+
+ .macro kuser_cmpxchg_check
+#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \
+ !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
+#ifndef CONFIG_MMU
+#warning "NPTL on non MMU needs fixing"
+#else
+ @ Make sure our user space atomic helper is restarted
+ @ if it was interrupted in a critical region. Here we
+ @ perform a quick test inline since it should be false
+ @ 99.9999% of the time. The rest is done out of line.
+ cmp r4, #TASK_SIZE
+ blhs kuser_cmpxchg64_fixup
+#endif
+#endif
+ .endm
+
+/*
+ * Vector stubs macro.
+ */
+ .macro vector_stub, name, mode, correction=0
+ .align 5
+
+vector_\name:
+ .if \correction
+ sub lr, lr, #\correction
+ .endif
+
+ @
+ @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
+ @ (parent CPSR)
+ @
+ stmia sp, {r0, lr} @ save r0, lr
+ mrs lr, spsr
+ str lr, [sp, #8] @ save spsr
+
+ @
+ @ Prepare for SVC32 mode. IRQs remain disabled.
+ @
+ mrs r0, cpsr
+ eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
+ msr spsr_cxsf, r0
+
+ @
+ @ the branch table must immediately follow this code
+ @
+ and lr, lr, #0x0f
+ THUMB( adr r0, 1f )
+ THUMB( ldr lr, [r0, lr, lsl #2] )
+ mov r0, sp
+ ARM( ldr lr, [pc, lr, lsl #2] )
+ movs pc, lr @ branch to handler in SVC mode
+ENDPROC(vector_\name)
+
+ .align 2
+ @ handler addresses follow this label
+1:
+ .endm
+
+
--
1.9.3

2014-07-10 08:06:33

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v8 4/4] ARM: Add KGDB/KDB FIQ debugger generic code

From: Anton Vorontsov <[email protected]>

The FIQ debugger may be used to debug situations when the kernel stuck
in uninterruptable sections, e.g. the kernel infinitely loops or
deadlocked in an interrupt or with interrupts disabled.

By default KGDB FIQ is disabled in runtime, but can be enabled with
kgdb_fiq.enable=1 kernel command line option.

Signed-off-by: Anton Vorontsov <[email protected]>
Signed-off-by: John Stultz <[email protected]>
[[email protected]: Added __fiq_abt, rewrote stack init]
Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Ben Dooks <[email protected]>
Cc: Dave Martin <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 18 ++++++
arch/arm/include/asm/kgdb.h | 7 +++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/kgdb_fiq.c | 132 +++++++++++++++++++++++++++++++++++++++
arch/arm/kernel/kgdb_fiq_entry.S | 130 ++++++++++++++++++++++++++++++++++++++
6 files changed, 290 insertions(+)
create mode 100644 arch/arm/kernel/kgdb_fiq.c
create mode 100644 arch/arm/kernel/kgdb_fiq_entry.S

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 245058b..f385b27 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -297,6 +297,7 @@ choice
config ARCH_MULTIPLATFORM
bool "Allow multiple platforms to be selected"
depends on MMU
+ select ARCH_MIGHT_HAVE_KGDB_FIQ
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_HAS_SG_CHAIN
select ARM_PATCH_PHYS_VIRT
@@ -346,6 +347,7 @@ config ARCH_REALVIEW

config ARCH_VERSATILE
bool "ARM Ltd. Versatile family"
+ select ARCH_MIGHT_HAVE_KGDB_FIQ
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_AMBA
select ARM_TIMER_SP804
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 26536f7..c7342b6 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -2,6 +2,24 @@ menu "Kernel hacking"

source "lib/Kconfig.debug"

+config ARCH_MIGHT_HAVE_KGDB_FIQ
+ bool
+
+config KGDB_FIQ
+ bool "KGDB FIQ support"
+ depends on KGDB_KDB && ARCH_MIGHT_HAVE_KGDB_FIQ && !THUMB2_KERNEL
+ select FIQ
+ help
+ The FIQ debugger may be used to debug situations when the
+ kernel stuck in uninterruptable sections, e.g. the kernel
+ infinitely loops or deadlocked in an interrupt or with
+ interrupts disabled.
+
+ By default KGDB FIQ is disabled at runtime, but can be
+ enabled with kgdb_fiq.enable=1 kernel command line option.
+
+ If unsure, say N.
+
config ARM_PTDUMP
bool "Export kernel pagetable layout to userspace via debugfs"
depends on DEBUG_KERNEL
diff --git a/arch/arm/include/asm/kgdb.h b/arch/arm/include/asm/kgdb.h
index 0a9d5dd..5de21f01 100644
--- a/arch/arm/include/asm/kgdb.h
+++ b/arch/arm/include/asm/kgdb.h
@@ -11,7 +11,9 @@
#define __ARM_KGDB_H__

#include <linux/ptrace.h>
+#include <linux/linkage.h>
#include <asm/opcodes.h>
+#include <asm/exception.h>

/*
* GDB assumes that we're a user process being debugged, so
@@ -48,6 +50,11 @@ static inline void arch_kgdb_breakpoint(void)
extern void kgdb_handle_bus_error(void);
extern int kgdb_fault_expected;

+extern char kgdb_fiq_handler;
+extern char kgdb_fiq_handler_end;
+asmlinkage void __exception_irq_entry kgdb_fiq_do_handle(struct pt_regs *regs);
+extern int kgdb_register_fiq(unsigned int fiq);
+
#endif /* !__ASSEMBLY__ */

/*
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 38ddd9f..30ee8f3 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -68,6 +68,7 @@ endif
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
obj-$(CONFIG_KGDB) += kgdb.o
+obj-$(CONFIG_KGDB_FIQ) += kgdb_fiq_entry.o kgdb_fiq.o
obj-$(CONFIG_ARM_UNWIND) += unwind.o
obj-$(CONFIG_HAVE_TCM) += tcm.o
obj-$(CONFIG_OF) += devtree.o
diff --git a/arch/arm/kernel/kgdb_fiq.c b/arch/arm/kernel/kgdb_fiq.c
new file mode 100644
index 0000000..a894dde
--- /dev/null
+++ b/arch/arm/kernel/kgdb_fiq.c
@@ -0,0 +1,132 @@
+/*
+ * KGDB FIQ
+ *
+ * Copyright 2010 Google, Inc.
+ * Arve Hjønnevåg <[email protected]>
+ * Colin Cross <[email protected]>
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/hardirq.h>
+#include <linux/atomic.h>
+#include <linux/kdb.h>
+#include <linux/kgdb.h>
+#include <asm/fiq.h>
+#include <asm/exception.h>
+
+static int kgdb_fiq_enabled;
+module_param_named(enable, kgdb_fiq_enabled, int, 0600);
+MODULE_PARM_DESC(enable, "set to 1 to enable FIQ KGDB");
+
+static unsigned int kgdb_fiq;
+
+struct fiq_stack {
+ u32 fiq[3];
+} ____cacheline_aligned;
+
+static struct fiq_stack stacks[NR_CPUS];
+
+asmlinkage void __exception_irq_entry kgdb_fiq_do_handle(struct pt_regs *regs)
+{
+ int actual;
+
+ nmi_enter();
+ actual = ack_fiq(kgdb_fiq);
+ WARN_ON(actual != kgdb_fiq);
+
+ /* there's no harm in doing this regardless of the above WARN_ON() */
+ if (kgdb_nmi_poll_knock())
+ kgdb_handle_exception(1, 0, 0, regs);
+
+ eoi_fiq(actual);
+ nmi_exit();
+}
+
+static struct fiq_handler kgdb_fiq_desc = {
+ .name = "kgdb",
+};
+
+static long kgdb_fiq_setup_stack(void *info)
+{
+ struct pt_regs regs;
+
+ regs.ARM_sp = (unsigned long) stacks[smp_processor_id()].fiq;
+ set_fiq_regs(&regs);
+
+ return 0;
+}
+
+/**
+ * kgdb_fiq_enable_nmi - Manage NMI-triggered entry to KGDB
+ * @on: Flag to either enable or disable an NMI
+ *
+ * This function manages NMIs that usually cause KGDB to enter. That is, not
+ * all NMIs should be enabled or disabled, but only those that issue
+ * kgdb_handle_exception().
+ *
+ * The call counts disable requests, and thus allows to nest disables. But
+ * trying to enable already enabled NMI is an error.
+ */
+static void kgdb_fiq_enable_nmi(bool on)
+{
+ static atomic_t cnt;
+ int ret;
+
+ ret = atomic_add_return(on ? 1 : -1, &cnt);
+
+ /*
+ * There should be only one instance that calls this function
+ * in "enable, disable" order. All other users must call
+ * disable first, then enable. If not, something is wrong.
+ */
+ if (WARN_ON(ret > 1 && on))
+ return;
+
+ if (ret > 0)
+ enable_fiq(kgdb_fiq);
+ else
+ disable_fiq(kgdb_fiq);
+}
+
+int kgdb_register_fiq(unsigned int fiq)
+{
+ int err;
+ int cpu;
+
+ if (!kgdb_fiq_enabled)
+ return -ENODEV;
+
+ if (!has_fiq(fiq)) {
+ pr_warn(
+ "%s: Cannot register %u (no FIQ with this number)\n",
+ __func__, fiq);
+ return -ENODEV;
+ }
+
+ kgdb_fiq = fiq;
+
+ err = claim_fiq(&kgdb_fiq_desc);
+ if (err) {
+ pr_warn("%s: unable to claim fiq", __func__);
+ return err;
+ }
+
+ for_each_possible_cpu(cpu)
+ work_on_cpu(cpu, kgdb_fiq_setup_stack, NULL);
+
+ set_fiq_handler(&kgdb_fiq_handler,
+ &kgdb_fiq_handler_end - &kgdb_fiq_handler);
+
+ arch_kgdb_ops.enable_nmi = kgdb_fiq_enable_nmi;
+ return 0;
+}
diff --git a/arch/arm/kernel/kgdb_fiq_entry.S b/arch/arm/kernel/kgdb_fiq_entry.S
new file mode 100644
index 0000000..50499c0
--- /dev/null
+++ b/arch/arm/kernel/kgdb_fiq_entry.S
@@ -0,0 +1,130 @@
+/*
+ * KGDB FIQ entry
+ *
+ * Copyright 1996,1997,1998 Russell King.
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/memory.h>
+#include <asm/unwind.h>
+#include "entry-header.S"
+
+ .text
+
+@ This is needed for usr_entry/alignment_trap
+.LCcralign:
+ .long cr_alignment
+.LCdohandle:
+ .long kgdb_fiq_do_handle
+
+ .macro fiq_handler
+ ldr r1, =.LCdohandle
+ mov r0, sp
+ adr lr, BSYM(9997f)
+ ldr pc, [r1]
+9997:
+ .endm
+
+/*
+ * svc_exit_via_fiq
+ *
+ * This macro acts in a similar manner to svc_exit but switches to FIQ
+ * mode to restore the final part of the register state.
+ *
+ * We cannot use the normal svc_exit procedure because that would
+ * clobber spsr_svc (FIQ could be delivered during the first few instructions
+ * of vector_swi meaning its contents have not been saved anywhere).
+ */
+ .macro svc_exit_via_fiq, rpsr
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ tst \rpsr, #PSR_I_BIT
+ bleq trace_hardirqs_on
+#endif
+
+ 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
+
+ .align 5
+__fiq_svc:
+ svc_entry
+ fiq_handler
+ svc_exit_via_fiq r5
+ UNWIND(.fnend )
+ENDPROC(__fiq_svc)
+ .ltorg
+
+ .align 5
+__fiq_abt:
+ svc_entry
+
+ msr cpsr_c, #ABT_MODE | PSR_I_BIT | PSR_F_BIT
+ mov r0, lr @ Save lr_abt
+ mrs r1, spsr @ Save spsr_abt, abort is now safe
+ msr cpsr_c, #SVC_MODE | PSR_I_BIT | PSR_F_BIT
+ push {r0 - r1}
+
+ 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_svc)
+ .ltorg
+
+ .align 5
+__fiq_usr:
+ usr_entry
+ kuser_cmpxchg_check
+ fiq_handler
+ get_thread_info tsk
+ mov why, #0
+ b ret_to_user_from_irq
+ UNWIND(.fnend )
+ENDPROC(__fiq_usr)
+ .ltorg
+
+ .global kgdb_fiq_handler
+kgdb_fiq_handler:
+
+ vector_stub fiq, FIQ_MODE, 4
+
+ .long __fiq_usr @ 0 (USR_26 / USR_32)
+ .long __fiq_svc @ 1 (FIQ_26 / FIQ_32)
+ .long __fiq_svc @ 2 (IRQ_26 / IRQ_32)
+ .long __fiq_svc @ 3 (SVC_26 / SVC_32)
+ .long __fiq_svc @ 4
+ .long __fiq_svc @ 5
+ .long __fiq_svc @ 6
+ .long __fiq_abt @ 7
+ .long __fiq_svc @ 8
+ .long __fiq_svc @ 9
+ .long __fiq_svc @ a
+ .long __fiq_svc @ b
+ .long __fiq_svc @ c
+ .long __fiq_svc @ d
+ .long __fiq_svc @ e
+ .long __fiq_svc @ f
+
+ .global kgdb_fiq_handler_end
+kgdb_fiq_handler_end:
--
1.9.3

2014-07-10 08:07:21

by Daniel Thompson

[permalink] [raw]
Subject: [PATCH v8 2/4] arm: fiq: Allow ACK and EOI to be passed to the intc

Modern ARM interrupt controllers require an ACK as interrupts are taken
and an EOI on completion. The FIQ code currently does not provide any
API to perform this.

This patch provides this API, implemented by adding two callbacks to the
fiq_chip structure.

Signed-off-by: Daniel Thompson <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
Cc: Russell King <[email protected]>
Cc: Fabio Estevam <[email protected]>
---
arch/arm/include/asm/fiq.h | 9 +++++++++
arch/arm/kernel/fiq.c | 19 +++++++++++++++++++
2 files changed, 28 insertions(+)

diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index ed44528..a25c952 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -22,6 +22,13 @@
struct fiq_chip {
void (*fiq_enable)(struct irq_data *data);
void (*fiq_disable)(struct irq_data *data);
+
+ /* .fiq_ack() and .fiq_eoi() will be called from the FIQ
+ * handler. For this reason they must not use spin locks (or any
+ * other locks).
+ */
+ int (*fiq_ack)(struct irq_data *data);
+ void (*fiq_eoi)(struct irq_data *data);
};

struct fiq_handler {
@@ -44,6 +51,8 @@ extern void release_fiq(struct fiq_handler *f);
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern int ack_fiq(int fiq);
+extern void eoi_fiq(int fiq);
extern bool has_fiq(int fiq);
extern void fiq_register_mapping(int irq, struct fiq_chip *chip);

diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 5d831cf..3ccaa8c 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -183,6 +183,25 @@ void disable_fiq(int fiq)
disable_irq(fiq + fiq_start);
}

+int ack_fiq(int fiq)
+{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data && data->fiq_chip->fiq_ack)
+ return data->fiq_chip->fiq_ack(data->irq_data);
+
+ return fiq;
+}
+
+void eoi_fiq(int fiq)
+{
+ struct fiq_data *data = lookup_fiq_data(fiq);
+
+ if (data && data->fiq_chip->fiq_eoi)
+ data->fiq_chip->fiq_eoi(data->irq_data);
+}
+EXPORT_SYMBOL(eoi_fiq);
+
bool has_fiq(int fiq)
{
struct fiq_data *data = lookup_fiq_data(fiq);
--
1.9.3

2014-07-14 13:51:32

by Harro Haan

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 10 July 2014 10:03, Daniel Thompson <[email protected]> wrote:
>
> This patchset makes it possible to use kgdb's NMI infrastructure on ARM
> platforms.
>
> The patches have been previously circulated as part of a large patchset
> mixing together ARM architecture code and driver changes
> (http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901 ). This
> patchset is dramatically cut down to include only the arch/arm code. The
> driver code (irqchip and tty/serial) will follow when/if the arch code
> is accepted.
>
> The first two patches modify the FIQ infrastructure to allow interrupt
> controller drivers to register callbacks (the fiq_chip structure) to
> manage FIQ routings and to ACK and EOI the FIQ. This makes it possible
> to use FIQ in multi-platform kernels and with recent ARM interrupt
> controllers.
>

Daniel,

Thanks for the patches. I have tested the fiq and irq-gic patches on
an i.MX6 (SabreSD board) for a different purpose:
A FIQ timer interrupt at 1 kHz. The TWD watchdog block is used in
timer mode for this (interrupt ID 30). The GIC affinity is set to CPU
core 0 only for this interrupt ID.

I was surprised by the following behavior:
$ cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
29: 5459 3381 3175 3029 GIC 29 twd
30: 11 0 0 0 GIC 30 fake-fiq

Once every few seconds is interrupt ID 30 handled by the regular GIC
handler instead of the FIQ exception path (which causes for example a
bit more latencies). The other thousands of FIQ's are handled by the
FIQ exception path. The problem is also confirmed by the following
stackframe of the Lauterbach tooling:
fake_fiq_handler(irq = 30, ...)
handle_percpu_devid_irq(irq = 30, ...)
generic_handle_irq(irq = 30)
handle_IRQ(irq = 30, ...)
gic_handle_irq
__irq_svc
-->exception

Notes:
- The problem will occur more often by executing the following command:
$ while true; do hackbench 20; done &
- Reading the GIC_CPU_INTACK register returns the interrupt ID of the
highest priority pending interrupt.
- The GIC_CPU_INTACK register (used by fiq_ack) will return spurious
interrupt ID 0x3FF when read in fake_fiq_handler, because
GIC_CPU_INTACK is already read by gic_handle_irq.
- The FIQ exception will not occur anymore after gic_handle_irq
read/acknowledges the FIQ by reading the GIC_CPU_INTACK register

>From the behavior above I conclude that the GIC_CPU_INTACK register is
first updated before the FIQ exception is generated, so there is a
small period of time that gic_handle_irq can read/acknowledge the FIQ.

I can reduce the number of occurrences (not prevent it) by adding the
following hack to irq-gic.c
@@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
gic_handle_irq(struct pt_regs *regs
u32 irqstat, irqnr;
struct gic_chip_data *gic = &gic_data[0];
void __iomem *cpu_base = gic_data_cpu_base(gic);

do {
+ while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
& (1 << 30))
+ printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & ~0x1c00;

Regards,

Harro Haan

2014-07-15 09:41:55

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 14/07/14 14:51, Harro Haan wrote:
> On 10 July 2014 10:03, Daniel Thompson <[email protected]> wrote:
>>
>> This patchset makes it possible to use kgdb's NMI infrastructure on ARM
>> platforms.
>>
>> The patches have been previously circulated as part of a large patchset
>> mixing together ARM architecture code and driver changes
>> (http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901 ). This
>> patchset is dramatically cut down to include only the arch/arm code. The
>> driver code (irqchip and tty/serial) will follow when/if the arch code
>> is accepted.
>>
>> The first two patches modify the FIQ infrastructure to allow interrupt
>> controller drivers to register callbacks (the fiq_chip structure) to
>> manage FIQ routings and to ACK and EOI the FIQ. This makes it possible
>> to use FIQ in multi-platform kernels and with recent ARM interrupt
>> controllers.
>>
>
> Daniel,
>
> Thanks for the patches. I have tested the fiq and irq-gic patches on
> an i.MX6 (SabreSD board) for a different purpose:
> A FIQ timer interrupt at 1 kHz. The TWD watchdog block is used in
> timer mode for this (interrupt ID 30). The GIC affinity is set to CPU
> core 0 only for this interrupt ID.
>
> I was surprised by the following behavior:
> $ cat /proc/interrupts
> CPU0 CPU1 CPU2 CPU3
> 29: 5459 3381 3175 3029 GIC 29 twd
> 30: 11 0 0 0 GIC 30 fake-fiq
>
> Once every few seconds is interrupt ID 30 handled by the regular GIC
> handler instead of the FIQ exception path (which causes for example a
> bit more latencies). The other thousands of FIQ's are handled by the
> FIQ exception path. The problem is also confirmed by the following
> stackframe of the Lauterbach tooling:
> fake_fiq_handler(irq = 30, ...)
> handle_percpu_devid_irq(irq = 30, ...)
> generic_handle_irq(irq = 30)
> handle_IRQ(irq = 30, ...)
> gic_handle_irq
> __irq_svc
> -->exception
>
> Notes:
> - The problem will occur more often by executing the following command:
> $ while true; do hackbench 20; done &
> - Reading the GIC_CPU_INTACK register returns the interrupt ID of the
> highest priority pending interrupt.
> - The GIC_CPU_INTACK register (used by fiq_ack) will return spurious
> interrupt ID 0x3FF when read in fake_fiq_handler, because
> GIC_CPU_INTACK is already read by gic_handle_irq.
> - The FIQ exception will not occur anymore after gic_handle_irq
> read/acknowledges the FIQ by reading the GIC_CPU_INTACK register
>
> From the behavior above I conclude that the GIC_CPU_INTACK register is
> first updated before the FIQ exception is generated, so there is a
> small period of time that gic_handle_irq can read/acknowledge the FIQ.

Makes sense.

Avoiding this problem on GICv2 is easy (thanks to the aliased intack
register) but on iMX.6 we have only a GICv1.


> I can reduce the number of occurrences (not prevent it) by adding the
> following hack to irq-gic.c
> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
> gic_handle_irq(struct pt_regs *regs
> u32 irqstat, irqnr;
> struct gic_chip_data *gic = &gic_data[0];
> void __iomem *cpu_base = gic_data_cpu_base(gic);
>
> do {
> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
> & (1 << 30))
> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
> irqnr = irqstat & ~0x1c00;

I've made a more complete attempt to fix this. Could you test the
following? (and be prepared to fuzz the line numbers)

diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 73ae896..309bf2c 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -303,6 +303,28 @@ static int gic_set_wake(struct irq_data *d,
unsigned int on)
#define gic_set_wake NULL
#endif

+/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
+ * workaround will only work for level triggered interrupts (and in
+ * its current form is actively harmful on systems that don't support
+ * FIQ).
+ */
+static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
irqstat)
+{
+ u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+
+ if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
+ return irqnr;
+
+ if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
+ (irqnr / 32 * 4)) &
+ BIT(irqnr % 32))
+ return irqnr;
+
+ /* this interrupt was spurious (needs to be handled as FIQ) */
+ writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
+ return 1023;
+}
+
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
@@ -310,8 +332,10 @@ static void __exception_irq_entry
gic_handle_irq(struct pt_regs *regs)
void __iomem *cpu_base = gic_data_cpu_base(gic);

do {
+ local_fiq_disable();
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
- irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+ irqnr = gic_handle_spurious_group_0(gic, irqstat);
+ local_fiq_enable();

if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);

2014-07-15 13:04:07

by Harro Haan

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 15 July 2014 11:41, Daniel Thompson <[email protected]> wrote:
> On 14/07/14 14:51, Harro Haan wrote:
>> On 10 July 2014 10:03, Daniel Thompson <[email protected]> wrote:
>>>
>>> This patchset makes it possible to use kgdb's NMI infrastructure on ARM
>>> platforms.
>>>
>>> The patches have been previously circulated as part of a large patchset
>>> mixing together ARM architecture code and driver changes
>>> (http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901 ). This
>>> patchset is dramatically cut down to include only the arch/arm code. The
>>> driver code (irqchip and tty/serial) will follow when/if the arch code
>>> is accepted.
>>>
>>> The first two patches modify the FIQ infrastructure to allow interrupt
>>> controller drivers to register callbacks (the fiq_chip structure) to
>>> manage FIQ routings and to ACK and EOI the FIQ. This makes it possible
>>> to use FIQ in multi-platform kernels and with recent ARM interrupt
>>> controllers.
>>>
>>
>> Daniel,
>>
>> Thanks for the patches. I have tested the fiq and irq-gic patches on
>> an i.MX6 (SabreSD board) for a different purpose:
>> A FIQ timer interrupt at 1 kHz. The TWD watchdog block is used in
>> timer mode for this (interrupt ID 30). The GIC affinity is set to CPU
>> core 0 only for this interrupt ID.
>>
>> I was surprised by the following behavior:
>> $ cat /proc/interrupts
>> CPU0 CPU1 CPU2 CPU3
>> 29: 5459 3381 3175 3029 GIC 29 twd
>> 30: 11 0 0 0 GIC 30 fake-fiq
>>
>> Once every few seconds is interrupt ID 30 handled by the regular GIC
>> handler instead of the FIQ exception path (which causes for example a
>> bit more latencies). The other thousands of FIQ's are handled by the
>> FIQ exception path. The problem is also confirmed by the following
>> stackframe of the Lauterbach tooling:
>> fake_fiq_handler(irq = 30, ...)
>> handle_percpu_devid_irq(irq = 30, ...)
>> generic_handle_irq(irq = 30)
>> handle_IRQ(irq = 30, ...)
>> gic_handle_irq
>> __irq_svc
>> -->exception
>>
>> Notes:
>> - The problem will occur more often by executing the following command:
>> $ while true; do hackbench 20; done &
>> - Reading the GIC_CPU_INTACK register returns the interrupt ID of the
>> highest priority pending interrupt.
>> - The GIC_CPU_INTACK register (used by fiq_ack) will return spurious
>> interrupt ID 0x3FF when read in fake_fiq_handler, because
>> GIC_CPU_INTACK is already read by gic_handle_irq.
>> - The FIQ exception will not occur anymore after gic_handle_irq
>> read/acknowledges the FIQ by reading the GIC_CPU_INTACK register
>>
>> From the behavior above I conclude that the GIC_CPU_INTACK register is
>> first updated before the FIQ exception is generated, so there is a
>> small period of time that gic_handle_irq can read/acknowledge the FIQ.
>
> Makes sense.
>
> Avoiding this problem on GICv2 is easy (thanks to the aliased intack
> register) but on iMX.6 we have only a GICv1.
>
>
>> I can reduce the number of occurrences (not prevent it) by adding the
>> following hack to irq-gic.c
>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>> gic_handle_irq(struct pt_regs *regs
>> u32 irqstat, irqnr;
>> struct gic_chip_data *gic = &gic_data[0];
>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>
>> do {
>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>> & (1 << 30))
>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>> irqnr = irqstat & ~0x1c00;
>
> I've made a more complete attempt to fix this. Could you test the
> following? (and be prepared to fuzz the line numbers)

Thanks Daniel, I have tested it, see the comments below.

>
> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
> index 73ae896..309bf2c 100644
> --- a/drivers/irqchip/irq-gic.c
> +++ b/drivers/irqchip/irq-gic.c
> @@ -303,6 +303,28 @@ static int gic_set_wake(struct irq_data *d,
> unsigned int on)
> #define gic_set_wake NULL
> #endif
>
> +/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
> + * workaround will only work for level triggered interrupts (and in
> + * its current form is actively harmful on systems that don't support
> + * FIQ).
> + */
> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
> irqstat)
> +{
> + u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
> +
> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
> + return irqnr;
> +
> + if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
> + (irqnr / 32 * 4)) &
> + BIT(irqnr % 32))
> + return irqnr;
> +
> + /* this interrupt was spurious (needs to be handled as FIQ) */
> + writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);

This will NOT work, because of the note I mentioned above:
"The FIQ exception will not occur anymore after gic_handle_irq
read/acknowledges the FIQ by reading the GIC_CPU_INTACK register"
So with this code you will say End Of Interrupt at the GIC level,
without actually handling the interrupt, so you are missing an
interrupt.
I did the following test to confirm the missing interrupt:
I have changed the periodic timer interrupt by an one-shot timer
interrupt. The one-shot timer interrupt is programmed by the FIQ
handler for the next FIQ interrupt. As expected: When the problem
occurs, no more FIQ interrupts are generated.

> + return 1023;
> +}
> +
> static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
> {
> u32 irqstat, irqnr;
> @@ -310,8 +332,10 @@ static void __exception_irq_entry
> gic_handle_irq(struct pt_regs *regs)
> void __iomem *cpu_base = gic_data_cpu_base(gic);
>
> do {
> + local_fiq_disable();

It is a bit weird to disable the "Non-Maskable Interrupt" at every
interrupt, because of a problem that occurs once every few thousand
interrupts

> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
> - irqnr = irqstat & GICC_IAR_INT_ID_MASK;
> + irqnr = gic_handle_spurious_group_0(gic, irqstat);
> + local_fiq_enable();
>
> if (likely(irqnr > 15 && irqnr < 1021)) {
> irqnr = irq_find_mapping(gic->domain, irqnr);


The following type of changes could fix the problem for me:

@@ -290,19 +290,66 @@ static int gic_set_wake(struct irq_data *d,
unsigned int on)

#else
#define gic_set_wake NULL
#endif

+extern void (*fiq_handler)(void);
+
+/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
+ * workaround will only work for level triggered interrupts (and in
+ * its current form is actively harmful on systems that don't support
+ * FIQ).
+ */
+static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32 irqstat)
+{
+ u32 irqnr = irqstat & ~0x1c00;
+
+ if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
+ return irqnr;
+
+ if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
+ (irqnr / 32 * 4)) & BIT(irqnr % 32))
+ return irqnr;
+
+ /*
+ * The FIQ should be disabled before the next FIQ interrupt occurs,
+ * so this only works when the next FIQ interrupt is not "too fast"
+ * after the previous one.
+ */
+ local_fiq_disable();
+
+ /*
+ * Notes:
+ * - The FIQ exception will not occur anymore for this current
+ * interrupt, because gic_handle_irq has already read/acknowledged
+ * the GIC_CPU_INTACK register. So handle the FIQ here.
+ * - The fiq_handler below should not call ack_fiq and eoi_fiq,
+ * because rereading the GIC_CPU_INTACK register returns spurious
+ * interrupt ID 0x3FF. So probably you will have to add sometime like
+ * the following to fiq_handler:
+ * u32 cpsr, irqstat;
+ * __asm__("mrs %0, cpsr" : "=r" (cpsr));
+ * if ((cpsr & MODE_MASK) == FIQ_MODE)
+ * irqstat = ack_fiq();
+ */
+ (*fiq_handler)();
+
+ writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
+ local_fiq_enable();
+
+ return 1023;
+}
+
static asmlinkage void __exception_irq_entry gic_handle_irq(struct
pt_regs *regs)
{
u32 irqstat, irqnr;
struct gic_chip_data *gic = &gic_data[0];
void __iomem *cpu_base = gic_data_cpu_base(gic);

do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
- irqnr = irqstat & ~0x1c00;
+ irqnr = gic_handle_spurious_group_0(gic, irqstat);

if (likely(irqnr > 15 && irqnr < 1021)) {

Regards,

Harro

2014-07-15 14:53:15

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 15/07/14 14:04, Harro Haan wrote:
>> Makes sense.
>>
>> Avoiding this problem on GICv2 is easy (thanks to the aliased intack
>> register) but on iMX.6 we have only a GICv1.
>>
>>
>>> I can reduce the number of occurrences (not prevent it) by adding the
>>> following hack to irq-gic.c
>>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>>> gic_handle_irq(struct pt_regs *regs
>>> u32 irqstat, irqnr;
>>> struct gic_chip_data *gic = &gic_data[0];
>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>
>>> do {
>>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>>> & (1 << 30))
>>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>> irqnr = irqstat & ~0x1c00;
>>
>> I've made a more complete attempt to fix this. Could you test the
>> following? (and be prepared to fuzz the line numbers)
>
> Thanks Daniel, I have tested it, see the comments below.
>
>>
>> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
>> index 73ae896..309bf2c 100644
>> --- a/drivers/irqchip/irq-gic.c
>> +++ b/drivers/irqchip/irq-gic.c
>> @@ -303,6 +303,28 @@ static int gic_set_wake(struct irq_data *d,
>> unsigned int on)
>> #define gic_set_wake NULL
>> #endif
>>
>> +/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
>> + * workaround will only work for level triggered interrupts (and in
>> + * its current form is actively harmful on systems that don't support
>> + * FIQ).
>> + */
>> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
>> irqstat)
>> +{
>> + u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>> +
>> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
>> + return irqnr;
>> +
>> + if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
>> + (irqnr / 32 * 4)) &
>> + BIT(irqnr % 32))
>> + return irqnr;
>> +
>> + /* this interrupt was spurious (needs to be handled as FIQ) */
>> + writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
>
> This will NOT work, because of the note I mentioned above:
> "The FIQ exception will not occur anymore after gic_handle_irq
> read/acknowledges the FIQ by reading the GIC_CPU_INTACK register"
> So with this code you will say End Of Interrupt at the GIC level,
> without actually handling the interrupt, so you are missing an
> interrupt.
> I did the following test to confirm the missing interrupt:
> I have changed the periodic timer interrupt by an one-shot timer
> interrupt. The one-shot timer interrupt is programmed by the FIQ
> handler for the next FIQ interrupt. As expected: When the problem
> occurs, no more FIQ interrupts are generated.

Can you confirm whether your timer interrupts are configured level or
edge triggered? Or whether EOIing the GIC causes them to be cleared by
some other means.

A level triggered interrupt that hasn't been serviced should return the
pending state (shouldn't it?).


>> + return 1023;
>> +}
>> +
>> static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
>> {
>> u32 irqstat, irqnr;
>> @@ -310,8 +332,10 @@ static void __exception_irq_entry
>> gic_handle_irq(struct pt_regs *regs)
>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>
>> do {
>> + local_fiq_disable();
>
> It is a bit weird to disable the "Non-Maskable Interrupt" at every
> interrupt, because of a problem that occurs once every few thousand
> interrupts

Given that simply reading from GIC_CPU_INTACK has significantly
interferes with FIQ reception means that I'm not too worried about this.

Note also that leaving the FIQ unmasked increases worst case latency
here because once we get a group 0 interrupt back from intack then
spurious entry to the FIQ handler (which would see an ACK of 1023) just
wastes cycles.


>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>> - irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>> + irqnr = gic_handle_spurious_group_0(gic, irqstat);
>> + local_fiq_enable();
>>
>> if (likely(irqnr > 15 && irqnr < 1021)) {
>> irqnr = irq_find_mapping(gic->domain, irqnr);
>
>
> The following type of changes could fix the problem for me:
>
> @@ -290,19 +290,66 @@ static int gic_set_wake(struct irq_data *d,
> unsigned int on)
>
> #else
> #define gic_set_wake NULL
> #endif
>
> +extern void (*fiq_handler)(void);
> +
> +/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
> + * workaround will only work for level triggered interrupts (and in
> + * its current form is actively harmful on systems that don't support
> + * FIQ).
> + */
> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32 irqstat)
> +{
> + u32 irqnr = irqstat & ~0x1c00;
> +
> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
> + return irqnr;
> +
> + if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
> + (irqnr / 32 * 4)) & BIT(irqnr % 32))
> + return irqnr;
> +
> + /*
> + * The FIQ should be disabled before the next FIQ interrupt occurs,
> + * so this only works when the next FIQ interrupt is not "too fast"
> + * after the previous one.
> + */
> + local_fiq_disable();
> +
> + /*
> + * Notes:
> + * - The FIQ exception will not occur anymore for this current
> + * interrupt, because gic_handle_irq has already read/acknowledged
> + * the GIC_CPU_INTACK register. So handle the FIQ here.
> + * - The fiq_handler below should not call ack_fiq and eoi_fiq,
> + * because rereading the GIC_CPU_INTACK register returns spurious
> + * interrupt ID 0x3FF. So probably you will have to add sometime like
> + * the following to fiq_handler:
> + * u32 cpsr, irqstat;
> + * __asm__("mrs %0, cpsr" : "=r" (cpsr));
> + * if ((cpsr & MODE_MASK) == FIQ_MODE)
> + * irqstat = ack_fiq();
> + */
> + (*fiq_handler)();

Any portable approach would have to switch to FIQ mode to run the
handler in order to provide the proper register banks for FIQ handlers
written in assembler.

If we can't get level triggering to work then we have to:

1. Write code to jump correctly into FIQ mode.

2. Modify the gic's ack_fiq() callback to automatically avoid reading
intack when the workaround is deployed.

The above is why I wanted to see if we can make do with level triggering
(and automatic re-triggering for interrupts such as SGIs that are
cleared by EOI).

2014-07-15 15:59:37

by Harro Haan

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 15 July 2014 16:52, Daniel Thompson <[email protected]> wrote:
> On 15/07/14 14:04, Harro Haan wrote:
>>> Makes sense.
>>>
>>> Avoiding this problem on GICv2 is easy (thanks to the aliased intack
>>> register) but on iMX.6 we have only a GICv1.
>>>
>>>
>>>> I can reduce the number of occurrences (not prevent it) by adding the
>>>> following hack to irq-gic.c
>>>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>>>> gic_handle_irq(struct pt_regs *regs
>>>> u32 irqstat, irqnr;
>>>> struct gic_chip_data *gic = &gic_data[0];
>>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>>
>>>> do {
>>>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>>>> & (1 << 30))
>>>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>>> irqnr = irqstat & ~0x1c00;
>>>
>>> I've made a more complete attempt to fix this. Could you test the
>>> following? (and be prepared to fuzz the line numbers)
>>
>> Thanks Daniel, I have tested it, see the comments below.
>>
>>>
>>> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
>>> index 73ae896..309bf2c 100644
>>> --- a/drivers/irqchip/irq-gic.c
>>> +++ b/drivers/irqchip/irq-gic.c
>>> @@ -303,6 +303,28 @@ static int gic_set_wake(struct irq_data *d,
>>> unsigned int on)
>>> #define gic_set_wake NULL
>>> #endif
>>>
>>> +/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
>>> + * workaround will only work for level triggered interrupts (and in
>>> + * its current form is actively harmful on systems that don't support
>>> + * FIQ).
>>> + */
>>> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
>>> irqstat)
>>> +{
>>> + u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>>> +
>>> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
>>> + return irqnr;
>>> +
>>> + if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
>>> + (irqnr / 32 * 4)) &
>>> + BIT(irqnr % 32))
>>> + return irqnr;
>>> +
>>> + /* this interrupt was spurious (needs to be handled as FIQ) */
>>> + writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
>>
>> This will NOT work, because of the note I mentioned above:
>> "The FIQ exception will not occur anymore after gic_handle_irq
>> read/acknowledges the FIQ by reading the GIC_CPU_INTACK register"
>> So with this code you will say End Of Interrupt at the GIC level,
>> without actually handling the interrupt, so you are missing an
>> interrupt.
>> I did the following test to confirm the missing interrupt:
>> I have changed the periodic timer interrupt by an one-shot timer
>> interrupt. The one-shot timer interrupt is programmed by the FIQ
>> handler for the next FIQ interrupt. As expected: When the problem
>> occurs, no more FIQ interrupts are generated.
>
> Can you confirm whether your timer interrupts are configured level or
> edge triggered? Or whether EOIing the GIC causes them to be cleared by
> some other means.

>From page 48 of DDI0407I_cortex_a9_mpcore_r4p1_trm.pdf:
Watchdog timers, PPI(3)
Each Cortex-A9 processor has its own watchdog timers that can generate
interrupts, using ID30.

>From page 56:
PPI[0], [2],and[3]:b11
interrupt is rising-edge sensitive.

>
> A level triggered interrupt that hasn't been serviced should return the
> pending state (shouldn't it?).
>
>
>>> + return 1023;
>>> +}
>>> +
>>> static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
>>> {
>>> u32 irqstat, irqnr;
>>> @@ -310,8 +332,10 @@ static void __exception_irq_entry
>>> gic_handle_irq(struct pt_regs *regs)
>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>
>>> do {
>>> + local_fiq_disable();
>>
>> It is a bit weird to disable the "Non-Maskable Interrupt" at every
>> interrupt, because of a problem that occurs once every few thousand
>> interrupts
>
> Given that simply reading from GIC_CPU_INTACK has significantly
> interferes with FIQ reception means that I'm not too worried about this.
>
> Note also that leaving the FIQ unmasked increases worst case latency
> here because once we get a group 0 interrupt back from intack then
> spurious entry to the FIQ handler (which would see an ACK of 1023) just
> wastes cycles.
>
>
>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>> - irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>>> + irqnr = gic_handle_spurious_group_0(gic, irqstat);
>>> + local_fiq_enable();
>>>
>>> if (likely(irqnr > 15 && irqnr < 1021)) {
>>> irqnr = irq_find_mapping(gic->domain, irqnr);
>>
>>
>> The following type of changes could fix the problem for me:
>>
>> @@ -290,19 +290,66 @@ static int gic_set_wake(struct irq_data *d,
>> unsigned int on)
>>
>> #else
>> #define gic_set_wake NULL
>> #endif
>>
>> +extern void (*fiq_handler)(void);
>> +
>> +/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
>> + * workaround will only work for level triggered interrupts (and in
>> + * its current form is actively harmful on systems that don't support
>> + * FIQ).
>> + */
>> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32 irqstat)
>> +{
>> + u32 irqnr = irqstat & ~0x1c00;
>> +
>> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
>> + return irqnr;
>> +
>> + if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
>> + (irqnr / 32 * 4)) & BIT(irqnr % 32))
>> + return irqnr;
>> +
>> + /*
>> + * The FIQ should be disabled before the next FIQ interrupt occurs,
>> + * so this only works when the next FIQ interrupt is not "too fast"
>> + * after the previous one.
>> + */
>> + local_fiq_disable();
>> +
>> + /*
>> + * Notes:
>> + * - The FIQ exception will not occur anymore for this current
>> + * interrupt, because gic_handle_irq has already read/acknowledged
>> + * the GIC_CPU_INTACK register. So handle the FIQ here.
>> + * - The fiq_handler below should not call ack_fiq and eoi_fiq,
>> + * because rereading the GIC_CPU_INTACK register returns spurious
>> + * interrupt ID 0x3FF. So probably you will have to add sometime like
>> + * the following to fiq_handler:
>> + * u32 cpsr, irqstat;
>> + * __asm__("mrs %0, cpsr" : "=r" (cpsr));
>> + * if ((cpsr & MODE_MASK) == FIQ_MODE)
>> + * irqstat = ack_fiq();
>> + */
>> + (*fiq_handler)();
>
> Any portable approach would have to switch to FIQ mode to run the
> handler in order to provide the proper register banks for FIQ handlers
> written in assembler.
>
> If we can't get level triggering to work then we have to:
>
> 1. Write code to jump correctly into FIQ mode.
>
> 2. Modify the gic's ack_fiq() callback to automatically avoid reading
> intack when the workaround is deployed.
>
> The above is why I wanted to see if we can make do with level triggering
> (and automatic re-triggering for interrupts such as SGIs that are
> cleared by EOI).

But the re-triggering introduces extra latencies and a lot of use
cases of FIQ's try to avoid that.

2014-07-15 17:09:05

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 15/07/14 16:59, Harro Haan wrote:
> On 15 July 2014 16:52, Daniel Thompson <[email protected]> wrote:
>> On 15/07/14 14:04, Harro Haan wrote:
>>>> Makes sense.
>>>>
>>>> Avoiding this problem on GICv2 is easy (thanks to the aliased intack
>>>> register) but on iMX.6 we have only a GICv1.
>>>>
>>>>
>>>>> I can reduce the number of occurrences (not prevent it) by adding the
>>>>> following hack to irq-gic.c
>>>>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>>>>> gic_handle_irq(struct pt_regs *regs
>>>>> u32 irqstat, irqnr;
>>>>> struct gic_chip_data *gic = &gic_data[0];
>>>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>>>
>>>>> do {
>>>>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>>>>> & (1 << 30))
>>>>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>>>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>>>> irqnr = irqstat & ~0x1c00;
>>>>
>>>> I've made a more complete attempt to fix this. Could you test the
>>>> following? (and be prepared to fuzz the line numbers)
>>>
>>> Thanks Daniel, I have tested it, see the comments below.
>>>
>>>>
>>>> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
>>>> index 73ae896..309bf2c 100644
>>>> --- a/drivers/irqchip/irq-gic.c
>>>> +++ b/drivers/irqchip/irq-gic.c
>>>> @@ -303,6 +303,28 @@ static int gic_set_wake(struct irq_data *d,
>>>> unsigned int on)
>>>> #define gic_set_wake NULL
>>>> #endif
>>>>
>>>> +/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
>>>> + * workaround will only work for level triggered interrupts (and in
>>>> + * its current form is actively harmful on systems that don't support
>>>> + * FIQ).
>>>> + */
>>>> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
>>>> irqstat)
>>>> +{
>>>> + u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>>>> +
>>>> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
>>>> + return irqnr;
>>>> +
>>>> + if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
>>>> + (irqnr / 32 * 4)) &
>>>> + BIT(irqnr % 32))
>>>> + return irqnr;
>>>> +
>>>> + /* this interrupt was spurious (needs to be handled as FIQ) */
>>>> + writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
>>>
>>> This will NOT work, because of the note I mentioned above:
>>> "The FIQ exception will not occur anymore after gic_handle_irq
>>> read/acknowledges the FIQ by reading the GIC_CPU_INTACK register"
>>> So with this code you will say End Of Interrupt at the GIC level,
>>> without actually handling the interrupt, so you are missing an
>>> interrupt.
>>> I did the following test to confirm the missing interrupt:
>>> I have changed the periodic timer interrupt by an one-shot timer
>>> interrupt. The one-shot timer interrupt is programmed by the FIQ
>>> handler for the next FIQ interrupt. As expected: When the problem
>>> occurs, no more FIQ interrupts are generated.
>>
>> Can you confirm whether your timer interrupts are configured level or
>> edge triggered? Or whether EOIing the GIC causes them to be cleared by
>> some other means.
>
> From page 48 of DDI0407I_cortex_a9_mpcore_r4p1_trm.pdf:
> Watchdog timers, PPI(3)
> Each Cortex-A9 processor has its own watchdog timers that can generate
> interrupts, using ID30.
>
> From page 56:
> PPI[0], [2],and[3]:b11
> interrupt is rising-edge sensitive.

Thanks. This is clear.


>> If we can't get level triggering to work then we have to:
>>
>> 1. Write code to jump correctly into FIQ mode.
>>
>> 2. Modify the gic's ack_fiq() callback to automatically avoid reading
>> intack when the workaround is deployed.
>>
>> The above is why I wanted to see if we can make do with level triggering
>> (and automatic re-triggering for interrupts such as SGIs that are
>> cleared by EOI).
>
> But the re-triggering introduces extra latencies and a lot of use
> cases of FIQ's try to avoid that.

I'm not really clear why retriggering should be significantly more
expensive than the dance required to fake up entry the FIQ handler.

On the other hand retriggering allows us to avoid hacks in the FIQ
handler to stop it acknowledging the interrupt. Given hacks like that
won't be needed on A7/A15 this seems like a good outcome.

Anyhow I've put together a new version of my earlier patch that I think
will retrigger all interrupts except SGIs (I'll look at SGIs and
compatibility with non-Freescale parts only if this improved approach
works).

Reported-by: Harro Haan <[email protected]>
Signed-off-by: Daniel Thompson <[email protected]>
---
drivers/irqchip/irq-gic.c | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 73ae896..88f92e6 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -303,6 +303,33 @@ static int gic_set_wake(struct irq_data *d,
unsigned int on)
#define gic_set_wake NULL
#endif

+/* This is a software emulation of the Aliased Interrupt Acknowledge
Register
+ * (GIC_AIAR) found in GICv2+.
+ */
+static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
irqstat)
+{
+ u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+ void __iomem *dist_base = gic_data_dist_base(gic);
+ u32 offset, mask;
+
+ if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
+ return irqnr;
+
+ offset = irqnr / 32 * 4;
+ mask = 1 << (irqnr % 32);
+ if (readl_relaxed(dist_base + GIC_DIST_IGROUP + offset) & mask)
+ return irqnr;
+
+ /* this interrupt must be taken as a FIQ so put it back into the
+ * pending state and end our own servicing of it.
+ */
+ writel_relaxed(mask, dist_base + GIC_DIST_PENDING_SET + offset);
+ readl_relaxed(dist_base + GIC_DIST_PENDING_SET + offset);
+ writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
+
+ return 1023;
+}
+
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
@@ -310,8 +337,10 @@ static void __exception_irq_entry
gic_handle_irq(struct pt_regs *regs)
void __iomem *cpu_base = gic_data_cpu_base(gic);

do {
+ local_fiq_disable();
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
- irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+ irqnr = gic_handle_spurious_group_0(gic, irqstat);
+ local_fiq_enable();

if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);
--
1.9.3


2014-07-15 18:45:13

by Marek Vasut

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On Tuesday, July 15, 2014 at 11:41:25 AM, Daniel Thompson wrote:
> On 14/07/14 14:51, Harro Haan wrote:
> > On 10 July 2014 10:03, Daniel Thompson <[email protected]> wrote:
> >> This patchset makes it possible to use kgdb's NMI infrastructure on ARM
> >> platforms.
> >>
> >> The patches have been previously circulated as part of a large patchset
> >> mixing together ARM architecture code and driver changes
> >> (http://thread.gmane.org/gmane.linux.ports.arm.kernel/333901 ). This
> >> patchset is dramatically cut down to include only the arch/arm code. The
> >> driver code (irqchip and tty/serial) will follow when/if the arch code
> >> is accepted.
> >>
> >> The first two patches modify the FIQ infrastructure to allow interrupt
> >> controller drivers to register callbacks (the fiq_chip structure) to
> >> manage FIQ routings and to ACK and EOI the FIQ. This makes it possible
> >> to use FIQ in multi-platform kernels and with recent ARM interrupt
> >> controllers.
> >
> > Daniel,
> >
> > Thanks for the patches. I have tested the fiq and irq-gic patches on
> > an i.MX6 (SabreSD board) for a different purpose:
> > A FIQ timer interrupt at 1 kHz. The TWD watchdog block is used in
> > timer mode for this (interrupt ID 30). The GIC affinity is set to CPU
> > core 0 only for this interrupt ID.
> >
> > I was surprised by the following behavior:
> > $ cat /proc/interrupts
> >
> > CPU0 CPU1 CPU2 CPU3
> >
> > 29: 5459 3381 3175 3029 GIC 29 twd
> > 30: 11 0 0 0 GIC 30 fake-fiq
> >
> > Once every few seconds is interrupt ID 30 handled by the regular GIC
> > handler instead of the FIQ exception path (which causes for example a
> > bit more latencies). The other thousands of FIQ's are handled by the
> > FIQ exception path. The problem is also confirmed by the following
> > stackframe of the Lauterbach tooling:
> > fake_fiq_handler(irq = 30, ...)
> > handle_percpu_devid_irq(irq = 30, ...)
> > generic_handle_irq(irq = 30)
> > handle_IRQ(irq = 30, ...)
> > gic_handle_irq
> > __irq_svc
> > -->exception
> >
> > Notes:
> >
> > - The problem will occur more often by executing the following command:
> > $ while true; do hackbench 20; done &
> >
> > - Reading the GIC_CPU_INTACK register returns the interrupt ID of the
> > highest priority pending interrupt.
> > - The GIC_CPU_INTACK register (used by fiq_ack) will return spurious
> > interrupt ID 0x3FF when read in fake_fiq_handler, because
> > GIC_CPU_INTACK is already read by gic_handle_irq.
> > - The FIQ exception will not occur anymore after gic_handle_irq
> > read/acknowledges the FIQ by reading the GIC_CPU_INTACK register
> >
> > From the behavior above I conclude that the GIC_CPU_INTACK register is
> > first updated before the FIQ exception is generated, so there is a
> > small period of time that gic_handle_irq can read/acknowledge the FIQ.
>
> Makes sense.
>
> Avoiding this problem on GICv2 is easy (thanks to the aliased intack
> register) but on iMX.6 we have only a GICv1.

Yep.

> > I can reduce the number of occurrences (not prevent it) by adding the
> > following hack to irq-gic.c
> > @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
> > gic_handle_irq(struct pt_regs *regs
> >
> > u32 irqstat, irqnr;
> > struct gic_chip_data *gic = &gic_data[0];
> > void __iomem *cpu_base = gic_data_cpu_base(gic);
> >
> > do {
> >
> > + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
> > & (1 << 30))
> > + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
> >
> > irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
> > irqnr = irqstat & ~0x1c00;
>
> I've made a more complete attempt to fix this. Could you test the
> following? (and be prepared to fuzz the line numbers)

There's also another workaround, look at [1], but it's really a perverse hack
thus far (blush). What I did there is I got hinted that an L1 page table can
have this NS bit set. If this bit is set for a mapping, all accesses to memory
area via that mapping will be non-secure. And then, in turn, by doing a non-
secure read of the INTACK register, it will not ever happen that the FIQ number
will pop up in the INTACK. I only do a non-secure read of the INTACK register,
all other registers of the GICv1 are read via regular secure-mode accesses.

[1] http://git.denx.de/?p=linux-denx/linux-denx-
marex.git;a=shortlog;h=refs/heads/topic/socfpga/fiq-2014-07-10_01

2014-07-16 12:55:26

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 15/07/14 19:45, Marek Vasut wrote:
>>> I can reduce the number of occurrences (not prevent it) by adding the
>>> following hack to irq-gic.c
>>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>>> gic_handle_irq(struct pt_regs *regs
>>>
>>> u32 irqstat, irqnr;
>>> struct gic_chip_data *gic = &gic_data[0];
>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>
>>> do {
>>>
>>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>>> & (1 << 30))
>>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>>>
>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>> irqnr = irqstat & ~0x1c00;
>>
>> I've made a more complete attempt to fix this. Could you test the
>> following? (and be prepared to fuzz the line numbers)
>
> There's also another workaround, look at [1], but it's really a perverse hack
> thus far (blush). What I did there is I got hinted that an L1 page table can
> have this NS bit set. If this bit is set for a mapping, all accesses to memory
> area via that mapping will be non-secure. And then, in turn, by doing a non-
> secure read of the INTACK register, it will not ever happen that the FIQ number
> will pop up in the INTACK. I only do a non-secure read of the INTACK register,
> all other registers of the GICv1 are read via regular secure-mode accesses.

I'll be looking into this approach.

It is technically a better approach than mine since it prevents the IRQ
handler from ever reading a group 0 interrupt from INTACK.

Unfortunately the tentacles of this workaround reach pretty deep in the
memory management code (rather than being concentrated in the GIC
driver) but the improved runtime behaviour might be worth it.

2014-07-16 17:15:18

by Harro Haan

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 15 July 2014 19:08, Daniel Thompson <[email protected]> wrote:
> On 15/07/14 16:59, Harro Haan wrote:
>> On 15 July 2014 16:52, Daniel Thompson <[email protected]> wrote:
>>> On 15/07/14 14:04, Harro Haan wrote:
>>>>> Makes sense.
>>>>>
>>>>> Avoiding this problem on GICv2 is easy (thanks to the aliased intack
>>>>> register) but on iMX.6 we have only a GICv1.
>>>>>
>>>>>
>>>>>> I can reduce the number of occurrences (not prevent it) by adding the
>>>>>> following hack to irq-gic.c
>>>>>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>>>>>> gic_handle_irq(struct pt_regs *regs
>>>>>> u32 irqstat, irqnr;
>>>>>> struct gic_chip_data *gic = &gic_data[0];
>>>>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>>>>
>>>>>> do {
>>>>>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>>>>>> & (1 << 30))
>>>>>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>>>>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>>>>> irqnr = irqstat & ~0x1c00;
>>>>>
>>>>> I've made a more complete attempt to fix this. Could you test the
>>>>> following? (and be prepared to fuzz the line numbers)
>>>>
>>>> Thanks Daniel, I have tested it, see the comments below.
>>>>
>>>>>
>>>>> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
>>>>> index 73ae896..309bf2c 100644
>>>>> --- a/drivers/irqchip/irq-gic.c
>>>>> +++ b/drivers/irqchip/irq-gic.c
>>>>> @@ -303,6 +303,28 @@ static int gic_set_wake(struct irq_data *d,
>>>>> unsigned int on)
>>>>> #define gic_set_wake NULL
>>>>> #endif
>>>>>
>>>>> +/* Check for group 0 interrupt spuriously acked as a normal IRQ. This
>>>>> + * workaround will only work for level triggered interrupts (and in
>>>>> + * its current form is actively harmful on systems that don't support
>>>>> + * FIQ).
>>>>> + */
>>>>> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
>>>>> irqstat)
>>>>> +{
>>>>> + u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>>>>> +
>>>>> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
>>>>> + return irqnr;
>>>>> +
>>>>> + if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_IGROUP +
>>>>> + (irqnr / 32 * 4)) &
>>>>> + BIT(irqnr % 32))
>>>>> + return irqnr;
>>>>> +
>>>>> + /* this interrupt was spurious (needs to be handled as FIQ) */
>>>>> + writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
>>>>
>>>> This will NOT work, because of the note I mentioned above:
>>>> "The FIQ exception will not occur anymore after gic_handle_irq
>>>> read/acknowledges the FIQ by reading the GIC_CPU_INTACK register"
>>>> So with this code you will say End Of Interrupt at the GIC level,
>>>> without actually handling the interrupt, so you are missing an
>>>> interrupt.
>>>> I did the following test to confirm the missing interrupt:
>>>> I have changed the periodic timer interrupt by an one-shot timer
>>>> interrupt. The one-shot timer interrupt is programmed by the FIQ
>>>> handler for the next FIQ interrupt. As expected: When the problem
>>>> occurs, no more FIQ interrupts are generated.
>>>
>>> Can you confirm whether your timer interrupts are configured level or
>>> edge triggered? Or whether EOIing the GIC causes them to be cleared by
>>> some other means.
>>
>> From page 48 of DDI0407I_cortex_a9_mpcore_r4p1_trm.pdf:
>> Watchdog timers, PPI(3)
>> Each Cortex-A9 processor has its own watchdog timers that can generate
>> interrupts, using ID30.
>>
>> From page 56:
>> PPI[0], [2],and[3]:b11
>> interrupt is rising-edge sensitive.
>
> Thanks. This is clear.
>
>
>>> If we can't get level triggering to work then we have to:
>>>
>>> 1. Write code to jump correctly into FIQ mode.
>>>
>>> 2. Modify the gic's ack_fiq() callback to automatically avoid reading
>>> intack when the workaround is deployed.
>>>
>>> The above is why I wanted to see if we can make do with level triggering
>>> (and automatic re-triggering for interrupts such as SGIs that are
>>> cleared by EOI).
>>
>> But the re-triggering introduces extra latencies and a lot of use
>> cases of FIQ's try to avoid that.
>
> I'm not really clear why retriggering should be significantly more
> expensive than the dance required to fake up entry the FIQ handler.
>
> On the other hand retriggering allows us to avoid hacks in the FIQ
> handler to stop it acknowledging the interrupt. Given hacks like that
> won't be needed on A7/A15 this seems like a good outcome.
>
> Anyhow I've put together a new version of my earlier patch that I think
> will retrigger all interrupts except SGIs (I'll look at SGIs and
> compatibility with non-Freescale parts only if this improved approach
> works).
>
> Reported-by: Harro Haan <[email protected]>
> Signed-off-by: Daniel Thompson <[email protected]>
> ---
> drivers/irqchip/irq-gic.c | 31 ++++++++++++++++++++++++++++++-
> 1 file changed, 30 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
> index 73ae896..88f92e6 100644
> --- a/drivers/irqchip/irq-gic.c
> +++ b/drivers/irqchip/irq-gic.c
> @@ -303,6 +303,33 @@ static int gic_set_wake(struct irq_data *d,
> unsigned int on)
> #define gic_set_wake NULL
> #endif
>
> +/* This is a software emulation of the Aliased Interrupt Acknowledge
> Register
> + * (GIC_AIAR) found in GICv2+.
> + */
> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
> irqstat)
> +{
> + u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
> + void __iomem *dist_base = gic_data_dist_base(gic);
> + u32 offset, mask;
> +
> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
> + return irqnr;
> +
> + offset = irqnr / 32 * 4;
> + mask = 1 << (irqnr % 32);
> + if (readl_relaxed(dist_base + GIC_DIST_IGROUP + offset) & mask)
> + return irqnr;
> +
> + /* this interrupt must be taken as a FIQ so put it back into the
> + * pending state and end our own servicing of it.
> + */
> + writel_relaxed(mask, dist_base + GIC_DIST_PENDING_SET + offset);
> + readl_relaxed(dist_base + GIC_DIST_PENDING_SET + offset);
> + writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
> +
> + return 1023;
> +}
> +
> static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
> {
> u32 irqstat, irqnr;
> @@ -310,8 +337,10 @@ static void __exception_irq_entry
> gic_handle_irq(struct pt_regs *regs)
> void __iomem *cpu_base = gic_data_cpu_base(gic);
>
> do {
> + local_fiq_disable();
> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
> - irqnr = irqstat & GICC_IAR_INT_ID_MASK;
> + irqnr = gic_handle_spurious_group_0(gic, irqstat);
> + local_fiq_enable();
>
> if (likely(irqnr > 15 && irqnr < 1021)) {
> irqnr = irq_find_mapping(gic->domain, irqnr);
> --
> 1.9.3
>
>
>

I just tested the above code. This approach also works as expected for
edge sensitive interrupts.

2014-07-16 17:21:44

by Harro Haan

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 16 July 2014 14:54, Daniel Thompson <[email protected]> wrote:
> On 15/07/14 19:45, Marek Vasut wrote:
>>>> I can reduce the number of occurrences (not prevent it) by adding the
>>>> following hack to irq-gic.c
>>>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>>>> gic_handle_irq(struct pt_regs *regs
>>>>
>>>> u32 irqstat, irqnr;
>>>> struct gic_chip_data *gic = &gic_data[0];
>>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>>
>>>> do {
>>>>
>>>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>>>> & (1 << 30))
>>>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>>>>
>>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>>> irqnr = irqstat & ~0x1c00;
>>>
>>> I've made a more complete attempt to fix this. Could you test the
>>> following? (and be prepared to fuzz the line numbers)
>>
>> There's also another workaround, look at [1], but it's really a perverse hack
>> thus far (blush). What I did there is I got hinted that an L1 page table can
>> have this NS bit set. If this bit is set for a mapping, all accesses to memory
>> area via that mapping will be non-secure. And then, in turn, by doing a non-
>> secure read of the INTACK register, it will not ever happen that the FIQ number
>> will pop up in the INTACK. I only do a non-secure read of the INTACK register,
>> all other registers of the GICv1 are read via regular secure-mode accesses.
>
> I'll be looking into this approach.
>
> It is technically a better approach than mine since it prevents the IRQ
> handler from ever reading a group 0 interrupt from INTACK.

Agree, preventing the problem is better than fixing it afterwards.

>
> Unfortunately the tentacles of this workaround reach pretty deep in the
> memory management code (rather than being concentrated in the GIC
> driver) but the improved runtime behaviour might be worth it.

I did some worst case measurements on the SabreSD while running:
$ while true; do hackbench 20; done &

Use banked non-secure GIC_CPU_INTACK register for regular interrupts
(patches by Marek):
The FIQ handler reads the TWD_TIMER_COUNTER 2570 ticks (which is x
1000 / 498 = 5161 nsec) after FIQ interrupt ID30 is generated.
The average is around 497 ticks.
The minimum is around 34 ticks.

Use re-trigger approach by putting it back to pending state (latest
patch by Daniel):
The FIQ handler reads the TWD_TIMER_COUNTER 2678 ticks (which is x
1000 / 498 = 5378 nsec) after FIQ interrupt ID30 is generated.
The average is around 563 ticks (note: almost everything is normal path)
The minimum is around 34 ticks (note: this is the normal path, not the
re-trigger path)

So the results are quite similar.

2014-07-17 09:02:25

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 16/07/14 18:15, Harro Haan wrote:
>> Anyhow I've put together a new version of my earlier patch that I think
>> will retrigger all interrupts except SGIs (I'll look at SGIs and
>> compatibility with non-Freescale parts only if this improved approach
>> works).
>>
>> Reported-by: Harro Haan <[email protected]>
>> Signed-off-by: Daniel Thompson <[email protected]>
>> ---
>> drivers/irqchip/irq-gic.c | 31 ++++++++++++++++++++++++++++++-
>> 1 file changed, 30 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
>> index 73ae896..88f92e6 100644
>> --- a/drivers/irqchip/irq-gic.c
>> +++ b/drivers/irqchip/irq-gic.c
>> @@ -303,6 +303,33 @@ static int gic_set_wake(struct irq_data *d,
>> unsigned int on)
>> #define gic_set_wake NULL
>> #endif
>>
>> +/* This is a software emulation of the Aliased Interrupt Acknowledge
>> Register
>> + * (GIC_AIAR) found in GICv2+.
>> + */
>> +static u32 gic_handle_spurious_group_0(struct gic_chip_data *gic, u32
>> irqstat)
>> +{
>> + u32 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>> + void __iomem *dist_base = gic_data_dist_base(gic);
>> + u32 offset, mask;
>> +
>> + if (!gic_data_fiq_enable(gic) || irqnr >= 1021)
>> + return irqnr;
>> +
>> + offset = irqnr / 32 * 4;
>> + mask = 1 << (irqnr % 32);
>> + if (readl_relaxed(dist_base + GIC_DIST_IGROUP + offset) & mask)
>> + return irqnr;
>> +
>> + /* this interrupt must be taken as a FIQ so put it back into the
>> + * pending state and end our own servicing of it.
>> + */
>> + writel_relaxed(mask, dist_base + GIC_DIST_PENDING_SET + offset);
>> + readl_relaxed(dist_base + GIC_DIST_PENDING_SET + offset);
>> + writel_relaxed(irqstat, gic_data_cpu_base(gic) + GIC_CPU_EOI);
>> +
>> + return 1023;
>> +}
>> +
>> static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
>> {
>> u32 irqstat, irqnr;
>> @@ -310,8 +337,10 @@ static void __exception_irq_entry
>> gic_handle_irq(struct pt_regs *regs)
>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>
>> do {
>> + local_fiq_disable();
>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>> - irqnr = irqstat & GICC_IAR_INT_ID_MASK;
>> + irqnr = gic_handle_spurious_group_0(gic, irqstat);
>> + local_fiq_enable();
>>
>> if (likely(irqnr > 15 && irqnr < 1021)) {
>> irqnr = irq_find_mapping(gic->domain, irqnr);
>> --
>> 1.9.3
>>
>>
>>
>
> I just tested the above code. This approach also works as expected for
> edge sensitive interrupts.

Awesome [and also a personal relief to get it right this time around ;-) ].

So we have two completely different confirmed-as-working workarounds!

2014-07-17 09:20:47

by Daniel Thompson

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] arm: KGDB NMI/FIQ support

On 16/07/14 18:21, Harro Haan wrote:
> On 16 July 2014 14:54, Daniel Thompson <[email protected]> wrote:
>> On 15/07/14 19:45, Marek Vasut wrote:
>>>>> I can reduce the number of occurrences (not prevent it) by adding the
>>>>> following hack to irq-gic.c
>>>>> @@ -297,10 +309,12 @@ static asmlinkage void __exception_irq_entry
>>>>> gic_handle_irq(struct pt_regs *regs
>>>>>
>>>>> u32 irqstat, irqnr;
>>>>> struct gic_chip_data *gic = &gic_data[0];
>>>>> void __iomem *cpu_base = gic_data_cpu_base(gic);
>>>>>
>>>>> do {
>>>>>
>>>>> + while(readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_PENDING_SET)
>>>>> & (1 << 30))
>>>>> + printk(KERN_ERR "TEMP: gic_handle_irq: wait for FIQ exception\n");
>>>>>
>>>>> irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
>>>>> irqnr = irqstat & ~0x1c00;
>>>>
>>>> I've made a more complete attempt to fix this. Could you test the
>>>> following? (and be prepared to fuzz the line numbers)
>>>
>>> There's also another workaround, look at [1], but it's really a perverse hack
>>> thus far (blush). What I did there is I got hinted that an L1 page table can
>>> have this NS bit set. If this bit is set for a mapping, all accesses to memory
>>> area via that mapping will be non-secure. And then, in turn, by doing a non-
>>> secure read of the INTACK register, it will not ever happen that the FIQ number
>>> will pop up in the INTACK. I only do a non-secure read of the INTACK register,
>>> all other registers of the GICv1 are read via regular secure-mode accesses.
>>
>> I'll be looking into this approach.
>>
>> It is technically a better approach than mine since it prevents the IRQ
>> handler from ever reading a group 0 interrupt from INTACK.
>
> Agree, preventing the problem is better than fixing it afterwards.
>
>>
>> Unfortunately the tentacles of this workaround reach pretty deep in the
>> memory management code (rather than being concentrated in the GIC
>> driver) but the improved runtime behaviour might be worth it.
>
> I did some worst case measurements on the SabreSD while running:
> $ while true; do hackbench 20; done &
>
> Use banked non-secure GIC_CPU_INTACK register for regular interrupts
> (patches by Marek):
> The FIQ handler reads the TWD_TIMER_COUNTER 2570 ticks (which is x
> 1000 / 498 = 5161 nsec) after FIQ interrupt ID30 is generated.
> The average is around 497 ticks.
> The minimum is around 34 ticks.
>
> Use re-trigger approach by putting it back to pending state (latest
> patch by Daniel):
> The FIQ handler reads the TWD_TIMER_COUNTER 2678 ticks (which is x
> 1000 / 498 = 5378 nsec) after FIQ interrupt ID30 is generated.
> The average is around 563 ticks (note: almost everything is normal path)
> The minimum is around 34 ticks (note: this is the normal path, not the
> re-trigger path)
>
> So the results are quite similar.

This is great work.

Be aware that I'd also expect the the performance of my workaround would
drop a little bit when when support for SGIs is added (mostly due to due
to increased code size).