diff options
Diffstat (limited to 'src/uctx.h')
-rw-r--r-- | src/uctx.h | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/uctx.h b/src/uctx.h new file mode 100644 index 0000000..03750e9 --- /dev/null +++ b/src/uctx.h @@ -0,0 +1,134 @@ +/* uctx.h - assembly stack switching + * + * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#include <libtf/fiber.h> + +#include <stdio.h> +#include <stdlib.h> +#ifdef VALGRIND +#include <valgrind/valgrind.h> +#endif + +#define STACK_GUARD 0xbad57ac4 + +struct tf_uctx { + int *stack_guard; + void *alloc; + void *current_sp; +#ifdef VALGRIND + unsigned int stack_id; +#endif + struct tf_fiber fiber; +}; + +#if defined(__i386__) + +#define STACK_GROWS_UP + +#else +#error Your architecture is not supported. Send email to timo.teras@iki.fi. +#endif + +#if defined(STACK_GROWS_UP) +static inline void *stack_pointer(void *base, size_t size) +{ + return base + size; +} + +static inline void *stack_guard(void *base, size_t size) +{ + return base; +} + +static inline void *stack_push(void **stackptr, size_t size) +{ + (*stackptr) -= size; + return (*stackptr); +} +#else +#error Stack growing direction undefined. +#endif + +static inline void stack_push_ptr(void **stackptr, void *ptr) +{ + *(void**) stack_push(stackptr, sizeof(ptr)) = ptr; +} + + +static inline +struct tf_fiber *tf_uctx_create(tf_fiber_proc fiber_main, int private_size) +{ + size_t size = TF_STACK_SIZE; + struct tf_uctx *uctx; + void *stack, *stack_base; + + /* Allocate new stack */ + stack_base = malloc(size); + if (stack_base == NULL) + return NULL; + + stack = stack_pointer(stack_base, size); + private_size += sizeof(struct tf_uctx); + + /* Construct inital frame for call the main function and if it + * happens to return, it'll jump back to tf_exit() which kills + * the fiber (cdecl calling convetion assumed) */ + uctx = stack_push(&stack, TF_ALIGN(private_size, 64)); + stack_push_ptr(&stack, uctx->fiber.data); + stack_push_ptr(&stack, &tf_exit); + stack_push_ptr(&stack, fiber_main); + uctx->current_sp = stack; + +#ifdef VALGRIND + uctx->stack_id = VALGRIND_STACK_REGISTER(stack_base, size); +#endif + uctx->alloc = stack_base; + uctx->stack_guard = stack_guard(stack_base, size); + *uctx->stack_guard = STACK_GUARD; + + return &uctx->fiber; +} + +static inline +void tf_uctx_destroy(struct tf_fiber *fiber) +{ + struct tf_uctx *uctx = container_of(fiber, struct tf_uctx, fiber); +#ifdef VALGRIND + VALGRIND_STACK_DEREGISTER(uctx->stack_id); +#endif + free(uctx->alloc); +} + +#define switch_fiber(oldspptr, newsp) \ + __asm__ __volatile__ ( \ + "push %%ebp ;" \ + "call .+5 ;" \ + "add $0x9,(%%esp) ;" \ + "mov %%esp, (%0) ;" \ + "mov %1, %%esp ;" \ + "ret ;" \ + "pop %%ebp" \ + : \ + : "a"(oldspptr), "d"(newsp) \ + : "%ebx", "%ecx", "%esi", "%edi", "memory", "cc"); + +static inline +void tf_uctx_transfer(struct tf_fiber *from, struct tf_fiber *to) +{ + + struct tf_uctx *ufrom = container_of(from, struct tf_uctx, fiber); + struct tf_uctx *uto = container_of(to, struct tf_uctx, fiber); + + /* Switch stack pointers */ + TF_BUG_ON(*ufrom->stack_guard != STACK_GUARD); + switch_fiber(&ufrom->current_sp, uto->current_sp); +} |