root/usr/src/lib/brand/shared/brand/sparc/handler.S
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <brand_misc.h>

#if defined(lint)

void
brand_handler(void)
{
}

#else   /* !lint */

#define PIC_SETUP(r)                                            \
        mov     %o7, %g1;                                       \
9:      call    8f;                                             \
        sethi   %hi(_GLOBAL_OFFSET_TABLE_ - (9b - .)), r;       \
8:      or      r, %lo(_GLOBAL_OFFSET_TABLE_ - (9b - .)), r;    \
        add     r, %o7, r;                                      \
        mov     %g1, %o7

/*
 * Translate a global symbol into an address.  The resulting address
 * is returned in the first register parameter.  The second register
 * is just for scratch space.
 */
#if defined(__sparcv9)
#define GET_SYM_ADDR(r1, r2, name)              \
        PIC_SETUP(r1)                           ;\
        sethi   %hi(name), r2                   ;\
        or      r2, %lo(name), r2               ;\
        ldn     [r2 + r1], r1
#else /* !__sparcv9 */
#define GET_SYM_ADDR(r1, r2, name)              \
        PIC_SETUP(r1);                  \
        ld      [r1 + name], r1
#endif /* !__sparcv9 */

        .section        ".text"

        /*
         * When we get here, %g1 should contain the system call and
         * %g5 should contain the address immediately after the trap
         * instruction.
         */
        ENTRY_NP(brand_handler)

        /*
         * 64-bit sparc may need to save 3 parameters on the stack.
         * 32-bit sparc may need to save 4 parameters on the stack.
         *
         * Our stack frame format is documented in brand_misc.h.
         */
        save    %sp, -SA(MINFRAME + EH_LOCALS_SIZE), %sp

        /*
         * Save the current caller state into gregs and gwins.
         * Note that this state isn't exact, %g1 and %g5 have been
         * already been lost.  Also, we've pushed a stack frame so
         * the callers output registers are our input registers.
         */
        stn     %g0, [%sp + EH_LOCALS_GREG(REG_G1)]     /* %g1 is lost */
        stn     %g2, [%sp + EH_LOCALS_GREG(REG_G2)]
        stn     %g3, [%sp + EH_LOCALS_GREG(REG_G3)]
        stn     %g4, [%sp + EH_LOCALS_GREG(REG_G4)]
        stn     %g0, [%sp + EH_LOCALS_GREG(REG_G5)]     /* %g5 is lost */
        stn     %g6, [%sp + EH_LOCALS_GREG(REG_G6)]
        stn     %g7, [%sp + EH_LOCALS_GREG(REG_G7)]
        stn     %i0, [%sp + EH_LOCALS_GREG(REG_O0)]
        stn     %i1, [%sp + EH_LOCALS_GREG(REG_O1)]
        stn     %i2, [%sp + EH_LOCALS_GREG(REG_O2)]
        stn     %i3, [%sp + EH_LOCALS_GREG(REG_O3)]
        stn     %i4, [%sp + EH_LOCALS_GREG(REG_O4)]
        stn     %i5, [%sp + EH_LOCALS_GREG(REG_O5)]
        stn     %i6, [%sp + EH_LOCALS_GREG(REG_O6)]
        stn     %i7, [%sp + EH_LOCALS_GREG(REG_O7)]
        sub     %g5, 4, %o0
        stn     %o0, [%sp + EH_LOCALS_GREG(REG_PC)]
        stn     %g5, [%sp + EH_LOCALS_GREG(REG_nPC)]
        rd      %y, %o0
        stn     %o0, [%sp + EH_LOCALS_GREG(REG_Y)]
#if defined(__sparcv9)
        stn     %g0, [%sp + EH_LOCALS_GREG(REG_ASI)]
        rd      %fprs, %o0
        stn     %o0, [%sp + EH_LOCALS_GREG(REG_FPRS)]
#endif /* __sparcv9 */

        /*
         * Look up the system call's entry in the sysent table
         * and obtain the address of the proper emulation routine (%l2).
         */
        mov     %g1, %l5                        /* save syscall number */
        GET_SYM_ADDR(%l1, %l2, brand_sysent_table)
        mov     %l5, %g1                        /* restore syscall number */
        sll     %g1, (1 + CLONGSHIFT), %l2      /* Each entry has 2 longs */
        add     %l2, %l1, %l2                   /* index to proper entry */
        ldn     [%l2], %l2                      /* emulation func address */

        /*
         * Look up the system call's entry in the sysent table,
         * taking into account the posibility of indirect system calls, and
         * obtain the number of arguments (%l4) and return value flag (%l3).
         */
#if defined(__sparcv9)
        mov     %g1, %l3                        /* %g1 == syscall number */
#else /* !__sparcv9 */
        /*
         * Check for indirect system calls, in which case the real syscall
         * number is the first parameter to the indirect system call.
         */
        cmp     %g1, %g0                        /* saved syscall number */
        bne,a,pt %icc, no_indir                 /* indirect syscall? */
        mov     %g1, %l3                        /* %g1 == syscall number */
        mov     %i0, %l3                        /* %i0 == syscall number */
no_indir:
#endif /* !__sparcv9 */
        sll     %l3, (1 + CLONGSHIFT), %l3      /* Each entry has 2 longs */
        add     %l3, %l1, %l3                   /* index to proper entry */
        ldn     [%l3 + CPTRSIZE], %l4           /* number of args + rv flag */
        sethi   %hi(RV_MASK), %l5
        or      %l5, %lo(RV_MASK), %l5
        andcc   %l4, %l5, %l3                   /* strip out number of args*/
        andcc   %l4, NARGS_MASK, %l4            /* strip out rv flag */

        /*
         * Setup arguments for our emulation call.  Our input arguments,
         * 0 to N, will become emulation call arguments 1 to N+1.
         * %l4 == number of arguments.
         */
        mov     %i0, %o1
        mov     %i1, %o2
        mov     %i2, %o3
        mov     %i3, %o4
        mov     %i4, %o5

        /* 7th argument and above get passed on the stack */
        cmp     %l4, 0x6
        bl,pt   %ncc, args_copied
        nop
        stn     %i5, [%sp + EH_ARGS_OFFSET(0)]  /* copy 6th syscall arg */
        cmp     %l4, 0x7
        bl,pt   %ncc, args_copied
        nop
        ldn     [%fp + EH_ARGS_OFFSET(0)], %l5  /* copy 7th syscall arg */
        stn     %l5, [%sp + EH_ARGS_OFFSET(1)]
        cmp     %l4, 0x8
        bl,pt   %ncc, args_copied
        nop
        ldn     [%fp + EH_ARGS_OFFSET(1)], %l5
        stn     %l5, [%sp + EH_ARGS_OFFSET(2)]  /* copy 8th syscall arg */
#if !defined(__sparcv9)
        cmp     %l4, 0x9
        bl,pt   %ncc, args_copied
        nop
        ldn     [%fp + EH_ARGS_OFFSET(2)], %l5
        stn     %l5, [%sp + EH_ARGS_OFFSET(3)]  /* copy 9th syscall arg */
#endif /* !__sparcv9 */

args_copied:
        /*
         * The first parameter to the emulation callback function is a
         * pointer to a sysret_t structure.
         *
         * invoke the emulation routine.
         */
        ALTENTRY(brand_handler_savepc)
        call    %l2
        add     %sp, EH_LOCALS_SYSRET, %o0      /* arg0 == sysret_t ptr */

        /* Check for syscall emulation success or failure */
        cmp     %g0, %o0
        be      success
        nop
        subcc   %g0, 1, %g0                     /* failure, set carry flag */
        ba      return
        mov     %o0, %i0                        /* return, %o0 == errno */

success:
        /* There is always at least one return value. */
        ldn     [%sp + EH_LOCALS_SYSRET1], %i0  /* %i0 == sys_rval1 */
        cmp     %l3, RV_DEFAULT                 /* check rv flag */
        be,a    clear_carry
        mov     %g0, %i1                        /* clear second rval */
        ldn     [%sp + EH_LOCALS_SYSRET2], %i1  /* %i1 == sys_rval2 */
clear_carry:
        addcc   %g0, %g0, %g0                   /* success, clear carry flag */

return:
        /*
         * Our syscall emulation is complete.  Return to the caller that
         * originally invoked a system which needed emulation.  Note that
         * we have to load the return address that we saved earlier because
         * it's possible that %g5 was overwritten by a nested call into
         * this emulation library.
         */
        ldn     [%sp + EH_LOCALS_GREG(REG_nPC)], %g5
        jmp     %g5
        restore                                 /* delay slot */
        SET_SIZE(brand_handler)


#endif  /* !lint */