summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Kconfig13
-rw-r--r--common/Makefile1
-rw-r--r--common/bthread.c219
-rw-r--r--common/clock.c4
-rw-r--r--common/console.c6
5 files changed, 238 insertions, 5 deletions
diff --git a/common/Kconfig b/common/Kconfig
index 2170f985be..56064d12c5 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -28,6 +28,11 @@ config HAS_DMA
Drivers that depend on a DMA implementation can depend on this
config, so that you don't get a compilation error.
+config HAS_ARCH_SJLJ
+ bool
+ help
+ Architecture has support implemented for setjmp()/longjmp()/initjmp()
+
config GENERIC_GPIO
bool
@@ -958,6 +963,14 @@ config BAREBOXCRC32_TARGET
config POLLER
bool "generic polling infrastructure"
+config BTHREAD
+ bool "barebox co-operative (green) thread infrastructure"
+ depends on HAS_ARCH_SJLJ
+ help
+ barebox threads are lightweight cooperative (green) threads that are
+ scheduled within delay loops and the console idle to asynchronously
+ execute actions, like checking for link up or feeding a watchdog.
+
config STATE
bool "generic state infrastructure"
select CRC32
diff --git a/common/Makefile b/common/Makefile
index 0e0ba384c9..c0b45d263e 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_OFTREE) += oftree.o
obj-$(CONFIG_PARTITION_DISK) += partitions.o partitions/
obj-$(CONFIG_PASSWORD) += password.o
obj-$(CONFIG_POLLER) += poller.o
+obj-$(CONFIG_BTHREAD) += bthread.o
obj-$(CONFIG_RESET_SOURCE) += reset_source.o
obj-$(CONFIG_SHELL_HUSH) += hush.o
obj-$(CONFIG_SHELL_SIMPLE) += parser.o
diff --git a/common/bthread.c b/common/bthread.c
new file mode 100644
index 0000000000..c811797130
--- /dev/null
+++ b/common/bthread.c
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 Ahmad Fatoum, Pengutronix
+ *
+ * ASAN bookkeeping based on Qemu coroutine-ucontext.c
+ */
+
+/* To avoid future issues; fortify doesn't like longjmp up the call stack */
+#ifndef __NO_FORTIFY
+#define __NO_FORTIFY
+#endif
+
+#include <common.h>
+#include <bthread.h>
+#include <asm/setjmp.h>
+#include <linux/overflow.h>
+
+#if defined CONFIG_ASAN && !defined CONFIG_32BIT
+#define HAVE_FIBER_SANITIZER
+#endif
+
+static struct bthread {
+ int (*threadfn)(void *);
+ union {
+ void *data;
+ int ret;
+ };
+ char *name;
+ jmp_buf jmp_buf;
+ void *stack;
+ u32 stack_size;
+ struct list_head list;
+#ifdef HAVE_FIBER_SANITIZER
+ void *fake_stack_save;
+#endif
+ u8 awake :1;
+ u8 should_stop :1;
+ u8 has_stopped :1;
+} main_thread = {
+ .list = LIST_HEAD_INIT(main_thread.list),
+ .name = "main",
+};
+
+struct bthread *current = &main_thread;
+
+/*
+ * When using ASAN, it needs to be told when we switch stacks.
+ */
+static void start_switch_fiber(struct bthread *, bool terminate_old);
+static void finish_switch_fiber(struct bthread *);
+
+static void __noreturn bthread_trampoline(void)
+{
+ finish_switch_fiber(current);
+ bthread_reschedule();
+
+ current->ret = current->threadfn(current->data);
+
+ bthread_suspend(current);
+ current->has_stopped = true;
+
+ current = &main_thread;
+ start_switch_fiber(current, true);
+ longjmp(current->jmp_buf, 1);
+}
+
+bool bthread_is_main(struct bthread *bthread)
+{
+ return bthread == &main_thread;
+}
+
+void bthread_free(struct bthread *bthread)
+{
+ free(bthread->stack);
+ free(bthread->name);
+ free(bthread);
+}
+
+const char *bthread_name(struct bthread *bthread)
+{
+ return bthread->name;
+}
+
+struct bthread *bthread_create(int (*threadfn)(void *), void *data,
+ const char *namefmt, ...)
+{
+ struct bthread *bthread;
+ va_list ap;
+ int len;
+
+ bthread = calloc(1, sizeof(*bthread));
+ if (!bthread)
+ goto err;
+
+ bthread->stack = memalign(16, CONFIG_STACK_SIZE);
+ if (!bthread->stack)
+ goto err;
+
+ bthread->stack_size = CONFIG_STACK_SIZE;
+ bthread->threadfn = threadfn;
+ bthread->data = data;
+
+ va_start(ap, namefmt);
+ len = vasprintf(&bthread->name, namefmt, ap);
+ va_end(ap);
+
+ if (len < 0)
+ goto err;
+
+ /* set up bthread context with the new stack */
+ initjmp(bthread->jmp_buf, bthread_trampoline,
+ bthread->stack + CONFIG_STACK_SIZE);
+
+ return bthread;
+err:
+ bthread_free(bthread);
+ return NULL;
+}
+
+void bthread_wake(struct bthread *bthread)
+{
+ if (bthread->awake)
+ return;
+ list_add_tail(&bthread->list, &current->list);
+ bthread->awake = true;
+}
+
+void bthread_suspend(struct bthread *bthread)
+{
+ if (!bthread->awake)
+ return;
+ bthread->awake = false;
+ list_del(&bthread->list);
+}
+
+int bthread_stop(struct bthread *bthread)
+{
+ bthread->should_stop = true;
+
+ while (!bthread->has_stopped)
+ bthread_reschedule();
+
+ return bthread->ret;
+}
+
+int bthread_should_stop(void)
+{
+ if (bthread_is_main(current))
+ return -EINTR;
+ bthread_reschedule();
+ return current->should_stop;
+}
+
+void bthread_info(void)
+{
+ struct bthread *bthread;
+
+ printf("Registered barebox threads:\n%s\n", current->name);
+
+ list_for_each_entry(bthread, &current->list, list)
+ printf("%s\n", bthread->name);
+}
+
+void bthread_reschedule(void)
+{
+ bthread_schedule(list_next_entry(current, list));
+}
+
+void bthread_schedule(struct bthread *to)
+{
+ struct bthread *from = current;
+ int ret;
+
+ start_switch_fiber(to, false);
+
+ ret = setjmp(from->jmp_buf);
+ if (ret == 0) {
+ current = to;
+ longjmp(to->jmp_buf, 1);
+ }
+
+ finish_switch_fiber(from);
+}
+
+#ifdef HAVE_FIBER_SANITIZER
+
+void __sanitizer_start_switch_fiber(void **fake_stack_save, const void *bottom, size_t size);
+void __sanitizer_finish_switch_fiber(void *fake_stack_save, const void **bottom_old, size_t *size_old);
+
+static void finish_switch_fiber(struct bthread *bthread)
+{
+ const void *bottom_old;
+ size_t size_old;
+
+ __sanitizer_finish_switch_fiber(bthread->fake_stack_save, &bottom_old, &size_old);
+
+ if (!main_thread.stack) {
+ main_thread.stack = (void *)bottom_old;
+ main_thread.stack_size = size_old;
+ }
+}
+
+static void start_switch_fiber(struct bthread *to, bool terminate_old)
+{
+ __sanitizer_start_switch_fiber(terminate_old ? &to->fake_stack_save : NULL,
+ to->stack, to->stack_size);
+}
+
+#else
+
+static void finish_switch_fiber(struct bthread *bthread)
+{
+}
+
+static void start_switch_fiber(struct bthread *to, bool terminate_old)
+{
+}
+
+#endif
diff --git a/common/clock.c b/common/clock.c
index 7eeba88317..fa90d1a457 100644
--- a/common/clock.c
+++ b/common/clock.c
@@ -13,7 +13,7 @@
#include <init.h>
#include <linux/math64.h>
#include <clock.h>
-#include <poller.h>
+#include <sched.h>
static uint64_t time_ns;
@@ -172,7 +172,7 @@ int is_timeout(uint64_t start_ns, uint64_t time_offset_ns)
int ret = is_timeout_non_interruptible(start_ns, time_offset_ns);
if (time_offset_ns >= 100 * USECOND)
- poller_call();
+ resched();
return ret;
}
diff --git a/common/console.c b/common/console.c
index 306149c99e..8a0af75a1f 100644
--- a/common/console.c
+++ b/common/console.c
@@ -17,7 +17,7 @@
#include <clock.h>
#include <kfifo.h>
#include <module.h>
-#include <poller.h>
+#include <sched.h>
#include <ratp_bb.h>
#include <magicvar.h>
#include <globalvar.h>
@@ -579,14 +579,14 @@ int ctrlc(void)
{
int ret = 0;
+ resched();
+
if (!ctrlc_allowed)
return 0;
if (ctrlc_abort)
return 1;
- poller_call();
-
#ifdef ARCH_HAS_CTRLC
ret = arch_ctrlc();
#else