diff --git a/arch/x86/defs.h b/arch/x86/defs.h new file mode 100644 index 0000000..65a4deb --- /dev/null +++ b/arch/x86/defs.h @@ -0,0 +1,18 @@ +#ifndef __ARCH_X86_DEFS_H +#define __ARCH_X86_DEFS_H + +#define OFFSET_REG_GS 20 +#define OFFSET_REG_FS 24 +#define OFFSET_REG_ES 28 +#define OFFSET_REG_DS 32 +#define OFFSET_REG_EDI 36 +#define OFFSET_REG_ESI 40 +#define OFFSET_REG_EBP 44 +#define OFFSET_REG_ESP 48 +#define OFFSET_REG_EBX 52 +#define OFFSET_REG_EDX 56 +#define OFFSET_REG_ECX 60 +#define OFFSET_REG_EAX 64 +#define OFFSET_REG_EIP 76 + +#endif diff --git a/arch/x86/getcontext.S b/arch/x86/getcontext.S new file mode 100644 index 0000000..99f9a3f --- /dev/null +++ b/arch/x86/getcontext.S @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 William Pitcock + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include "defs.h" + +.globl __getcontext; +__getcontext: + /* load address of the ucontext structure */ + movl 4(%esp), %eax + + /* EAX is not a preserved register */ + movl $0, OFFSET_REG_EAX(%eax) + + /* copy all of the current registers into the ucontext structure */ + movl %ecx, OFFSET_REG_ECX(%eax) + movl %ebx, OFFSET_REG_EBX(%eax) + movl %edx, OFFSET_REG_EDX(%eax) + movl %edi, OFFSET_REG_EDI(%eax) + movl %esi, OFFSET_REG_ESI(%eax) + movl %ebp, OFFSET_REG_EBP(%eax) + + /* the first argument on the stack is the jump target (%eip), so we store it in the EIP + register in the ucontext structure. */ + movl (%esp), %ecx + movl %ecx, OFFSET_REG_EIP(%eax) + + /* take the stack pointer address (%esp) offsetting by 4 to skip over the jump target. */ + leal 4(%esp), %ecx + movl %ecx, OFFSET_REG_ESP(%eax) + + /* finally, save the FS segment register */ + xorl %ecx, %ecx + movw %fs, %cx + movl %ecx, OFFSET_REG_FS(%eax) + + /* we need to restore %ecx because we clobbered it earlier */ + movl OFFSET_REG_ECX(%eax), %ecx + + /* we're all done here, return 0 */ + xorl %eax, %eax + ret + + +.weak getcontext; +getcontext = __getcontext; diff --git a/arch/x86/makecontext.c b/arch/x86/makecontext.c new file mode 100644 index 0000000..7a9c436 --- /dev/null +++ b/arch/x86/makecontext.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 William Pitcock + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + + +extern void __start_context(void); + + +void +__makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) +{ + greg_t *sp, *argp; + va_list va; + int i; + unsigned int uc_link; + + uc_link = (argc > 6 ? argc - 6 : 0) + 1; + + sp = (greg_t *) ((uintptr_t) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size); + sp -= uc_link; + sp = (greg_t *) (((uintptr_t) sp & -16L) - 8); + + ucp->uc_mcontext.gregs[REG_EIP] = (uintptr_t) func; + ucp->uc_mcontext.gregs[REG_EBX] = (uintptr_t) argc; + ucp->uc_mcontext.gregs[REG_ESP] = (uintptr_t) sp; + + argp = sp; + *argp++ = (uintptr_t) &__start_context; + *argp++ = (uintptr_t) ucp->uc_link; + + va_start(va, argc); + + for (i = 0; i < argc; i++) + *argp++ = va_arg (va, greg_t); + + va_end(va); +} + + +extern __typeof(__makecontext) makecontext __attribute__((weak, __alias__("__makecontext"))); diff --git a/arch/x86/setcontext.S b/arch/x86/setcontext.S new file mode 100644 index 0000000..1e7cf5b --- /dev/null +++ b/arch/x86/setcontext.S @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 William Pitcock + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include "defs.h" + +.globl __setcontext; +__setcontext: + /* load address of the ucontext structure */ + movl 4(%esp), %eax + + /* set up the FS segment register */ + movl OFFSET_REG_FS(%eax), %ecx + movw %cx, %fs + + /* fetch the new EIP */ + movl OFFSET_REG_EIP(%eax), %ecx + + /* set up the new stack pointer */ + movl OFFSET_REG_ESP(%eax), %esp + + /* push the return address onto the stack */ + pushl %ecx + + /* set all of the registers */ + movl OFFSET_REG_EBX(%eax), %ebx + movl OFFSET_REG_ECX(%eax), %ecx + movl OFFSET_REG_EDX(%eax), %edx + movl OFFSET_REG_EBP(%eax), %ebp + movl OFFSET_REG_EDI(%eax), %edi + movl OFFSET_REG_ESI(%eax), %esi + movl OFFSET_REG_EAX(%eax), %eax + + ret + + +.weak setcontext; +setcontext = __setcontext; diff --git a/arch/x86/startcontext.S b/arch/x86/startcontext.S new file mode 100644 index 0000000..165804d --- /dev/null +++ b/arch/x86/startcontext.S @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 William Pitcock + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +.globl __start_context; +__start_context: + /* get the proper context into position and test for NULL */ + leal (%esp,%ebx,4), %esp + cmpl $0, (%esp) + je hosed + + /* call setcontext to switch to the linked context */ + call __setcontext + movl %eax, (%esp) + +hosed: + /* we are returning into a null context, it seems, so maybe we should exit */ + call exit@plt + + /* something is really hosed, call hlt to force termination */ + hlt diff --git a/arch/x86/swapcontext.S b/arch/x86/swapcontext.S new file mode 100644 index 0000000..8fbafbf --- /dev/null +++ b/arch/x86/swapcontext.S @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 William Pitcock + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include "defs.h" + +.globl __swapcontext; +__swapcontext: + /* load address of the ucontext structure */ + movl 4(%esp), %eax + + /* EAX is not a preserved register */ + movl $0, OFFSET_REG_EAX(%eax) + + /* copy all of the current registers into the ucontext structure */ + movl %ecx, OFFSET_REG_ECX(%eax) + movl %ebx, OFFSET_REG_EBX(%eax) + movl %edx, OFFSET_REG_EDX(%eax) + movl %edi, OFFSET_REG_EDI(%eax) + movl %esi, OFFSET_REG_ESI(%eax) + movl %ebp, OFFSET_REG_EBP(%eax) + + /* the first argument on the stack is the jump target (%eip), so we store it in the EIP + register in the ucontext structure. */ + movl (%esp), %ecx + movl %ecx, OFFSET_REG_EIP(%eax) + + /* take the stack pointer address (%esp) offsetting by 4 to skip over the jump target. */ + leal 4(%esp), %ecx + movl %ecx, OFFSET_REG_ESP(%eax) + + /* finally, save the FS segment register */ + xorl %ecx, %ecx + movw %fs, %cx + movl %ecx, OFFSET_REG_FS(%eax) + + /* load address of the ucontext structure */ + movl 8(%esp), %eax + + /* set up the FS segment register */ + movl OFFSET_REG_FS(%eax), %ecx + movw %cx, %fs + + /* fetch the new EIP */ + movl OFFSET_REG_EIP(%eax), %ecx + + /* set up the new stack pointer */ + movl OFFSET_REG_ESP(%eax), %esp + + /* push the return address onto the stack */ + pushl %ecx + + /* set all of the registers */ + movl OFFSET_REG_EBX(%eax), %ebx + movl OFFSET_REG_ECX(%eax), %ecx + movl OFFSET_REG_EDX(%eax), %edx + movl OFFSET_REG_EBP(%eax), %ebp + movl OFFSET_REG_EDI(%eax), %edi + movl OFFSET_REG_ESI(%eax), %esi + movl OFFSET_REG_EAX(%eax), %eax + + ret + + +.weak swapcontext; +swapcontext = __swapcontext;