diff options
Diffstat (limited to 'arch/sandbox/os')
-rw-r--r-- | arch/sandbox/os/Makefile | 21 | ||||
-rw-r--r-- | arch/sandbox/os/common.c | 249 | ||||
-rw-r--r-- | arch/sandbox/os/libc_malloc.c | 56 | ||||
-rw-r--r-- | arch/sandbox/os/sdl.c | 184 | ||||
-rw-r--r-- | arch/sandbox/os/setjmp.c | 180 | ||||
-rw-r--r-- | arch/sandbox/os/tap.c | 2 |
6 files changed, 585 insertions, 107 deletions
diff --git a/arch/sandbox/os/Makefile b/arch/sandbox/os/Makefile index ed921443e0..055ce1a316 100644 --- a/arch/sandbox/os/Makefile +++ b/arch/sandbox/os/Makefile @@ -1,18 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only + machine-y := sandbox machdirs := $(patsubst %,arch/sandbox/mach-%/,$(machine-y)) KBUILD_CPPFLAGS = $(patsubst %,-I$(srctree)/%include,$(machdirs)) -KBUILD_CPPFLAGS += -DCONFIG_MALLOC_SIZE=$(CONFIG_MALLOC_SIZE) +KBUILD_CPPFLAGS += -DCONFIG_MALLOC_SIZE=$(CONFIG_MALLOC_SIZE) -D_FILE_OFFSET_BITS=64 \ + -DCONFIG_STACK_SIZE=$(CONFIG_STACK_SIZE) + +KBUILD_CFLAGS := -Wall -KBUILD_CFLAGS += -Wall NOSTDINC_FLAGS := -obj-y = common.o tap.o +ifeq ($(CONFIG_SANDBOX_LINUX_I386),y) +KBUILD_CFLAGS += -m32 +endif + +obj-y = common.o tap.o setjmp.o +obj-$(CONFIG_MALLOC_LIBC) += libc_malloc.o -CFLAGS_sdl.o = $(shell pkg-config sdl --cflags) -obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o +CFLAGS_sdl.o = $(shell $(PKG_CONFIG) sdl2 --cflags) +obj-$(CONFIG_SDL) += sdl.o -CFLAGS_ftdi.o = $(shell pkg-config libftdi1 --cflags) +CFLAGS_ftdi.o = $(shell $(PKG_CONFIG) libftdi1 --cflags) obj-$(CONFIG_GPIO_LIBFTDI1) += ftdi.o diff --git a/arch/sandbox/os/common.c b/arch/sandbox/os/common.c index 382a923040..3446074f99 100644 --- a/arch/sandbox/os/common.c +++ b/arch/sandbox/os/common.c @@ -19,6 +19,7 @@ * These are host includes. Never include any barebox header * files here... */ +#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <stdint.h> @@ -38,12 +39,17 @@ #include <sys/wait.h> #include <sys/ioctl.h> #include <linux/fs.h> +#include <sys/time.h> /* * ...except the ones needed to connect with barebox */ #include <mach/linux.h> #include <mach/hostfile.h> +#define DELETED_OFFSET (sizeof(" (deleted)") - 1) + +void __sanitizer_set_death_callback(void (*callback)(void)); + int sdl_xres; int sdl_yres; @@ -69,6 +75,15 @@ static void cookmode(void) tcsetattr(0, TCSANOW, &term_orig); } +static char *stickypage_path; + +static void prepare_exit(void) +{ + cookmode(); + if (stickypage_path) + remove(stickypage_path); +} + int linux_tstc(int fd) { struct timeval tv = { @@ -116,13 +131,58 @@ uint64_t linux_get_time(void) void __attribute__((noreturn)) linux_exit(void) { - cookmode(); + prepare_exit(); exit(0); } +static char **saved_argv; + +static int selfpath(char *buf, size_t len) +{ + int ret; + + /* we must follow the symlink, so we can exec an updated executable */ + ret = readlink("/proc/self/exe", buf, len - 1); + if (ret < 0) + return ret; + + if (0 < ret && ret < len - 1) + buf[ret] = '\0'; + + return ret; +} + +void linux_reexec(void) +{ + char buf[4097]; + ssize_t ret; + + cookmode(); + + /* we must follow the symlink, so we can exec an updated executable */ + ret = selfpath(buf, sizeof(buf)); + if (ret > 0) { + execv(buf, saved_argv); + if (!strcmp(&buf[ret - DELETED_OFFSET], " (deleted)")) { + printf("barebox image on disk changed. Loading new.\n"); + buf[ret - DELETED_OFFSET] = '\0'; + execv(buf, saved_argv); + } + } + + printf("exec(%s) failed: %d\n", buf, errno); + /* falls through to generic hang() */ +} + +void linux_hang(void) +{ + prepare_exit(); + /* falls through to generic hang() */ +} + int linux_open(const char *filename, int readwrite) { - return open(filename, readwrite ? O_RDWR : O_RDONLY); + return open(filename, (readwrite ? O_RDWR : O_RDONLY) | O_CLOEXEC); } int linux_read(int fd, void *buf, size_t count) @@ -181,7 +241,7 @@ ssize_t linux_write(int fd, const void *buf, size_t count) return write(fd, buf, count); } -off_t linux_lseek(int fd, off_t offset) +loff_t linux_lseek(int fd, loff_t offset) { return lseek(fd, offset, SEEK_SET); } @@ -207,31 +267,60 @@ int linux_execve(const char * filename, char *const argv[], char *const envp[]) } } +static void linux_watchdog(int signo) +{ + linux_reexec(); + _exit(0); +} + +int linux_watchdog_set_timeout(unsigned int timeout) +{ + static int signal_handler_installed; + + if (!signal_handler_installed) { + struct sigaction sact = { + .sa_flags = SA_NODEFER, .sa_handler = linux_watchdog + }; + + sigemptyset(&sact.sa_mask); + sigaction(SIGALRM, &sact, NULL); + signal_handler_installed = 1; + } + + return alarm(timeout); +} + extern void start_barebox(void); extern void mem_malloc_init(void *start, void *end); -static int add_image(char *str, char *devname_template, int *devname_number) +extern char * strsep_unescaped(char **s, const char *ct); + +static int add_image(const char *_str, char *devname_template, int *devname_number) { - struct hf_info *hf = malloc(sizeof(struct hf_info)); - char *filename, *devname; + struct hf_info *hf = calloc(1, sizeof(struct hf_info)); + char *str, *filename, *devname; char tmp[16]; - int readonly = 0; - struct stat s; char *opt; - int fd, ret; + int ret; if (!hf) return -1; - filename = strtok(str, ","); - while ((opt = strtok(NULL, ","))) { + str = strdup(_str); + + filename = strsep_unescaped(&str, ","); + while ((opt = strsep_unescaped(&str, ","))) { if (!strcmp(opt, "ro")) - readonly = 1; + hf->is_readonly = 1; + if (!strcmp(opt, "cdev")) + hf->is_cdev = 1; + if (!strcmp(opt, "blkdev")) + hf->is_blockdev = 1; } /* parses: "devname=filename" */ - devname = strtok(filename, "="); - filename = strtok(NULL, "="); + devname = strsep_unescaped(&filename, "="); + filename = strsep_unescaped(&filename, "="); if (!filename) { filename = devname; snprintf(tmp, sizeof(tmp), @@ -239,13 +328,86 @@ static int add_image(char *str, char *devname_template, int *devname_number) devname = tmp; } - printf("add %s backed by file %s%s\n", devname, - filename, readonly ? "(ro)" : ""); - - fd = open(filename, readonly ? O_RDONLY : O_RDWR); - hf->fd = fd; hf->filename = filename; + hf->devname = strdup(devname); + ret = barebox_register_filedev(hf); + if (ret) + free(hf); + + return ret; +} + +extern uint8_t stickypage[4096]; + +char *linux_get_stickypage_path(void) +{ + size_t nwritten; + ssize_t ret; + int fd; + + ret = asprintf(&stickypage_path, "%s/barebox/stickypage.%lu", + getenv("XDG_RUNTIME_DIR") ?: "/run", (long)getpid()); + if (ret < 0) + goto err_asprintf; + + ret = mkdir(dirname(stickypage_path), 0755); + if (ret < 0 && errno != EEXIST) { + perror("mkdir"); + goto err_creat; + } + + stickypage_path[strlen(stickypage_path)] = '/'; + + fd = open(stickypage_path, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); + if (fd < 0) { + if (errno == EEXIST) + return stickypage_path; + + perror("open"); + goto err_creat; + } + + for (nwritten = 0; nwritten < sizeof(stickypage); ) { + ret = write(fd, &stickypage[nwritten], sizeof(stickypage) - nwritten); + if (ret < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + perror("write"); + goto err_write; + } + + nwritten += ret; + } + + close(fd); + + return stickypage_path; + +err_write: + close(fd); +err_creat: + free(stickypage_path); +err_asprintf: + stickypage_path = NULL; + + return NULL; +} + +int linux_open_hostfile(struct hf_info *hf) +{ + char *buf = NULL; + struct stat s; + int fd = -1; + + printf("add %s %sbacked by file %s%s\n", hf->devname, + hf->filename ? "" : "initially un", hf->filename ?: "", + hf->is_readonly ? "(ro)" : ""); + + if (!hf->filename) + return -ENOENT; + + fd = hf->fd = open(hf->filename, (hf->is_readonly ? O_RDONLY : O_RDWR) | O_CLOEXEC); if (fd < 0) { perror("open"); goto err_out; @@ -256,30 +418,41 @@ static int add_image(char *str, char *devname_template, int *devname_number) goto err_out; } + hf->base = (unsigned long)MAP_FAILED; hf->size = s.st_size; - hf->devname = strdup(devname); if (S_ISBLK(s.st_mode)) { if (ioctl(fd, BLKGETSIZE64, &hf->size) == -1) { perror("ioctl"); goto err_out; } + if (!hf->is_cdev) + hf->is_blockdev = 1; + } + if (hf->size <= SIZE_MAX) { + hf->base = (unsigned long)mmap(NULL, hf->size, + PROT_READ | (hf->is_readonly ? 0 : PROT_WRITE), + MAP_SHARED, fd, 0); + + if (hf->base == (unsigned long)MAP_FAILED) + printf("warning: mmapping %s failed: %s\n", + hf->filename, strerror(errno)); + } else { + printf("warning: %s: contiguous map failed\n", hf->filename); + } + + if (hf->is_blockdev && hf->size % 512 != 0) { + printf("warning: registering %s as block device failed: invalid block size\n", + hf->filename); + return -EINVAL; } - hf->base = (unsigned long)mmap(NULL, hf->size, - PROT_READ | (readonly ? 0 : PROT_WRITE), - MAP_SHARED, fd, 0); - if ((void *)hf->base == MAP_FAILED) - printf("warning: mmapping %s failed: %s\n", filename, strerror(errno)); - ret = barebox_register_filedev(hf); - if (ret) - goto err_out; return 0; err_out: - if (fd > 0) + if (fd >= 0) close(fd); - free(hf); + free(buf); return -1; } @@ -289,7 +462,7 @@ static int add_dtb(const char *file) void *dtb = NULL; int fd; - fd = open(file, O_RDONLY); + fd = open(file, O_RDONLY | O_CLOEXEC); if (fd < 0) { perror("open"); goto err_out; @@ -345,6 +518,10 @@ int main(int argc, char *argv[]) int fdno = 0, envno = 0, option_index = 0; char *aux; +#ifdef CONFIG_ASAN + __sanitizer_set_death_callback(prepare_exit); +#endif + while (1) { option_index = 0; opt = getopt_long(argc, argv, optstring, @@ -382,6 +559,8 @@ int main(int argc, char *argv[]) } } + saved_argv = argv; + ram = malloc(malloc_size); if (!ram) { printf("unable to get malloc space\n"); @@ -416,7 +595,7 @@ int main(int argc, char *argv[]) exit(1); break; case 'O': - fd = open(optarg, O_WRONLY); + fd = open(optarg, O_WRONLY | O_CLOEXEC); if (fd < 0) { perror("open"); exit(1); @@ -425,7 +604,7 @@ int main(int argc, char *argv[]) barebox_register_console(-1, fd); break; case 'I': - fd = open(optarg, O_RDWR); + fd = open(optarg, O_RDWR | O_CLOEXEC); if (fd < 0) { perror("open"); exit(1); @@ -441,7 +620,7 @@ int main(int argc, char *argv[]) } /* open stdout file */ - fd = open(aux + 1, O_WRONLY); + fd = open(aux + 1, O_WRONLY | O_CLOEXEC); if (fd < 0) { perror("open stdout"); exit(1); @@ -449,7 +628,7 @@ int main(int argc, char *argv[]) /* open stdin file */ aux = strndup(optarg, aux - optarg); - fd2 = open(aux, O_RDWR); + fd2 = open(aux, O_RDWR | O_CLOEXEC); if (fd2 < 0) { perror("open stdin"); exit(1); diff --git a/arch/sandbox/os/libc_malloc.c b/arch/sandbox/os/libc_malloc.c new file mode 100644 index 0000000000..975c41b0ec --- /dev/null +++ b/arch/sandbox/os/libc_malloc.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Ahmad Fatoum <a.fatoum@pengutronix.de> + */ + +#include <stdlib.h> +#include <malloc.h> + +#define BAREBOX_ENOMEM 12 +extern int barebox_errno; + +void barebox_malloc_stats(void) +{ +} + +void *barebox_memalign(size_t alignment, size_t bytes) +{ + void *mem = memalign(alignment, bytes); + if (!mem) + barebox_errno = BAREBOX_ENOMEM; + + return mem; +} + +void *barebox_malloc(size_t size) +{ + + void *mem = malloc(size); + if (!mem) + barebox_errno = BAREBOX_ENOMEM; + + return mem; +} + +void barebox_free(void *ptr) +{ + free(ptr); +} + +void *barebox_realloc(void *ptr, size_t size) +{ + void *mem = realloc(ptr, size); + if (!mem) + barebox_errno = BAREBOX_ENOMEM; + + return mem; +} + +void *barebox_calloc(size_t n, size_t elem_size) +{ + void *mem = calloc(n, elem_size); + if (!mem) + barebox_errno = BAREBOX_ENOMEM; + + return mem; +} diff --git a/arch/sandbox/os/sdl.c b/arch/sandbox/os/sdl.c index 9a35279eb7..13178abfc0 100644 --- a/arch/sandbox/os/sdl.c +++ b/arch/sandbox/os/sdl.c @@ -1,103 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * GPL v2 + * Copyright (c) 2021 Ahmad Fatoum */ #include <stdio.h> +#include <stdbool.h> #include <SDL.h> -#include <time.h> -#include <signal.h> #include <mach/linux.h> -#include <unistd.h> -#include <pthread.h> -struct fb_bitfield { - uint32_t offset; /* beginning of bitfield */ - uint32_t length; /* length of bitfield */ - uint32_t msb_right; /* != 0 : Most significant bit is */ - /* right */ -}; +static void sdl_perror(const char *what) +{ + printf("SDL: Could not %s: %s.\n", what, SDL_GetError()); +} -static SDL_Surface *real_screen; -static void *buffer = NULL; -pthread_t th; +static struct sdl_fb_info info; +static SDL_atomic_t shutdown; +SDL_Window *window; -static void sdl_copy_buffer(SDL_Surface *screen) +static int scanout(void *ptr) { - if (SDL_MUSTLOCK(screen)) { - if (SDL_LockSurface(screen) < 0) - return; + SDL_Renderer *renderer; + SDL_Surface *surface; + SDL_Texture *texture; + void *buf = info.screen_base; + int ret = -1; + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + sdl_perror("create renderer"); + return -1; } - memcpy(screen->pixels, buffer, screen->pitch * screen->h); + surface = SDL_CreateRGBSurface(0, info.xres, info.yres, info.bpp, + info.rmask, info.gmask, info.bmask, info.amask); + if (!surface) { + sdl_perror("create surface"); + goto destroy_renderer; + } - if(SDL_MUSTLOCK(screen)) - SDL_UnlockSurface(screen); -} + texture = SDL_CreateTextureFromSurface(renderer, surface); + if (!texture) { + sdl_perror("create texture"); + goto free_surface; + } -static void *threadStart(void *ptr) -{ - while (1) { - usleep(1000 * 100); + while (!SDL_AtomicGet(&shutdown)) { + SDL_Delay(100); - sdl_copy_buffer(real_screen); - SDL_Flip(real_screen); + SDL_UpdateTexture(texture, NULL, buf, surface->pitch); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); } - return 0; -} + ret = 0; -void sdl_start_timer(void) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_create(&th, &attr, threadStart, NULL); -} + SDL_DestroyTexture(texture); +free_surface: + SDL_FreeSurface(surface); +destroy_renderer: + SDL_DestroyRenderer(renderer); -void sdl_stop_timer(void) -{ - pthread_cancel(th); + return ret; } -void sdl_get_bitfield_rgba(struct fb_bitfield *r, struct fb_bitfield *g, - struct fb_bitfield *b, struct fb_bitfield *a) +static SDL_Thread *thread; + +void sdl_video_close(void) { - SDL_Surface *screen = real_screen; - - r->length = 8 - screen->format->Rloss; - r->offset = screen->format->Rshift; - g->length = 8 - screen->format->Gloss; - g->offset = screen->format->Gshift; - b->length = 8 - screen->format->Bloss; - b->offset = screen->format->Bshift; - a->length = 8 - screen->format->Aloss; - a->offset = screen->format->Ashift; + SDL_AtomicSet(&shutdown, true); /* implies full memory barrier */ + SDL_WaitThread(thread, NULL); + SDL_AtomicSet(&shutdown, false); + SDL_DestroyWindow(window); + SDL_QuitSubSystem(SDL_INIT_VIDEO); } -int sdl_open(int xres, int yres, int bpp, void* buf) +int sdl_video_open(const struct sdl_fb_info *_info) { - int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; + info = *_info; - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0) { - printf("Could not initialize SDL: %s.\n", SDL_GetError()); + if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { + sdl_perror("initialize SDL Video"); return -1; } - real_screen = SDL_SetVideoMode(xres, yres, bpp, flags); - if (!real_screen) { - sdl_close(); - fprintf(stderr, "Couldn't create renderer: %s\n", SDL_GetError()); + window = SDL_CreateWindow("barebox", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + info.xres, info.yres, 0); + if (!window) { + sdl_perror("create window"); + goto quit_subsystem; + } + + /* All scanout needs to happen in the same thread, because not all + * graphic backends are thread-safe. The window is created in the main + * thread though to work around libEGL crashing with SDL_VIDEODRIVER=wayland + */ + + thread = SDL_CreateThread(scanout, "video-scanout", NULL); + if (!thread) { + sdl_perror("start scanout thread"); + goto destroy_window; + } + + return 0; + +destroy_window: + SDL_DestroyWindow(window); +quit_subsystem: + SDL_QuitSubSystem(SDL_INIT_VIDEO); + + return -1; +} + +static SDL_AudioDeviceID dev; + +int sdl_sound_init(unsigned sample_rate) +{ + SDL_AudioSpec audiospec = { + .freq = sample_rate, + .format = AUDIO_S16, + .channels = 1, + .samples = 2048, + }; + + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + sdl_perror("initialize SDL Audio"); return -1; } - buffer = buf; + dev = SDL_OpenAudioDevice(NULL, 0, &audiospec, NULL, 0); + if (!dev) { + sdl_perror("initialize open audio device"); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + return -1; + } + SDL_PauseAudioDevice(dev, 0); return 0; } -void sdl_close(void) +void sdl_sound_close(void) +{ + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +int sdl_sound_play(const void *data, unsigned nsamples) +{ + /* core sound support handles all the queueing for us */ + SDL_ClearQueuedAudio(dev); + return SDL_QueueAudio(dev, data, nsamples * sizeof(uint16_t)); +} + +void sdl_sound_stop(void) { - sdl_stop_timer(); - SDL_Quit(); + SDL_ClearQueuedAudio(dev); } 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); +} diff --git a/arch/sandbox/os/tap.c b/arch/sandbox/os/tap.c index 72b7fbb5ac..83b97ffd49 100644 --- a/arch/sandbox/os/tap.c +++ b/arch/sandbox/os/tap.c @@ -30,7 +30,7 @@ int tap_alloc(const char *dev) struct ifreq ifr; int fd, err; - if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { + if ((fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC)) < 0) { perror("could not open /dev/net/tun"); return -1; } |