sn1_brand_asm.s revision 7933:6a8cc62fe2a1
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 2008 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 <sn1_offsets.h> 36#include "assym.h" 37 38#endif /* lint */ 39 40#ifdef lint 41 42void 43sn1_brand_sysenter_callback(void) 44{ 45} 46 47void 48sn1_brand_syscall_callback(void) 49{ 50} 51 52#if defined(__amd64) 53void 54sn1_brand_syscall32_callback(void) 55{ 56} 57#endif /* amd64 */ 58 59void 60sn1_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 * 24 | saved stack pointer | 72 * | 16 | lwp pointer | 73 * v 8 | user return address (*) | 74 * 0 | BRAND_CALLBACK()'s return addr | 75 * -------------------------------------- 76 * (*) This is actually just the bottom value from the user's 77 * stack. syscall puts this in %rcx instead of the stack, 78 * so it's just garbage for that entry point. 79 */ 80 81#define V_COUNT 4 82#define V_END (CLONGSIZE * 4) 83#define V_SSP (CLONGSIZE * 3) 84#define V_LWP (CLONGSIZE * 2) 85#define V_URET_ADDR (CLONGSIZE * 1) 86#define V_CB_ADDR (CLONGSIZE * 0) 87 88#define SP_REG %rsp 89 90#else /* !__amd64 */ 91/* 92 * When our syscall interposition callback entry point gets invoked the 93 * stack looks like this: 94 * -------------------------------------- 95 * | 24 | 'scatch 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_LWP (CLONGSIZE * 2) 108#define V_URET_ADDR (CLONGSIZE * 1) 109#define V_CB_ADDR (CLONGSIZE * 0) 110 111#define SP_REG %esp 112 113#endif /* !__amd64 */ 114 115/* 116 * The following macros allow us to access to variables/parameters passed 117 * in on the stack. They take the following variables: 118 * sp - a register with the current stack pointer value 119 * pcnt - the number of words currently pushed onto the stack 120 * var - the variable to lookup 121 * reg - a register to read the variable into, or 122 * a register to write to the variable 123 */ 124#define V_OFFSET(pcnt, var) \ 125 (var + (pcnt * CLONGSIZE)) 126 127#define GET_V(sp, pcnt, var, reg) \ 128 mov V_OFFSET(pcnt, var)(sp), reg 129 130#define SET_V(sp, pcnt, var, reg) \ 131 mov reg, V_OFFSET(pcnt, var)(sp) 132 133#define GET_PROCP(sp, pcnt, reg) \ 134 GET_V(sp, pcnt, V_LWP, reg) /* get lwp pointer */ ;\ 135 mov LWP_PROCP(reg), reg /* get proc pointer */ 136 137#define GET_P_BRAND_DATA(sp, pcnt, reg) \ 138 GET_PROCP(sp, pcnt, reg) ;\ 139 mov P_BRAND_DATA(reg), reg /* get p_brand_data */ 140 141/* 142 * Each of the following macros returns to the standard syscall codepath if 143 * it detects that this process is not able, or intended, to emulate this 144 * system call. They all assume that the routine provides a 'bail-out' 145 * label of '9'. 146 */ 147 148/* 149 * See if this process has a user-space hdlr registered for it. For the 150 * sn1 brand, the per-process brand data holds the address of the handler. 151 * As shown in the stack diagrams below, the callback code leaves that data 152 * at these offsets. So check if sn1_proc_data_t->spd_handler is non-NULL. 153 */ 154#define CHECK_FOR_HANDLER(scr) \ 155 GET_P_BRAND_DATA(SP_REG, 1, scr) /* get p_brand_data */ ;\ 156 cmp $0, scr ;\ 157 je 9f ;\ 158 cmp $0, SPD_HANDLER(scr) /* check spd_handler */ ;\ 159 je 9f 160 161/* 162 * If the system call number is >= 1024, then it is coming from the 163 * emulation support library. As such we should handle it natively instead 164 * of sending it back to the emulation library. 165 */ 166#define CHECK_FOR_NATIVE(reg) \ 167 cmp $1024, reg ;\ 168 jl 1f ;\ 169 sub $1024, reg ;\ 170 jmp 9f ;\ 1711: 172 173/* 174 * Check to see if we want to interpose on this system call. If not, we 175 * jump back into the normal syscall path and pretend nothing happened. 176 */ 177#define CHECK_FOR_INTERPOSITION(sysr, scr, scr_low) \ 178 lea sn1_emulation_table, scr ;\ 179 mov (scr), scr ;\ 180 add sysr, scr ;\ 181 movb (scr), scr_low ;\ 182 cmpb $0, scr_low ;\ 183 je 9f 184 185#define CALLBACK_PROLOGUE(call, scr, scr_low) \ 186 push scr /* Save scratch register */ ;\ 187 CHECK_FOR_HANDLER(scr) ;\ 188 CHECK_FOR_NATIVE(call) ;\ 189 CHECK_FOR_INTERPOSITION(call, scr, scr_low) 190 191/* 192 * The callback routines: 193 */ 194 195#if defined(__amd64) 196 197/* 198 * syscall handler for 32-bit user processes: 199 * %rax - syscall number 200 * %ecx - the address of the instruction after the syscall 201 */ 202ENTRY(sn1_brand_syscall32_callback) 203 204 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 205 movq %rsp, %r15 /* save our stack pointer */ 206 207 /* 208 * Adjust the user's stack so that the 'ret' from our user-space 209 * hdlr takes us to the post-syscall instruction instead of to 210 * the routine that called the system call. 211 */ 212 GET_V(%r15, 1, V_SSP, %rsp) /* restore user's stack pointer */ 213 subq $4, %rsp /* save room for the post-syscall addr */ 214 movl %ecx, (%rsp) /* Save post-syscall addr on stack */ 215 216 /* 217 * To 'return' to our user-space handler, we just need to copy 218 * its address into %rcx. 219 */ 220 GET_P_BRAND_DATA(%r15, 1, %rcx);/* get p_brand_data ptr */ 221 movq SPD_HANDLER(%rcx), %rcx /* get p_brand_data->spd_handler ptr */ 222 movq (%r15), %r15 /* Restore scratch register */ 223 jmp nopop_sys_syscall32_sysretl 2249: 225 popq %r15 226 retq 227SET_SIZE(sn1_brand_syscall32_callback) 228 229/* 230 * syscall handler for 64-bit user processes: 231 * %rax - syscall number 232 * %rcx - user space %rip 233 */ 234ENTRY(sn1_brand_syscall_callback) 235 236 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 237 movq %rsp, %r15 /* save our stack pointer */ 238 239 GET_V(%r15, 1, V_SSP, %rsp) /* restore user's stack pointer */ 240 subq $8, %rsp /* save room for the post-syscall addr */ 241 movq %rcx, (%rsp) /* Save post-syscall addr on stack */ 242 243 /* 244 * To 'return' to our user-space handler, we just need to copy 245 * its address into %rcx. 246 */ 247 GET_P_BRAND_DATA(%r15, 1, %rcx);/* get p_brand_data ptr */ 248 movq SPD_HANDLER(%rcx), %rcx /* get p_brand_data->spd_handler ptr */ 249 movq (%r15), %r15 /* Restore scratch register */ 250 jmp nopop_sys_syscall_sysretq 2519: 252 popq %r15 253 retq 254 255SET_SIZE(sn1_brand_syscall_callback) 256 257/* 258 * %rax - syscall number 259 * %rcx - user space %esp 260 * %rdx - user space return address 261 * 262 * XXX: not tested yet. Need a Nocona machine first. 263 */ 264ENTRY(sn1_brand_sysenter_callback) 265 266 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 267 268 subq $4, %rcx /* Save room for user ret addr */ 269 movq %rdx, (%rcx) /* Save current return addr */ 270 GET_P_BRAND_DATA(%rsp, 1, %rdx) /* get p_brand_data */ 271 movq SPD_HANDLER(%rdx), %rdx /* get p_brand_data->spd_handler ptr */ 272 popq %r15 /* Restore scratch register */ 273 sysexit 2749: 275 popq %r15 276 ret 277SET_SIZE(sn1_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 */ 290ENTRY(sn1_brand_int91_callback) 291 292 CALLBACK_PROLOGUE(%rax, %r15, %r15b) 293 pushq %rax /* Save scratch register */ 294 295 GET_P_BRAND_DATA(%rsp, 2, %r15) /* get p_brand_data */ 296 movq SPD_HANDLER(%r15), %r15 /* get sn1_proc_data->spd_handler ptr */ 297 GET_V(%rsp, 2, V_SSP, %rax) /* Get saved %esp */ 298 movq %r15, (%rax) /* replace iret target address with hdlr */ 299 300 /* 301 * Adjust the caller's stack so we return to the instruction after 302 * the syscall on the next 'ret' in userspace - not to the parent 303 * routine. 304 */ 305 movq 24(%rax), %r15 /* Get user's %esp */ 306 subq $4, %r15 /* Make room for new ret addr */ 307 movq %r15, 24(%rax) /* Replace current with updated %esp */ 308 309 GET_V(%rsp, 2, V_URET_ADDR, %rax) 310 movl %eax, (%r15) /* Put it on the user's stack */ 311 312 popq %rax /* Restore scratch register */ 313 popq %r15 /* Restore scratch register */ 314 movq V_SSP(%rsp), %rsp /* Remove callback stuff from stack */ 315 jmp nopop_sys_rtt_syscall32 3169: 317 popq %r15 318 retq 319SET_SIZE(sn1_brand_int91_callback) 320 321#else /* !__amd64 */ 322 323/* 324 * lcall handler for 32-bit OS 325 * %eax - syscall number 326 * 327 * Above the stack contents common to all callbacks is the 328 * int/lcall-specific state: 329 * -------------------------------------- 330 * | 44 | user's %ss | 331 * | 40 | user's %esp | 332 * | 36 | EFLAGS register | 333 * v 32 | user's %cs | 334 * 28 | user's %eip | 335 * -------------------------------------- 336 */ 337#define V_U_SS (V_END + (CLONGSIZE * 4)) 338#define V_U_ESP (V_END + (CLONGSIZE * 3)) 339#define V_EFLAGS (V_END + (CLONGSIZE * 2)) 340#define V_U_CS (V_END + (CLONGSIZE * 1)) 341#define V_U_EIP (V_END + (CLONGSIZE * 0)) 342 343ENTRY(sn1_brand_syscall_callback) 344 345 CALLBACK_PROLOGUE(%eax, %ebx, %bl) 346 pushl %eax /* Save scratch register */ 347 348 /* replace iret target address with user-space hdlr */ 349 GET_P_BRAND_DATA(%esp, 2, %ebx) /* get p_brand_data ptr */ 350 movl SPD_HANDLER(%ebx), %ebx /* get p_brand_data->spd_handler ptr */ 351 SET_V(%esp, 2, V_U_EIP, %ebx) /* set iret target address to hdlr */ 352 353 /* 354 * Adjust the caller's stack so we return to the instruction after 355 * the syscall on the next 'ret' in userspace - not to the parent 356 * routine. 357 */ 358 GET_V(%esp, 2, V_URET_ADDR, %ebx) /* Get new post-syscall ret addr */ 359 GET_V(%esp, 2, V_U_ESP, %eax) /* Get user %esp */ 360 subl $4, %eax /* Make room for new ret addr */ 361 SET_V(%esp, 2, V_U_ESP, %eax) /* Updated user %esp */ 362 movl %ebx, (%eax) /* Put new ret addr on user stack */ 363 364 popl %eax /* Restore scratch register */ 365 popl %ebx /* Restore scratch register */ 366 addl $V_END, %esp /* Remove all callback stuff from stack */ 367 jmp nopop_sys_rtt_syscall 3689: 369 popl %ebx 370 ret 371SET_SIZE(sn1_brand_syscall_callback) 372 373/* 374 * %eax - syscall number 375 * %ecx - user space %esp 376 * %edx - user space return address 377 */ 378ENTRY(sn1_brand_sysenter_callback) 379 380 CALLBACK_PROLOGUE(%eax, %ebx, %bl) 381 382 subl $4, %ecx /* Save room for user ret addr */ 383 movl %edx, (%ecx) /* Save current return addr */ 384 GET_P_BRAND_DATA(%esp, 1, %edx) /* get p_brand_data */ 385 movl SPD_HANDLER(%edx), %edx /* get p_brand_data->spd_handler */ 386 popl %ebx /* Restore scratch register */ 387 sysexit 3889: 389 popl %ebx 390 ret 391SET_SIZE(sn1_brand_sysenter_callback) 392 393#endif /* !__amd64 */ 394#endif /* lint */ 395