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