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/* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26#include <brand_misc.h> 27 28/* 29 * Each JMP must occupy 16 bytes 30 */ 31#define JMP \ 32 pushl $_CONST(. - brand_handler_table); \ 33 jmp brand_handler; \ 34 .align 16; 35 36#define JMP4 JMP; JMP; JMP; JMP 37#define JMP16 JMP4; JMP4; JMP4; JMP4 38#define JMP64 JMP16; JMP16; JMP16; JMP16 39#define JMP256 JMP64; JMP64; JMP64; JMP64 40 41#if defined(lint) 42 43void 44brand_handler_table(void) 45{} 46 47void 48brand_handler(void) 49{ 50} 51 52#else /* lint */ 53 54 /* 55 * On entry to this table, %eax will hold the return address. The 56 * location where we enter the table is a function of the system 57 * call number. The table needs the same alignment as the individual 58 * entries. 59 */ 60 .align 16 61 ENTRY_NP(brand_handler_table) 62 JMP256 63 SET_SIZE(brand_handler_table) 64 65#define PIC_SETUP(r) \ 66 call 9f; \ 679: \ 68 popl r; \ 69 addl $_GLOBAL_OFFSET_TABLE_ + [. - 9b], r 70 71 /* 72 * %eax - userland return address 73 * stack contains: 74 * | -------------------------------------- 75 * v 4 | syscall arguments | 76 * %esp+0 | syscall number | 77 * -------------------------------------- 78 */ 79 ENTRY_NP(brand_handler) 80 pushl %ebp /* allocate a stack frame */ 81 movl %esp, %ebp 82 83 /* Save registers at the time of the syscall. */ 84 movl $0, EH_LOCALS_GREG(TRAPNO)(%ebp) 85 movl $0, EH_LOCALS_GREG(ERR)(%ebp) 86 movl %ebx, EH_LOCALS_GREG(EBX)(%ebp) 87 movl %ecx, EH_LOCALS_GREG(ECX)(%ebp) 88 movl %edx, EH_LOCALS_GREG(EDX)(%ebp) 89 movl %edi, EH_LOCALS_GREG(EDI)(%ebp) 90 movl %esi, EH_LOCALS_GREG(ESI)(%ebp) 91 mov %cs, EH_LOCALS_GREG(CS)(%ebp) 92 mov %ds, EH_LOCALS_GREG(DS)(%ebp) 93 mov %es, EH_LOCALS_GREG(ES)(%ebp) 94 mov %fs, EH_LOCALS_GREG(FS)(%ebp) 95 mov %gs, EH_LOCALS_GREG(GS)(%ebp) 96 pushfl /* save syscall flags */ 97 popl %ecx 98 movl %ecx, EH_LOCALS_GREG(EFL)(%ebp) 99 movl EH_ARGS_OFFSET(0)(%ebp), %ecx /* save syscall ebp */ 100 movl %ecx, EH_LOCALS_GREG(EBP)(%ebp) 101 movl %ebp, %ecx /* save syscall esp */ 102 addl $CPTRSIZE, %ecx 103 movl %ecx, EH_LOCALS_GREG(ESP)(%ebp) 104 105 /* 106 * The kernel drops us into the middle of the brand_handle_table 107 * above that then pushes that table offset onto the stack, and calls 108 * into brand_handler. That offset indicates the system call number 109 * while %eax holds the return address for the system call. We replace 110 * the value on the stack with the return address, and use the value to 111 * compute the system call number by dividing by the table entry size. 112 */ 113 xchgl CPTRSIZE(%ebp), %eax /* swap JMP table offset and ret addr */ 114 shrl $4, %eax /* table_offset/size = syscall num */ 115 movl %eax, EH_LOCALS_GREG(EAX)(%ebp) /* save syscall num */ 116 117 /* 118 * Finish setting up our stack frame. We would normally do this 119 * upon entry to this function, but in this case we delayed it 120 * because a "sub" operation can modify flags and we wanted to 121 * save the flags into the gregset_t above before they get modified. 122 * 123 * Our stack frame format is documented in brand_misc.h. 124 */ 125 subl $EH_LOCALS_SIZE, %esp 126 127 /* Look up the system call's entry in the sysent table */ 128 PIC_SETUP(%ecx) 129 movl brand_sysent_table@GOT(%ecx), %edx /* %edx = sysent_table */ 130 shll $3, %eax /* each entry is 8 bytes */ 131 add %eax, %edx /* %edx = sysent entry address */ 132 133 /* 134 * Get the return value flag and the number of arguments from the 135 * sysent table. 136 */ 137 movl CPTRSIZE(%edx), %ecx /* number of args + rv flag */ 138 andl $RV_MASK, %ecx /* strip out number of args */ 139 movl %ecx, EH_LOCALS_RVFLAG(%ebp) /* save rv flag */ 140 movl CPTRSIZE(%edx), %ecx /* number of args + rv flag */ 141 andl $NARGS_MASK, %ecx /* strip out rv flag */ 142 143 /* 144 * Setup arguments for our emulation call. Our input arguments, 145 * 0 to N, will become emulation call arguments 1 to N+1. 146 * %ecx == number of arguments. 147 */ 148 movl %ebp, %esi /* args are at 12(%ebp) */ 149 addl $EH_ARGS_OFFSET(3), %esi 150 movl %esp, %edi /* copy args to 4(%esp) */ 151 addl $EH_ARGS_OFFSET(1), %edi 152 rep; smovl /* copy: (%esi) -> (%edi) */ 153 /* copy: %ecx 32-bit words */ 154 movl EH_LOCALS_GREG(ESI)(%ebp), %esi /* restore %esi */ 155 movl EH_LOCALS_GREG(EDI)(%ebp), %edi /* restore %edi */ 156 157 /* 158 * The first parameter to the emulation callback function is a 159 * pointer to a sysret_t structure. 160 */ 161 movl %ebp, %ecx 162 addl $EH_LOCALS_SYSRET, %ecx 163 movl %ecx, EH_ARGS_OFFSET(0)(%esp) /* arg0 == sysret_t ptr */ 164 165 /* invoke the emulation routine */ 166 ALTENTRY(brand_handler_savepc) 167 call *(%edx) /* call emulation routine */ 168 169 /* restore scratch registers */ 170 movl EH_LOCALS_GREG(ECX)(%ebp), %ecx /* restore %ecx */ 171 movl EH_LOCALS_GREG(EDX)(%ebp), %edx /* restore %edx */ 172 173 /* Check for syscall emulation success or failure */ 174 cmpl $0, %eax /* check for an error */ 175 je success 176 stc /* failure, set carry flag */ 177 jmp return /* return, %rax == errno */ 178 179success: 180 /* There is always at least one return value. */ 181 movl EH_LOCALS_SYSRET1(%ebp), %eax /* %eax == sys_rval1 */ 182 cmpl $RV_DEFAULT, EH_LOCALS_RVFLAG(%ebp) /* check rv flag */ 183 je clear_carry 184 mov EH_LOCALS_SYSRET2(%ebp), %edx /* %edx == sys_rval2 */ 185clear_carry: 186 clc /* success, clear carry flag */ 187 188return: 189 movl %ebp, %esp /* restore stack */ 190 popl %ebp 191 ret /* ret to instr after syscall */ 192 SET_SIZE(brand_handler) 193 194 195#endif /* lint */ 196