sandbox: add handler for exceptions

Add a handler for SIGILL, SIGBUS, SIGSEGV.

When an exception occurs print the program counter and the loaded
UEFI binaries and reset the system if CONFIG_SANDBOX_CRASH_RESET=y
or exit to the OS otherwise.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c
index 0d8efd8..b56fa04 100644
--- a/arch/sandbox/cpu/os.c
+++ b/arch/sandbox/cpu/os.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2011 The Chromium OS Authors.
  */
 
+#define _GNU_SOURCE
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -15,11 +17,13 @@
 #include <string.h>
 #include <termios.h>
 #include <time.h>
+#include <ucontext.h>
 #include <unistd.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <linux/compiler_attributes.h>
 #include <linux/types.h>
 
 #include <asm/getopt.h>
@@ -191,6 +195,42 @@
 	raise(SIGINT);
 }
 
+static void os_signal_handler(int sig, siginfo_t *info, void *con)
+{
+	ucontext_t __maybe_unused *context = con;
+	unsigned long pc;
+
+#if defined(__x86_64__)
+	pc = context->uc_mcontext.gregs[REG_RIP];
+#elif defined(__aarch64__)
+	pc = context->uc_mcontext.pc;
+#elif defined(__riscv)
+	pc = context->uc_mcontext.__gregs[REG_PC];
+#else
+	const char msg[] =
+		"\nUnsupported architecture, cannot read program counter\n";
+
+	os_write(1, msg, sizeof(msg));
+	pc = 0;
+#endif
+
+	os_signal_action(sig, pc);
+}
+
+int os_setup_signal_handlers(void)
+{
+	struct sigaction act;
+
+	act.sa_sigaction = os_signal_handler;
+	sigemptyset(&act.sa_mask);
+	act.sa_flags = SA_SIGINFO | SA_NODEFER;
+	if (sigaction(SIGILL, &act, NULL) ||
+	    sigaction(SIGBUS, &act, NULL) ||
+	    sigaction(SIGSEGV, &act, NULL))
+		return -1;
+	return 0;
+}
+
 /* Put tty into raw mode so <tab> and <ctrl+c> work */
 void os_tty_raw(int fd, bool allow_sigs)
 {