1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2023 Oxide Computer Company
  29  */
  30 
  31         .file   "getcontext.s"
  32 
  33 #include <sys/asm_linkage.h>
  34 
  35         ANSI_PRAGMA_WEAK(getcontext,function)
  36         ANSI_PRAGMA_WEAK(swapcontext,function)
  37 
  38 #include "SYS.h"
  39 #include <../assym.h>
  40 
  41 /*
  42  * getcontext() is written in assembler since it has to capture the correct
  43  * machine state of the calle.
  44  *
  45  * As swapcontext() is actually equivalent to getcontext() + setcontext(),
  46  * swapcontext() shares the most code with getcontext().
  47  */
  48 
  49 #define GETCONTEXT_IMPL(offset, func)                                           \
  50         pushq   %rdi;           /* preserve the ucontext_t pointer */   \
  51         call    func;                                           \
  52                                 /* call getcontext: syscall */          \
  53         popq    %rdx;                                                   \
  54         andl    %eax, %eax;     /* if (error_return_from_syscall) */    \
  55         je      1f;                                                     \
  56         addq    $offset, %rsp;                                          \
  57         ret;                    /*      then just return */             \
  58 1:                                                                      \
  59         /*                                                              \
  60          * fix up %rsp and %rip                                         \
  61          */                                                             \
  62         addq    $UC_MCONTEXT_GREGS, %rdx;                               \
  63                                 /* &ucp->uc_mcontext.gregs */            \
  64         movq    offset+0(%rsp), %rax;                                   \
  65                                 /* read return PC from stack */         \
  66         movq    %rax, RIP_OFF (%rdx);                                   \
  67                                 /* store ret PC in EIP of env var */    \
  68         leaq    offset+8(%rsp), %rax;                                   \
  69                                 /* get caller's sp at time of call */   \
  70         movq    %rax, RSP_OFF (%rdx);                                   \
  71                                 /* store the sp into UESP of env var */ \
  72         xorq    %rax, %rax;     /* return 0 */                          \
  73         movq    %rax, RAX_OFF (%rdx);                                   \
  74                                 /* getcontext returns 0 after setcontext */
  75 
  76 /*
  77  * int getcontext(ucontext_t *ucp)
  78  */
  79 
  80         ENTRY(getcontext)
  81         GETCONTEXT_IMPL(0, __getcontext)
  82         ret
  83         SET_SIZE(getcontext)
  84 
  85 /*
  86  * int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
  87  */
  88 
  89         ENTRY(swapcontext)
  90         pushq   %rsi                    /* preserve the 2nd argument */
  91 
  92         GETCONTEXT_IMPL(8, __getcontext)
  93 
  94         /* call setcontext */
  95         popq    %rdi
  96         call    setcontext
  97         ret
  98         SET_SIZE(swapcontext)
  99 
 100 /*
 101  * int getcontext_extd(ucontext_t * ctx, uint32_t flags)
 102  */
 103         ENTRY(getcontext_extd)
 104         cmpl    $0, %esi
 105         jne     2f
 106         GETCONTEXT_IMPL(0, __getcontext_extd)
 107         ret
 108 2:
 109         movl    $EINVAL, %eax           /* errno = EINVAL */
 110         jmp     __cerror                /* return (-1) */
 111         SET_SIZE(getcontext_extd)
 112 
 113 /*
 114  * int swapcontext_extd(ucontext_t *oucp, uint32_t flags, const ucontext_t *ucp)
 115  */
 116 
 117         ENTRY(swapcontext_extd)
 118         cmpl    $0, %esi
 119         jne     2f
 120         pushq   %rdx                    /* preserve the 3rd argument */
 121 
 122         GETCONTEXT_IMPL(8, __getcontext_extd)
 123 
 124         /* call setcontext */
 125         popq    %rdi
 126         call    setcontext
 127         ret
 128 2:
 129         movl    $EINVAL, %eax           /* errno = EINVAL */
 130         jmp     __cerror                /* return (-1) */
 131         SET_SIZE(swapcontext_extd)