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#include <brand_misc.h> 26 27/* 28 * Each JMP must occupy 16 bytes 29 */ 30#define JMP \ 31 pushq $_CONST(. - brand_handler_table); \ 32 jmp brand_handler; \ 33 .align 16; 34 35#define JMP4 JMP; JMP; JMP; JMP 36#define JMP16 JMP4; JMP4; JMP4; JMP4 37#define JMP64 JMP16; JMP16; JMP16; JMP16 38#define JMP256 JMP64; JMP64; JMP64; JMP64 39 40#if defined(lint) 41 42void 43brand_handler_table(void) 44{} 45 46void 47brand_handler(void) 48{ 49} 50 51#else /* lint */ 52 53 /* 54 * On entry to this table, %rax will hold the return address. The 55 * location where we enter the table is a function of the system 56 * call number. The table needs the same alignment as the individual 57 * entries. 58 */ 59 .align 16 60 ENTRY_NP(brand_handler_table) 61 JMP256 62 SET_SIZE(brand_handler_table) 63 64 /* 65 * %rax - userland return address 66 * stack contains: 67 * | -------------------------------------- 68 * v 8 | syscall arguments | 69 * %rsp+0 | syscall number | 70 * -------------------------------------- 71 */ 72 ENTRY_NP(brand_handler) 73 pushq %rbp /* allocate stack frame */ 74 movq %rsp, %rbp 75 76 /* Save registers at the time of the syscall. */ 77 movq $0, EH_LOCALS_GREG(REG_TRAPNO)(%rbp) 78 movq $0, EH_LOCALS_GREG(REG_ERR)(%rbp) 79 movq %r15, EH_LOCALS_GREG(REG_R15)(%rbp) 80 movq %r14, EH_LOCALS_GREG(REG_R14)(%rbp) 81 movq %r13, EH_LOCALS_GREG(REG_R13)(%rbp) 82 movq %r12, EH_LOCALS_GREG(REG_R12)(%rbp) 83 movq %r11, EH_LOCALS_GREG(REG_R11)(%rbp) 84 movq %r10, EH_LOCALS_GREG(REG_R10)(%rbp) 85 movq %r9, EH_LOCALS_GREG(REG_R9)(%rbp) 86 movq %r8, EH_LOCALS_GREG(REG_R8)(%rbp) 87 movq %rdi, EH_LOCALS_GREG(REG_RDI)(%rbp) 88 movq %rsi, EH_LOCALS_GREG(REG_RSI)(%rbp) 89 movq %rbx, EH_LOCALS_GREG(REG_RBX)(%rbp) 90 movq %rcx, EH_LOCALS_GREG(REG_RCX)(%rbp) 91 movq %rdx, EH_LOCALS_GREG(REG_RDX)(%rbp) 92 xorq %rcx, %rcx 93 movw %cs, %cx 94 movq %rcx, EH_LOCALS_GREG(REG_CS)(%rbp) 95 movw %ds, %cx 96 movq %rcx, EH_LOCALS_GREG(REG_DS)(%rbp) 97 movw %es, %cx 98 movq %rcx, EH_LOCALS_GREG(REG_ES)(%rbp) 99 movw %fs, %cx 100 movq %rcx, EH_LOCALS_GREG(REG_FS)(%rbp) 101 movw %gs, %cx 102 movq %rcx, EH_LOCALS_GREG(REG_GS)(%rbp) 103 movw %ss, %cx 104 movq %rcx, EH_LOCALS_GREG(REG_SS)(%rbp) 105 pushfq /* save syscall flags */ 106 popq %r12 107 movq %r12, EH_LOCALS_GREG(REG_RFL)(%rbp) 108 movq EH_ARGS_OFFSET(0)(%rbp), %r12 /* save syscall rbp */ 109 movq %r12, EH_LOCALS_GREG(REG_RBP)(%rbp) 110 movq %rbp, %r12 /* save syscall rsp */ 111 addq $CPTRSIZE, %r12 112 movq %r12, EH_LOCALS_GREG(REG_RSP)(%rbp) 113 movq %fs:0, %r12 /* save syscall fsbase */ 114 movq %r12, EH_LOCALS_GREG(REG_FSBASE)(%rbp) 115 movq $0, EH_LOCALS_GREG(REG_GSBASE)(%rbp) 116 117 /* 118 * The kernel drops us into the middle of the brand_handle_table 119 * above that then pushes that table offset onto the stack, and calls 120 * into brand_handler. That offset indicates the system call number 121 * while %rax holds the return address for the system call. We replace 122 * the value on the stack with the return address, and use the value to 123 * compute the system call number by dividing by the table entry size. 124 */ 125 xchgq CPTRSIZE(%rbp), %rax /* swap JMP table offset and ret addr */ 126 shrq $4, %rax /* table_offset/size = syscall num */ 127 movq %rax, EH_LOCALS_GREG(REG_RAX)(%rbp) /* save syscall num */ 128 129 /* 130 * Finish setting up our stack frame. We would normally do this 131 * upon entry to this function, but in this case we delayed it 132 * because a "sub" operation can modify flags and we wanted to 133 * save the flags into the gregset_t above before they get modified. 134 * 135 * Our stack frame format is documented in brand_misc.h. 136 */ 137 subq $EH_LOCALS_SIZE, %rsp 138 139 /* Look up the system call's entry in the sysent table */ 140 movq brand_sysent_table@GOTPCREL(%rip), %r11 /* %r11 = sysent_tbl */ 141 shlq $4, %rax /* each entry is 16 bytes */ 142 addq %rax, %r11 /* %r11 = sysent entry address */ 143 144 /* 145 * Get the return value flag and the number of arguments from the 146 * sysent table. 147 */ 148 movq CPTRSIZE(%r11), %r12 /* number of args + rv flag */ 149 andq $RV_MASK, %r12 /* strip out number of args */ 150 movq %r12, EH_LOCALS_RVFLAG(%rbp) /* save rv flag */ 151 152 /* 153 * Setup arguments for our emulation call. Our input arguments, 154 * 0 to N, will become emulation call arguments 1 to N+1. 155 * 156 * Note: Syscall argument passing is different from function call 157 * argument passing on amd64. For function calls, the fourth arg 158 * is passed via %rcx, but for system calls the 4th argument is 159 * passed via %r10. This is because in amd64, the syscall 160 * instruction puts lower 32 bit of %rflags in %r11 and puts the 161 * %rip value to %rcx. 162 */ 163 movq EH_ARGS_OFFSET(4)(%rbp), %r12 /* copy 8th arg */ 164 movq %r12, EH_ARGS_OFFSET(2)(%rsp) 165 movq EH_ARGS_OFFSET(3)(%rbp), %r12 /* copy 7th arg */ 166 movq %r12, EH_ARGS_OFFSET(1)(%rsp) 167 movq %r9, EH_ARGS_OFFSET(0)(%rsp) 168 movq %r8, %r9 169 movq %r10, %r8 170 movq %rdx, %rcx 171 movq %rsi, %rdx 172 movq %rdi, %rsi 173 174 /* 175 * The first parameter to the emulation callback function is a 176 * pointer to a sysret_t structure. 177 */ 178 movq %rbp, %rdi 179 addq $EH_LOCALS_SYSRET, %rdi /* arg0 == sysret_t ptr */ 180 181 /* invoke the emulation routine */ 182 ALTENTRY(brand_handler_savepc) 183 call *(%r11) 184 185 /* restore scratch and parameter registers */ 186 movq EH_LOCALS_GREG(REG_R12)(%rbp), %r12 /* restore %r12 */ 187 movq EH_LOCALS_GREG(REG_R11)(%rbp), %r11 /* restore %r11 */ 188 movq EH_LOCALS_GREG(REG_R10)(%rbp), %r10 /* restore %r10 */ 189 movq EH_LOCALS_GREG(REG_R9)(%rbp), %r9 /* restore %r9 */ 190 movq EH_LOCALS_GREG(REG_R8)(%rbp), %r8 /* restore %r8 */ 191 movq EH_LOCALS_GREG(REG_RCX)(%rbp), %rcx /* restore %rcx */ 192 movq EH_LOCALS_GREG(REG_RDX)(%rbp), %rdx /* restore %rdx */ 193 movq EH_LOCALS_GREG(REG_RSI)(%rbp), %rsi /* restore %rsi */ 194 movq EH_LOCALS_GREG(REG_RDI)(%rbp), %rdi /* restore %rdi */ 195 196 /* Check for syscall emulation success or failure */ 197 cmpq $0, %rax 198 je success 199 stc /* failure, set carry flag */ 200 jmp return /* return, %rax == errno */ 201 202success: 203 /* There is always at least one return value. */ 204 movq EH_LOCALS_SYSRET1(%rbp), %rax /* %rax == sys_rval1 */ 205 cmpq $RV_DEFAULT, EH_LOCALS_RVFLAG(%rbp) /* check rv flag */ 206 je clear_carry 207 mov EH_LOCALS_SYSRET2(%rbp), %rdx /* %rdx == sys_rval2 */ 208clear_carry: 209 clc /* success, clear carry flag */ 210 211return: 212 movq %rbp, %rsp /* restore stack */ 213 popq %rbp 214 ret /* ret to instr after syscall */ 215 SET_SIZE(brand_handler) 216 217 218#endif /* lint */ 219