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#if defined(lint)
2N/A
2N/Avoid
2N/Abrand_handler(void)
2N/A{
2N/A}
2N/A
2N/A#else /* !lint */
2N/A
2N/A#define PIC_SETUP(r) \
2N/A mov %o7, %g1; \
2N/A9: call 8f; \
2N/A sethi %hi(_GLOBAL_OFFSET_TABLE_ - (9b - .)), r; \
2N/A8: or r, %lo(_GLOBAL_OFFSET_TABLE_ - (9b - .)), r; \
2N/A add r, %o7, r; \
2N/A mov %g1, %o7
2N/A
2N/A/*
2N/A * Translate a global symbol into an address. The resulting address
2N/A * is returned in the first register parameter. The second register
2N/A * is just for scratch space.
2N/A */
2N/A#if defined(__sparcv9)
2N/A#define GET_SYM_ADDR(r1, r2, name) \
2N/A PIC_SETUP(r1) ;\
2N/A sethi %hi(name), r2 ;\
2N/A or r2, %lo(name), r2 ;\
2N/A ldn [r2 + r1], r1
2N/A#else /* !__sparcv9 */
2N/A#define GET_SYM_ADDR(r1, r2, name) \
2N/A PIC_SETUP(r1); \
2N/A ld [r1 + name], r1
2N/A#endif /* !__sparcv9 */
2N/A
2N/A .section ".text"
2N/A
2N/A /*
2N/A * When we get here, %g1 should contain the system call and
2N/A * %g5 should contain the address immediately after the trap
2N/A * instruction.
2N/A */
2N/A ENTRY_NP(brand_handler)
2N/A
2N/A /*
2N/A * 64-bit sparc may need to save 3 parameters on the stack.
2N/A * 32-bit sparc may need to save 4 parameters on the stack.
2N/A *
2N/A * Our stack frame format is documented in brand_misc.h.
2N/A */
2N/A save %sp, -SA(MINFRAME + EH_LOCALS_SIZE), %sp
2N/A
2N/A /*
2N/A * Save the current caller state into gregs and gwins.
2N/A * Note that this state isn't exact, %g1 and %g5 have been
2N/A * already been lost. Also, we've pushed a stack frame so
2N/A * the callers output registers are our input registers.
2N/A */
2N/A stn %g0, [%sp + EH_LOCALS_GREG(REG_G1)] /* %g1 is lost */
2N/A stn %g2, [%sp + EH_LOCALS_GREG(REG_G2)]
2N/A stn %g3, [%sp + EH_LOCALS_GREG(REG_G3)]
2N/A stn %g4, [%sp + EH_LOCALS_GREG(REG_G4)]
2N/A stn %g0, [%sp + EH_LOCALS_GREG(REG_G5)] /* %g5 is lost */
2N/A stn %g6, [%sp + EH_LOCALS_GREG(REG_G6)]
2N/A stn %g7, [%sp + EH_LOCALS_GREG(REG_G7)]
2N/A stn %i0, [%sp + EH_LOCALS_GREG(REG_O0)]
2N/A stn %i1, [%sp + EH_LOCALS_GREG(REG_O1)]
2N/A stn %i2, [%sp + EH_LOCALS_GREG(REG_O2)]
2N/A stn %i3, [%sp + EH_LOCALS_GREG(REG_O3)]
2N/A stn %i4, [%sp + EH_LOCALS_GREG(REG_O4)]
2N/A stn %i5, [%sp + EH_LOCALS_GREG(REG_O5)]
2N/A stn %i6, [%sp + EH_LOCALS_GREG(REG_O6)]
2N/A stn %i7, [%sp + EH_LOCALS_GREG(REG_O7)]
2N/A sub %g5, 4, %o0
2N/A stn %o0, [%sp + EH_LOCALS_GREG(REG_PC)]
2N/A stn %g5, [%sp + EH_LOCALS_GREG(REG_nPC)]
2N/A rd %y, %o0
2N/A stn %o0, [%sp + EH_LOCALS_GREG(REG_Y)]
2N/A#if defined(__sparcv9)
2N/A stn %g0, [%sp + EH_LOCALS_GREG(REG_ASI)]
2N/A rd %fprs, %o0
2N/A stn %o0, [%sp + EH_LOCALS_GREG(REG_FPRS)]
2N/A#endif /* __sparcv9 */
2N/A
2N/A /*
2N/A * Look up the system call's entry in the sysent table
2N/A * and obtain the address of the proper emulation routine (%l2).
2N/A */
2N/A mov %g1, %l5 /* save syscall number */
2N/A GET_SYM_ADDR(%l1, %l2, brand_sysent_table)
2N/A mov %l5, %g1 /* restore syscall number */
2N/A sll %g1, (1 + CLONGSHIFT), %l2 /* Each entry has 2 longs */
2N/A add %l2, %l1, %l2 /* index to proper entry */
2N/A ldn [%l2], %l2 /* emulation func address */
2N/A
2N/A /*
2N/A * Look up the system call's entry in the sysent table,
2N/A * taking into account the posibility of indirect system calls, and
2N/A * obtain the number of arguments (%l4) and return value flag (%l3).
2N/A */
2N/A#if defined(__sparcv9)
2N/A mov %g1, %l3 /* %g1 == syscall number */
2N/A#else /* !__sparcv9 */
2N/A /*
2N/A * Check for indirect system calls, in which case the real syscall
2N/A * number is the first parameter to the indirect system call.
2N/A */
2N/A cmp %g1, %g0 /* saved syscall number */
2N/A bne,a,pt %icc, no_indir /* indirect syscall? */
2N/A mov %g1, %l3 /* %g1 == syscall number */
2N/A mov %i0, %l3 /* %i0 == syscall number */
2N/Ano_indir:
2N/A#endif /* !__sparcv9 */
2N/A sll %l3, (1 + CLONGSHIFT), %l3 /* Each entry has 2 longs */
2N/A add %l3, %l1, %l3 /* index to proper entry */
2N/A ldn [%l3 + CPTRSIZE], %l4 /* number of args + rv flag */
2N/A sethi %hi(RV_MASK), %l5
2N/A or %l5, %lo(RV_MASK), %l5
2N/A andcc %l4, %l5, %l3 /* strip out number of args*/
2N/A andcc %l4, NARGS_MASK, %l4 /* strip out 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 * %l4 == number of arguments.
2N/A */
2N/A mov %i0, %o1
2N/A mov %i1, %o2
2N/A mov %i2, %o3
2N/A mov %i3, %o4
2N/A mov %i4, %o5
2N/A
2N/A /* 7th argument and above get passed on the stack */
2N/A cmp %l4, 0x6
2N/A bl,pt %ncc, args_copied
2N/A nop
2N/A stn %i5, [%sp + EH_ARGS_OFFSET(0)] /* copy 6th syscall arg */
2N/A cmp %l4, 0x7
2N/A bl,pt %ncc, args_copied
2N/A nop
2N/A ldn [%fp + EH_ARGS_OFFSET(0)], %l5 /* copy 7th syscall arg */
2N/A stn %l5, [%sp + EH_ARGS_OFFSET(1)]
2N/A cmp %l4, 0x8
2N/A bl,pt %ncc, args_copied
2N/A nop
2N/A ldn [%fp + EH_ARGS_OFFSET(1)], %l5
2N/A stn %l5, [%sp + EH_ARGS_OFFSET(2)] /* copy 8th syscall arg */
2N/A#if !defined(__sparcv9)
2N/A cmp %l4, 0x9
2N/A bl,pt %ncc, args_copied
2N/A nop
2N/A ldn [%fp + EH_ARGS_OFFSET(2)], %l5
2N/A stn %l5, [%sp + EH_ARGS_OFFSET(3)] /* copy 9th syscall arg */
2N/A#endif /* !__sparcv9 */
2N/A
2N/Aargs_copied:
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 * invoke the emulation routine.
2N/A */
2N/A ALTENTRY(brand_handler_savepc)
2N/A call %l2
2N/A add %sp, EH_LOCALS_SYSRET, %o0 /* arg0 == sysret_t ptr */
2N/A
2N/A /* Check for syscall emulation success or failure */
2N/A cmp %g0, %o0
2N/A be success
2N/A nop
2N/A subcc %g0, 1, %g0 /* failure, set carry flag */
2N/A ba return
2N/A mov %o0, %i0 /* return, %o0 == errno */
2N/A
2N/Asuccess:
2N/A /* There is always at least one return value. */
2N/A ldn [%sp + EH_LOCALS_SYSRET1], %i0 /* %i0 == sys_rval1 */
2N/A cmp %l3, RV_DEFAULT /* check rv flag */
2N/A be,a clear_carry
2N/A mov %g0, %i1 /* clear second rval */
2N/A ldn [%sp + EH_LOCALS_SYSRET2], %i1 /* %i1 == sys_rval2 */
2N/Aclear_carry:
2N/A addcc %g0, %g0, %g0 /* success, clear carry flag */
2N/A
2N/Areturn:
2N/A /*
2N/A * Our syscall emulation is complete. Return to the caller that
2N/A * originally invoked a system which needed emulation. Note that
2N/A * we have to load the return address that we saved earlier because
2N/A * it's possible that %g5 was overwritten by a nested call into
2N/A * this emulation library.
2N/A */
2N/A ldn [%sp + EH_LOCALS_GREG(REG_nPC)], %g5
2N/A jmp %g5
2N/A restore /* delay slot */
2N/A SET_SIZE(brand_handler)
2N/A
2N/A
2N/A#endif /* !lint */