/* uctx.h - assembly stack switching * * Copyright (C) 2009 Timo Teräs * 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 #include #include #ifdef VALGRIND #include #else #define VALGRIND_STACK_REGISTER(stack_base, size) 0 #define VALGRIND_STACK_DEREGISTER(stack_id) #endif #define STACK_GUARD 0xbad57ac4 struct tf_uctx { int *stack_guard; void *alloc; void *current_sp; unsigned int stack_id; }; #if defined(__i386__) #define switch_fiber(prev, next) \ do { \ unsigned eax, ebx, ecx, edx, esi, edi; \ __asm__ __volatile__ ( \ "call 1f \n" \ "1: \n" \ "addl $2f-1b, (%%esp) \n" \ "push %%ebp \n" \ "movl %%esp, %[prev_sp] \n" \ "movl %[next_sp], %%esp \n" \ "pop %%ebp \n" \ "ret \n" \ "2: \n" \ : [prev_sp] "=m"(prev->current_sp), \ "=a"(eax), "=b"(ebx), "=c"(ecx), \ "=d"(edx), "=S"(esi), "=D"(edi) \ : [next_sp] "m"(next->current_sp) \ : "memory", "cc"); \ } while (0) #define STACK_GROWS_UP #else #error Your architecture is not supported. Contact 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 void tf_uctx_create_self(struct tf_uctx *uctx) { static int dummy_guard = STACK_GUARD; *uctx = (struct tf_uctx) { .stack_guard = &dummy_guard, }; } static inline void * tf_uctx_create_embedded( size_t stack_size, size_t private_size, off_t uctx_offset, void (*stack_frame_main)(void*, void*), void *main_argument) { size_t size = TF_STACK_SIZE; void *user_data; struct tf_uctx *uctx; void *stack, *stack_base; /* Allocate new stack */ stack_base = malloc(size); if (stack_base == NULL) return NULL; /* Create initial stack frame (cdecl convention) */ stack = stack_pointer(stack_base, size); user_data = stack_push(&stack, TF_ALIGN(private_size, 64)); stack_push_ptr(&stack, main_argument); stack_push_ptr(&stack, user_data); stack_push_ptr(&stack, NULL); stack_push_ptr(&stack, stack_frame_main); /* eip */ stack_push_ptr(&stack, NULL); /* ebp */ uctx = user_data + uctx_offset; *uctx = (struct tf_uctx) { .stack_guard = stack_guard(stack_base, size), .alloc = stack_base, .current_sp = stack, .stack_id = VALGRIND_STACK_REGISTER(stack_base, stack_base+size), }; *uctx->stack_guard = STACK_GUARD; return user_data; } static inline void tf_uctx_destroy(struct tf_uctx *uctx) { if (uctx->alloc != NULL) { VALGRIND_STACK_DEREGISTER(uctx->stack_id); free(uctx->alloc); } } static inline void tf_uctx_transfer(struct tf_uctx *from, struct tf_uctx *to) { /* Switch stack pointers */ TF_BUG_ON(*from->stack_guard != STACK_GUARD); switch_fiber(from, to); }