summaryrefslogtreecommitdiffstats
path: root/arch/sandbox/os
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sandbox/os')
-rw-r--r--arch/sandbox/os/Makefile21
-rw-r--r--arch/sandbox/os/common.c249
-rw-r--r--arch/sandbox/os/libc_malloc.c56
-rw-r--r--arch/sandbox/os/sdl.c184
-rw-r--r--arch/sandbox/os/setjmp.c180
-rw-r--r--arch/sandbox/os/tap.c2
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;
}