2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <brand_misc.h>
2N/A
2N/A/*
2N/A * Each JMP must occupy 16 bytes
2N/A */
2N/A#define JMP \
2N/A pushq $_CONST(. - brand_handler_table); \
2N/A jmp brand_handler; \
2N/A .align 16;
2N/A
2N/A#define JMP4 JMP; JMP; JMP; JMP
2N/A#define JMP16 JMP4; JMP4; JMP4; JMP4
2N/A#define JMP64 JMP16; JMP16; JMP16; JMP16
2N/A#define JMP256 JMP64; JMP64; JMP64; JMP64
2N/A
2N/A#if defined(lint)
2N/A
2N/Avoid
2N/Abrand_handler_table(void)
2N/A{}
2N/A
2N/Avoid
2N/Abrand_handler(void)
2N/A{
2N/A}
2N/A
2N/A#else /* lint */
2N/A
2N/A /*
2N/A * On entry to this table, %rax will hold the return address. The
2N/A * location where we enter the table is a function of the system
2N/A * call number. The table needs the same alignment as the individual
2N/A * entries.
2N/A */
2N/A .align 16
2N/A ENTRY_NP(brand_handler_table)
2N/A JMP256
2N/A SET_SIZE(brand_handler_table)
2N/A
2N/A /*
2N/A * %rax - userland return address
2N/A * stack contains:
2N/A * | --------------------------------------
2N/A * v 8 | syscall arguments |
2N/A * %rsp+0 | syscall number |
2N/A * --------------------------------------
2N/A */
2N/A ENTRY_NP(brand_handler)
2N/A pushq %rbp /* allocate stack frame */
2N/A movq %rsp, %rbp
2N/A
2N/A /* Save registers at the time of the syscall. */
2N/A movq $0, EH_LOCALS_GREG(REG_TRAPNO)(%rbp)
2N/A movq $0, EH_LOCALS_GREG(REG_ERR)(%rbp)
2N/A movq %r15, EH_LOCALS_GREG(REG_R15)(%rbp)
2N/A movq %r14, EH_LOCALS_GREG(REG_R14)(%rbp)
2N/A movq %r13, EH_LOCALS_GREG(REG_R13)(%rbp)
2N/A movq %r12, EH_LOCALS_GREG(REG_R12)(%rbp)
2N/A movq %r11, EH_LOCALS_GREG(REG_R11)(%rbp)
2N/A movq %r10, EH_LOCALS_GREG(REG_R10)(%rbp)
2N/A movq %r9, EH_LOCALS_GREG(REG_R9)(%rbp)
2N/A movq %r8, EH_LOCALS_GREG(REG_R8)(%rbp)
2N/A movq %rdi, EH_LOCALS_GREG(REG_RDI)(%rbp)
2N/A movq %rsi, EH_LOCALS_GREG(REG_RSI)(%rbp)
2N/A movq %rbx, EH_LOCALS_GREG(REG_RBX)(%rbp)
2N/A movq %rcx, EH_LOCALS_GREG(REG_RCX)(%rbp)
2N/A movq %rdx, EH_LOCALS_GREG(REG_RDX)(%rbp)
2N/A xorq %rcx, %rcx
2N/A movw %cs, %cx
2N/A movq %rcx, EH_LOCALS_GREG(REG_CS)(%rbp)
2N/A movw %ds, %cx
2N/A movq %rcx, EH_LOCALS_GREG(REG_DS)(%rbp)
2N/A movw %es, %cx
2N/A movq %rcx, EH_LOCALS_GREG(REG_ES)(%rbp)
2N/A movw %fs, %cx
2N/A movq %rcx, EH_LOCALS_GREG(REG_FS)(%rbp)
2N/A movw %gs, %cx
2N/A movq %rcx, EH_LOCALS_GREG(REG_GS)(%rbp)
2N/A movw %ss, %cx
2N/A movq %rcx, EH_LOCALS_GREG(REG_SS)(%rbp)
2N/A pushfq /* save syscall flags */
2N/A popq %r12
2N/A movq %r12, EH_LOCALS_GREG(REG_RFL)(%rbp)
2N/A movq EH_ARGS_OFFSET(0)(%rbp), %r12 /* save syscall rbp */
2N/A movq %r12, EH_LOCALS_GREG(REG_RBP)(%rbp)
2N/A movq %rbp, %r12 /* save syscall rsp */
2N/A addq $CPTRSIZE, %r12
2N/A movq %r12, EH_LOCALS_GREG(REG_RSP)(%rbp)
2N/A movq %fs:0, %r12 /* save syscall fsbase */
2N/A movq %r12, EH_LOCALS_GREG(REG_FSBASE)(%rbp)
2N/A movq $0, EH_LOCALS_GREG(REG_GSBASE)(%rbp)
2N/A
2N/A /*
2N/A * The kernel drops us into the middle of the brand_handle_table
2N/A * above that then pushes that table offset onto the stack, and calls
2N/A * into brand_handler. That offset indicates the system call number
2N/A * while %rax holds the return address for the system call. We replace
2N/A * the value on the stack with the return address, and use the value to
2N/A * compute the system call number by dividing by the table entry size.
2N/A */
2N/A xchgq CPTRSIZE(%rbp), %rax /* swap JMP table offset and ret addr */
2N/A shrq $4, %rax /* table_offset/size = syscall num */
2N/A movq %rax, EH_LOCALS_GREG(REG_RAX)(%rbp) /* save syscall num */
2N/A
2N/A /*
2N/A * Finish setting up our stack frame. We would normally do this
2N/A * upon entry to this function, but in this case we delayed it
2N/A * because a "sub" operation can modify flags and we wanted to
2N/A * save the flags into the gregset_t above before they get modified.
2N/A *
2N/A * Our stack frame format is documented in brand_misc.h.
2N/A */
2N/A subq $EH_LOCALS_SIZE, %rsp
2N/A
2N/A /* Look up the system call's entry in the sysent table */
2N/A movq brand_sysent_table@GOTPCREL(%rip), %r11 /* %r11 = sysent_tbl */
2N/A shlq $4, %rax /* each entry is 16 bytes */
2N/A addq %rax, %r11 /* %r11 = sysent entry address */
2N/A
2N/A /*
2N/A * Get the return value flag and the number of arguments from the
2N/A * sysent table.
2N/A */
2N/A movq CPTRSIZE(%r11), %r12 /* number of args + rv flag */
2N/A andq $RV_MASK, %r12 /* strip out number of args */
2N/A movq %r12, EH_LOCALS_RVFLAG(%rbp) /* save rv flag */
2N/A
2N/A /*
2N/A * Setup arguments for our emulation call. Our input arguments,
2N/A * 0 to N, will become emulation call arguments 1 to N+1.
2N/A *
2N/A * Note: Syscall argument passing is different from function call
2N/A * argument passing on amd64. For function calls, the fourth arg
2N/A * is passed via %rcx, but for system calls the 4th argument is
2N/A * passed via %r10. This is because in amd64, the syscall
2N/A * instruction puts lower 32 bit of %rflags in %r11 and puts the
2N/A * %rip value to %rcx.
2N/A */
2N/A movq EH_ARGS_OFFSET(4)(%rbp), %r12 /* copy 8th arg */
2N/A movq %r12, EH_ARGS_OFFSET(2)(%rsp)
2N/A movq EH_ARGS_OFFSET(3)(%rbp), %r12 /* copy 7th arg */
2N/A movq %r12, EH_ARGS_OFFSET(1)(%rsp)
2N/A movq %r9, EH_ARGS_OFFSET(0)(%rsp)
2N/A movq %r8, %r9
2N/A movq %r10, %r8
2N/A movq %rdx, %rcx
2N/A movq %rsi, %rdx
2N/A movq %rdi, %rsi
2N/A
2N/A /*
2N/A * The first parameter to the emulation callback function is a
2N/A * pointer to a sysret_t structure.
2N/A */
2N/A movq %rbp, %rdi
2N/A addq $EH_LOCALS_SYSRET, %rdi /* arg0 == sysret_t ptr */
2N/A
2N/A /* invoke the emulation routine */
2N/A ALTENTRY(brand_handler_savepc)
2N/A call *(%r11)
2N/A
2N/A /* restore scratch and parameter registers */
2N/A movq EH_LOCALS_GREG(REG_R12)(%rbp), %r12 /* restore %r12 */
2N/A movq EH_LOCALS_GREG(REG_R11)(%rbp), %r11 /* restore %r11 */
2N/A movq EH_LOCALS_GREG(REG_R10)(%rbp), %r10 /* restore %r10 */
2N/A movq EH_LOCALS_GREG(REG_R9)(%rbp), %r9 /* restore %r9 */
2N/A movq EH_LOCALS_GREG(REG_R8)(%rbp), %r8 /* restore %r8 */
2N/A movq EH_LOCALS_GREG(REG_RCX)(%rbp), %rcx /* restore %rcx */
2N/A movq EH_LOCALS_GREG(REG_RDX)(%rbp), %rdx /* restore %rdx */
2N/A movq EH_LOCALS_GREG(REG_RSI)(%rbp), %rsi /* restore %rsi */
2N/A movq EH_LOCALS_GREG(REG_RDI)(%rbp), %rdi /* restore %rdi */
2N/A
2N/A /* Check for syscall emulation success or failure */
2N/A cmpq $0, %rax
2N/A je success
2N/A stc /* failure, set carry flag */
2N/A jmp return /* return, %rax == errno */
2N/A
2N/Asuccess:
2N/A /* There is always at least one return value. */
2N/A movq EH_LOCALS_SYSRET1(%rbp), %rax /* %rax == sys_rval1 */
2N/A cmpq $RV_DEFAULT, EH_LOCALS_RVFLAG(%rbp) /* check rv flag */
2N/A je clear_carry
2N/A mov EH_LOCALS_SYSRET2(%rbp), %rdx /* %rdx == sys_rval2 */
2N/Aclear_carry:
2N/A clc /* success, clear carry flag */
2N/A
2N/Areturn:
2N/A movq %rbp, %rsp /* restore stack */
2N/A popq %rbp
2N/A ret /* ret to instr after syscall */
2N/A SET_SIZE(brand_handler)
2N/A
2N/A
2N/A#endif /* lint */