root/usr/src/uts/sparc/v9/ml/ddi_v9_asm.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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/asi.h>
#include <sys/asm_linkage.h>
#include <sys/machthread.h>
#include <sys/privregs.h>
#include <sys/ontrap.h>
#include <sys/dditypes.h>

#include "assym.h"

/*
 * This file implements the following ddi common access
 * functions:
 *
 *      ddi_get{8,16,32,64}
 *      ddi_put{8,16,32,64}
 *
 * and the underlying "trivial" implementations
 *
 *      i_ddi_{get,put}{8,16,32,64}
 *
 * which assume that there is no need to check the access handle -
 * byte swapping will be done by the mmu and the address is always
 * accessible via ld/st instructions.
 */

/*
 * The functionality of each of the ddi_get/put routines is performed by
 * the respective indirect function defined in the access handle.  Use of
 * the access handle functions provides compatibility across platforms for
 * drivers.
 *
 * By default, the indirect access handle functions are initialized to the
 * i_ddi_get/put routines to perform memory mapped IO.  If memory mapped IO
 * is not possible or desired, the access handle must be intialized to another
 * valid routine to perform the sepcified IO operation.
 *
 * The alignment and placement of the following functions have been optimized
 * such that the implementation specific versions, i_ddi*, fall within the
 * same cache-line of the generic versions, ddi_*.  This insures that an
 * I-cache hit will occur thus minimizing the performance impact of using the
 * access handle.
 */

        .align 32
        ENTRY(ddi_get8)
        ALTENTRY(ddi_io_get8)
        ALTENTRY(ddi_mem_get8)
        ldn      [%o0 + AHI_GET8], %g1   /* hdl->ahi_get8 access hndl */
        jmpl    %g1, %g0                 /* jump to access handle routine */
        nop
        SET_SIZE(ddi_get8)
        SET_SIZE(ddi_io_get8)
        SET_SIZE(ddi_mem_get8)

        .align 16
        ENTRY(i_ddi_get8)
        retl
        ldub    [%o1], %o0
        SET_SIZE(i_ddi_get8)

        .align 32
        ENTRY(ddi_get16)
        ALTENTRY(ddi_io_get16)
        ALTENTRY(ddi_mem_get16)
        ldn      [%o0 + AHI_GET16], %g1   /* hdl->ahi_get16 access hndl */
        jmpl    %g1, %g0                  /* jump to access handle routine */
        nop
        SET_SIZE(ddi_get16)
        SET_SIZE(ddi_io_get16)
        SET_SIZE(ddi_mem_get16)

        .align 16
        ENTRY(i_ddi_get16)
        ALTENTRY(i_ddi_swap_get16)
        retl
        lduh    [%o1], %o0
        SET_SIZE(i_ddi_get16)
        SET_SIZE(i_ddi_swap_get16)

        .align 32
        ENTRY(ddi_get32)
        ALTENTRY(ddi_io_get32)
        ALTENTRY(ddi_mem_get32)
        ldn      [%o0 + AHI_GET32], %g1   /* hdl->ahi_get32 access handle */
        jmpl    %g1, %g0                  /* jump to access handle routine */
        nop
        SET_SIZE(ddi_get32)
        SET_SIZE(ddi_io_get32)
        SET_SIZE(ddi_mem_get32)

        .align 16
        ENTRY(i_ddi_get32)
        ALTENTRY(i_ddi_swap_get32)
        retl
        ld      [%o1], %o0
        SET_SIZE(i_ddi_get32)
        SET_SIZE(i_ddi_swap_get32)

        .align 32
        ENTRY(ddi_get64)
        ALTENTRY(ddi_io_get64)
        ALTENTRY(ddi_mem_get64)
        ldn      [%o0 + AHI_GET64], %g1   /* hdl->ahi_get64 access handle */
        jmpl    %g1, %g0                  /* jump to access handle routine */
        nop
        SET_SIZE(ddi_get64)
        SET_SIZE(ddi_io_get64)
        SET_SIZE(ddi_mem_get64)

        .align 16
        ENTRY(i_ddi_get64)
        ALTENTRY(i_ddi_swap_get64)
        retl
        ldx     [%o1], %o0
        SET_SIZE(i_ddi_get64)
        SET_SIZE(i_ddi_swap_get64)

        .align 32
        ENTRY(ddi_put8)
        ALTENTRY(ddi_io_put8)
        ALTENTRY(ddi_mem_put8)
        ldn      [%o0 + AHI_PUT8], %g1   /* hdl->ahi_put8 access handle */
        jmpl    %g1, %g0                 /* jump to access handle routine */
        nop
        SET_SIZE(ddi_put8)
        SET_SIZE(ddi_io_put8)
        SET_SIZE(ddi_mem_put8)

        .align 16
        ENTRY(i_ddi_put8)
        retl
        stub    %o2, [%o1]
        SET_SIZE(i_ddi_put8)

        .align 32
        ENTRY(ddi_put16)
        ALTENTRY(ddi_io_put16)
        ALTENTRY(ddi_mem_put16)
        ldn      [%o0 + AHI_PUT16], %g1   /* hdl->ahi_put16 access handle */
        jmpl    %g1, %g0                  /* jump to access handle routine */
        nop
        SET_SIZE(ddi_put16)
        SET_SIZE(ddi_io_put16)
        SET_SIZE(ddi_mem_put16)

        .align 16
        ENTRY(i_ddi_put16)
        ALTENTRY(i_ddi_swap_put16)
        retl
        stuh    %o2, [%o1]
        SET_SIZE(i_ddi_put16)
        SET_SIZE(i_ddi_swap_put16)

        .align 32
        ENTRY(ddi_put32)
        ALTENTRY(ddi_io_put32)
        ALTENTRY(ddi_mem_put32)
        ldn      [%o0 + AHI_PUT32], %g1   /* hdl->ahi_put16 access handle */
        jmpl    %g1, %g0                  /* jump to access handle routine */
        nop
        SET_SIZE(ddi_put32)
        SET_SIZE(ddi_io_put32)
        SET_SIZE(ddi_mem_put32)

        .align 16
        ENTRY(i_ddi_put32)
        ALTENTRY(i_ddi_swap_put32)
        retl
        st      %o2, [%o1]
        SET_SIZE(i_ddi_put32)
        SET_SIZE(i_ddi_swap_put32)

        .align 32
        ENTRY(ddi_put64)
        ALTENTRY(ddi_io_put64)
        ALTENTRY(ddi_mem_put64)
        ldn      [%o0 + AHI_PUT64], %g1   /* hdl->ahi_put64 access handle */
        jmpl    %g1, %g0                  /* jump to access handle routine */
        nop
        SET_SIZE(ddi_put64)
        SET_SIZE(ddi_io_put64)
        SET_SIZE(ddi_mem_put64)

        .align 16
        ENTRY(i_ddi_put64)
        ALTENTRY(i_ddi_swap_put64)
        retl
        stx     %o2, [%o1]
        SET_SIZE(i_ddi_put64)
        SET_SIZE(i_ddi_swap_put64)

/*
 * The ddi_io_rep_get/put routines don't take a flag argument like the "plain"
 * and mem versions do.  This flag is used to determine whether or not the
 * device address or port should be automatically incremented.  For IO space,
 * the device port is never incremented and as such, the flag is always set
 * to DDI_DEV_NO_AUTOINCR.
 *
 * This define processes the repetitive get functionality.  Automatic
 * incrementing of the device address is determined by the flag field
 * %o4.  If this is set for AUTOINCR, %o4 is updated with 1 for the
 * subsequent increment in 2:.
 *
 * If this flag is not set for AUTOINCR, %o4 is update with a value of 0 thus
 * making the increment operation a non-operation.
 */

#define DDI_REP_GET(n,s)                        \
        cmp     DDI_DEV_NO_AUTOINCR, %o4;       \
        mov     %g0, %o4;                       \
        brz,pn  %o3, 1f;                        \
        movnz   %xcc, n, %o4;                   \
2:                                              \
        dec     %o3;                            \
        ld##s   [%o2], %g4;                     \
        add     %o2, %o4, %o2;                  \
        st##s   %g4, [%o1];                     \
        brnz,pt %o3, 2b;                        \
        add     %o1, n, %o1;                    \
1:

        .align 32
        ENTRY(ddi_rep_get8)
        ALTENTRY(ddi_mem_rep_get8)
        ldn      [%o0 + AHI_REP_GET8], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_get8)
        SET_SIZE(ddi_mem_rep_get8)

        .align 16
        ENTRY(i_ddi_rep_get8)
        DDI_REP_GET(1,ub)
        retl
        nop
        SET_SIZE(i_ddi_rep_get8)

        .align 32
        ENTRY(ddi_rep_get16)
        ALTENTRY(ddi_mem_rep_get16)
        ldn     [%o0 + AHI_REP_GET16], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_get16)
        SET_SIZE(ddi_mem_rep_get16)

        .align 16
        ENTRY(i_ddi_rep_get16)
        ALTENTRY(i_ddi_swap_rep_get16)
        DDI_REP_GET(2,uh)
        retl
        nop
        SET_SIZE(i_ddi_rep_get16)
        SET_SIZE(i_ddi_swap_rep_get16)

        .align 32
        ENTRY(ddi_rep_get32)
        ALTENTRY(ddi_mem_rep_get32)
        ldn      [%o0 + AHI_REP_GET32], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_get32)
        SET_SIZE(ddi_mem_rep_get32)

        .align 16
        ENTRY(i_ddi_rep_get32)
        ALTENTRY(i_ddi_swap_rep_get32)
        DDI_REP_GET(4,/**/)
        retl
        nop
        SET_SIZE(i_ddi_rep_get32)
        SET_SIZE(i_ddi_swap_rep_get32)

        .align 32
        ENTRY(ddi_rep_get64)
        ALTENTRY(ddi_mem_rep_get64)
        ldn      [%o0 + AHI_REP_GET64], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_get64)
        SET_SIZE(ddi_mem_rep_get64)

        .align 16
        ENTRY(i_ddi_rep_get64)
        ALTENTRY(i_ddi_swap_rep_get64)
        DDI_REP_GET(8,x)
        retl
        nop
        SET_SIZE(i_ddi_rep_get64)
        SET_SIZE(i_ddi_swap_rep_get64)

/*
 * This define processes the repetitive put functionality.  Automatic
 * incrementing of the device address is determined by the flag field
 * %o4.  If this is set for AUTOINCR, %o4 is updated with 1 for the
 * subsequent increment in 2:.
 *
 * If this flag is not set for AUTOINCR, %o4 is update with a value of 0 thus
 * making the increment operation a non-operation.
 */
#define DDI_REP_PUT(n,s)                        \
        cmp     DDI_DEV_NO_AUTOINCR, %o4;       \
        mov     %g0, %o4;                       \
        brz,pn  %o3, 1f;                        \
        movnz   %xcc, n, %o4;                   \
2:                                              \
        dec     %o3;                            \
        ld##s   [%o1], %g4;                     \
        add     %o1, n, %o1;                    \
        st##s   %g4, [%o2];                     \
        brnz,pt %o3, 2b;                        \
        add     %o2, %o4, %o2;                  \
1:

        .align 32
        ENTRY(ddi_rep_put8)
        ALTENTRY(ddi_mem_rep_put8)
        ldn      [%o0 + AHI_REP_PUT8], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_put8)
        SET_SIZE(ddi_mem_rep_put8)

        .align 16
        ENTRY(i_ddi_rep_put8)
        DDI_REP_PUT(1,ub)
        retl
        nop
        SET_SIZE(i_ddi_rep_put8)

        .align 32
        ENTRY(ddi_rep_put16)
        ALTENTRY(ddi_mem_rep_put16)
        ldn      [%o0 + AHI_REP_PUT16], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_put16)
        SET_SIZE(ddi_mem_rep_put16)

        .align 16
        ENTRY(i_ddi_rep_put16)
        ALTENTRY(i_ddi_swap_rep_put16)
        DDI_REP_PUT(2,uh)
        retl
        nop
        SET_SIZE(i_ddi_rep_put16)
        SET_SIZE(i_ddi_swap_rep_put16)

        .align 32
        ENTRY(ddi_rep_put32)
        ALTENTRY(ddi_mem_rep_put32)
        ldn      [%o0 + AHI_REP_PUT32], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_put32)
        SET_SIZE(ddi_mem_rep_put32)

        .align 16
        ENTRY(i_ddi_rep_put32)
        ALTENTRY(i_ddi_swap_rep_put32)
        DDI_REP_PUT(4,/**/)
        retl
        nop
        SET_SIZE(i_ddi_rep_put32)
        SET_SIZE(i_ddi_swap_rep_put32)

        .align 32
        ENTRY(ddi_rep_put64)
        ALTENTRY(ddi_mem_rep_put64)
        ldn      [%o0 + AHI_REP_PUT64], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_rep_put64)
        SET_SIZE(ddi_mem_rep_put64)

        .align 16
        ENTRY(i_ddi_rep_put64)
        ALTENTRY(i_ddi_swap_rep_put64)
        DDI_REP_PUT(8,x)
        retl
        nop
        SET_SIZE(i_ddi_rep_put64)
        SET_SIZE(i_ddi_swap_rep_put64)

        .align 16
        ENTRY(ddi_io_rep_get8)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_GET8], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_get8)

        .align 16
        ENTRY(ddi_io_rep_get16)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_GET16], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_get16)

        .align 16
        ENTRY(ddi_io_rep_get32)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_GET32], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_get32)

        .align 16
        ENTRY(ddi_io_rep_get64)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_GET64], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_get64)

        .align 64
        ENTRY(ddi_check_acc_handle)
        save    %sp, -SA(WINDOWSIZE), %sp       ! get a new window
        ldn     [%i0 + AHI_FAULT_CHECK], %g1
        jmpl    %g1, %o7
        mov     %i0, %o0
        brnz,a,pn %o0, 0f                       ! if (return_value != 0)
        mov     -1, %o0                         !       return (DDI_FAILURE)
0:                                              ! else  return (DDI_SUCCESS)
        sra     %o0, 0, %i0
        ret
        restore
        SET_SIZE(ddi_check_acc_handle)

        .align 16
        ENTRY(i_ddi_acc_fault_check)
        retl
        ld      [%o0 + AHI_FAULT], %o0
        SET_SIZE(i_ddi_acc_fault_check)

        .align 16
        ENTRY(ddi_io_rep_put8)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_PUT8], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_put8)

        .align 16
        ENTRY(ddi_io_rep_put16)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_PUT16], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_put16)

        .align 16
        ENTRY(ddi_io_rep_put32)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_PUT32], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_put32)

        .align 16
        ENTRY(ddi_io_rep_put64)
        set     DDI_DEV_NO_AUTOINCR, %o4 /* Set flag to DDI_DEV_NO_AUTOINCR */
        ldn     [%o0 + AHI_REP_PUT64], %g1
        jmpl    %g1, %g0
        nop
        SET_SIZE(ddi_io_rep_put64)

        ENTRY(do_peek)
        rdpr    %pstate, %o3    ! check ints
        andcc   %o3, PSTATE_IE, %g0
        bz,a    done
        or      %g0, 1, %o0     ! Return failure if ints are disabled
        wrpr    %o3, PSTATE_IE, %pstate
        cmp     %o0, 8          ! 64-bit?
        bne,a   .peek_int
        cmp     %o0, 4          ! 32-bit?
        ldx     [%o1], %g1
        ba      .peekdone
        stx     %g1, [%o2]
.peek_int:
        bne,a   .peek_half
        cmp     %o0, 2          ! 16-bit?
        lduw    [%o1], %g1
        ba      .peekdone
        stuw    %g1, [%o2]
.peek_half:
        bne,a   .peek_byte
        ldub    [%o1], %g1      ! 8-bit!
        lduh    [%o1], %g1
        ba      .peekdone
        stuh    %g1, [%o2]
.peek_byte:
        stub    %g1, [%o2]
.peekdone:
        membar  #Sync           ! Make sure the loads take
        rdpr    %pstate, %o3    ! check&enable ints
        andcc   %o3, PSTATE_IE, %g0
        bnz     1f
        nop
        wrpr    %o3, PSTATE_IE, %pstate
1:
        mov     %g0, %o0
done:
        retl
        nop
        SET_SIZE(do_peek)

        ENTRY(do_poke)
        cmp     %o0, 8          ! 64 bit?
        bne,a   .poke_int
        cmp     %o0, 4          ! 32-bit?
        ldx     [%o2], %g1
        ba      .pokedone
        stx     %g1, [%o1]
.poke_int:
        bne,a   .poke_half
        cmp     %o0, 2          ! 16-bit?
        lduw    [%o2], %g1
        ba      .pokedone
        stuw    %g1, [%o1]
.poke_half:
        bne,a   .poke_byte
        ldub    [%o2], %g1      ! 8-bit!
        lduh    [%o2], %g1
        ba      .pokedone
        stuh    %g1, [%o1]
.poke_byte:
        stub    %g1, [%o1]
.pokedone:
        membar  #Sync
        retl
        mov     %g0, %o0
        SET_SIZE(do_poke)


/*
 * The peek_fault() and poke_fault() routines below are used as on_trap()
 * trampoline routines.  i_ddi_peek and i_ddi_poke execute do_peek and do_poke
 * under on_trap protection (see <sys/ontrap.h>), but modify ot_trampoline to
 * refer to the corresponding routine below.  If a trap occurs, the trap code
 * will bounce back to the trampoline code, which will effectively cause
 * do_peek or do_poke to return DDI_FAILURE, instead of longjmp'ing back to
 * on_trap.  In the case of a peek, we may also need to re-enable interrupts.
 */
        .seg    ".data"
.peek_panic:
        .asciz  "peek_fault: missing or invalid on_trap_data"
.poke_panic:
        .asciz  "poke_fault: missing or invalid on_trap_data"

        ENTRY(peek_fault)
        ldn     [THREAD_REG + T_ONTRAP], %o0    ! %o0 = on_trap_data pointer
        brz,pn  %o0, .peekfail                  ! if (%o0 == NULL) panic
        nop
        lduh    [%o0 + OT_PROT], %o1            ! %o1 = %o0->ot_prot
        andcc   %o1, OT_DATA_ACCESS, %g0        ! if (!(%o1 & OT_DATA_ACCESS))
        bz,pn   %icc, .peekfail                 !     panic
        rdpr    %pstate, %o3

        andcc   %o3, PSTATE_IE, %g0             ! enable interrupts
        bnz     1f
        nop
        wrpr    %o3, PSTATE_IE, %pstate
1:
        retl
        sub     %g0, 1, %o0                     ! return (DDI_FAILURE);
.peekfail:
        set     .peek_panic, %o0                ! Load panic message
        call    panic                           ! Panic if bad t_ontrap data
        nop
        SET_SIZE(peek_fault)


        ENTRY(poke_fault)
        ldn     [THREAD_REG + T_ONTRAP], %o0    ! %o0 = on_trap_data pointer
        brz,pn  %o0, .pokefail                  ! if (%o0 == NULL) panic
        nop
        lduh    [%o0 + OT_PROT], %o1            ! %o1 = %o0->ot_prot
        andcc   %o1, OT_DATA_ACCESS, %g0        ! if (!(%o1 & OT_DATA_ACCESS))
        bz,pn   %icc, .pokefail                 !     panic
        nop
        retl
        sub     %g0, 1, %o0                     ! return (DDI_FAILURE);
.pokefail:
        set     .poke_panic, %o0                ! Load panic message
        call    panic                           ! Panic if bad t_ontrap data
        nop
        SET_SIZE(poke_fault)


/*
 * IO Fault Services
 *
 * Support for protected IO accesses is implemented in the following
 * functions.  A driver may request one of three protection mechanisms
 * that enable the system to survive an access errors.  The protection
 * mechansim is set-up during ddi_regs_map_setup time and may be one of:
 *
 *      DDI_DEFAULT_ACC - no error protection requested.  We will
 *                      use the standard ddi_get/ddi_put operations
 *                      defined above.
 *
 *      DDI_FLAGERR - Driver requests that errors encountered will
 *                      be flagged by the system.  The driver is
 *                      responsible for checking the error status
 *                      of the access with a call to ddi_acc_err_get()
 *                      upon return of ddi_get or ddi_put.  To prevent
 *                      an access from causing a system we use internal
 *                      on_trap semantics.
 *
 *                      The system, depending upon the error,
 *                      may or may not panic.
 *
 *      DDI_CAUTIOUS_ACC - Driver expects that the access may cause
 *                      an error to occur.  The system will return
 *                      an error status but will not generate an ereport.
 *                      The system will also ensure synchronous and
 *                      exclusive access to the IO space accessed by
 *                      the caller.
 *
 *                      To prevent an access from causing a system panic,
 *                      we use on_trap semantics to catch the error and
 *                      set error status.
 *
 *      If a read access error is detected and DDI_CAUTIOUS_ACC or
 *      DDI_FLAGERR_ACC protection was requested, we will trampoline to the
 *      error handler, i_ddi_trampoline.  i_ddi_trampoline will:
 *              - check for proper protection semantics
 *              - set the error status of the access handle to DDI_FM_NONFATAL
 *              - re-enable interrupts if neccessary
 *              - longjmp back to the initiating access function.

 *      If a write access error is detected, an interrupt is typically
 *      generated and claimed by a bus nexus responsible for the write
 *      transaction.  The nexus error handler is expected to set the
 *      error status and the IO initiating driver is expected to check
 *      for a failed transaction via ddi_fm_acc_err_get().
 *
 */

        .seg    ".data"
.acc_panic:
        .asciz  "DDI access: missing or invalid on_trap_data"

        ENTRY(i_ddi_caut_trampoline)
        ldn     [THREAD_REG + T_ONTRAP], %o5    ! %o5 = curthread->t_ontrap
        lduh    [%o5 + OT_PROT], %o1            ! %o1 = %o0->ot_prot
        andcc   %o1, OT_DATA_ACCESS, %g0        ! if (!(%o1 & OT_DATA_ACCESS))
        bz,pn   %icc, .cautaccfail              !     panic
        rdpr    %pstate, %o3
        andcc   %o3, PSTATE_IE, %g0             ! enable interrupts
        bnz     1f
        nop
        wrpr    %o3, PSTATE_IE, %pstate
1:
        ldn     [%o5 + OT_HANDLE], %o0          ! %o0 = ot_handle
        brz,pn  %o0, .cautaccfail               ! if (ot_handle == NULL) panic
        nop
        ldn     [%o0 + AHI_ERR], %o4            ! %o4 = hp->ahi_err
        membar  #Sync
        stx     %g0, [%o4 + ERR_ENA]            ! ahi_err->err_ena = 0
        mov     -2, %o0
        st      %o0, [%o4 + ERR_STATUS]         ! ahi_err->err_status = NONFATAL
        b       longjmp                         ! longjmp back
        add     %o5, OT_JMPBUF, %o0             ! %o0 = &ot_jmpbuf
.cautaccfail:
        set     .acc_panic, %o0                 ! Load panic message
        call    panic                           ! Panic if bad t_ontrap data
        nop
        SET_SIZE(i_ddi_caut_trampoline)

/*
 * DDI on_trap set-up functions,  i_ddi_ontrap() and i_ddinotrap() are used
 * to protect * ddi_get accesses for DDI_CAUT_ACC.  i_ddi_ontrap() sets
 * the jumpbuf (setjmp) that will return back to the access routine from
 * i_ddi_trampoline().  DDI_NOPROTECT() clears the ontrap set-up.
 */
        ENTRY(i_ddi_ontrap)
        ldn     [%o0 + AHI_ERR], %o4
        ldn     [%o4 + ERR_ONTRAP],  %o4        ! %o4 = hp->ahi_err->err_ontrap
        ldn     [THREAD_REG + T_ONTRAP], %o5    ! %o5 = curthread->t_ontrap
        stn     %o5, [%o4 + OT_PREV]            ! ot_prev = t_ontrap
        membar  #Sync                           ! force error barrier
        stn     %o4, [THREAD_REG + T_ONTRAP]    ! t_ontrap = err_ontrap
        b       setjmp
        add     %o4, OT_JMPBUF, %o0
        SET_SIZE(i_ddi_ontrap)

        ENTRY(i_ddi_notrap)
        membar  #Sync                           ! force error barrier
        ldn     [%o0 + AHI_ERR], %o4
        ldn     [%o4 + ERR_ONTRAP],  %o4        ! %o4 = hp->ahi_err->err_ontrap
        ldn     [%o4 + OT_PREV], %o4
        retl
        stn     %o4, [THREAD_REG + T_ONTRAP]    ! restore curthread->t_ontrap
        SET_SIZE(i_ddi_notrap)

/*
 * Internal on_trap set-up macros.  DDI_PROTECT() and DDI_NOPROTECT() are used
 * to protect * ddi_get accesses for DDI_FLAGERR_ACC.  DDI_NOPROTECT() sets
 * the jumpbuf that will return back to the access routine from
 * i_ddi_protect_trampoline().  DDI_NOPROTECT() clears the ontrap set-up.
 */
        ENTRY(i_ddi_prot_trampoline)
        ldn     [THREAD_REG + T_ONTRAP], %o5    ! %o5 = curthread->t_ontrap
        lduh    [%o5 + OT_PROT], %o1            ! %o1 = %o0->ot_prot
        andcc   %o1, OT_DATA_ACCESS, %g0        ! if (!(%o1 & OT_DATA_ACCESS))
        bz,pn   %icc, .protaccfail              !     panic
        rdpr    %pstate, %o3
        andcc   %o3, PSTATE_IE, %g0             ! enable interrupts
        bnz     1f
        nop
        wrpr    %o3, PSTATE_IE, %pstate
1:
        ldn     [%o5 + OT_HANDLE], %o0          ! %o0 = ot_handle
        brz,pn  %o0, .protaccfail               ! if (ot_handle == NULL) panic
        nop
        ldn     [%o0 + AHI_ERR], %o4            ! %o4 = hp->ahi_err
        stn     %g0, [%o4 + ERR_ENA]            ! ahi_err->err_ena = 0
        mov     -2, %o0
        st      %o0, [%o4 + ERR_STATUS]         ! ahi_err->err_status = NONFATAL
        ldn     [%o5 + OT_PREV], %o0            ! restore ontrap
        membar  #Sync                           ! force error barrier
        stn     %o0, [THREAD_REG + T_ONTRAP];
        b       longjmp                         ! longjmp back
        add     %o5, OT_JMPBUF, %o0             ! %o0 = &ot_jmpbuf
.protaccfail:
        set     .acc_panic, %o0                 ! Load panic message
        call    panic                           ! Panic if bad t_ontrap data
        nop
        SET_SIZE(i_ddi_prot_trampoline)

#define DDI_PROTECT()                           \
        ldn     [%o0 + AHI_ERR], %o4;           \
        ldn     [%o4 + ERR_ONTRAP],  %o4;       \
        ldn     [THREAD_REG + T_ONTRAP], %o5;   \
        stn     %o5, [%o4 + OT_PREV];           \
        membar  #Sync;                          \
        stn     %o4, [THREAD_REG + T_ONTRAP];   \
        add     %o4, OT_JMPBUF, %o0;            \
        stn     %o7, [%o0 + L_PC];              \
        stn     %sp, [%o0 + L_SP];              \
        clr     %o0;

#define DDI_NOPROTECT()                         \
        ldn     [THREAD_REG + T_ONTRAP], %o4;   \
        ldn     [%o4 + OT_PREV], %o5;           \
        membar  #Sync;                          \
        stn     %o5, [THREAD_REG + T_ONTRAP];

/*
 * DDI_FLAGERR_ACC specific get/put routines.
 */
        .align 16
        ENTRY(i_ddi_prot_get8)
        DDI_PROTECT()                           ! set ontrap protection
        ldub    [%o1], %o2                      ! do the io access
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        mov     %o2, %o0                        ! set return value
        SET_SIZE(i_ddi_prot_get8)

        .align 16
        ENTRY(i_ddi_prot_get16)
        DDI_PROTECT()                           ! set ontrap protection
        lduh    [%o1], %o2                      ! do the io access
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        mov     %o2, %o0                        ! set return value
        SET_SIZE(i_ddi_prot_get16)

        .align 16
        ENTRY(i_ddi_prot_get32)
        DDI_PROTECT()                           ! set ontrap protection
        ld      [%o1], %o2                      ! do the io access
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        mov     %o2, %o0                        ! set return value
        SET_SIZE(i_ddi_prot_get32)

        .align 16
        ENTRY(i_ddi_prot_get64)
        DDI_PROTECT()                           ! set ontrap protection
        ldx     [%o1], %o2                      ! do the io access
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        mov     %o2, %o0                        ! set return value
        SET_SIZE(i_ddi_prot_get64)

        .align 16
        ENTRY(i_ddi_prot_put8)
        stub    %o2, [%o1]                      ! do the io access
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_put8)

        .align 16
        ENTRY(i_ddi_prot_put16)
        stuh    %o2, [%o1]                      ! do the io access
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_put16)

        .align 16
        ENTRY(i_ddi_prot_put32)
        st      %o2, [%o1]                      ! do the io access
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_put32)

        .align 16
        ENTRY(i_ddi_prot_put64)
        stx     %o2, [%o1]                      ! do the io access
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_put64)

        .align 16
        ENTRY(i_ddi_prot_rep_get8)
        DDI_PROTECT()                           ! set ontrap protection
        tst     %o0                             ! check access error
        bnz,a   1f
        nop
        DDI_REP_GET(1,ub)
1:
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        nop
        SET_SIZE(i_ddi_prot_rep_get8)

        .align 16
        ENTRY(i_ddi_prot_rep_get16)
        DDI_PROTECT()                           ! set ontrap protection
        tst     %o0                             ! check access error
        bnz,a   1f
        nop
        DDI_REP_GET(2,uh)
1:
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        nop
        SET_SIZE(i_ddi_prot_rep_get16)

        .align 16
        ENTRY(i_ddi_prot_rep_get32)
        DDI_PROTECT()                           ! set ontrap protection
        tst     %o0                             ! check access error
        bnz,a   1f
        nop
        DDI_REP_GET(4,/**/)
1:
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        nop
        SET_SIZE(i_ddi_prot_rep_get32)

        .align 16
        ENTRY(i_ddi_prot_rep_get64)
        DDI_PROTECT()                           ! set ontrap protection
        tst     %o0                             ! check access error
        bnz,a   1f
        nop
        DDI_REP_GET(8,x)
1:
        DDI_NOPROTECT()                         ! remove protection & ret
        retl
        nop
        SET_SIZE(i_ddi_prot_rep_get64)

        .align 16
        ENTRY(i_ddi_prot_rep_put8)
        DDI_REP_PUT(1,ub)
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_rep_put8)

        .align 16
        ENTRY(i_ddi_prot_rep_put16)
        DDI_REP_PUT(2,uh)
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_rep_put16)

        .align 16
        ENTRY(i_ddi_prot_rep_put32)
        DDI_REP_PUT(4,/**/)
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_rep_put32)

        .align 16
        ENTRY(i_ddi_prot_rep_put64)
        DDI_REP_PUT(8,x)
        retl
        membar  #Sync;
        SET_SIZE(i_ddi_prot_rep_put64)

/*
 * Common DDI_CAUTIOUS_ACC routine called from cautious access routines
 * in ddi_impl.c
 */
        ENTRY(i_ddi_caut_get)
        rdpr    %pstate, %o3    ! check ints
        andcc   %o3, PSTATE_IE, %g0
        bz,a    cautdone
        nop
        wrpr    %o3, PSTATE_IE, %pstate
        cmp     %o0, 8          ! 64-bit?
        bne,a   .get_int
        cmp     %o0, 4          ! 32-bit?
        ldx     [%o1], %g1
        ba      .getdone
        stx     %g1, [%o2]
.get_int:
        bne,a   .get_half
        cmp     %o0, 2          ! 16-bit?
        lduw    [%o1], %g1
        ba      .getdone
        stuw    %g1, [%o2]
.get_half:
        bne,a   .get_byte
        ldub    [%o1], %g1      ! 8-bit!
        lduh    [%o1], %g1
        ba      .getdone
        stuh    %g1, [%o2]
.get_byte:
        stub    %g1, [%o2]
.getdone:
        rdpr    %pstate, %o3    ! check&enable ints
        andcc   %o3, PSTATE_IE, %g0
        bnz,a   cautdone
        nop
        wrpr    %o3, PSTATE_IE, %pstate
cautdone:
        retl
        nop
        SET_SIZE(i_ddi_caut_get)