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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 #include "thr_uberdata.h"
  27 #include <procfs.h>
  28 #include <ucontext.h>
  29 #include <setjmp.h>
  30 
  31 /*
  32  * The stack needs to be 16-byte aligned with a 4-byte bias.  See comment in
  33  * lib/libc/i386/gen/makectxt.c.
  34  *
  35  * Note: If you change it, you need to change it in the following files as
  36  * well:
  37  *
  38  *  - lib/libc/i386/gen/makectxt.c
  39  *  - lib/common/i386/crti.s
  40  *  - lib/common/i386/crt1.s
  41  */
  42 #undef  STACK_ALIGN
  43 #define STACK_ALIGN     16
  44 
  45 extern int getlwpstatus(thread_t, lwpstatus_t *);
  46 extern int putlwpregs(thread_t, prgregset_t);
  47 
  48 void *
  49 setup_top_frame(void *stk, size_t stksize, ulwp_t *ulwp)
  50 {
  51         uint32_t *stack;
  52         struct {
  53                 uint32_t        rpc;
  54                 uint32_t        arg;
  55                 uint32_t        pad;
  56                 uint32_t        fp;
  57                 uint32_t        pc;
  58         } frame;
  59 
  60         /*
  61          * Top-of-stack must be rounded down to STACK_ALIGN and
  62          * there must be a minimum frame.  Note: 'frame' is not a true
  63          * stack frame (see <sys/frame.h>) but a construction made here to
  64          * make it look like _lwp_start called the thread start function
  65          * with a 16-byte aligned stack pointer (the address of frame.arg
  66          * is the address that muet be aligned on a 16-byte boundary).
  67          */
  68         stack = (uint32_t *)(((uintptr_t)stk + stksize) & ~(STACK_ALIGN-1));
  69 
  70         /*
  71          * This will return NULL if the kernel cannot allocate
  72          * a page for the top page of the stack.  This will cause
  73          * thr_create(), pthread_create() or pthread_attr_setstack()
  74          * to fail, passing the problem up to the application.
  75          */
  76         stack -= 5;     /* make the address of frame.arg be 16-byte aligned */
  77         frame.pc = 0;
  78         frame.fp = 0;   /* initial address for %ebp (see EBP below) */
  79         frame.pad = 0;
  80         frame.arg = (uint32_t)ulwp;
  81         frame.rpc = (uint32_t)_lwp_start;
  82         if (uucopy(&frame, (void *)stack, sizeof (frame)) == 0)
  83                 return (stack);
  84         return (NULL);
  85 }
  86 
  87 int
  88 setup_context(ucontext_t *ucp, void *(*func)(ulwp_t *),
  89         ulwp_t *ulwp, caddr_t stk, size_t stksize)
  90 {
  91         static int initialized;
  92         static greg_t fs, es, ds, cs, ss;
  93 
  94         uint32_t *stack;
  95 
  96         if (!initialized) {
  97                 ucontext_t uc;
  98 
  99                 /* do this once to load the segment registers */
 100                 uc.uc_flags = UC_CPU;
 101                 (void) __getcontext(&uc);
 102                 fs = uc.uc_mcontext.gregs[FS];
 103                 es = uc.uc_mcontext.gregs[ES];
 104                 ds = uc.uc_mcontext.gregs[DS];
 105                 cs = uc.uc_mcontext.gregs[CS];
 106                 ss = uc.uc_mcontext.gregs[SS];
 107                 initialized = 1;
 108         }
 109         /* clear the context and set the segment registers */
 110         (void) memset(ucp, 0, sizeof (*ucp));
 111         ucp->uc_mcontext.gregs[FS] = fs;
 112         ucp->uc_mcontext.gregs[ES] = es;
 113         ucp->uc_mcontext.gregs[DS] = ds;
 114         ucp->uc_mcontext.gregs[CS] = cs;
 115         ucp->uc_mcontext.gregs[SS] = ss;
 116 
 117         /*
 118          * Yuck.
 119          * Use unused kernel pointer field in ucontext
 120          * to pass down self pointer and set %gs selector
 121          * value so __lwp_create() can setup %gs atomically.
 122          * Without this we would need to block all signals
 123          * and directly call ___lwp_private() in _thrp_setup
 124          * on the other side of __lwp_create().
 125          */
 126         ucp->uc_mcontext.gregs[ESP] = (greg_t)ulwp;
 127         ucp->uc_mcontext.gregs[GS] = (greg_t)LWPGS_SEL;
 128 
 129         /*
 130          * Setup the top stack frame.
 131          * If this fails, pass the problem up to the application.
 132          */
 133         if ((stack = setup_top_frame(stk, stksize, ulwp)) == NULL)
 134                 return (ENOMEM);
 135 
 136         /* fill in registers of interest */
 137         ucp->uc_flags |= UC_CPU;
 138         ucp->uc_mcontext.gregs[EIP] = (greg_t)func;
 139         ucp->uc_mcontext.gregs[UESP] = (greg_t)stack;
 140         ucp->uc_mcontext.gregs[EBP] = (greg_t)(stack + 3);
 141 
 142         return (0);
 143 }
 144 
 145 /*
 146  * Machine-dependent startup code for a newly-created thread.
 147  */
 148 void *
 149 _thrp_setup(ulwp_t *self)
 150 {
 151         self->ul_ustack.ss_sp = (void *)(self->ul_stktop - self->ul_stksiz);
 152         self->ul_ustack.ss_size = self->ul_stksiz;
 153         self->ul_ustack.ss_flags = 0;
 154         (void) setustack(&self->ul_ustack);
 155 
 156         update_sched(self);
 157         tls_setup();
 158 
 159         /* signals have been deferred until now */
 160         sigon(self);
 161 
 162         if (self->ul_cancel_pending == 2 && !self->ul_cancel_disabled)
 163                 return (NULL);  /* cancelled by pthread_create() */
 164         return (self->ul_startpc(self->ul_startarg));
 165 }
 166 
 167 void
 168 _fpinherit(ulwp_t *ulwp)
 169 {
 170         ulwp->ul_fpuenv.ftag = 0xffffffff;
 171 }
 172 
 173 void
 174 getgregs(ulwp_t *ulwp, gregset_t rs)
 175 {
 176         lwpstatus_t status;
 177 
 178         if (getlwpstatus(ulwp->ul_lwpid, &status) == 0) {
 179                 rs[EIP] = status.pr_reg[EIP];
 180                 rs[EDI] = status.pr_reg[EDI];
 181                 rs[ESI] = status.pr_reg[ESI];
 182                 rs[EBP] = status.pr_reg[EBP];
 183                 rs[EBX] = status.pr_reg[EBX];
 184                 rs[UESP] = status.pr_reg[UESP];
 185         } else {
 186                 rs[EIP] = 0;
 187                 rs[EDI] = 0;
 188                 rs[ESI] = 0;
 189                 rs[EBP] = 0;
 190                 rs[EBX] = 0;
 191                 rs[UESP] = 0;
 192         }
 193 }
 194 
 195 void
 196 setgregs(ulwp_t *ulwp, gregset_t rs)
 197 {
 198         lwpstatus_t status;
 199 
 200         if (getlwpstatus(ulwp->ul_lwpid, &status) == 0) {
 201                 status.pr_reg[EIP] = rs[EIP];
 202                 status.pr_reg[EDI] = rs[EDI];
 203                 status.pr_reg[ESI] = rs[ESI];
 204                 status.pr_reg[EBP] = rs[EBP];
 205                 status.pr_reg[EBX] = rs[EBX];
 206                 status.pr_reg[UESP] = rs[UESP];
 207                 (void) putlwpregs(ulwp->ul_lwpid, status.pr_reg);
 208         }
 209 }
 210 
 211 int
 212 __csigsetjmp(greg_t cs, greg_t ss, greg_t gs,
 213         greg_t fs, greg_t es, greg_t ds,
 214         greg_t edi, greg_t esi, greg_t ebp, greg_t esp,
 215         greg_t ebx, greg_t edx, greg_t ecx, greg_t eax, greg_t eip,
 216         sigjmp_buf env, int savemask)
 217 {
 218         ucontext_t *ucp = (ucontext_t *)env;
 219         ulwp_t *self = curthread;
 220 
 221         ucp->uc_link = self->ul_siglink;
 222         if (self->ul_ustack.ss_flags & SS_ONSTACK)
 223                 ucp->uc_stack = self->ul_ustack;
 224         else {
 225                 ucp->uc_stack.ss_sp =
 226                     (void *)(self->ul_stktop - self->ul_stksiz);
 227                 ucp->uc_stack.ss_size = self->ul_stksiz;
 228                 ucp->uc_stack.ss_flags = 0;
 229         }
 230         ucp->uc_flags = UC_STACK | UC_CPU;
 231         if (savemask) {
 232                 ucp->uc_flags |= UC_SIGMASK;
 233                 enter_critical(self);
 234                 ucp->uc_sigmask = self->ul_sigmask;
 235                 exit_critical(self);
 236         }
 237         ucp->uc_mcontext.gregs[GS] = gs;
 238         ucp->uc_mcontext.gregs[FS] = fs;
 239         ucp->uc_mcontext.gregs[ES] = es;
 240         ucp->uc_mcontext.gregs[DS] = ds;
 241         ucp->uc_mcontext.gregs[EDI] = edi;
 242         ucp->uc_mcontext.gregs[ESI] = esi;
 243         ucp->uc_mcontext.gregs[EBP] = ebp;
 244         ucp->uc_mcontext.gregs[ESP] = esp + 4;
 245         ucp->uc_mcontext.gregs[EBX] = ebx;
 246         ucp->uc_mcontext.gregs[EDX] = edx;
 247         ucp->uc_mcontext.gregs[ECX] = ecx;
 248         ucp->uc_mcontext.gregs[EAX] = eax;
 249         ucp->uc_mcontext.gregs[TRAPNO] = 0;
 250         ucp->uc_mcontext.gregs[ERR] = 0;
 251         ucp->uc_mcontext.gregs[EIP] = eip;
 252         ucp->uc_mcontext.gregs[CS] = cs;
 253         ucp->uc_mcontext.gregs[EFL] = 0;
 254         ucp->uc_mcontext.gregs[UESP] = esp + 4;
 255         ucp->uc_mcontext.gregs[SS] = ss;
 256 
 257         return (0);
 258 }
 259 
 260 void
 261 smt_pause(void)
 262 {
 263         SMT_PAUSE();
 264 }