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  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 /*
  26  * This is an assembly file that gets #include-ed into the brand-specific
  27  * assembly files (e.g. sn1_brand_asm.s) for Solaris-derived brands.
  28  * We can't make these into functions since in the trap context there's
  29  * no easy place to save the extra parameters that would be required, so
  30  * each brand module needs its own copy of this code.  We #include this and
  31  * use brand-specific #defines to replace the XXX_brand_... definitions.
  32  */ 
  33 
  34 #ifdef lint
  35 
  36 #include <sys/systm.h>
  37 
  38 void
  39 XXX_brand_syscall32_callback(void)
  40 {
  41 }
  42 
  43 void
  44 XXX_brand_syscall_callback(void)
  45 {
  46 }
  47 
  48 #else   /* !lint */
  49 
  50 #include <sys/asm_linkage.h>
  51 #include <sys/machthread.h>
  52 #include <sys/privregs.h>
  53 #include "assym.h"
  54 
  55 #ifdef _ASM     /* The remainder of this file is only for assembly files */
  56 
  57 #if defined(sun4v)
  58 
  59 #define GLOBALS_SWAP(reg)                               \
  60         rdpr    %gl, reg;                               \
  61         wrpr    reg, 1, %gl
  62 
  63 /*
  64  * The GLOBALS_RESTORE macro can only be one instruction since it's
  65  * used in a delay slot.
  66  */
  67 #define GLOBALS_RESTORE(reg)                            \
  68         wrpr    reg, 0, %gl
  69 
  70 #else /* !sun4v */
  71 
  72 #define GLOBALS_SWAP(reg)                               \
  73         rdpr    %pstate, reg;                           \
  74         wrpr    reg, PSTATE_AG, %pstate
  75 
  76 /*
  77  * The GLOBALS_RESTORE macro can only be one instruction since it's
  78  * used in a delay slot.
  79  */
  80 #define GLOBALS_RESTORE(reg)                            \
  81         wrpr    reg, %g0, %pstate
  82 
  83 #endif /* !sun4v */
  84 
  85 /*
  86  * Input parameters:
  87  * %g1: return point
  88  * %g2: pointer to our cpu structure
  89  */
  90 ENTRY(XXX_brand_syscall32_callback)
  91         /*
  92          * If the trapping thread has the address mask bit clear, then it's
  93          * a 64-bit process, and has no business calling 32-bit syscalls.
  94          */
  95         rdpr    %tstate, %g3;           /* %tstate.am is the trapping */
  96         andcc   %g3, TSTATE_AM, %g3;    /*   threads address mask bit */
  97         bne,pt  %xcc, _entry;
  98         nop;
  99         jmp     %g1;                    /* 64 bit process, bail out */
 100         nop;
 101 SET_SIZE(XXX_brand_syscall32_callback)
 102 
 103 /*
 104  * Input parameters:
 105  * %g1: return point
 106  * %g2: pointer to our cpu structure
 107  */
 108 ENTRY(XXX_brand_syscall_callback)
 109         /*
 110          * If the trapping thread has the address mask bit set, then it's
 111          * a 32-bit process, and has no business calling 64-bit syscalls.
 112          */
 113         rdpr    %tstate, %g3;           /* %tstate.am is the trapping */
 114         andcc   %g3, TSTATE_AM, %g3;    /*   threads address mask bit */
 115         be,pt   %xcc, _entry;
 116         nop;
 117         jmp     %g1;                    /* 32 bit process, bail out */
 118         nop;
 119 SET_SIZE(XXX_brand_syscall_callback)
 120 
 121 ENTRY(XXX_brand_syscall_callback_common)
 122 _entry:
 123         /*
 124          * Input parameters:
 125          * %g1: return point
 126          * %g2: pointer to our cpu structure
 127          *
 128          * Note that we're free to use any %g? registers as long as
 129          * we are are executing with alternate globals.  If we're
 130          * executing with user globals we need to backup any registers
 131          * that we want to use so that we can restore them when we're
 132          * done.
 133          *
 134          * Save some locals in the CPU tmp area to give us a little
 135          * room to work.
 136          */
 137         stn     %l0, [%g2 + CPU_TMP1];
 138         stn     %l1, [%g2 + CPU_TMP2];
 139 
 140 #if defined(sun4v)
 141         /*
 142          * On sun4v save our input parameters (which are stored in the
 143          * alternate globals) since we'll need to switch between alternate
 144          * globals and normal globals, and on sun4v the alternate globals
 145          * are not preserved across these types of switches.
 146          */
 147         stn     %l2, [%g2 + CPU_TMP3];
 148         stn     %l3, [%g2 + CPU_TMP4];
 149 
 150         mov     %g1, %l2;               /* save %g1 in %l2 */
 151         mov     %g2, %l3;               /* save %g2 in %l3 */
 152 #endif /* sun4v */
 153 
 154         /*
 155          * Switch from the alternate to user globals to grab the syscall
 156          * number.
 157          */
 158         GLOBALS_SWAP(%l0);              /* switch to normal globals */
 159 
 160         /*
 161          * If the system call number is >= 1024, then it is a native
 162          * syscall that doesn't need emulation.
 163          */
 164         cmp     %g1, 1024;              /* is this a native syscall? */
 165         bl,a    _indirect_check;        /* probably not, continue checking */
 166         mov     %g1, %l1;               /* delay slot - grab syscall number */
 167 
 168         /*
 169          * This is a native syscall, probably from the emulation library.
 170          * Subtract 1024 from the syscall number and let it go through.
 171          */
 172         sub     %g1, 1024, %g1;         /* convert magic num to real syscall */
 173         ba      _exit;                  /* jump back into syscall path */
 174         GLOBALS_RESTORE(%l0);           /* delay slot - */
 175                                         /* switch back to alternate globals */
 176 
 177 _indirect_check:
 178         /*
 179          * If the system call number is 0 (SYS_syscall), then this might be
 180          * an indirect syscall, in which case the actual syscall number
 181          * would be stored in %o0, in which case we need to redo the
 182          * the whole >= 1024 check.
 183          */
 184         brnz,pt %g1, _emulation_check;  /* is this an indirect syscall? */
 185         nop;                            /* if not, goto the emulation check */
 186 
 187         /*
 188          * Indirect syscalls are only supported for 32 bit processes so
 189          * consult the tstate address mask again.
 190          */
 191         rdpr    %tstate, %l1;           /* %tstate.am is the trapping */
 192         andcc   %l1, TSTATE_AM, %l1;    /*   threads address mask bit */
 193         be,a,pn %xcc, _exit;
 194         GLOBALS_RESTORE(%l0);           /* delay slot - */
 195                                         /* switch back to alternate globals */
 196 
 197         /*
 198          * The caller is 32 bit and this an indirect system call.
 199          */
 200         cmp     %o0, 1024;              /* is this a native syscall? */
 201         bl,a    _emulation_check;       /* no, goto the emulation check */
 202         mov     %o0, %l1;               /* delay slot - grab syscall number */
 203 
 204         /*
 205          * This is native indirect syscall, probably from the emulation
 206          * library.  Subtract 1024 from the syscall number and let it go
 207          * through.
 208          */
 209         sub     %o0, 1024, %o0;         /* convert magic num to real syscall */
 210         ba      _exit;                  /* jump back into syscall path */
 211         GLOBALS_RESTORE(%l0);           /* delay slot - */
 212                                         /* switch back to alternate globals */
 213 
 214 _emulation_check:
 215         GLOBALS_RESTORE(%l0);           /* switch back to alternate globals */
 216 
 217         /*
 218          * Check to see if we want to interpose on this system call.  If
 219          * not, we jump back into the normal syscall path and pretend
 220          * nothing happened.  %l1 contains the syscall we're invoking.
 221          */
 222         set     XXX_emulation_table, %g3;
 223         ldn     [%g3], %g3;
 224         add     %g3, %l1, %g3;
 225         ldub    [%g3], %g3;
 226         brz     %g3, _exit;
 227         nop;
 228 
 229         /*
 230          * Find the address of the userspace handler.
 231          * cpu->cpu_thread->t_procp->p_brand_data->spd_handler.
 232          */
 233 #if defined(sun4v)
 234         /* restore the alternate global registers after incrementing %gl */
 235         mov     %l3, %g2;
 236 #endif /* sun4v */
 237         ldn     [%g2 + CPU_THREAD], %g3;        /* get thread ptr */
 238         ldn     [%g3 + T_PROCP], %g4;           /* get proc ptr */
 239         ldn     [%g4 + P_BRAND_DATA], %g5;      /* get brand data ptr */
 240         ldn     [%g5 + SPD_HANDLER], %g5;       /* get userland brnd hdlr ptr */
 241         brz     %g5, _exit;                     /* has it been set? */
 242         nop;
 243 
 244         /*
 245          * Make sure this isn't an agent lwp.  We can't do syscall
 246          * interposition for system calls made by a agent lwp.  See
 247          * the block comments in the top of the brand emulation library
 248          * for more information.
 249          */
 250         ldn     [%g4 + P_AGENTTP], %g4;         /* get agent thread ptr */
 251         cmp     %g3, %g4;                       /* is this an agent thread? */
 252         be,pn   %xcc, _exit;                    /* if so don't emulate */
 253         nop;
 254 
 255         /*
 256          * Now the magic happens.  Grab the trap return address and then
 257          * reset it to point to the user space handler.  When we execute
 258          * the 'done' instruction, we will jump into our handler instead of
 259          * the user's code.  We also stick the old return address in %g5,
 260          * so we can return to the proper instruction in the user's code.
 261          * Note: we also pass back the base address of the syscall
 262          * emulation table.  This is a performance hack to avoid having to
 263          * look it up on every call.
 264          */
 265         rdpr    %tnpc, %l1;             /* save old tnpc */
 266         wrpr    %g0, %g5, %tnpc;        /* setup tnpc */
 267         GLOBALS_SWAP(%l0);              /* switch to normal globals */
 268         mov     %l1, %g5;               /* pass tnpc to user code in %g5 */
 269         GLOBALS_RESTORE(%l0);           /* switch back to alternate globals */
 270 
 271         /* Update the address we're going to return to */
 272 #if defined(sun4v)
 273         set     fast_trap_done_chk_intr, %l2;
 274 #else /* !sun4v */
 275         set     fast_trap_done_chk_intr, %g1;
 276 #endif /* !sun4v */
 277 
 278 _exit:
 279         /*
 280          * Restore registers before returning.
 281          *
 282          * Note that %g2 should be loaded with the CPU struct addr and
 283          * %g1 should be loaded the address we're going to return to.
 284          */
 285 #if defined(sun4v)
 286         /* restore the alternate global registers after incrementing %gl */
 287         mov     %l2, %g1;               /* restore %g1 from %l2 */
 288         mov     %l3, %g2;               /* restore %g2 from %l3 */
 289 
 290         ldn     [%g2 + CPU_TMP4], %l3;  /* restore locals */
 291         ldn     [%g2 + CPU_TMP3], %l2;
 292 #endif /* sun4v */
 293 
 294         ldn     [%g2 + CPU_TMP2], %l1;  /* restore locals */
 295         ldn     [%g2 + CPU_TMP1], %l0;
 296 
 297         jmp     %g1;
 298         nop;
 299 SET_SIZE(XXX_brand_syscall_callback_common)
 300 
 301 #endif  /* _ASM */
 302 #endif  /* !lint */