mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] ARM: introduce sjlj structured exception handling
@ 2020-04-01  9:31 Ahmad Fatoum
  2020-04-01  9:31 ` [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH " Ahmad Fatoum
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Ahmad Fatoum @ 2020-04-01  9:31 UTC (permalink / raw)
  To: barebox

We are already using data_abort_mask at a hand full of places to integrate
Hardware exceptions with normal flow control mechanisms.
While cumbersome to use, these few code blocks have a terrific track
record in stability, having so far never crashed due to NULL pointer
exceptions.

Having something like this everywhere in barebox would have been great
for software stability[1][2][3][4][5] in the past.

This patch takes this idea a bit further, by introducing new TRY/CATCH
primitives, inspired by Microsoft Visual C Structured Exception
Handling (SEH), into barebox. These can now be wielded to beat any and all
error conditions into total and utter submission:

    u32 cycles;
    TRY {
        u32 div = readl(NULL);
        cycles = ticks / div;
        cycles = ((void (*)())cycles)();
        if (cycles == 0)
            THROW(RuntimeError);
    } CATCH_3(DataAbortException, UndefinedInstructionException, RuntimerError) {
        cycles = 42;
    }
    ENDTRY;

As a first step, this patch series is introduced as a RFC for ARM.
Other platforms can follow when setjmp/longjump (sjlj) is implemented for them.
This will allow all barebox hackers to benefit from state-of-the-art tooling
in building the same highly robust and crash resistant software that has been
possible since SEH's introduction with Windows 95.

This is of course not only limited to hardware faults, as the example above shows.

By blurring the line between hardware fault handling and software error propagation,
it also allows a more natural control flow than having to deal with arcane UNIX
signals running in restricted contexts or *shudder* check against NULL
before using a pointer.

Hoping you are as hyped as I am.

[1]: dc5100e6b ("state: backend_storage: deal
     gracefully with runtime bucket corruption")
[2]: 09d11c546 ("video/ssd1307fb: fix NULL pointer dereference in probe")
[3]: 671a7d5df ("driver: bail out, don't crash, if drv->name is not set")
[4]: 8a774977b ("mci: dove: fix dereference of nullable pointer")
[5]: bb414a64b ("drivers: video: Fix parsing oftree timings")

Cheers,
Ahmad (4):
  ARM: implement sjlj-based TRY/CATCH exception handling
  startup: wrap barebox startup in TRY/CATCH
  ARM: rethrow CPU exceptions as sjlj-exceptions
  commands: implement except test command

 arch/arm/cpu/interrupts.c |  32 +--
 commands/Kconfig          |   5 +
 commands/Makefile         |   1 +
 commands/except.c         | 130 ++++++++++++
 common/Makefile           |   3 +
 common/except.c           | 282 +++++++++++++++++++++++++
 common/startup.c          |  70 +++++--
 include/except.h          | 156 ++++++++++++++
 include/exceptions.h      | 419 ++++++++++++++++++++++++++++++++++++++
 9 files changed, 1058 insertions(+), 40 deletions(-)
 create mode 100644 commands/except.c
 create mode 100644 common/except.c
 create mode 100644 include/except.h
 create mode 100644 include/exceptions.h

-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH exception handling
  2020-04-01  9:31 [RFC PATCH 0/4] ARM: introduce sjlj structured exception handling Ahmad Fatoum
@ 2020-04-01  9:31 ` Ahmad Fatoum
  2020-04-02 19:51   ` Roland Hieber
  2020-04-01  9:31 ` [RFC PATCH 2/4] startup: wrap barebox startup in TRY/CATCH Ahmad Fatoum
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Ahmad Fatoum @ 2020-04-01  9:31 UTC (permalink / raw)
  To: barebox

Now with setjmp supported in barebox, we don't need to limit ourselves
to outdated of return-value-based error propagation.
Instead, embrace the state-of-the-art and port kazlib's ANSI C exceptions
to barebox.

Reviewed-by: Ahmad Fatoum <ahmad@a3f.at>
---
 common/Makefile      |   3 +
 common/except.c      | 282 +++++++++++++++++++++++++++++
 include/except.h     | 156 ++++++++++++++++
 include/exceptions.h | 419 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 860 insertions(+)
 create mode 100644 common/except.c
 create mode 100644 include/except.h
 create mode 100644 include/exceptions.h

diff --git a/common/Makefile b/common/Makefile
index 84463b4d485a..ee0d75ae86e2 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -69,6 +69,9 @@ obj-$(CONFIG_SERIAL_DEV_BUS)	+= serdev.o
 obj-$(CONFIG_USBGADGET_START)	+= usbgadget.o
 pbl-$(CONFIG_PBL_OPTEE)		+= optee.o
 obj-$(CONFIG_BOOTM_OPTEE)	+= optee.o
+obj-y				+= except.o
+
+UBSAN_SANITIZE_except.o := n
 
 ifdef CONFIG_PASSWORD
 
diff --git a/common/except.c b/common/except.c
new file mode 100644
index 000000000000..0e408c669ee0
--- /dev/null
+++ b/common/except.c
@@ -0,0 +1,282 @@
+/*
+ * Portable Exception Handling for ANSI C.
+ * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * modified for barebox:
+ * Copyright (C) 2019 Ahmad Fatoum
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ * $Id: except.c,v 1.27.2.2 2001/07/27 01:20:34 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+#include <stdlib.h>
+#include <common.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <linux/kernel.h>
+#include "except.h"
+
+#define XCEPT_BUFFER_SIZE	1024
+
+#define group except_group
+#define code except_code
+#define id except_id
+#define message except_message
+#define dyndata except_dyndata
+#define func except_func
+#define context except_context
+#define id except_id
+#define size except_size
+#define obj except_obj
+#define jmp except_jmp
+#define down except_down
+#define type except_type
+#define catcher except_catcher
+#define cleanup except_cleanup
+#define info except_info
+
+static int init_counter;
+static void unhandled_catcher(except_t *);
+static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
+static void *(*allocator)(size_t) = malloc;
+static void (*deallocator)(void *) = free;
+static struct except_stacknode *stack_top;
+
+#define get_top() (stack_top)
+#define set_top(T) (stack_top = (T))
+#define get_catcher() (uh_catcher_ptr)
+#define set_catcher(C) (uh_catcher_ptr = (C))
+#define get_alloc() (allocator)
+#define set_alloc(A) (allocator = (A))
+#define get_dealloc() (deallocator)
+#define set_dealloc(D) (deallocator = (D))
+
+int except_init(void)
+{
+    WARN_ON(init_counter == INT_MAX);
+    init_counter++;
+    return 1;
+}
+
+void except_deinit(void)
+{
+    WARN_ON(init_counter <= 0);
+    init_counter--;
+}
+
+static int match(const volatile except_id_t *thrown, const except_id_t *caught)
+{
+    int group_match = (caught->group == XCEPT_GROUP_ANY || caught->group == thrown->group);
+    int code_match = (caught->code == XCEPT_CODE_ANY || caught->code == thrown->code);
+
+    return group_match && code_match;
+}
+
+static __noreturn void do_throw(except_t *except)
+{
+    struct except_stacknode *top;
+
+    WARN_ON(except->id.group == 0 || except->id.code == 0);
+
+    for (top = get_top(); top != 0; top = top->down) {
+	if (top->type == XCEPT_CLEANUP) {
+	    top->info.cleanup->func(top->info.cleanup->context);
+	} else {
+	    struct except_catch *catcher = top->info.catcher;
+	    const except_id_t *pi = catcher->id;
+	    size_t i;
+
+	    WARN_ON(top->type != XCEPT_CATCHER);
+	    except_free(catcher->obj.dyndata);
+
+	    for (i = 0; i < catcher->size; pi++, i++) {
+		if (match(&except->id, pi)) {
+		    catcher->obj = *except;
+		    set_top(top);
+		    longjmp(catcher->jmp, 1);
+		}
+	    }
+	}
+    }
+
+    set_top(top);
+    get_catcher()(except);	/* unhandled exception */
+    panic("");
+}
+
+static const char * const exception_strs[] = {
+	[LogicError]			= "LogicError",
+	[RuntimeError]			= "RuntimeError",
+	[OutOfMemoryError]		= "OutOfMemoryError",
+	[UndefinedInstructionException]	= "UndefinedInstructionException",
+	[SoftwareInterruptException]	= "SoftwareInterruptException",
+	[PrefetchAbortException]	= "PrefetchAbortException",
+	[DataAbortException]		= "DataAbortExceotion",
+	[FiqException]			= "FiqException",
+	[IrqException]			= "IrqException",
+};
+
+static void unhandled_catcher(except_t *except)
+{
+	printk("Unhandled exception (\"%s\", msg=%p, group=%ld, code=%ld)\n",
+	       exception_strs[except->id.code],
+	       except->message, except->id.group, except->id.code);
+	dump_stack();
+	panic("");
+}
+
+static void stack_push(struct except_stacknode *node)
+{
+    node->down = get_top();
+    set_top(node);
+}
+
+void except_setup_clean(struct except_stacknode *esn,
+	struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
+{
+    esn->type = XCEPT_CLEANUP;
+    ecl->func = cleanf;
+    ecl->context = context;
+    esn->info.cleanup = ecl;
+    stack_push(esn);
+}
+
+void except_setup_try(struct except_stacknode *esn,
+	struct except_catch *ech, const except_id_t id[], size_t size)
+{
+   ech->id = id;
+   ech->size = size;
+   ech->obj.dyndata = 0;
+   esn->type = XCEPT_CATCHER;
+   esn->info.catcher = ech;
+   stack_push(esn);
+}
+
+struct except_stacknode *except_pop(void)
+{
+    struct except_stacknode *top = get_top();
+    set_top(top->down);
+    return top;
+}
+
+__noreturn void except_rethrow(except_t *except)
+{
+    struct except_stacknode *top = get_top();
+    WARN_ON(top == 0);
+    WARN_ON(top->type != XCEPT_CATCHER);
+    WARN_ON(&top->info.catcher->obj != except);
+    set_top(top->down);
+    do_throw(except);
+}
+
+__noreturn void except_throw(long group, long code, const char *msg)
+{
+    except_t except;
+
+    except.id.group = group;
+    except.id.code = code;
+    except.message = msg;
+    except.dyndata = 0;
+
+    do_throw(&except);
+}
+
+__noreturn void except_throwd(long group, long code, const char *msg, void *data)
+{
+    except_t except;
+
+    except.id.group = group;
+    except.id.code = code;
+    except.message = msg;
+    except.dyndata = data;
+
+    do_throw(&except);
+}
+
+__noreturn void except_vthrowf(long group, long code, const char *fmt,
+			       va_list vl)
+{
+    char *buf = except_alloc(XCEPT_BUFFER_SIZE);
+
+    vsnprintf(buf, XCEPT_BUFFER_SIZE, fmt, vl);
+    except_throwd(group, code, buf, buf);
+}
+
+__noreturn void except_throwf(long group, long code, const char *fmt, ...)
+{
+    va_list vl;
+
+    va_start (vl, fmt);
+    except_vthrowf(group, code, fmt, vl);
+    va_end (vl);
+}
+
+void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
+{
+    void (*old_catcher)(except_t *) = get_catcher();
+    set_catcher(new_catcher);
+    return old_catcher;
+}
+
+#undef except_code
+#undef except_group
+#undef except_message
+#undef except_data
+
+unsigned long except_code(except_t *ex)
+{
+    return ex->id.code;
+}
+
+unsigned long except_group(except_t *ex)
+{
+    return ex->id.group;
+}
+
+const char *except_message(except_t *ex)
+{
+    return ex->message;
+}
+
+void *except_data(except_t *ex)
+{
+    return ex->dyndata;
+}
+
+void *except_take_data(except_t *ex)
+{
+    void *data = ex->dyndata;
+    ex->dyndata = 0;
+    return data;
+}
+
+void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
+{
+    set_alloc(alloc);
+    set_dealloc(dealloc);
+}
+
+void *except_alloc(size_t size)
+{
+    void *ptr = get_alloc()(size);
+
+    if (ptr == 0)
+	except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
+    return ptr;
+}
+
+void except_free(void *ptr)
+{
+    get_dealloc()(ptr);
+}
diff --git a/include/except.h b/include/except.h
new file mode 100644
index 000000000000..ccf99d5d03d3
--- /dev/null
+++ b/include/except.h
@@ -0,0 +1,156 @@
+/*
+ * Portable Exception Handling for ANSI C.
+ * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * modified for barebox:
+ * Copyright (C) 2019 Ahmad Fatoum
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: except.h,v 1.18.2.1 2001/07/25 03:37:06 kaz Exp $
+ * $Name: kazlib_1_20 $
+ */
+
+#ifndef XCEPT_H
+#define XCEPT_H
+
+#include <asm/setjmp.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <linux/bug.h>
+
+#define LogicError			0
+#define RuntimeError			1
+#define OutOfMemoryError		2
+#define UndefinedInstructionException	3
+#define	SoftwareInterruptException	4
+#define	PrefetchAbortException		5
+#define	DataAbortException		6
+#define	FiqException			7
+#define	IrqException			8
+
+#define XCEPT_GROUP_ANY	0
+#define XCEPT_CODE_ANY	0
+#define XCEPT_BAD_ALLOC	1
+
+#define XCEPT_DATA_NONDYN(x) ((void *volatile)((unsigned long)x | 1))
+
+enum { except_no_call, except_call };
+
+typedef struct {
+	unsigned long except_group;
+	unsigned long except_code;
+} except_id_t;
+
+typedef struct {
+	except_id_t volatile except_id;
+	const char *volatile except_message;
+	void *volatile except_dyndata;
+} except_t;
+
+struct except_cleanup {
+	void (*except_func)(void *);
+	void *except_context;
+};
+
+struct except_catch {
+	const except_id_t *except_id;
+	size_t except_size;
+	except_t except_obj;
+	jmp_buf except_jmp;
+};
+
+enum except_stacktype {
+	XCEPT_CLEANUP, XCEPT_CATCHER
+};
+
+struct except_stacknode {
+	struct except_stacknode *except_down;
+	enum except_stacktype except_type;
+	union {
+		struct except_catch *except_catcher;	
+		struct except_cleanup *except_cleanup;
+	} except_info;
+};
+
+/* private functions made external so they can be used in macros */
+void except_setup_clean(struct except_stacknode *,
+			struct except_cleanup *, void (*)(void *), void *);
+void except_setup_try(struct except_stacknode *,
+		      struct except_catch *, const except_id_t [], size_t);
+struct except_stacknode *except_pop(void);
+
+/* public interface functions */
+int except_init(void);
+void except_deinit(void);
+__noreturn void except_rethrow(except_t *);
+__noreturn void except_throw(long, long, const char *);
+__noreturn void except_throwd(long, long, const char *, void *);
+__noreturn void except_vthrowf(long group, long code, const char *fmt, va_list vl);
+__noreturn void except_throwf(long, long, const char *, ...) __printf(3, 4);
+void (*except_unhandled_catcher(void (*)(except_t *)))(except_t *);
+unsigned long except_code(except_t *);
+unsigned long except_group(except_t *);
+const char *except_message(except_t *);
+void *except_data(except_t *);
+void *except_take_data(except_t *);
+void except_set_allocator(void *(*)(size_t), void (*)(void *));
+void *except_alloc(size_t);
+void except_free(void *);
+
+#define except_code(E) ((E)->except_id.except_code)
+#define except_group(E) ((E)->except_id.except_group)
+#define except_message(E) ((E)->except_message)
+#define except_data(E) ((E)->except_dyndata)
+
+/* void except_cleanup_push(void (*)(void *), void *); */
+#define except_cleanup_push(F, C) 				\
+{								\
+	struct except_stacknode except_sn;			\
+	struct except_cleanup except_cl;			\
+	except_setup_clean(&except_sn, &except_cl, F, C)
+
+/* void except_cleanup_pop(int); */
+#define except_cleanup_pop(E)					\
+	except_pop();						\
+	if (E)							\
+	except_cl.except_func(except_cl.except_context);	\
+}
+
+/* void except_checked_cleanup_pop(void (*)(void *), int); */
+#define except_checked_cleanup_pop(F, E)			\
+	except_pop();						\
+	WARN_ON(except_cl.except_func != (F));			\
+	if (E)							\
+	except_cl.except_func(except_cl.except_context);	\
+	}
+
+/* void except_try_push(const except_id_t [], size_t, except_t **); */
+#define except_try_push(ID, NUM, PPE)				\
+{								\
+	struct except_stacknode except_sn;			\
+	struct except_catch except_ch;				\
+	except_setup_try(&except_sn, &except_ch, ID, NUM);	\
+	if (setjmp(except_ch.except_jmp))			\
+	*(PPE) = &except_ch.except_obj;				\
+	else							\
+	*(PPE) = 0
+
+/* void except_try_pop(void); */
+#define except_try_pop()							\
+	if ((((unsigned long)except_ch.except_obj.except_dyndata) & 1) == 0)	\
+		except_free(except_ch.except_obj.except_dyndata);		\
+	except_pop();								\
+}
+
+#endif
diff --git a/include/exceptions.h b/include/exceptions.h
new file mode 100644
index 000000000000..8183b7f86fbb
--- /dev/null
+++ b/include/exceptions.h
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 1998 Gerald Combs, original Wireshark code
+ * Copyright 2019 Ahmad Fatoum, barebox port
+ *
+ */
+
+#ifndef __EXCEPTIONS_H__
+#define __EXCEPTIONS_H__
+
+#include "except.h"
+
+/* We've only one exception group, to make these macros simple */
+#define XCEPT_GROUP_BAREBOX 1
+
+#define CATCH_ERRORS \
+	CATCH3(LogicError, RuntimeError, OutOfMemoryError)
+
+#define CATCH_EXCEPTIONS \
+	CATCH6(UndefinedInstructionException, \
+		SoftwareInterruptException, \
+		PrefetchAbortException, \
+		DataAbortException, \
+		FiqException, \
+		IrqException)
+
+/* Usage:
+ *
+ * TRY {
+ * 	code;
+ * }
+ *
+ * CATCH(exception) {
+ * 	code;
+ * }
+ *
+ * CATCH2(exception1, exception2) {
+ * 	code;
+ * }
+ *
+ * CATCH3(exception1, exception2, exception3) {
+ * 	code;
+ * }
+ *
+ * CATCH4(exception1, exception2, exception3, exception4) {
+ * 	code;
+ * }
+ *
+ * CATCH5(exception1, exception2, exception3, exception4, exception5) {
+ * 	code;
+ * }
+ *
+ * CATCH6(exception1, exception2, exception3, exception4, exception5, exception6) {
+ * 	code;
+ * }
+ *
+ * CATCH7(exception1, exception2, exception3, exception4, exception5, exception6, exception7) {
+ * 	code;
+ * }
+ *
+ * CATCH8(exception1, exception2, exception3, exception4, exception5, exception6, exception7, exception8) {
+ * 	code;
+ * }
+ *
+ * CATCH9(exception1, exception2, exception3, exception4, exception5, exception6, exception7, exception8, exception9) {
+ * 	code;
+ * }
+ *
+ * CATCH_NONFATAL_ERRORS {
+ *	code;
+ * }
+ *
+ * CATCH_BOUNDS_ERRORS {
+ *	code;
+ * }
+ *
+ * CATCH_ALL {
+ * 	code;
+ * }
+ *
+ * FINALLY {
+ * 	code;
+ * }
+ *
+ * ENDTRY;
+ *
+ * ********* Never use 'goto' or 'return' inside the TRY, CATCH*, or
+ * ********* FINALLY blocks. Execution must proceed through ENDTRY before
+ * ********* branching out.
+ *
+ * This is really something like:
+ *
+ * {
+ * 	caught = FALSE:
+ * 	x = setjmp();
+ * 	if (x == 0) {
+ * 		<TRY code>
+ * 	}
+ * 	if (!caught && x == 1) {
+ * 		caught = TRUE;
+ * 		<CATCH(1) code>
+ * 	}
+ * 	if (!caught && x == 2) {
+ * 		caught = TRUE;
+ * 		<CATCH(2) code>
+ * 	}
+ * 	if (!caught && (x == 3 || x == 4)) {
+ * 		caught = TRUE;
+ * 		<CATCH2(3,4) code>
+ * 	}
+ * 	if (!caught && (x == 5 || x == 6 || x == 7)) {
+ * 		caught = TRUE;
+ * 		<CATCH3(5,6,7) code>
+ * 	}
+ * 	if (!caught && x != 0) {
+ *		caught = TRUE;
+ * 		<CATCH_ALL code>
+ * 	}
+ * 	<FINALLY code>
+ * 	if(!caught) {
+ *      	RETHROW(x)
+ * 	}
+ * }<ENDTRY tag>
+ *
+ * All CATCH's must precede a CATCH_ALL.
+ * FINALLY must occur after any CATCH or CATCH_ALL.
+ * ENDTRY marks the end of the TRY code.
+ * TRY and ENDTRY are the mandatory parts of a TRY block.
+ * CATCH, CATCH_ALL, and FINALLY are all optional (although
+ * you'll probably use at least one, otherwise why "TRY"?)
+ *
+ * GET_MESSAGE	returns string ptr to exception message
+ * 		when exception is thrown via THROW_MESSAGE()
+ *
+ * To throw/raise an exception.
+ *
+ * THROW(exception)
+ * RETHROW				rethrow the caught exception
+ *
+ * A cleanup callback is a function called in case an exception occurs
+ * and is not caught. It should be used to free any dynamically-allocated data.
+ * A pop or call_and_pop should occur at the same statement-nesting level
+ * as the push.
+ *
+ * CLEANUP_CB_PUSH(func, data)
+ * CLEANUP_CB_POP
+ * CLEANUP_CB_CALL_AND_POP
+ */
+
+/* we do up to three passes through the bit of code after except_try_push(),
+ * and except_state is used to keep track of where we are.
+ */
+#define EXCEPT_CAUGHT   1 /* exception has been caught, no need to rethrow at
+                           * ENDTRY */
+
+#define EXCEPT_RETHROWN 2 /* the exception was rethrown from a CATCH
+                           * block. Don't reenter the CATCH blocks, but do
+                           * execute FINALLY and rethrow at ENDTRY */
+
+#define EXCEPT_FINALLY  4 /* we've entered the FINALLY block - don't allow
+                           * RETHROW, and don't reenter FINALLY if a
+                           * different exception is thrown */
+
+#define TRY \
+{\
+	except_t *volatile exc; \
+	volatile int except_state = 0; \
+	static const except_id_t catch_spec[] = { \
+		{ XCEPT_GROUP_BAREBOX, XCEPT_CODE_ANY } }; \
+	except_try_push(catch_spec, 1, &exc); \
+	                                               \
+    	if(except_state & EXCEPT_CAUGHT)               \
+            except_state |= EXCEPT_RETHROWN;           \
+	except_state &= ~EXCEPT_CAUGHT;                \
+	                                               \
+	if (except_state == 0 && exc == 0)             \
+		/* user's code goes here */
+
+#define ENDTRY \
+	/* rethrow the exception if necessary */ \
+	if(!(except_state&EXCEPT_CAUGHT) && exc != 0)  \
+	    except_rethrow(exc);                 \
+	except_try_pop();\
+}
+
+/* the (except_state |= EXCEPT_CAUGHT) in the below is a way of setting
+ * except_state before the user's code, without disrupting the user's code if
+ * it's a one-liner.
+ */
+#define CATCH(x) \
+	if (except_state == 0 && exc != 0 && \
+	    exc->except_id.except_code == (x) && \
+	    (except_state |= EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH2(x,y) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH3(x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH4(w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH5(v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH6(u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH7(t,u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (t) || \
+	     exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH8(s,t,u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (s) || \
+	    (exc->except_id.except_code == (t) || \
+	     exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH9(r,s,t,u,v,w,x,y,z) \
+	if (except_state == 0 && exc != 0 && \
+	    (exc->except_id.except_code == (r) || \
+	    (exc->except_id.except_code == (s) || \
+	    (exc->except_id.except_code == (t) || \
+	     exc->except_id.except_code == (u) || \
+	     exc->except_id.except_code == (v) || \
+	     exc->except_id.except_code == (w) || \
+	     exc->except_id.except_code == (x) || \
+	     exc->except_id.except_code == (y) || \
+	     exc->except_id.except_code == (z)) && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define CATCH_ALL \
+	if (except_state == 0 && exc != 0 && \
+	    (except_state|=EXCEPT_CAUGHT)) \
+		/* user's code goes here */
+
+#define FINALLY \
+	if( !(except_state & EXCEPT_FINALLY) && (except_state|=EXCEPT_FINALLY)) \
+		/* user's code goes here */
+
+#define THROW(x) \
+	except_throw(XCEPT_GROUP_BAREBOX, (x), NULL)
+
+#define THROW_ON(cond, x) do { \
+	if ((cond)) \
+		except_throw(XCEPT_GROUP_BAREBOX, (x), NULL); \
+} while (0)
+
+#define THROW_MESSAGE(x, y) \
+	except_throw(XCEPT_GROUP_BAREBOX, (x), (y))
+
+#define THROW_MESSAGE_ON(cond, x, y) do { \
+	if ((cond)) \
+		except_throw(XCEPT_GROUP_BAREBOX, (x), (y)); \
+} while (0)
+
+#define THROW_DATA(x, s, d) \
+	except_throwd(XCEPT_GROUP_BAREBOX, (x), (s), (d))
+
+#define THROW_DATA_ON(cond, x, s, d) do { \
+	if ((cond)) \
+		except_throwd(XCEPT_GROUP_BAREBOX, (x), (s), (d)); \
+} while (0)
+
+/* Throws a formatted message, its memory is cleared after catching it. */
+#define THROW_FORMATTED(x, ...) \
+	except_throwf(XCEPT_GROUP_BAREBOX, (x), __VA_ARGS__)
+
+/* Like THROW_FORMATTED, but takes a va_list as an argument */
+#define VTHROW_FORMATTED(x, format, args) \
+	except_vthrowf(XCEPT_GROUP_BAREBOX, (x), format, args)
+
+#define GET_MESSAGE			except_message(exc)
+#define GET_DATA			except_data(exc)
+
+#define RETHROW                                     \
+    {                                               \
+        /* check we're in a catch block */          \
+        WARN_ON(except_state != EXCEPT_CAUGHT);     \
+	/* we can't use except_rethrow here, as that pops a catch block \
+	 * off the stack, and we don't want to do that, because we want to \
+	 * excecute the FINALLY {} block first.     \
+	 * except_throw doesn't provide an interface to rethrow an existing \
+	 * exception; however, longjmping back to except_try_push() has the \
+	 * desired effect.			    \
+	 *					    \
+	 * Note also that THROW and RETHROW should provide much the same \
+	 * functionality in terms of which blocks to enter, so any messing \
+	 * about with except_state in here would indicate that THROW is \
+	 * doing the wrong thing.                   \
+	 */					    \
+        longjmp(except_ch.except_jmp,1);            \
+    }
+
+#define EXCEPT_CODE			except_code(exc)
+
+/* Register cleanup functions in case an exception is thrown and not caught.
+ * From the Kazlib documentation, with modifications for use with the
+ * Wireshark-specific macros:
+ *
+ * CLEANUP_PUSH(func, arg)
+ *
+ *  The call to CLEANUP_PUSH shall be matched with a call to
+ *  CLEANUP_CALL_AND_POP or CLEANUP_POP which must occur in the same
+ *  statement block at the same level of nesting. This requirement allows
+ *  an implementation to provide a CLEANUP_PUSH macro which opens up a
+ *  statement block and a CLEANUP_POP which closes the statement block.
+ *  The space for the registered pointers can then be efficiently
+ *  allocated from automatic storage.
+ *
+ *  The CLEANUP_PUSH macro registers a cleanup handler that will be
+ *  called if an exception subsequently occurs before the matching
+ *  CLEANUP_[CALL_AND_]POP is executed, and is not intercepted and
+ *  handled by a try-catch region that is nested between the two.
+ *
+ *  The first argument to CLEANUP_PUSH is a pointer to the cleanup
+ *  handler, a function that returns nothing and takes a single
+ *  argument of type void*. The second argument is a void* value that
+ *  is registered along with the handler.  This value is what is passed
+ *  to the registered handler, should it be called.
+ *
+ *  Cleanup handlers are called in the reverse order of their nesting:
+ *  inner handlers are called before outer handlers.
+ *
+ *  The program shall not leave the cleanup region between
+ *  the call to the macro CLEANUP_PUSH and the matching call to
+ *  CLEANUP_[CALL_AND_]POP by means other than throwing an exception,
+ *  or calling CLEANUP_[CALL_AND_]POP.
+ *
+ *  Within the call to the cleanup handler, it is possible that new
+ *  exceptions may happen.  Such exceptions must be handled before the
+ *  cleanup handler terminates. If the call to the cleanup handler is
+ *  terminated by an exception, the behavior is undefined. The exception
+ *  which triggered the cleanup is not yet caught; thus the program
+ *  would be effectively trying to replace an exception with one that
+ *  isn't in a well-defined state.
+ *
+ *
+ * CLEANUP_POP and CLEANUP_CALL_AND_POP
+ *
+ *  A call to the CLEANUP_POP or CLEANUP_CALL_AND_POP macro shall match
+ *  each call to CLEANUP_PUSH which shall be in the same statement block
+ *  at the same nesting level.  It shall match the most recent such a
+ *  call that is not matched by a previous CLEANUP_[CALL_AND_]POP at
+ *  the same level.
+ *
+ *  These macros causes the registered cleanup handler to be removed. If
+ *  CLEANUP_CALL_AND_POP is called, the cleanup handler is called.
+ *  In that case, the registered context pointer is passed to the cleanup
+ *  handler. If CLEANUP_POP is called, the cleanup handler is not called.
+ *
+ *  The program shall not leave the region between the call to the
+ *  macro CLEANUP_PUSH and the matching call to CLEANUP_[CALL_AND_]POP
+ *  other than by throwing an exception, or by executing the
+ *  CLEANUP_CALL_AND_POP.
+ *
+ */
+
+
+#define CLEANUP_PUSH(f,a)		except_cleanup_push((f),(a))
+#define CLEANUP_POP			except_cleanup_pop(0)
+#define CLEANUP_CALL_AND_POP		except_cleanup_pop(1)
+
+/* Variants to allow nesting of except_cleanup_push w/o "shadowing" variables */
+#define CLEANUP_PUSH_PFX(pfx,f,a)	except_cleanup_push_pfx(pfx,(f),(a))
+#define CLEANUP_POP_PFX(pfx)		except_cleanup_pop_pfx(pfx,0)
+#define CLEANUP_CALL_AND_POP_PFX(pfx)	except_cleanup_pop_pfx(pfx,1)
+
+
+
+#endif /* __EXCEPTIONS_H__ */
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH 2/4] startup: wrap barebox startup in TRY/CATCH
  2020-04-01  9:31 [RFC PATCH 0/4] ARM: introduce sjlj structured exception handling Ahmad Fatoum
  2020-04-01  9:31 ` [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH " Ahmad Fatoum
@ 2020-04-01  9:31 ` Ahmad Fatoum
  2020-04-01  9:31 ` [RFC PATCH 3/4] ARM: rethrow CPU exceptions as sjlj-exceptions Ahmad Fatoum
  2020-04-01  9:31 ` [RFC PATCH 4/4] commands: implement except test command Ahmad Fatoum
  3 siblings, 0 replies; 7+ messages in thread
From: Ahmad Fatoum @ 2020-04-01  9:31 UTC (permalink / raw)
  To: barebox

Install a top level exception handler around barebox_main, so we may
catch THROWs that aren't contained in a TRY { } block.

Reviewed-by: Ahmad Fatoum <ahmad@a3f.at>
---
 common/startup.c | 42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/common/startup.c b/common/startup.c
index c417a4d0781a..41da1359def6 100644
--- a/common/startup.c
+++ b/common/startup.c
@@ -348,27 +348,35 @@ void __noreturn start_barebox(void)
 	if (!IS_ENABLED(CONFIG_SHELL_NONE) && IS_ENABLED(CONFIG_COMMAND_SUPPORT))
 		barebox_main = run_init;
 
-	for (initcall = __barebox_initcalls_start;
-			initcall < __barebox_initcalls_end; initcall++) {
-		pr_debug("initcall-> %pS\n", *initcall);
-		result = (*initcall)();
-		if (result)
-			pr_err("initcall %pS failed: %s\n", *initcall,
-					strerror(-result));
-	}
+	TRY {
+		for (initcall = __barebox_initcalls_start;
+				initcall < __barebox_initcalls_end; initcall++) {
+			pr_debug("initcall-> %pS\n", *initcall);
+			result = (*initcall)();
+			if (result)
+				pr_err("initcall %pS failed: %s\n", *initcall,
+						strerror(-result));
+		}
 
-	pr_debug("initcalls done\n");
+		pr_debug("initcalls done\n");
 
-	if (barebox_main)
-		barebox_main();
+		if (barebox_main)
+			barebox_main();
 
-	if (IS_ENABLED(CONFIG_SHELL_NONE)) {
-		pr_err("Nothing left to do\n");
-		hang();
-	} else {
-		while (1)
-			run_shell();
+		if (IS_ENABLED(CONFIG_SHELL_NONE)) {
+			pr_err("Nothing left to do\n");
+			hang();
+		} else {
+			while (1)
+				run_shell();
+		}
+	} CATCH_ALL {
+		panic("Unhandled exception");
 	}
+
+
+	ENDTRY;
+
 }
 
 void __noreturn hang (void)
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH 3/4] ARM: rethrow CPU exceptions as sjlj-exceptions
  2020-04-01  9:31 [RFC PATCH 0/4] ARM: introduce sjlj structured exception handling Ahmad Fatoum
  2020-04-01  9:31 ` [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH " Ahmad Fatoum
  2020-04-01  9:31 ` [RFC PATCH 2/4] startup: wrap barebox startup in TRY/CATCH Ahmad Fatoum
@ 2020-04-01  9:31 ` Ahmad Fatoum
  2020-04-01  9:31 ` [RFC PATCH 4/4] commands: implement except test command Ahmad Fatoum
  3 siblings, 0 replies; 7+ messages in thread
From: Ahmad Fatoum @ 2020-04-01  9:31 UTC (permalink / raw)
  To: barebox

Having to differentiate between hardware and software exceptions is
confusing, especially for newcomers. Be more welcoming by just treating
them the same.

Reviewed-by: Ahmad Fatoum <ahmad@a3f.at>
---
 arch/arm/cpu/interrupts.c | 32 +++++++++-----------------------
 common/startup.c          | 32 ++++++++++++++++++++++++++++++--
 2 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/arch/arm/cpu/interrupts.c b/arch/arm/cpu/interrupts.c
index b9b91f315312..497167e4d2ab 100644
--- a/arch/arm/cpu/interrupts.c
+++ b/arch/arm/cpu/interrupts.c
@@ -27,6 +27,7 @@
 #include <asm/ptrace.h>
 #include <asm/unwind.h>
 #include <init.h>
+#include "exceptions.h"
 
 /* Avoid missing prototype warning, called from assembly */
 void do_undefined_instruction (struct pt_regs *pt_regs);
@@ -80,11 +81,9 @@ void show_regs (struct pt_regs *regs)
 #endif
 }
 
-static void __noreturn do_exception(struct pt_regs *pt_regs)
+static void __noreturn do_exception(int exception, struct pt_regs *pt_regs)
 {
-	show_regs(pt_regs);
-
-	panic("");
+	THROW_DATA(exception, NULL, XCEPT_DATA_NONDYN(pt_regs));
 }
 
 /**
@@ -93,8 +92,7 @@ static void __noreturn do_exception(struct pt_regs *pt_regs)
  */
 void do_undefined_instruction (struct pt_regs *pt_regs)
 {
-	printf ("undefined instruction\n");
-	do_exception(pt_regs);
+	do_exception(UndefinedInstructionException, pt_regs);
 }
 
 /**
@@ -106,8 +104,7 @@ void do_undefined_instruction (struct pt_regs *pt_regs)
  */
 void do_software_interrupt (struct pt_regs *pt_regs)
 {
-	printf ("software interrupt\n");
-	do_exception(pt_regs);
+	do_exception(SoftwareInterruptException, pt_regs);
 }
 
 /**
@@ -118,8 +115,7 @@ void do_software_interrupt (struct pt_regs *pt_regs)
  */
 void do_prefetch_abort (struct pt_regs *pt_regs)
 {
-	printf ("prefetch abort\n");
-	do_exception(pt_regs);
+	do_exception(PrefetchAbortException, pt_regs);
 }
 
 /**
@@ -130,15 +126,7 @@ void do_prefetch_abort (struct pt_regs *pt_regs)
  */
 void do_data_abort (struct pt_regs *pt_regs)
 {
-	u32 far;
-
-	asm volatile ("mrc     p15, 0, %0, c6, c0, 0" : "=r" (far) : : "cc");
-
-	printf("unable to handle %s at address 0x%08x\n",
-			far < PAGE_SIZE ? "NULL pointer dereference" :
-			"paging request", far);
-
-	do_exception(pt_regs);
+	do_exception(DataAbortException, pt_regs);
 }
 
 /**
@@ -149,8 +137,7 @@ void do_data_abort (struct pt_regs *pt_regs)
  */
 void do_fiq (struct pt_regs *pt_regs)
 {
-	printf ("fast interrupt request\n");
-	do_exception(pt_regs);
+	do_exception(FiqException, pt_regs);
 }
 
 /**
@@ -161,8 +148,7 @@ void do_fiq (struct pt_regs *pt_regs)
  */
 void do_irq (struct pt_regs *pt_regs)
 {
-	printf ("interrupt request\n");
-	do_exception(pt_regs);
+	do_exception(IrqException, pt_regs);
 }
 
 extern volatile int arm_ignore_data_abort;
diff --git a/common/startup.c b/common/startup.c
index 41da1359def6..32f247c0276a 100644
--- a/common/startup.c
+++ b/common/startup.c
@@ -45,6 +45,8 @@
 #include <console_countdown.h>
 #include <environment.h>
 #include <linux/ctype.h>
+#include <asm/unwind.h>
+#include <exceptions.h>
 
 extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[],
 		  __barebox_initcalls_end[];
@@ -370,13 +372,39 @@ void __noreturn start_barebox(void)
 			while (1)
 				run_shell();
 		}
-	} CATCH_ALL {
-		panic("Unhandled exception");
+	} CATCH_ERRORS {
+		unwind_backtrace(GET_DATA);
+	} CATCH(UndefinedInstructionException) {
+		printf ("undefined instruction\n");
+		show_regs(GET_DATA);
+	} CATCH(SoftwareInterruptException) {
+		printf ("software interrupt\n");
+		show_regs(GET_DATA);
+	} CATCH(PrefetchAbortException) {
+		printf ("prefetch abort\n");
+		show_regs(GET_DATA);
+	} CATCH(DataAbortException) {
+		u32 far;
+
+		asm volatile ("mrc     p15, 0, %0, c6, c0, 0" : "=r" (far) : : "cc");
+
+		printf("unable to handle %s at address 0x%08x\n",
+				far < PAGE_SIZE ? "NULL pointer dereference" :
+				"paging request", far);
+
+		show_regs((void*)((unsigned long)GET_DATA & ~1U));
+	} CATCH(FiqException) {
+		printf ("fast interrupt request\n");
+		show_regs(GET_DATA);
+	} CATCH (IrqException) {
+		printf ("interrupt request\n");
+		show_regs(GET_DATA);
 	}
 
 
 	ENDTRY;
 
+	panic("Unhandled exception");
 }
 
 void __noreturn hang (void)
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH 4/4] commands: implement except test command
  2020-04-01  9:31 [RFC PATCH 0/4] ARM: introduce sjlj structured exception handling Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2020-04-01  9:31 ` [RFC PATCH 3/4] ARM: rethrow CPU exceptions as sjlj-exceptions Ahmad Fatoum
@ 2020-04-01  9:31 ` Ahmad Fatoum
  3 siblings, 0 replies; 7+ messages in thread
From: Ahmad Fatoum @ 2020-04-01  9:31 UTC (permalink / raw)
  To: barebox

This command is not meant for production, but is a useful tool to test
software and hardware exception handling in the works, especially when
porting to new platforms.

Reviewed-by: Ahmad Fatoum <ahmad@a3f.at>
---
 commands/Kconfig  |   5 ++
 commands/Makefile |   1 +
 commands/except.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 136 insertions(+)
 create mode 100644 commands/except.c

diff --git a/commands/Kconfig b/commands/Kconfig
index 7784966282ac..bb415c2d7edd 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -2196,6 +2196,11 @@ config CMD_UBSAN
 	  This is a test command for the undefined behavior sanitizer.
 	  It triggers various undefined behavior, and detect it.
 
+config CMD_EXCEPT
+	tristate "except"
+	help
+	  This is a test command for the barebox exceptions facility.
+
 # end Miscellaneous commands
 endmenu
 
diff --git a/commands/Makefile b/commands/Makefile
index 01082de44c9b..65167ca92081 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -128,5 +128,6 @@ obj-$(CONFIG_CMD_NAND_BITFLIP)	+= nand-bitflip.o
 obj-$(CONFIG_CMD_SEED)		+= seed.o
 obj-$(CONFIG_CMD_IP_ROUTE_GET)  += ip-route-get.o
 obj-$(CONFIG_CMD_UBSAN)		+= ubsan.o
+obj-$(CONFIG_CMD_EXCEPT)	+= except.o
 
 UBSAN_SANITIZE_ubsan.o := y
diff --git a/commands/except.c b/commands/except.c
new file mode 100644
index 000000000000..e083cd5a1367
--- /dev/null
+++ b/commands/except.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <common.h>
+#include <command.h>
+#include <exceptions.h>
+
+struct test_except {
+	const char *cmd;
+	void(*fun)(void);
+};
+
+static noinline void throw_here(int exception)
+{
+	pr_info("\tThrowing %d\n", exception);
+	THROW(exception);
+}
+
+
+static void test_segfault(void)
+{
+	long * volatile p = NULL;
+	u32 value;
+
+	TRY {
+		value = __raw_readl(p);
+	} CATCH (DataAbortException) {
+		value = 0x42;
+	}
+
+	ENDTRY;
+
+	pr_info("value == 0x%x\n", value);
+}
+
+static void test_catch(void)
+{
+	TRY {
+		pr_info("Try\n");
+		throw_here(RuntimeError);
+	} CATCH(RuntimeError) {
+		pr_info("Catch\n");
+	}
+
+	ENDTRY;
+}
+
+static void test_nothrow(void)
+{
+	TRY {
+		pr_info("Try\n");
+	} CATCH_ALL {
+		pr_info("Catch\n");
+	}
+
+	ENDTRY;
+}
+
+static void test_nocatch(void)
+{
+	TRY {
+		pr_info("Try\n");
+		THROW(RuntimeError);
+	} CATCH(LogicError) {
+		pr_info("Catch\n");
+	}
+
+	ENDTRY;
+}
+
+static void test_finally(void)
+{
+	TRY {
+		pr_info("Throw\n");
+		throw_here(RuntimeError);
+	} CATCH (RuntimeError) {
+		pr_info("Catch\n");
+	} FINALLY {
+		pr_info("Finally\n");
+	}
+
+	ENDTRY;
+}
+
+static void test_throw_notry(void)
+{
+	throw_here(RuntimeError);
+}
+
+static const struct test_except test_except_array[] = {
+	{ .cmd = "segfault", .fun = test_segfault },
+	{ .cmd = "catch", .fun = test_catch },
+	{ .cmd = "nothrow", .fun = test_nothrow },
+	{ .cmd = "nocatch", .fun = test_nocatch },
+	{ .cmd = "finally", .fun = test_finally },
+	{ .cmd = "notry", .fun = test_throw_notry },
+	{ /* sentinel */ }
+};
+
+static int do_except(int argc, char *argv[])
+{
+	const struct test_except *test;
+	const char *arg = argv[1];
+
+	if (argc == 1)
+		arg = "segfault";
+	else if (argc != 2)
+		return COMMAND_ERROR_USAGE;
+
+	for (test = test_except_array; test->cmd; test++) {
+		if (strcmp(test->cmd, arg) == 0) {
+			test->fun();
+			return 0;
+		}
+	}
+
+	return COMMAND_ERROR_USAGE;
+}
+
+BAREBOX_CMD_HELP_START(except)
+BAREBOX_CMD_HELP_TEXT("test throwing and catching exceptions")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Functions:")
+BAREBOX_CMD_HELP_TEXT("notry catch nothrow nocatch finally segfault")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(except)
+	.cmd		= do_except,
+	BAREBOX_CMD_DESC("test throwing and catching exceptions")
+	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+	BAREBOX_CMD_HELP(cmd_except_help)
+BAREBOX_CMD_END
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH exception handling
  2020-04-01  9:31 ` [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH " Ahmad Fatoum
@ 2020-04-02 19:51   ` Roland Hieber
  2020-04-03  6:09     ` Ahmad Fatoum
  0 siblings, 1 reply; 7+ messages in thread
From: Roland Hieber @ 2020-04-02 19:51 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Wed, Apr 01, 2020 at 11:31:01AM +0200, Ahmad Fatoum wrote:
> Now with setjmp supported in barebox, we don't need to limit ourselves
> to outdated of return-value-based error propagation.
> Instead, embrace the state-of-the-art and port kazlib's ANSI C exceptions
> to barebox.
> 
> Reviewed-by: Ahmad Fatoum <ahmad@a3f.at>

Self-review …? Or supposed to be a S-o-b?

  - Roland

-- 
Roland Hieber, Pengutronix e.K.          | r.hieber@pengutronix.de     |
Steuerwalder Str. 21                     | https://www.pengutronix.de/ |
31137 Hildesheim, Germany                | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686         | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH exception handling
  2020-04-02 19:51   ` Roland Hieber
@ 2020-04-03  6:09     ` Ahmad Fatoum
  0 siblings, 0 replies; 7+ messages in thread
From: Ahmad Fatoum @ 2020-04-03  6:09 UTC (permalink / raw)
  To: Roland Hieber, Ahmad Fatoum; +Cc: barebox

On 4/2/20 9:51 PM, Roland Hieber wrote:
> On Wed, Apr 01, 2020 at 11:31:01AM +0200, Ahmad Fatoum wrote:
>> Now with setjmp supported in barebox, we don't need to limit ourselves
>> to outdated of return-value-based error propagation.
>> Instead, embrace the state-of-the-art and port kazlib's ANSI C exceptions
>> to barebox.
>>
>> Reviewed-by: Ahmad Fatoum <ahmad@a3f.at>
> 
> Self-review …? Or supposed to be a S-o-b?

Too humbled to claim co-authorship of such intricate beauty.
This is not made, but discovered, and as such transcends the DCO...

Cheers
Ahmad

> 
>   - Roland
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-04-03  6:09 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-01  9:31 [RFC PATCH 0/4] ARM: introduce sjlj structured exception handling Ahmad Fatoum
2020-04-01  9:31 ` [RFC PATCH 1/4] ARM: implement sjlj-based TRY/CATCH " Ahmad Fatoum
2020-04-02 19:51   ` Roland Hieber
2020-04-03  6:09     ` Ahmad Fatoum
2020-04-01  9:31 ` [RFC PATCH 2/4] startup: wrap barebox startup in TRY/CATCH Ahmad Fatoum
2020-04-01  9:31 ` [RFC PATCH 3/4] ARM: rethrow CPU exceptions as sjlj-exceptions Ahmad Fatoum
2020-04-01  9:31 ` [RFC PATCH 4/4] commands: implement except test command Ahmad Fatoum

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox