summaryrefslogtreecommitdiffstats
path: root/src/uctx.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/uctx.h')
-rw-r--r--src/uctx.h134
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);
+}