[PATCH 09/18] arm/arm64: maintain per thread exception handlers

Andrew Jones
2015-02-01T18:35:06Z

Add exception handlers to thread info. And, since we allow threads
running in user mode to install exception handlers too (a convenience
for unit test developers), check for handlers on the user mode stack
thread info too. But, unit test developers will likely also expect the
installation of exception handlers done in kernel mode, before switching
to user mode, to work. So, if there's no handler in the thread info
hanging off the user mode stack, then still check the kernel mode stack
thread info for one.

Use THREAD_SIZE == PAGE_SIZE, when PAGE_SIZE is larger than 16K.
This is for arm64, which uses 64K pages, because the exception
handler arrays are 8K together, making the stack too small with
THREAD_SIZE == 16K.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
arm/flat.lds | 2 +-
lib/arm/asm/thread_info.h | 17 ++++++++++++++--
lib/arm/processor.c | 21 ++++++++++++++-----
lib/arm64/asm/processor.h | 1 +
lib/arm64/processor.c | 52 ++++++++++++++++++++++++++++++++++-------------
5 files changed, 71 insertions(+), 22 deletions(-)

diff --git a/arm/flat.lds b/arm/flat.lds
index df80d3678e556..c01f01e7b8682 100644
--- a/arm/flat.lds
+++ b/arm/flat.lds
@@ -19,7 +19,7 @@ SECTIONS
. += 64K;
. = ALIGN(64K);
/*
- * stack depth is ~16K, see THREAD_SIZE
+ * stack depth is 16K for arm and PAGE_SIZE for arm64, see THREAD_SIZE
* sp must be 16 byte aligned for arm64, and 8 byte aligned for arm
* sp must always be strictly less than the true stacktop
*/
diff --git a/lib/arm/asm/thread_info.h b/lib/arm/asm/thread_info.h
index 17997e21d1274..5f7104f7c234f 100644
--- a/lib/arm/asm/thread_info.h
+++ b/lib/arm/asm/thread_info.h
@@ -7,8 +7,15 @@
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
-
-#define THREAD_SIZE 16384
+#include <asm/processor.h>
+#include <asm/page.h>
+
+#define __MIN_THREAD_SIZE 16384
+#if PAGE_SIZE > __MIN_THREAD_SIZE
+#define THREAD_SIZE PAGE_SIZE
+#else
+#define THREAD_SIZE __MIN_THREAD_SIZE
+#endif
#define THREAD_START_SP (THREAD_SIZE - 16)

#define TIF_USER_MODE (1U << 0)
@@ -16,6 +23,12 @@
struct thread_info {
int cpu;
unsigned int flags;
+#ifdef __arm__
+ exception_fn exception_handlers[EXCPTN_MAX];
+#else
+ vector_fn vector_handlers[VECTOR_MAX];
+ exception_fn exception_handlers[VECTOR_MAX][EC_MAX];
+#endif
char ext[0]; /* allow unit tests to add extended info */
};

diff --git a/lib/arm/processor.c b/lib/arm/processor.c
index 8a514a29c063b..1cef46ab28647 100644
--- a/lib/arm/processor.c
+++ b/lib/arm/processor.c
@@ -8,6 +8,7 @@
#include <libcflat.h>
#include <asm/ptrace.h>
#include <asm/processor.h>
+#include <asm/thread_info.h>

static const char *processor_modes[] = {
"USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" ,
@@ -64,18 +65,28 @@ void show_regs(struct pt_regs *regs)
}
}

-static exception_fn exception_handlers[EXCPTN_MAX];
-
void install_exception_handler(enum vector v, exception_fn fn)
{
+ struct thread_info *ti = current_thread_info();
+
if (v < EXCPTN_MAX)
- exception_handlers[v] = fn;
+ ti->exception_handlers[v] = fn;
}

void do_handle_exception(enum vector v, struct pt_regs *regs)
{
- if (v < EXCPTN_MAX && exception_handlers[v]) {
- exception_handlers[v](regs);
+ struct thread_info *ti = thread_info_sp(regs->ARM_sp);
+
+ if (ti->flags & TIF_USER_MODE) {
+ if (v < EXCPTN_MAX && ti->exception_handlers[v]) {
+ ti->exception_handlers[v](regs);
+ return;
+ }
+ ti = current_thread_info();
+ }
+
+ if (v < EXCPTN_MAX && ti->exception_handlers[v]) {
+ ti->exception_handlers[v](regs);
return;
}

diff --git a/lib/arm64/asm/processor.h b/lib/arm64/asm/processor.h
index c1326351d201f..228a21c7f8856 100644
--- a/lib/arm64/asm/processor.h
+++ b/lib/arm64/asm/processor.h
@@ -49,6 +49,7 @@ extern void install_exception_handler(enum vector v, unsigned int ec,
exception_fn fn);
extern void default_vector_handler(enum vector v, struct pt_regs *regs,
unsigned int esr);
+extern void vector_handlers_default_init(vector_fn *handlers);

extern void show_regs(struct pt_regs *regs);
extern bool get_far(unsigned int esr, unsigned long *far);
diff --git a/lib/arm64/processor.c b/lib/arm64/processor.c
index 152767eecf062..c240ce33c3d0c 100644
--- a/lib/arm64/processor.c
+++ b/lib/arm64/processor.c
@@ -9,6 +9,7 @@
#include <asm/ptrace.h>
#include <asm/processor.h>
#include <asm/esr.h>
+#include <asm/thread_info.h>

static const char *vector_names[] = {
"el1t_sync",
@@ -128,44 +129,66 @@ static void bad_exception(enum vector v, struct pt_regs *regs,
abort();
}

-static exception_fn exception_handlers[VECTOR_MAX][EC_MAX];
-
void install_exception_handler(enum vector v, unsigned int ec, exception_fn fn)
{
+ struct thread_info *ti = current_thread_info();
+
if (v < VECTOR_MAX && ec < EC_MAX)
- exception_handlers[v][ec] = fn;
+ ti->exception_handlers[v][ec] = fn;
}

void default_vector_handler(enum vector v, struct pt_regs *regs,
unsigned int esr)
{
+ struct thread_info *ti = thread_info_sp(regs->sp);
unsigned int ec = esr >> ESR_EL1_EC_SHIFT;

- if (ec < EC_MAX && exception_handlers[v][ec])
- exception_handlers[v][ec](regs, esr);
+ if (ti->flags & TIF_USER_MODE) {
+ if (ec < EC_MAX && ti->exception_handlers[v][ec]) {
+ ti->exception_handlers[v][ec](regs, esr);
+ return;
+ }
+ ti = current_thread_info();
+ }
+
+ if (ec < EC_MAX && ti->exception_handlers[v][ec])
+ ti->exception_handlers[v][ec](regs, esr);
else
bad_exception(v, regs, esr, false);
}

-static vector_fn vector_handlers[VECTOR_MAX] = {
- [EL1H_SYNC] = default_vector_handler,
- [EL1H_IRQ] = default_vector_handler,
- [EL0_SYNC_64] = default_vector_handler,
- [EL0_IRQ_64] = default_vector_handler,
-};
+void vector_handlers_default_init(vector_fn *handlers)
+{
+ handlers[EL1H_SYNC] = default_vector_handler;
+ handlers[EL1H_IRQ] = default_vector_handler;
+ handlers[EL0_SYNC_64] = default_vector_handler;
+ handlers[EL0_IRQ_64] = default_vector_handler;
+}

void do_handle_exception(enum vector v, struct pt_regs *regs, unsigned int esr)
{
- if (v < VECTOR_MAX && vector_handlers[v])
- vector_handlers[v](v, regs, esr);
+ struct thread_info *ti = thread_info_sp(regs->sp);
+
+ if (ti->flags & TIF_USER_MODE) {
+ if (v < VECTOR_MAX && ti->vector_handlers[v]) {
+ ti->vector_handlers[v](v, regs, esr);
+ return;
+ }
+ ti = current_thread_info();
+ }
+
+ if (v < VECTOR_MAX && ti->vector_handlers[v])
+ ti->vector_handlers[v](v, regs, esr);
else
bad_exception(v, regs, esr, true);
}

void install_vector_handler(enum vector v, vector_fn fn)
{
+ struct thread_info *ti = current_thread_info();
+
if (v < VECTOR_MAX)
- vector_handlers[v] = fn;
+ ti->vector_handlers[v] = fn;
}

void thread_info_init(struct thread_info *ti, unsigned int flags)
@@ -173,6 +196,7 @@ void thread_info_init(struct thread_info *ti, unsigned int flags)
memset(ti, 0, sizeof(struct thread_info));
ti->cpu = mpidr_to_cpu(get_mpidr());
ti->flags = flags;
+ vector_handlers_default_init(ti->vector_handlers);
}

void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr)
--
1.9.3