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 */