s10_brand_asm.s revision 11460:3e8510aaac0f
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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26#if defined(lint) 27 28#include <sys/systm.h> 29 30#else /* lint */ 31 32#include <sys/asm_linkage.h> 33#include <sys/privregs.h> 34#include <sys/segments.h> 35#include <s10_offsets.h> 36#include "assym.h" 37 38#endif /* lint */ 39 40#ifdef lint 41 42void 43s10_brand_sysenter_callback(void) 44{ 45} 46 47void 48s10_brand_syscall_callback(void) 49{ 50} 51 52#if defined(__amd64) 53void 54s10_brand_syscall32_callback(void) 55{ 56} 57#endif /* amd64 */ 58 59void 60s10_brand_int91_callback(void) 61{ 62} 63 64#else /* lint */ 65 66#if defined(__amd64) 67/* 68 * When our syscall interposition callback entry point gets invoked the 69 * stack looks like this: 70 * -------------------------------------- 71 * 40 | user %gs | 72 * 32 | callback pointer | 73 * 24 | saved stack pointer | 74 * | 16 | lwp pointer | 75 * v 8 | user return address | 76 * 0 | BRAND_CALLBACK()'s return addr | 77 * -------------------------------------- 78 */ 79 80#define V_COUNT 6 81#define V_END (CLONGSIZE * 6) 82#define V_SSP (CLONGSIZE * 3) 83#define V_LWP (CLONGSIZE * 2) 84#define V_URET_ADDR (CLONGSIZE * 1) 85#define V_CB_ADDR (CLONGSIZE * 0) 86 87#define SP_REG %rsp 88#define SYSCALL_REG %rax 89 90#else /* !__amd64 */ 91/* 92 * When our syscall interposition callback entry point gets invoked the 93 * stack looks like this: 94 * -------------------------------------- 95 * | 24 | 'scratch space' | 96 * | 20 | user's %ebx | 97 * | 16 | user's %gs selector | 98 * | 12 | kernel's %gs selector | 99 * | 8 | lwp pointer | 100 * v 4 | user return address | 101 * 0 | callback wrapper return addr | 102 * -------------------------------------- 103 */ 104 105#define V_COUNT 7 106#define V_END (CLONGSIZE * 7) 107#define V_U_GS (CLONGSIZE * 4) 108#define V_K_GS (CLONGSIZE * 3) 109#define V_LWP (CLONGSIZE * 2) 110#define V_URET_ADDR (CLONGSIZE * 1) 111#define V_CB_ADDR (CLONGSIZE * 0) 112 113#define SP_REG %esp 114#define SYSCALL_REG %eax 115 116#endif /* !__amd64 */ 117 118/* 119 * The following macros allow us to access to variables/parameters passed 120 * in on the stack. They take the following variables: 121 * sp - a register with the current stack pointer value 122 * pcnt - the number of words currently pushed onto the stack 123 * var - the variable to lookup 124 * reg - a register to read the variable into, or 125 * a register to write to the variable 126 */ 127#define V_OFFSET(pcnt, var) \ 128 (var + (pcnt * CLONGSIZE)) 129 130#define GET_V(sp, pcnt, var, reg) \ 131 mov V_OFFSET(pcnt, var)(sp), reg 132 133#define SET_V(sp, pcnt, var, reg) \ 134 mov reg, V_OFFSET(pcnt, var)(sp) 135 136#define GET_PROCP(sp, pcnt, reg) \ 137 GET_V(sp, pcnt, V_LWP, reg) /* get lwp pointer */ ;\ 138 mov LWP_PROCP(reg), reg /* get proc pointer */ 139 140#define GET_P_BRAND_DATA(sp, pcnt, reg) \ 141 GET_PROCP(sp, pcnt, reg) ;\ 142 mov P_BRAND_DATA(reg), reg /* get p_brand_data */ 143 144/* 145 * Each of the following macros returns to the standard syscall codepath if 146 * it detects that this process is not able, or intended, to emulate this 147 * system call. They all assume that the routine provides a 'bail-out' 148 * label of '9'. 149 */ 150 151/* 152 * See if this process has a user-space hdlr registered for it. For the 153 * s10 brand, the per-process brand data holds the address of the handler. 154 * As shown in the stack diagrams below, the callback code leaves that data 155 * at these offsets. So check if s10_proc_data_t->spd_handler is non-NULL. 156 */ 157#define CHECK_FOR_HANDLER(scr) \ 158 GET_P_BRAND_DATA(SP_REG, 1, scr) /* get p_brand_data */ ;\ 159 cmp $0, scr ;\ 160 je 9f ;\ 161 cmp $0, SPD_HANDLER(scr) /* check spd_handler */ ;\ 162 je 9f 163 164/* 165 * If the system call number is >= 1024, then it is coming from the 166 * emulation support library. As such we should handle it natively instead 167 * of sending it back to the emulation library. 168 */ 169#define CHECK_FOR_NATIVE(reg) \ 170 cmp $1024, reg ;\ 171 jl 1f ;\ 172 sub $1024, reg ;\ 173 jmp 9f ;\ 1741: 175 176/* 177 * Check to see if we want to interpose on this system call. If not, we 178 * jump back into the normal syscall path and pretend nothing happened. 179 */ 180#define CHECK_FOR_INTERPOSITION(sysr, scr, scr_low) \ 181 cmp $NSYSCALL, sysr /* is 0 <= syscall <= MAX? */ ;\ 182 ja 9f /* no, take normal err path */ ;\ 183 lea s10_emulation_table, scr ;\ 184 mov (scr), scr ;\ 185 add sysr, scr ;\ 186 movb (scr), scr_low ;\ 187 cmpb $0, scr_low ;\ 188 je 9f 189 190#define CALLBACK_PROLOGUE(call, scr, scr_low) \ 191 push scr /* Save scratch register */ ;\ 192 CHECK_FOR_HANDLER(scr) ;\ 193 CHECK_FOR_NATIVE(call) ;\ 194 CHECK_FOR_INTERPOSITION(call, scr, scr_low) 195 196/* 197 * Rather than returning to the instruction after the syscall, we need to 198 * transfer control into the brand library's handler table at 199 * table_addr + (16 * syscall_num), thus encoding the system call number in the 200 * instruction pointer. The CALC_TABLE_ADDR macro performs that calculation. 201 * 202 * This macro assumes the syscall number is in SYSCALL_REG and it clobbers 203 * that register. It leaves the calculated handler table return address in 204 * the scratch reg. 205 */ 206#define CALC_TABLE_ADDR(scr) \ 207 GET_P_BRAND_DATA(SP_REG, 1, scr) /* get p_brand_data ptr */ ;\ 208 mov SPD_HANDLER(scr), scr /* get p_brand_data->spd_handler */ ;\ 209 shl $4, SYSCALL_REG /* syscall_num * 16 */ ;\ 210 add SYSCALL_REG, scr /* leave return addr in scr reg. */ 211 212/* 213 * To 'return' to our user-space handler, we just need to place its address 214 * into 'retreg'. The original return address is passed in SYSCALL_REG. 215 */ 216#define SETUP_RET_DATA(scr, retreg) \ 217 CALC_TABLE_ADDR(scr) ;\ 218 mov retreg, SYSCALL_REG /* save orig return addr in %rax */ ;\ 219 mov scr, retreg /* save new return addr in ret reg */ ;\ 220 pop scr /* restore scratch register */ 221 222/* 223 * The callback routines: 224 */ 225 226#if defined(__amd64) 227 228/* 229 * syscall handler for 32-bit user processes: 230 * %rax - syscall number 231 * %ecx - the address of the instruction after the syscall 232 */ 233ENTRY(s10_brand_syscall32_callback) 234 235 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 236 237 SETUP_RET_DATA(%r15, %rcx) 238 GET_V(%rsp, 0, V_SSP, %rsp) /* restore user's stack pointer */ 239 jmp nopop_sys_syscall32_swapgs_sysretl 2409: 241 popq %r15 242 retq 243SET_SIZE(s10_brand_syscall32_callback) 244 245/* 246 * syscall handler for 64-bit user processes: 247 * %rax - syscall number 248 * %rcx - user space %rip 249 */ 250ENTRY(s10_brand_syscall_callback) 251 252 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 253 254 SETUP_RET_DATA(%r15, %rcx) 255 GET_V(%rsp, 0, V_SSP, %rsp) /* restore user's stack pointer */ 256 jmp nopop_sys_syscall_swapgs_sysretq 2579: 258 popq %r15 259 retq 260 261SET_SIZE(s10_brand_syscall_callback) 262 263/* 264 * %eax - syscall number 265 * %ecx - user space %esp 266 * %edx - user space return address 267 */ 268ENTRY(s10_brand_sysenter_callback) 269 270 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 271 272 SETUP_RET_DATA(%r15, %rdx) 273 jmp sys_sysenter_swapgs_sysexit 2749: 275 popq %r15 276 ret 277SET_SIZE(s10_brand_sysenter_callback) 278 279/* 280 * The saved stack pointer points at the state saved when we took 281 * the interrupt: 282 * -------------------------------------- 283 * | 32 | user's %ss | 284 * | 24 | user's %esp | 285 * | 16 | EFLAGS register | 286 * v 8 | user's %cs | 287 * 0 | user's %eip | 288 * -------------------------------------- 289 */ 290#define V_U_EIP (CLONGSIZE * 0) 291 292ENTRY(s10_brand_int91_callback) 293 294 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 295 296 /* 297 * To 'return' to our user-space handler we need to update the user's 298 * %eip pointer in the saved interrupt state. The interrupt state was 299 * pushed onto our stack automatically when the interrupt occured; see 300 * the comments above. The original return address is passed in %rax. 301 */ 302 CALC_TABLE_ADDR(%r15) 303 GET_V(%rsp, 1, V_SSP, %rax) /* get saved stack pointer */ 304 SET_V(%rax, 0, V_U_EIP, %r15) /* save new return addr in %eip */ 305 GET_V(%rsp, 1, V_URET_ADDR, %rax) /* %rax has orig. return addr. */ 306 307 popq %r15 /* Restore scratch register */ 308 movq V_SSP(%rsp), %rsp /* Remove callback stuff from stack */ 309 jmp sys_sysint_swapgs_iret 3109: 311 popq %r15 312 retq 313SET_SIZE(s10_brand_int91_callback) 314 315#else /* !__amd64 */ 316 317/* 318 * lcall handler for 32-bit OS 319 * %eax - syscall number 320 * 321 * Above the stack contents common to all callbacks is the 322 * int/lcall-specific state: 323 * -------------------------------------- 324 * | 44 | user's %ss | 325 * | 40 | user's %esp | 326 * | 36 | EFLAGS register | 327 * v 32 | user's %cs | 328 * 28 | user's %eip | 329 * -------------------------------------- 330 */ 331#define V_U_SS (V_END + (CLONGSIZE * 4)) 332#define V_U_ESP (V_END + (CLONGSIZE * 3)) 333#define V_EFLAGS (V_END + (CLONGSIZE * 2)) 334#define V_U_CS (V_END + (CLONGSIZE * 1)) 335#define V_U_EIP (V_END + (CLONGSIZE * 0)) 336 337ENTRY(s10_brand_syscall_callback) 338 339 CALLBACK_PROLOGUE(%eax, %ebx, %bl) 340 341 /* 342 * To 'return' to our user-space handler, we need to replace the 343 * iret target address. 344 * The original return address is passed in %eax. 345 */ 346 CALC_TABLE_ADDR(%ebx) /* new return addr is in %ebx */ 347 SET_V(%esp, 1, V_U_EIP, %ebx) /* set iret target address to hdlr */ 348 GET_V(%esp, 1, V_URET_ADDR, %eax) /* save orig return addr in %eax */ 349 350 GET_V(%esp, 1, V_U_GS, %ebx) /* grab the the user %gs */ 351 movw %bx, %gs /* restore the user %gs */ 352 353 popl %ebx /* Restore scratch register */ 354 addl $V_END, %esp /* Remove all callback stuff from stack */ 355 jmp nopop_sys_rtt_syscall 3569: 357 popl %ebx 358 ret 359SET_SIZE(s10_brand_syscall_callback) 360 361/* 362 * %eax - syscall number 363 * %ecx - user space %esp 364 * %edx - user space return address 365 */ 366ENTRY(s10_brand_sysenter_callback) 367 368 CALLBACK_PROLOGUE(%eax, %ebx, %bl) 369 370 /* 371 * To 'return' to our user-space handler, we just need to place its 372 * address into %edx. 373 * The original return address is passed in %eax. 374 */ 375 movl %edx, %ebx /* save orig return addr in tmp reg */ 376 CALC_TABLE_ADDR(%edx) /* new return addr is in %edx */ 377 movl %ebx, %eax /* save orig return addr in %eax */ 378 379 GET_V(%esp, 1, V_U_GS, %ebx) /* grab the the user %gs */ 380 movw %bx, %gs /* restore the user %gs */ 381 382 popl %ebx /* restore scratch register */ 383 sysexit 3849: 385 popl %ebx 386 ret 387SET_SIZE(s10_brand_sysenter_callback) 388 389#endif /* !__amd64 */ 390#endif /* lint */ 391