diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2021-03-23 12:15:16 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2021-03-23 12:15:16 +0100 |
commit | ac7267ac2580590a9253549d8e129c853b6b30c7 (patch) | |
tree | 9425e0d8cfff345312da3af7e963715e776de039 /arch/sandbox | |
parent | f4d9908504e2a35afc4f39da6aa3436903039121 (diff) | |
parent | 412806653a147177cba75fb62ea633d74c5ad1ee (diff) | |
download | barebox-ac7267ac2580590a9253549d8e129c853b6b30c7.tar.gz barebox-ac7267ac2580590a9253549d8e129c853b6b30c7.tar.xz |
Merge branch 'for-next/bthreads'
Diffstat (limited to 'arch/sandbox')
-rw-r--r-- | arch/sandbox/Kconfig | 4 | ||||
-rw-r--r-- | arch/sandbox/Makefile | 5 | ||||
-rw-r--r-- | arch/sandbox/include/asm/setjmp.h | 17 | ||||
-rw-r--r-- | arch/sandbox/os/Makefile | 5 | ||||
-rw-r--r-- | arch/sandbox/os/setjmp.c | 180 |
5 files changed, 207 insertions, 4 deletions
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 1a4e3bacf6..01078bca97 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -13,6 +13,7 @@ config SANDBOX select PARTITION_DISK select ARCH_HAS_STACK_DUMP if ASAN select GENERIC_FIND_NEXT_BIT + select HAS_ARCH_SJLJ default y config ARCH_TEXT_BASE @@ -42,6 +43,9 @@ config 64BIT select ARCH_DMA_ADDR_T_64BIT select PHYS_ADDR_T_64BIT +config 32BIT + def_bool !64BIT + config SANDBOX_LINUX_I386 bool "32-bit x86 barebox" if CC_HAS_LINUX_I386_SUPPORT diff --git a/arch/sandbox/Makefile b/arch/sandbox/Makefile index ea594944e4..5fc7e227be 100644 --- a/arch/sandbox/Makefile +++ b/arch/sandbox/Makefile @@ -27,7 +27,8 @@ KBUILD_CFLAGS += -Dmalloc=barebox_malloc -Dcalloc=barebox_calloc \ -Dftruncate=barebox_ftruncate -Dasprintf=barebox_asprintf \ -Dopendir=barebox_opendir -Dreaddir=barebox_readdir \ -Dclosedir=barebox_closedir -Dreadlink=barebox_readlink \ - -Doptarg=barebox_optarg -Doptind=barebox_optind + -Doptarg=barebox_optarg -Doptind=barebox_optind \ + -Dsetjmp=barebox_setjmp -Dlongjmp=barebox_longjmp machdirs := $(patsubst %,arch/sandbox/mach-%/,$(machine-y)) @@ -64,7 +65,7 @@ endif BAREBOX_LDFLAGS += \ -Wl,-T,$(BAREBOX_LDS) \ -Wl,--whole-archive $(BAREBOX_OBJS) -Wl,--no-whole-archive \ - -lrt $(SDL_LIBS) $(FTDI1_LIBS) \ + -lrt -pthread $(SDL_LIBS) $(FTDI1_LIBS) \ $(SANITIZER_LIBS) cmd_barebox__ = $(CC) -o $@ $(BAREBOX_LDFLAGS) diff --git a/arch/sandbox/include/asm/setjmp.h b/arch/sandbox/include/asm/setjmp.h new file mode 100644 index 0000000000..a300758c3d --- /dev/null +++ b/arch/sandbox/include/asm/setjmp.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __SETJMP_H_ +#define __SETJMP_H_ + +struct jmp_buf_data { + unsigned char opaque[512] __aligned(16); +}; + +typedef struct jmp_buf_data jmp_buf[1]; + +int setjmp(jmp_buf jmp) __attribute__((returns_twice)); +void longjmp(jmp_buf jmp, int ret) __attribute__((noreturn)); + +int initjmp(jmp_buf jmp, void __attribute__((noreturn)) (*func)(void), void *stack_top); + +#endif diff --git a/arch/sandbox/os/Makefile b/arch/sandbox/os/Makefile index fb2c3cfd86..5d0c938ce6 100644 --- a/arch/sandbox/os/Makefile +++ b/arch/sandbox/os/Makefile @@ -4,7 +4,8 @@ machdirs := $(patsubst %,arch/sandbox/mach-%/,$(machine-y)) KBUILD_CPPFLAGS = $(patsubst %,-I$(srctree)/%include,$(machdirs)) -KBUILD_CPPFLAGS += -DCONFIG_MALLOC_SIZE=$(CONFIG_MALLOC_SIZE) -D_FILE_OFFSET_BITS=64 +KBUILD_CPPFLAGS += -DCONFIG_MALLOC_SIZE=$(CONFIG_MALLOC_SIZE) -D_FILE_OFFSET_BITS=64 \ + -DCONFIG_STACK_SIZE=$(CONFIG_STACK_SIZE) KBUILD_CFLAGS := -Wall @@ -14,7 +15,7 @@ ifeq ($(CONFIG_SANDBOX_LINUX_I386),y) KBUILD_CFLAGS += -m32 endif -obj-y = common.o tap.o +obj-y = common.o tap.o setjmp.o obj-$(CONFIG_MALLOC_LIBC) += libc_malloc.o CFLAGS_sdl.o = $(shell pkg-config sdl2 --cflags) diff --git a/arch/sandbox/os/setjmp.c b/arch/sandbox/os/setjmp.c new file mode 100644 index 0000000000..7f686b0fc6 --- /dev/null +++ b/arch/sandbox/os/setjmp.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * sigaltstack coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws> + * Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com> + * Copyright (C) 2012 Alex Barcelo <abarcelo@ac.upc.edu> + * Copyright (C) 2021 Ahmad Fatoum, Pengutronix + * This file is partly based on pth_mctx.c, from the GNU Portable Threads + * Copyright (c) 1999-2006 Ralf S. Engelschall <rse@engelschall.com> + */ + +/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ +#ifdef _FORTIFY_SOURCE +#undef _FORTIFY_SOURCE +#endif + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <signal.h> + +typedef sigjmp_buf _jmp_buf __attribute__((aligned((16)))); +_Static_assert(sizeof(_jmp_buf) <= 512, "sigjmp_buf size exceeds expectation"); + +/* + * Information for the signal handler (trampoline) + */ +static struct { + _jmp_buf *reenter; + void (*entry)(void); + volatile sig_atomic_t called; +} tr_state; + +/* + * "boot" function + * This is what starts the coroutine, is called from the trampoline + * (from the signal handler when it is not signal handling, read ahead + * for more information). + */ +static void __attribute__((noinline, noreturn)) +coroutine_bootstrap(void (*entry)(void)) +{ + for (;;) + entry(); +} + +/* + * This is used as the signal handler. This is called with the brand new stack + * (thanks to sigaltstack). We have to return, given that this is a signal + * handler and the sigmask and some other things are changed. + */ +static void coroutine_trampoline(int signal) +{ + /* Get the thread specific information */ + tr_state.called = 1; + + /* + * Here we have to do a bit of a ping pong between the caller, given that + * this is a signal handler and we have to do a return "soon". Then the + * caller can reestablish everything and do a siglongjmp here again. + */ + if (!sigsetjmp(*tr_state.reenter, 0)) { + return; + } + + /* + * Ok, the caller has siglongjmp'ed back to us, so now prepare + * us for the real machine state switching. We have to jump + * into another function here to get a new stack context for + * the auto variables (which have to be auto-variables + * because the start of the thread happens later). Else with + * PIC (i.e. Position Independent Code which is used when PTH + * is built as a shared library) most platforms would + * horrible core dump as experience showed. + */ + coroutine_bootstrap(tr_state.entry); +} + +int initjmp(_jmp_buf jmp, void (*func)(void), void *stack_top) +{ + struct sigaction sa; + struct sigaction osa; + stack_t ss; + stack_t oss; + sigset_t sigs; + sigset_t osigs; + + /* The way to manipulate stack is with the sigaltstack function. We + * prepare a stack, with it delivering a signal to ourselves and then + * put sigsetjmp/siglongjmp where needed. + * This has been done keeping coroutine-ucontext (from the QEMU project) + * as a model and with the pth ideas (GNU Portable Threads). + * See coroutine-ucontext for the basics of the coroutines and see + * pth_mctx.c (from the pth project) for the + * sigaltstack way of manipulating stacks. + */ + + tr_state.entry = func; + tr_state.reenter = (void *)jmp; + + /* + * Preserve the SIGUSR2 signal state, block SIGUSR2, + * and establish our signal handler. The signal will + * later transfer control onto the signal stack. + */ + sigemptyset(&sigs); + sigaddset(&sigs, SIGUSR2); + pthread_sigmask(SIG_BLOCK, &sigs, &osigs); + sa.sa_handler = coroutine_trampoline; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK; + if (sigaction(SIGUSR2, &sa, &osa) != 0) { + return -1; + } + + /* + * Set the new stack. + */ + ss.ss_sp = stack_top - CONFIG_STACK_SIZE; + ss.ss_size = CONFIG_STACK_SIZE; + ss.ss_flags = 0; + if (sigaltstack(&ss, &oss) < 0) { + return -1; + } + + /* + * Now transfer control onto the signal stack and set it up. + * It will return immediately via "return" after the sigsetjmp() + * was performed. Be careful here with race conditions. The + * signal can be delivered the first time sigsuspend() is + * called. + */ + tr_state.called = 0; + pthread_kill(pthread_self(), SIGUSR2); + sigfillset(&sigs); + sigdelset(&sigs, SIGUSR2); + while (!tr_state.called) { + sigsuspend(&sigs); + } + + /* + * Inform the system that we are back off the signal stack by + * removing the alternative signal stack. Be careful here: It + * first has to be disabled, before it can be removed. + */ + sigaltstack(NULL, &ss); + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) < 0) { + return -1; + } + sigaltstack(NULL, &ss); + if (!(oss.ss_flags & SS_DISABLE)) { + sigaltstack(&oss, NULL); + } + + /* + * Restore the old SIGUSR2 signal handler and mask + */ + sigaction(SIGUSR2, &osa, NULL); + pthread_sigmask(SIG_SETMASK, &osigs, NULL); + + /* + * jmp can now be used to enter the trampoline again, but not as a + * signal handler. Instead it's longjmp'd to directly. + */ + + return 0; +} + +int __attribute__((returns_twice)) barebox_setjmp(_jmp_buf jmp) +{ + return sigsetjmp(jmp, 0); +} + +void __attribute((noreturn)) barebox_longjmp(_jmp_buf jmp, int ret) +{ + siglongjmp(jmp, ret); +} |