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