diff options
65 files changed, 1712 insertions, 35 deletions
diff --git a/Documentation/devel/background-execution.rst b/Documentation/devel/background-execution.rst index eadad9d898..fa4d23e6d2 100644 --- a/Documentation/devel/background-execution.rst +++ b/Documentation/devel/background-execution.rst @@ -1,10 +1,10 @@ Background execution in barebox =============================== -barebox is single-threaded and doesn't support interrupts. Nevertheless it is -sometimes desired to execute code "in the background", like for example polling -for completion of transfers or to regularly blink a heartbeat LED. For these -scenarios barebox offers the techniques described below. +barebox does not use interrupts to avoid the associated increase in complexity. +Nevertheless it is sometimes desired to execute code "in the background", +like for example polling for completion of transfers or to regularly blink a +heartbeat LED. For these scenarios barebox offers the techniques described below. Pollers ------- @@ -71,6 +71,35 @@ actually queueing a work item on a work queue. This can be called from poller code. Usually a work item is allocated by the poller and then freed either in ``work_queue.fn()`` or in ``work_queue.cancel()``. +bthreads +-------- + +barebox threads are co-operative green threads, which are scheduled whenever +``is_timeout()`` is called. This has a few implications. First of all, +bthreads are not scheduled when ``is_timeout()`` is not called. +For this and other reasons, loops polling for hardware events should always +use a timeout, which is best implemented with ``is_timeout()``. +Another thing to remember is that bthreads can be scheduled anywhere +in the middle of other device accesses whenever ``is_timeout()`` is +called. Care must be taken that a green thread doesn't access the very same device +again itself. See "slices" below on how devices can safely be accessed from +bthreads. + +The bthread interface is declared in ``include/bthread.h``. +``bthread_create()`` is used to allocate a bthread control block along with +its stack. ``bthread_wake()`` can be used to add it into the run queue. +From this moment on and until the thread terminates, the thread will be +switched to regularly as long as someone calls ``is_timeout()``. +bthreads are allowed to call ``is_timeout()``, which will arrange for +other threads to execute. + +barebox threads are planned to replace previous infrastructure, pollers +and workqueues. Poller like behavior can be easily achieved by looping +and yielding on every iteration. There's ``bthread_should_stop()``, which +can be used as condition for continuing the loop. Workqueues could be +replaced along the same line, but with mutexes protecting underlying device +access. + Slices ------ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8eba19dfae..d2e3661892 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -5,6 +5,7 @@ config ARM select HAVE_CONFIGURABLE_TEXT_BASE if !RELOCATABLE select HAVE_IMAGE_COMPRESSION select HAVE_ARCH_KASAN + select HAS_ARCH_SJLJ select ARM_OPTIMZED_STRING_FUNCTIONS if KASAN default y diff --git a/arch/arm/include/asm/setjmp.h b/arch/arm/include/asm/setjmp.h index 62bac613d6..0cee5bfdda 100644 --- a/arch/arm/include/asm/setjmp.h +++ b/arch/arm/include/asm/setjmp.h @@ -23,7 +23,9 @@ struct jmp_buf_data { typedef struct jmp_buf_data jmp_buf[1]; -int setjmp(jmp_buf jmp); -void longjmp(jmp_buf jmp, int ret); +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 /* _SETJMP_H_ */ diff --git a/arch/arm/lib32/setjmp.S b/arch/arm/lib32/setjmp.S index f0606a7f66..626d915da1 100644 --- a/arch/arm/lib32/setjmp.S +++ b/arch/arm/lib32/setjmp.S @@ -33,4 +33,12 @@ ENTRY(longjmp) 1: bx lr ENDPROC(longjmp) + +.pushsection .text.initjmp, "ax" +ENTRY(initjmp) + str a3, [a1, #32] /* stack pointer */ + str a2, [a1, #36] /* return address */ + mov a1, #0 + bx lr +ENDPROC(initjmp) .popsection diff --git a/arch/arm/lib64/setjmp.S b/arch/arm/lib64/setjmp.S index 0910e2f5a6..80be8cb0f2 100644 --- a/arch/arm/lib64/setjmp.S +++ b/arch/arm/lib64/setjmp.S @@ -36,3 +36,12 @@ ENTRY(longjmp) ret ENDPROC(longjmp) .popsection + +.pushsection .text.initjmp, "ax" +ENTRY(initjmp) + str x2, [x0, #96] /* stack pointer */ + str x1, [x0, #88] /* return address */ + mov x0, #0 + ret +ENDPROC(initjmp) +.popsection diff --git a/arch/kvx/Kconfig b/arch/kvx/Kconfig index 3327021e1a..0934440880 100644 --- a/arch/kvx/Kconfig +++ b/arch/kvx/Kconfig @@ -11,6 +11,9 @@ config KVX select ELF select FLEXIBLE_BOOTARGS select GENERIC_FIND_NEXT_BIT + select HAS_ARCH_SJLJ + select HAS_CACHE + select HAS_DMA select LIBFDT select MFD_SYSCON select OF_BAREBOX_DRIVERS diff --git a/arch/kvx/cpu/start.S b/arch/kvx/cpu/start.S index d90272c71f..a647e8a513 100644 --- a/arch/kvx/cpu/start.S +++ b/arch/kvx/cpu/start.S @@ -115,6 +115,10 @@ ENDPROC(kvx_start) wfxl $psow = $r21 ;\ ;; ;\ wfxm $psow = $r22 ;\ + ;; ;\ + make $r21 = DO_WFXL_VALUE_##__pl ;\ + ;; ;\ + wfxl $dow = $r21 ;\ ;; /** diff --git a/arch/kvx/include/asm/cache.h b/arch/kvx/include/asm/cache.h index 3be1767250..0bf3c8f06e 100644 --- a/arch/kvx/include/asm/cache.h +++ b/arch/kvx/include/asm/cache.h @@ -8,6 +8,8 @@ #include <linux/types.h> +void invalidate_dcache_range(unsigned long addr, unsigned long stop); + static inline void sync_caches_for_execution(void) { __builtin_kvx_fence(); @@ -15,4 +17,15 @@ static inline void sync_caches_for_execution(void) __builtin_kvx_barrier(); } +static inline void sync_dcache_icache(void) +{ + sync_caches_for_execution(); +} + +static inline void dcache_inval(void) +{ + __builtin_kvx_fence(); + __builtin_kvx_dinval(); +} + #endif /* __KVX_CACHE_H */ diff --git a/arch/kvx/include/asm/dma.h b/arch/kvx/include/asm/dma.h new file mode 100644 index 0000000000..a7ecf279a9 --- /dev/null +++ b/arch/kvx/include/asm/dma.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-FileCopyrightText: 2021 Yann Sionneau <ysionneau@kalray.eu>, Kalray Inc. */ + +#ifndef __ASM_DMA_H +#define __ASM_DMA_H + +#include <common.h> + +#define KVX_DDR_32BIT_RAM_WINDOW_BA (0x80000000ULL) +#define KVX_DDR_64BIT_RAM_WINDOW_BA (0x100000000ULL) +#define MAX_32BIT_ADDR (0xffffffffULL) + +#define dma_alloc dma_alloc +static inline void *dma_alloc(size_t size) +{ + return xmemalign(64, ALIGN(size, 64)); +} + +static inline void *dma_alloc_coherent(size_t size, dma_addr_t *dma_handle) +{ + BUILD_BUG_ON_MSG(1, "dma_alloc_coherent not supported: " + "MMU support is required to map uncached pages"); + return NULL; +} + +static inline void dma_free_coherent(void *mem, dma_addr_t dma_handle, + size_t size) +{ + free(mem); +} + +#endif /* __ASM_DMA_H */ diff --git a/arch/kvx/include/asm/privilege.h b/arch/kvx/include/asm/privilege.h index 36b9ade494..f183b24d42 100644 --- a/arch/kvx/include/asm/privilege.h +++ b/arch/kvx/include/asm/privilege.h @@ -114,6 +114,21 @@ #define ITO_WFXM_VALUE_PL_CUR ITO_WFXM_VALUE(PL_CUR) /** + * Debug Owner configuration + */ + +#define DO_WFXL_OWN(__field, __pl) \ + SFR_SET_VAL_WFXL(DO, __field, __pl) + +#define DO_WFXL_VALUE(__pl) (DO_WFXL_OWN(B0, __pl) | \ + DO_WFXL_OWN(B1, __pl) | \ + DO_WFXL_OWN(W0, __pl) | \ + DO_WFXL_OWN(W1, __pl)) + +#define DO_WFXL_VALUE_PL_CUR_PLUS_1 DO_WFXL_VALUE(PL_CUR_PLUS_1) +#define DO_WFXL_VALUE_PL_CUR DO_WFXL_VALUE(PL_CUR) + +/** * Misc owner configuration */ #define MO_WFXL_OWN(__field, __pl) \ @@ -160,6 +175,7 @@ PSO_WFXL_OWN(IE, __pl) | \ PSO_WFXL_OWN(HLE, __pl) | \ PSO_WFXL_OWN(SRE, __pl) | \ + PSO_WFXL_OWN(DAUS, __pl) | \ PSO_WFXL_OWN(ICE, __pl) | \ PSO_WFXL_OWN(USE, __pl) | \ PSO_WFXL_OWN(DCE, __pl) | \ diff --git a/arch/kvx/include/asm/setjmp.h b/arch/kvx/include/asm/setjmp.h new file mode 100644 index 0000000000..3c23576e6e --- /dev/null +++ b/arch/kvx/include/asm/setjmp.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-FileCopyrightText: 2021 Jules Maselbas <jmaselbas@kalray.eu>, Kalray Inc. */ + +#ifndef _ASM_KVX_SETJMP_H_ +#define _ASM_KVX_SETJMP_H_ + +#include <linux/types.h> + +typedef u64 jmp_buf[22]; + +int initjmp(jmp_buf jmp, void __attribute__((noreturn)) (*func)(void), void *stack_top); +int setjmp(jmp_buf jmp) __attribute__((returns_twice)); +void longjmp(jmp_buf jmp, int ret) __attribute__((noreturn)); + +#endif /* _ASM_KVX_SETJMP_H_ */ diff --git a/arch/kvx/include/asm/sys_arch.h b/arch/kvx/include/asm/sys_arch.h index 9df32c4e72..ce07a55986 100644 --- a/arch/kvx/include/asm/sys_arch.h +++ b/arch/kvx/include/asm/sys_arch.h @@ -11,6 +11,9 @@ #define EXCEPTION_STRIDE 0x40 #define EXCEPTION_ALIGNMENT 0x100 +#define kvx_cluster_id() ((int) \ + ((kvx_sfr_get(PCR) & KVX_SFR_PCR_CID_MASK) \ + >> KVX_SFR_PCR_CID_SHIFT)) #define KVX_SFR_START(__sfr_reg) \ (KVX_SFR_## __sfr_reg ## _SHIFT) diff --git a/arch/kvx/lib/Makefile b/arch/kvx/lib/Makefile index 6e56462daa..c730e1c23f 100644 --- a/arch/kvx/lib/Makefile +++ b/arch/kvx/lib/Makefile @@ -3,4 +3,4 @@ # Copyright (C) 2019 Kalray Inc. # -obj-y += cpuinfo.o board.o dtb.o poweroff.o bootm.o +obj-y += cpuinfo.o board.o dtb.o poweroff.o bootm.o setjmp.o cache.o dma-default.o diff --git a/arch/kvx/lib/cache.c b/arch/kvx/lib/cache.c new file mode 100644 index 0000000000..bd074de79b --- /dev/null +++ b/arch/kvx/lib/cache.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2021 Yann Sionneau <ysionneau@kalray.eu>, Kalray Inc. + +#include <asm/cache.h> +#include <linux/kernel.h> + +#define KVX_DCACHE_LINE_SIZE (64) +#define KVX_DCACHE_SIZE (128*1024) + +void invalidate_dcache_range(unsigned long addr, unsigned long stop) +{ + long size; + + addr = ALIGN_DOWN(addr, KVX_DCACHE_LINE_SIZE); + size = stop - addr; + + if (size < KVX_DCACHE_SIZE) { + while (addr < stop) { + __builtin_kvx_dinvall((void *)addr); + addr += KVX_DCACHE_LINE_SIZE; + } + } else { + __builtin_kvx_dinval(); + } +} diff --git a/arch/kvx/lib/dma-default.c b/arch/kvx/lib/dma-default.c new file mode 100644 index 0000000000..2a4144696c --- /dev/null +++ b/arch/kvx/lib/dma-default.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2021 Yann Sionneau <ysionneau@kalray.eu>, Kalray Inc. + +#include <dma.h> +#include <asm/barrier.h> +#include <asm/io.h> +#include <asm/cache.h> +#include <asm/sfr.h> +#include <asm/sys_arch.h> + +/* + * The implementation of arch should follow the following rules: + * map for_cpu for_device unmap + * TO_DEV writeback none writeback none + * FROM_DEV invalidate invalidate(*) invalidate invalidate(*) + * BIDIR writeback invalidate writeback invalidate + * + * (*) - only necessary if the CPU speculatively prefetches. + * + * (see https://lkml.org/lkml/2018/5/18/979) + */ + +void dma_sync_single_for_device(dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + /* dcache is Write-Through: no need to flush to force writeback */ + switch (dir) { + case DMA_FROM_DEVICE: + invalidate_dcache_range(addr, addr + size); + break; + case DMA_TO_DEVICE: + case DMA_BIDIRECTIONAL: + /* allow device to read buffer written by CPU */ + wmb(); + break; + default: + BUG(); + } +} + +void dma_sync_single_for_cpu(dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + /* CPU does not speculatively prefetches */ + switch (dir) { + case DMA_FROM_DEVICE: + /* invalidate has been done during map/for_device */ + case DMA_TO_DEVICE: + break; + case DMA_BIDIRECTIONAL: + invalidate_dcache_range(addr, addr + size); + break; + default: + BUG(); + } +} + +#define KVX_DDR_ALIAS_OFFSET \ + (KVX_DDR_64BIT_RAM_WINDOW_BA - KVX_DDR_32BIT_RAM_WINDOW_BA) +#define KVX_DDR_ALIAS_WINDOW \ + (KVX_DDR_64BIT_RAM_WINDOW_BA + KVX_DDR_ALIAS_OFFSET) + +/* Local smem is aliased between 0 and 16MB */ +#define KVX_SMEM_LOCAL_ALIAS 0x1000000ULL + +dma_addr_t dma_map_single(struct device_d *dev, void *ptr, size_t size, + enum dma_data_direction dir) +{ + uintptr_t addr = (uintptr_t) ptr; + + dma_sync_single_for_device(addr, size, dir); + + /* Local smem alias should never be used for dma */ + if (addr < KVX_SMEM_LOCAL_ALIAS) + return addr + (1 + kvx_cluster_id()) * KVX_SMEM_LOCAL_ALIAS; + + if (dev->dma_mask && addr <= dev->dma_mask) + return addr; + + if (addr >= KVX_DDR_ALIAS_WINDOW) + return DMA_ERROR_CODE; + + addr -= KVX_DDR_ALIAS_OFFSET; + if (dev->dma_mask && addr > dev->dma_mask) + return DMA_ERROR_CODE; + + return addr; +} + +void dma_unmap_single(struct device_d *dev, dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + dma_sync_single_for_cpu(addr, size, dir); +} diff --git a/arch/kvx/lib/dtb.c b/arch/kvx/lib/dtb.c index 54ffddaf0a..09898017c9 100644 --- a/arch/kvx/lib/dtb.c +++ b/arch/kvx/lib/dtb.c @@ -9,9 +9,6 @@ static int of_kvx_init(void) { - int ret; - struct device_node *root; - barebox_register_fdt(boot_dtb); return 0; diff --git a/arch/kvx/lib/setjmp.S b/arch/kvx/lib/setjmp.S new file mode 100644 index 0000000000..829299711d --- /dev/null +++ b/arch/kvx/lib/setjmp.S @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* SPDX-FileCopyrightText: 2021 Jules Maselbas <jmaselbas@kalray.eu>, Kalray Inc. */ + +#define REG_SIZE 8 + +#include <linux/linkage.h> + +/* jmp_buf layout: + * [0] = $ra, $sp, $cs, $r14, + * [4] = $r20, $r21, $r22, $r23, + * [8] = $r24, $r25, $r26, $r27, + * [12] = $r28, $r29, $r30, $r31, + * [16] = $r18, $r19, + * [18] = $lc, $le, $ls, xxxx + */ + +/** + * int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top); + */ +ENTRY(initjmp) + /* store $ra */ + sd (0 * REG_SIZE)[$r0] = $r1 + ;; + /* store $sp */ + sd (1 * REG_SIZE)[$r0] = $r2 + make $r0 = 0 + ret + ;; +ENDPROC(initjmp) + +/** + * int setjmp(jmp_buf jmp); + */ +ENTRY(setjmp) + sq (16 * REG_SIZE)[$r0] = $r18r19 + get $r40 = $ra + copyd $r41 = $sp + ;; + so (4 * REG_SIZE)[$r0] = $r20r21r22r23 + get $r42 = $cs + copyd $r43 = $r14 + ;; + so (0 * REG_SIZE)[$r0] = $r40r41r42r43 + get $r40 = $lc + ;; + so (8 * REG_SIZE)[$r0] = $r24r25r26r27 + get $r41 = $le + ;; + so (12 * REG_SIZE)[$r0] = $r28r29r30r31 + get $r42 = $ls + ;; + so (18 * REG_SIZE)[$r0] = $r40r41r42r43 + make $r0 = 0 + ret + ;; +ENDPROC(setjmp) + +/** + * void longjmp(jmp_buf jmp, int ret); + */ +ENTRY(longjmp) + lo $r40r41r42r43 = (0 * REG_SIZE)[$r0] + ;; + lo $r44r45r46r47 = (18 * REG_SIZE)[$r0] + set $ra = $r40 + copyd $sp = $r41 + ;; + lo $r20r21r22r23 = (4 * REG_SIZE)[$r0] + set $cs = $r42 + copyd $r14 = $r43 + ;; + lo $r24r25r26r27 = (8 * REG_SIZE)[$r0] + set $lc = $r44 + ;; + lo $r28r29r30r31 = (12 * REG_SIZE)[$r0] + set $le = $r45 + ;; + lq $r18r19 = (16 * REG_SIZE)[$r0] + set $ls = $r46 + ;; + /* According to man, if retval is equal to 0, then we should return 1 */ + maxud $r0 = $r1, 1 + ret + ;; +ENDPROC(longjmp) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 48f97c4bbf..7774abe948 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -303,6 +303,7 @@ choice config 32BIT bool "32-bit barebox" depends on CPU_SUPPORTS_32BIT_KERNEL && SYS_SUPPORTS_32BIT_KERNEL + select HAS_ARCH_SJLJ help Select this option if you want to build a 32-bit barebox. diff --git a/arch/mips/include/asm/linkage.h b/arch/mips/include/asm/linkage.h new file mode 100644 index 0000000000..14835f5139 --- /dev/null +++ b/arch/mips/include/asm/linkage.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +#ifdef __ASSEMBLY__ +#include <asm/asm.h> +#endif + +#endif diff --git a/arch/mips/include/asm/setjmp.h b/arch/mips/include/asm/setjmp.h new file mode 100644 index 0000000000..81f4d4c15f --- /dev/null +++ b/arch/mips/include/asm/setjmp.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +/* + * Define the machine-dependent type `jmp_buf'. MIPS version. + * Copyright (C) 1992-2021 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + */ + +#ifndef _MIPS_BITS_SETJMP_H +#define _MIPS_BITS_SETJMP_H 1 + +#include <asm/sgidefs.h> + +typedef struct __jmp_buf_internal_tag { + /* Program counter. */ + void *__pc; + + /* Stack pointer. */ + void *__sp; + + /* Callee-saved registers s0 through s7. */ + int __regs[8]; + + /* The frame pointer. */ + void *__fp; +} 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 /* _MIPS_BITS_SETJMP_H */ diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 0761588ae8..88a2bdbd28 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -8,6 +8,7 @@ obj-y += reloc.o obj-y += sections.o obj-y += shutdown.o obj-y += dma-default.o +obj-$(CONFIG_HAS_ARCH_SJLJ) += setjmp.o obj-$(CONFIG_MIPS_OPTIMIZED_STRING_FUNCTIONS) += memcpy.o obj-$(CONFIG_MIPS_OPTIMIZED_STRING_FUNCTIONS) += memset.o diff --git a/arch/mips/lib/setjmp.S b/arch/mips/lib/setjmp.S new file mode 100644 index 0000000000..b09a7c5529 --- /dev/null +++ b/arch/mips/lib/setjmp.S @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <asm/regdef.h> +#include <asm/asm.h> +#include <linux/linkage.h> + +/* int setjmp (jmp_buf); */ +LEAF(setjmp) + sw ra, (0 * 4)(a0) + sw sp, (1 * 4)(a0) + sw s0, (2 * 4)(a0) + sw s1, (3 * 4)(a0) + sw s2, (4 * 4)(a0) + sw s3, (5 * 4)(a0) + sw s4, (6 * 4)(a0) + sw s5, (7 * 4)(a0) + sw s6, (8 * 4)(a0) + sw s7, (9 * 4)(a0) + sw fp, (10 * 4)(a0) + move v0, zero + j ra +END(setjmp) + +/* volatile void longjmp (jmp_buf, int); */ +LEAF(longjmp) + lw ra, (0 * 4)(a0) + lw sp, (1 * 4)(a0) + lw s0, (2 * 4)(a0) + lw s1, (3 * 4)(a0) + lw s2, (4 * 4)(a0) + lw s3, (5 * 4)(a0) + lw s4, (6 * 4)(a0) + lw s5, (7 * 4)(a0) + lw s6, (8 * 4)(a0) + lw s7, (9 * 4)(a0) + lw fp, (10 * 4)(a0) + bne a1, zero, 1f + li a1, 1 +1: + move v0, a1 + j ra +END(longjmp) + +/* int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top); */ +LEAF(initjmp) + sw a1, (0 * 4)(a0) + sw a2, (1 * 4)(a0) + move v0, zero + j ra +END(initjmp) diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 32d23029d8..bd8851e4b3 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -4,6 +4,7 @@ config OPENRISC select HAS_CACHE select HAVE_CONFIGURABLE_MEMORY_LAYOUT select GENERIC_FIND_NEXT_BIT + select HAS_ARCH_SJLJ default y # not used diff --git a/arch/openrisc/cpu/start.S b/arch/openrisc/cpu/start.S index 7ac790b055..c448d3775f 100644 --- a/arch/openrisc/cpu/start.S +++ b/arch/openrisc/cpu/start.S @@ -37,10 +37,10 @@ .org 0x100 __reset: /* there is no guarantee r0 is hardwired to zero, clear it here */ - l.andi r0, r0, 0 + l.movhi r0, 0x0 /* reset stack and frame pointers */ - l.andi r1, r0, 0 - l.andi r2, r0, 0 + l.movhi r1, 0x0 + l.movhi r2, 0x0 /* set supervisor mode */ l.ori r3,r0,SPR_SR_SM diff --git a/arch/openrisc/dts/.gitignore b/arch/openrisc/dts/.gitignore new file mode 100644 index 0000000000..077903c50a --- /dev/null +++ b/arch/openrisc/dts/.gitignore @@ -0,0 +1 @@ +*dtb* diff --git a/arch/openrisc/include/asm/linkage.h b/arch/openrisc/include/asm/linkage.h new file mode 100644 index 0000000000..dbe4b4e31a --- /dev/null +++ b/arch/openrisc/include/asm/linkage.h @@ -0,0 +1,7 @@ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +#define __ALIGN .align 0 +#define __ALIGN_STR ".align 0" + +#endif diff --git a/arch/openrisc/include/asm/setjmp.h b/arch/openrisc/include/asm/setjmp.h new file mode 100644 index 0000000000..ee73306d18 --- /dev/null +++ b/arch/openrisc/include/asm/setjmp.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Define the machine-dependent type `jmp_buf'. OpenRISC version. + * Copyright (C) 2021 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + */ + +#ifndef _OR1K_BITS_SETJMP_H +#define _OR1K_BITS_SETJMP_H 1 + +typedef long int jmp_buf[13]; + +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 /* _OR1K_BITS_SETJMP_H */ diff --git a/arch/openrisc/lib/Makefile b/arch/openrisc/lib/Makefile index 62082feed0..808b09f3aa 100644 --- a/arch/openrisc/lib/Makefile +++ b/arch/openrisc/lib/Makefile @@ -5,4 +5,5 @@ obj-y += muldi3.o obj-y += lshrdi3.o obj-y += ashldi3.o obj-y += ashrdi3.o +obj-y += setjmp.o obj-$(CONFIG_BUILTIN_DTB) += dtb.o diff --git a/arch/openrisc/lib/setjmp.S b/arch/openrisc/lib/setjmp.S new file mode 100644 index 0000000000..7da3477808 --- /dev/null +++ b/arch/openrisc/lib/setjmp.S @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <linux/linkage.h> + +/* int setjmp (jmp_buf); */ +ENTRY(setjmp) + l.sw 0(r3), r1 + l.sw 4(r3), r2 + l.sw 8(r3), r9 + l.sw 12(r3), r10 + l.sw 16(r3), r14 + l.sw 20(r3), r16 + l.sw 24(r3), r18 + l.sw 28(r3), r20 + l.sw 32(r3), r22 + l.sw 36(r3), r24 + l.sw 40(r3), r26 + l.sw 44(r3), r28 + l.sw 48(r3), r30 + l.jr r9 + l.movhi r11, 0x0 +END(setjmp) + +/* volatile void longjmp (jmp_buf, int); */ +ENTRY(longjmp) + l.lwz r1, 0(r3) + l.lwz r2, 4(r3) + + /* if r4 is 0, something wrong, so set it to 1 */ + l.sfeqi r4, 0x0 + l.bnf 1f /* r4 != 0, longjmp value sensible */ + l.nop + l.ori r4, r0, 0x1 /* make nonzero */ +1: + l.lwz r9, 8(r3) + l.lwz r10, 12(r3) + l.lwz r14, 16(r3) + l.lwz r16, 20(r3) + l.lwz r18, 24(r3) + l.lwz r20, 28(r3) + l.lwz r22, 32(r3) + l.lwz r24, 36(r3) + l.lwz r26, 40(r3) + l.lwz r28, 44(r3) + l.lwz r30, 48(r3) + l.jr r9 + l.addi r11, r4, 0x0 +END(longjmp) + +/* int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top); */ +ENTRY(initjmp) + l.sw 8(r3), r4 + l.sw 0(r3), r5 + l.jr r9 + l.movhi r11, 0x0 +END(initjmp) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 90ec7b1702..376c1bf42b 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -6,6 +6,7 @@ config PPC select HAS_CACHE select GENERIC_FIND_NEXT_BIT select OFTREE + select HAS_ARCH_SJLJ default y choice diff --git a/arch/powerpc/include/asm/linkage.h b/arch/powerpc/include/asm/linkage.h new file mode 100644 index 0000000000..370eb27728 --- /dev/null +++ b/arch/powerpc/include/asm/linkage.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_POWERPC_LINKAGE_H +#define _ASM_POWERPC_LINKAGE_H + +#endif /* _ASM_POWERPC_LINKAGE_H */ diff --git a/arch/powerpc/include/asm/setjmp.h b/arch/powerpc/include/asm/setjmp.h new file mode 100644 index 0000000000..91bfcdb7f4 --- /dev/null +++ b/arch/powerpc/include/asm/setjmp.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH + * (C) Copyright 2016 Alexander Graf <agraf@suse.de> + */ + +#ifndef _SETJMP_H_ +#define _SETJMP_H_ 1 + +#include <asm/types.h> + +typedef struct __jmp_buf_internal_tag { + long int __regs[24]; +} 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 /* _SETJMP_H_ */ diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index ba2f078b62..405351c199 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -9,4 +9,4 @@ obj-$(CONFIG_CMD_BOOTM) += ppclinux.o obj-$(CONFIG_MODULES) += module.o obj-y += crtsavres.o obj-y += reloc.o - +obj-y += setjmp.o diff --git a/arch/powerpc/lib/setjmp.S b/arch/powerpc/lib/setjmp.S new file mode 100644 index 0000000000..021a57eebc --- /dev/null +++ b/arch/powerpc/lib/setjmp.S @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * This is a simple version of setjmp and longjmp for the PowerPC. + * Ian Lance Taylor, Cygnus Support, 9 Feb 1994. + */ + +#include <linux/linkage.h> +#include <asm/ppc_asm.tmpl> + +ENTRY(setjmp) + addi r3,r3,7 # align to 8 byte boundary + rlwinm r3,r3,0,0,28 + stw r1,0(r3) # offset 0 + stwu r2,4(r3) # offset 4 + stwu r13,4(r3) # offset 8 + stwu r14,4(r3) # offset 12 + stwu r15,4(r3) # offset 16 + stwu r16,4(r3) # offset 20 + stwu r17,4(r3) # offset 24 + stwu r18,4(r3) # offset 28 + stwu r19,4(r3) # offset 32 + stwu r20,4(r3) # offset 36 + stwu r21,4(r3) # offset 40 + stwu r22,4(r3) # offset 44 + stwu r23,4(r3) # offset 48 + stwu r24,4(r3) # offset 52 + stwu r25,4(r3) # offset 56 + stwu r26,4(r3) # offset 60 + stwu r27,4(r3) # offset 64 + stwu r28,4(r3) # offset 68 + stwu r29,4(r3) # offset 72 + stwu r30,4(r3) # offset 76 + stwu r31,4(r3) # offset 80 + mflr r4 + stwu r4,4(r3) # offset 84 + mfcr r4 + stwu r4,4(r3) # offset 88 + + li r3,0 + blr +END(setjmp) + +ENTRY(longjmp) + addi r3,r3,7 # align to 8 byte boundary + rlwinm r3,r3,0,0,28 + lwz r1,0(r3) # offset 0 + lwzu r2,4(r3) # offset 4 + lwzu r13,4(r3) # offset 8 + lwzu r14,4(r3) # offset 12 + lwzu r15,4(r3) # offset 16 + lwzu r16,4(r3) # offset 20 + lwzu r17,4(r3) # offset 24 + lwzu r18,4(r3) # offset 28 + lwzu r19,4(r3) # offset 32 + lwzu r20,4(r3) # offset 36 + lwzu r21,4(r3) # offset 40 + lwzu r22,4(r3) # offset 44 + lwzu r23,4(r3) # offset 48 + lwzu r24,4(r3) # offset 52 + lwzu r25,4(r3) # offset 56 + lwzu r26,4(r3) # offset 60 + lwzu r27,4(r3) # offset 64 + lwzu r28,4(r3) # offset 68 + lwzu r29,4(r3) # offset 72 + lwzu r30,4(r3) # offset 76 + lwzu r31,4(r3) # offset 80 + lwzu r5,4(r3) # offset 84 + mtlr r5 + lwzu r5,4(r3) # offset 88 + mtcrf 255,r5 + + mr. r3,r4 + bclr+ 4,2 + li r3,1 + blr +END(longjmp) + +ENTRY(initjmp) + addi r3,r3,7 # align to 8 byte boundary + rlwinm r3,r3,0,0,28 + stw r5,0(r3) # offset 0 + stwu r4,88(r3) # offset 88 + li r3,0 + blr +END(initjmp) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 16c3eecce6..c8d63f99bb 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -36,6 +36,7 @@ config ARCH_RV32I select GENERIC_LIB_ASHLDI3 select GENERIC_LIB_ASHRDI3 select GENERIC_LIB_LSHRDI3 + select HAS_ARCH_SJLJ endchoice diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h new file mode 100644 index 0000000000..9c992a88d8 --- /dev/null +++ b/arch/riscv/include/asm/asm.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2015 Regents of the University of California + */ + +#ifndef _ASM_RISCV_ASM_H +#define _ASM_RISCV_ASM_H + +#ifdef __ASSEMBLY__ +#define __ASM_STR(x) x +#else +#define __ASM_STR(x) #x +#endif + +#if __riscv_xlen == 64 +#define __REG_SEL(a, b) __ASM_STR(a) +#elif __riscv_xlen == 32 +#define __REG_SEL(a, b) __ASM_STR(b) +#else +#error "Unexpected __riscv_xlen" +#endif + +#define REG_L __REG_SEL(ld, lw) +#define REG_S __REG_SEL(sd, sw) +#define REG_SC __REG_SEL(sc.d, sc.w) +#define SZREG __REG_SEL(8, 4) +#define LGREG __REG_SEL(3, 2) + +#if __SIZEOF_POINTER__ == 8 +#ifdef __ASSEMBLY__ +#define RISCV_PTR .dword +#define RISCV_SZPTR 8 +#define RISCV_LGPTR 3 +#else +#define RISCV_PTR ".dword" +#define RISCV_SZPTR "8" +#define RISCV_LGPTR "3" +#endif +#elif __SIZEOF_POINTER__ == 4 +#ifdef __ASSEMBLY__ +#define RISCV_PTR .word +#define RISCV_SZPTR 4 +#define RISCV_LGPTR 2 +#else +#define RISCV_PTR ".word" +#define RISCV_SZPTR "4" +#define RISCV_LGPTR "2" +#endif +#else +#error "Unexpected __SIZEOF_POINTER__" +#endif + +#if (__SIZEOF_INT__ == 4) +#define RISCV_INT __ASM_STR(.word) +#define RISCV_SZINT __ASM_STR(4) +#define RISCV_LGINT __ASM_STR(2) +#else +#error "Unexpected __SIZEOF_INT__" +#endif + +#if (__SIZEOF_SHORT__ == 2) +#define RISCV_SHORT __ASM_STR(.half) +#define RISCV_SZSHORT __ASM_STR(2) +#define RISCV_LGSHORT __ASM_STR(1) +#else +#error "Unexpected __SIZEOF_SHORT__" +#endif + +#endif /* _ASM_RISCV_ASM_H */ diff --git a/arch/riscv/include/asm/linkage.h b/arch/riscv/include/asm/linkage.h new file mode 100644 index 0000000000..9e88ba23cd --- /dev/null +++ b/arch/riscv/include/asm/linkage.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2015 Regents of the University of California + */ + +#ifndef _ASM_RISCV_LINKAGE_H +#define _ASM_RISCV_LINKAGE_H + +#define __ALIGN .balign 4 +#define __ALIGN_STR ".balign 4" + +#endif /* _ASM_RISCV_LINKAGE_H */ diff --git a/arch/riscv/include/asm/setjmp.h b/arch/riscv/include/asm/setjmp.h new file mode 100644 index 0000000000..468fc4b10a --- /dev/null +++ b/arch/riscv/include/asm/setjmp.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH + * (C) Copyright 2016 Alexander Graf <agraf@suse.de> + */ + +#ifndef _SETJMP_H_ +#define _SETJMP_H_ 1 + +#include <asm/types.h> + +typedef struct __jmp_buf_internal_tag + { + /* Program counter. */ + long int __pc; + /* Callee-saved registers. */ + long int __regs[12]; + /* Stack pointer. */ + long int __sp; +} 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 /* _SETJMP_H_ */ diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index a1df0b7dc7..7fe57b0b55 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -1,3 +1,4 @@ extra-y += barebox.lds obj-y += riscv_timer.o +obj-$(CONFIG_HAS_ARCH_SJLJ) += setjmp.o longjmp.o diff --git a/arch/riscv/lib/longjmp.S b/arch/riscv/lib/longjmp.S new file mode 100644 index 0000000000..7bcb5b3049 --- /dev/null +++ b/arch/riscv/lib/longjmp.S @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/* longjmp, RISC-V version. */ + +#include <linux/linkage.h> +#include <asm/asm.h> + +ENTRY (longjmp) + REG_L ra, 0*SZREG(a0) + REG_L s0, 1*SZREG(a0) + REG_L s1, 2*SZREG(a0) + REG_L s2, 3*SZREG(a0) + REG_L s3, 4*SZREG(a0) + REG_L s4, 5*SZREG(a0) + REG_L s5, 6*SZREG(a0) + REG_L s6, 7*SZREG(a0) + REG_L s7, 8*SZREG(a0) + REG_L s8, 9*SZREG(a0) + REG_L s9, 10*SZREG(a0) + REG_L s10,11*SZREG(a0) + REG_L s11,12*SZREG(a0) + REG_L sp, 13*SZREG(a0) + + seqz a0, a1 + add a0, a0, a1 # a0 = (a1 == 0) ? 1 : a1 + ret + +END (longjmp) diff --git a/arch/riscv/lib/setjmp.S b/arch/riscv/lib/setjmp.S new file mode 100644 index 0000000000..e8ad01635d --- /dev/null +++ b/arch/riscv/lib/setjmp.S @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/* setjmp for RISC-V. */ + +#include <linux/linkage.h> +#include <asm/asm.h> + +ENTRY (setjmp) + REG_S ra, 0*SZREG(a0) + REG_S s0, 1*SZREG(a0) + REG_S s1, 2*SZREG(a0) + REG_S s2, 3*SZREG(a0) + REG_S s3, 4*SZREG(a0) + REG_S s4, 5*SZREG(a0) + REG_S s5, 6*SZREG(a0) + REG_S s6, 7*SZREG(a0) + REG_S s7, 8*SZREG(a0) + REG_S s8, 9*SZREG(a0) + REG_S s9, 10*SZREG(a0) + REG_S s10,11*SZREG(a0) + REG_S s11,12*SZREG(a0) + REG_S sp, 13*SZREG(a0) + + li a0, 0 + ret + +END (setjmp) + +ENTRY (initjmp) + REG_S a1, 0*SZREG(a0) + REG_S a2, 13*SZREG(a0) + li a0, 0 + ret + +END (initjmp) 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); +} diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 0e3e5d6187..e942c79cbd 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -36,10 +36,12 @@ config 64BIT config X86_32 def_bool y depends on !64BIT + select HAS_ARCH_SJLJ config X86_64 def_bool y depends on 64BIT + select HAS_ARCH_SJLJ endmenu diff --git a/arch/x86/include/asm/setjmp.h b/arch/x86/include/asm/setjmp.h new file mode 100644 index 0000000000..5af5e62489 --- /dev/null +++ b/arch/x86/include/asm/setjmp.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Written by H. Peter Anvin <hpa@zytor.com> + * Brought in from Linux v4.4 and modified for U-Boot + * From Linux arch/um/sys-i386/setjmp.S + */ + +#ifndef __setjmp_h +#define __setjmp_h + +#include <linux/compiler.h> + +struct jmp_buf_data { +#if defined CONFIG_X86_64 +#define __sjlj_attr + unsigned long __rip; + unsigned long __rsp; + unsigned long __rbp; + unsigned long __rbx; + unsigned long __r12; + unsigned long __r13; + unsigned long __r14; + unsigned long __r15; +#elif defined CONFIG_X86_32 +#define __sjlj_attr __attribute__((regparm(3))) + unsigned int __ebx; + unsigned int __esp; + unsigned int __ebp; + unsigned int __esi; + unsigned int __edi; + unsigned int __eip; +#else +#error "Unsupported configuration" +#endif +}; + +typedef struct jmp_buf_data jmp_buf[1]; + +int setjmp(jmp_buf jmp) __attribute__((returns_twice)) __sjlj_attr; +void longjmp(jmp_buf jmp, int ret) __attribute__((noreturn)) __sjlj_attr; + +int initjmp(jmp_buf jmp, void __attribute__((noreturn)) (*func)(void), void *stack_top) __sjlj_attr; + +#endif diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 05e43f0f2b..6a8fa7c0ff 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -4,5 +4,8 @@ obj-y += memory.o obj-y += gdt.o endif +obj-$(CONFIG_X86_32) += setjmp_32.o +obj-$(CONFIG_X86_64) += setjmp_64.o + # needed, when running via a 16 bit BIOS obj-$(CONFIG_CMD_LINUX16) += linux_start.o diff --git a/arch/x86/lib/setjmp_32.S b/arch/x86/lib/setjmp_32.S new file mode 100644 index 0000000000..38dcb68c1b --- /dev/null +++ b/arch/x86/lib/setjmp_32.S @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Written by H. Peter Anvin <hpa@zytor.com> + * Brought in from Linux v4.4 and modified for U-Boot + * From Linux arch/um/sys-i386/setjmp.S + */ + +#define _REGPARM + +#include <linux/linkage.h> + +.text +.align 8 + +/* + * The jmp_buf is assumed to contain the following, in order: + * %ebx + * %esp + * %ebp + * %esi + * %edi + * <return address> + */ + +ENTRY(setjmp) + + movl %eax, %edx + popl %ecx /* Return address, and adjust the stack */ + xorl %eax, %eax /* Return value */ + movl %ebx, (%edx) + movl %esp, 4(%edx) /* Post-return %esp! */ + pushl %ecx /* Make the call/return stack happy */ + movl %ebp, 8(%edx) + movl %esi, 12(%edx) + movl %edi, 16(%edx) + movl %ecx, 20(%edx) /* Return address */ + ret + +ENDPROC(setjmp) + +ENTRY(longjmp) + + xchgl %eax, %edx + movl (%edx), %ebx + movl 4(%edx), %esp + movl 8(%edx), %ebp + movl 12(%edx), %esi + movl 16(%edx), %edi + jmp *20(%edx) + +ENDPROC(longjmp) + +ENTRY(initjmp) + + movl %edx, 20(%eax) /* Return address */ + movl %ecx, 4(%eax) /* Post-return %esp! */ + xorl %eax, %eax /* Return value */ + ret + +ENDPROC(initjmp) diff --git a/arch/x86/lib/setjmp_64.S b/arch/x86/lib/setjmp_64.S new file mode 100644 index 0000000000..28ea576cd2 --- /dev/null +++ b/arch/x86/lib/setjmp_64.S @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Intel Corporation + * + * See arch/x86/include/asm/setjmp.h for jmp_buf format + */ + +#include <linux/linkage.h> + +.text +.align 8 + +ENTRY(setjmp) + + pop %rcx + movq %rcx, (%rdi) /* Return address */ + movq %rsp, 8(%rdi) + movq %rbp, 16(%rdi) + movq %rbx, 24(%rdi) + movq %r12, 32(%rdi) + movq %r13, 40(%rdi) + movq %r14, 48(%rdi) + movq %r15, 56(%rdi) + xorq %rax, %rax /* Direct invocation returns 0 */ + jmpq *%rcx + +ENDPROC(setjmp) + +.align 8 + +ENTRY(longjmp) + + movq (%rdi), %rcx /* Return address */ + movq 8(%rdi), %rsp + movq 16(%rdi), %rbp + movq 24(%rdi), %rbx + movq 32(%rdi), %r12 + movq 40(%rdi), %r13 + movq 48(%rdi), %r14 + movq 56(%rdi), %r15 + + movq %rsi, %rax /* Value to be returned by setjmp() */ + testq %rax, %rax /* cannot be 0 in this case */ + jnz 1f + incq %rax /* Return 1 instead */ +1: + jmpq *%rcx + +ENDPROC(longjmp) + +.align 8 + +ENTRY(initjmp) + + movq %rsi, (%rdi) /* Return address */ + movq %rdx, 8(%rdi) /* Stack top */ + xorq %rax, %rax + ret + +ENDPROC(initjmp) diff --git a/commands/Kconfig b/commands/Kconfig index 520ad4b1de..6d84c956e5 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -253,6 +253,15 @@ config CMD_POLLER is_timeout() or one of the various delay functions. The poller command prints informations about registered pollers. +config CMD_BTHREAD + tristate + prompt "bthread" + depends on BTHREAD + help + barebox threads are cooperatively-scheduled (green) threads that are running in + the background whenever code executes is_timeout() or one of the various delay + functions. The bthread command prints informations about registered bthreads. + config CMD_SLICE tristate prompt "slice" diff --git a/commands/Makefile b/commands/Makefile index 034c0e6383..cdf14a5e1d 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -129,6 +129,7 @@ obj-$(CONFIG_CMD_MMC_EXTCSD) += mmc_extcsd.o obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o obj-$(CONFIG_CMD_SEED) += seed.o obj-$(CONFIG_CMD_IP_ROUTE_GET) += ip-route-get.o +obj-$(CONFIG_CMD_BTHREAD) += bthread.o obj-$(CONFIG_CMD_UBSAN) += ubsan.o UBSAN_SANITIZE_ubsan.o := y diff --git a/commands/bthread.c b/commands/bthread.c new file mode 100644 index 0000000000..964a1044c5 --- /dev/null +++ b/commands/bthread.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 Ahmad Fatoum, Pengutronix + */ + +#include <bthread.h> +#include <errno.h> +#include <malloc.h> +#include <stdio.h> +#include <command.h> +#include <getopt.h> +#include <clock.h> + +static int bthread_time(void) +{ + uint64_t start = get_time_ns(); + int i = 0; + + /* + * How many background tasks can we have in one second? + * + * A low number here may point to problems with bthreads taking too + * much time. + */ + while (!is_timeout(start, SECOND)) + i++; + + return i; +} + +static int bthread_infinite(void *data) +{ + while (!bthread_should_stop()) + ; + + return 0; +} + +static int bthread_isolated_time(void) +{ + uint64_t start = get_time_ns(); + struct bthread *bthread; + int i = 0; + + bthread = bthread_run(bthread_infinite, NULL, "infinite"); + if (!bthread) + return -ENOMEM; + + /* main thread is the first in the run queue. Newly created bthread + * is the last. So if main_thread explicitly schedules new bthread, + * it will schedule back to main_thread afterwards and we won't + * execute any other threads in-between. + */ + + while (!is_timeout_non_interruptible(start, SECOND)) { + bthread_schedule(bthread); + i += 2; + } + + bthread_stop(bthread); + bthread_free(bthread); + + return i; +} + +static int bthread_printer(void *arg) +{ + volatile u64 start; + volatile unsigned long i = 0; + start = get_time_ns(); + + while (!bthread_should_stop()) { + if (!is_timeout_non_interruptible(start, 225 * MSECOND)) + continue; + + if ((unsigned long)arg == i++) + printf("%s yield #%lu\n", bthread_name(current), i); + start = get_time_ns(); + } + + return i; +} + +static int yields; + +static int bthread_spawner(void *arg) +{ + struct bthread *bthread[4]; + volatile u64 start; + volatile unsigned long i = 0; + int ret = 0; + int ecode; + + start = get_time_ns(); + + for (i = 0; i < ARRAY_SIZE(bthread); i++) { + bthread[i] = bthread_run(bthread_printer, (void *)(long)i, + "%s-bthread%u", bthread_name(current), i+1); + if (!bthread[i]) { + ret = -ENOMEM; + goto cleanup; + } + } + + while (!bthread_should_stop()) + ; + +cleanup: + while (i--) { + ecode = bthread_stop(bthread[i]); + bthread_free(bthread[i]); + + if (!ret && (ecode != 4 || yields < ecode)) + ret = -EIO; + } + + return ret; +} + +struct spawn { + struct bthread *bthread; + struct list_head list; +}; + +static int do_bthread(int argc, char *argv[]) +{ + LIST_HEAD(spawners); + struct spawn *spawner, *tmp; + int ret = 0; + int ecode, opt, i = 0; + bool time = false; + + while ((opt = getopt(argc, argv, "itcv")) > 0) { + switch (opt) { + case 'i': + bthread_info(); + break; + case 'c': + yields = bthread_isolated_time(); + printf("%d bthread context switches possible in 1s\n", yields); + break; + case 'v': + spawner = xzalloc(sizeof(*spawner)); + spawner->bthread = bthread_run(bthread_spawner, NULL, + "spawner%u", ++i); + if (!spawner->bthread) { + free(spawner); + ret = -ENOMEM; + goto cleanup; + } + + /* We create intermediate spawning threads to test thread + * creation and scheduling from non-main thread. + */ + list_add(&spawner->list, &spawners); + + /* fallthrough */ + case 't': + time = true; + } + } + + if (time) { + yields = bthread_time(); + printf("%d bthread yield calls in 1s\n", yields); + } + +cleanup: + list_for_each_entry_safe(spawner, tmp, &spawners, list) { + ecode = bthread_stop(spawner->bthread); + bthread_free(spawner->bthread); + if (!ret && ecode) + ret = ecode; + free(spawner); + } + + return ret; +} + +BAREBOX_CMD_HELP_START(bthread) + BAREBOX_CMD_HELP_TEXT("print info about registered barebox threads") + BAREBOX_CMD_HELP_TEXT("") + BAREBOX_CMD_HELP_TEXT("Options:") + BAREBOX_CMD_HELP_OPT ("-i", "Print information about registered bthreads") + BAREBOX_CMD_HELP_OPT ("-t", "measure how many bthreads we currently run in 1s") + BAREBOX_CMD_HELP_OPT ("-c", "count maximum context switches in 1s") + BAREBOX_CMD_HELP_OPT ("-v", "verify correct bthread operation") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(bthread) + .cmd = do_bthread, + BAREBOX_CMD_DESC("print info about registered bthreads") + BAREBOX_CMD_GROUP(CMD_GRP_MISC) + BAREBOX_CMD_HELP(cmd_bthread_help) +BAREBOX_CMD_END 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, ¤t->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, ¤t->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 diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c index b373e31be6..ced3d1f47f 100644 --- a/fs/ubifs/ubifs.c +++ b/fs/ubifs/ubifs.c @@ -25,8 +25,6 @@ #include <linux/err.h> -struct task_struct *current; - struct ubifs_priv { struct cdev *cdev; struct ubi_volume_desc *ubi; diff --git a/include/bthread.h b/include/bthread.h new file mode 100644 index 0000000000..e3871fb115 --- /dev/null +++ b/include/bthread.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 Ahmad Fatoum, Pengutronix + */ + +#ifndef __BTHREAD_H_ +#define __BTHREAD_H_ + +#include <linux/stddef.h> + +struct bthread; + +extern struct bthread *current; + +struct bthread *bthread_create(int (*threadfn)(void *), void *data, const char *namefmt, ...); +void bthread_free(struct bthread *bthread); + +void bthread_schedule(struct bthread *); +void bthread_wake(struct bthread *bthread); +void bthread_suspend(struct bthread *bthread); +int bthread_should_stop(void); +int bthread_stop(struct bthread *bthread); +void bthread_info(void); +const char *bthread_name(struct bthread *bthread); +bool bthread_is_main(struct bthread *bthread); + +/** + * bthread_run - create and wake a thread. + * @threadfn: the function to run for coming reschedule cycles + * @data: data ptr for @threadfn. + * @namefmt: printf-style name for the thread. + * + * Description: Convenient wrapper for bthread_create() followed by + * bthread_wakeup(). Returns the bthread or NULL + */ +#define bthread_run(threadfn, data, namefmt, ...) \ +({ \ + struct bthread *__b \ + = bthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \ + if (__b) \ + bthread_wake(__b); \ + __b; \ +}) + +#ifdef CONFIG_BTHREAD +void bthread_reschedule(void); +#else +static inline void bthread_reschedule(void) +{ +} +#endif + +#endif diff --git a/include/poller.h b/include/poller.h index db773265b2..371dafc6f8 100644 --- a/include/poller.h +++ b/include/poller.h @@ -39,6 +39,8 @@ static inline bool poller_async_active(struct poller_async *pa) return pa->active; } +extern int poller_active; + #ifdef CONFIG_POLLER void poller_call(void); #else diff --git a/include/sched.h b/include/sched.h new file mode 100644 index 0000000000..57be1678fd --- /dev/null +++ b/include/sched.h @@ -0,0 +1,15 @@ +/* SPDX License Identifier: GPL-2.0 */ +#ifndef __BAREBOX_SCHED_H_ +#define __BAREBOX_SCHED_H_ + +#include <bthread.h> +#include <poller.h> + +static inline void resched(void) +{ + poller_call(); + if (!IS_ENABLED(CONFIG_POLLER) || !poller_active) + bthread_reschedule(); +} + +#endif diff --git a/include/slice.h b/include/slice.h index b2d65b80cd..cf684300a8 100644 --- a/include/slice.h +++ b/include/slice.h @@ -1,6 +1,8 @@ #ifndef __SLICE_H #define __SLICE_H +#include <bthread.h> + enum slice_action { SLICE_ACQUIRE = 1, SLICE_RELEASE = -1, @@ -35,12 +37,11 @@ void command_slice_release(void); extern int poller_active; -#ifdef CONFIG_POLLER -#define assert_command_context() ({ \ - WARN_ONCE(poller_active, "%s called in poller\n", __func__); \ -}) -#else -#define assert_command_context() do { } while (0) -#endif +#define assert_command_context() do { \ + WARN_ONCE(IS_ENABLED(CONFIG_POLLER) && poller_active, \ + "%s called in poller\n", __func__); \ + WARN_ONCE(IS_ENABLED(CONFIG_BTHREAD) && !bthread_is_main(current), \ + "%s called in secondary bthread\n", __func__); \ +} while (0) -#endif /* __SLICE_H */ +#endif diff --git a/lib/readline.c b/lib/readline.c index e5370f9c7b..25aa99b95e 100644 --- a/lib/readline.c +++ b/lib/readline.c @@ -2,7 +2,7 @@ #include <readkey.h> #include <init.h> #include <libbb.h> -#include <poller.h> +#include <sched.h> #include <xfuncs.h> #include <complete.h> #include <linux/ctype.h> @@ -200,7 +200,7 @@ int readline(const char *prompt, char *buf, int len) while (1) { while (!tstc()) - poller_call(); + resched(); ichar = read_key(); |