root/sys/arch/ia64/ia64/support.S
/*      $NetBSD: support.S,v 1.11 2025/08/18 20:59:55 andvar Exp $      */

/*-
 * Copyright (c) 1998 Doug Rabson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD$
 */

/*-
 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

#include <machine/asm.h>

#include "assym.h"

.text

/*
 * ia64_change_mode:    change mode to/from physical mode
 *
 * Arguments:
 *      r14     psr for desired mode
 *
 * Modifies:
 *      r15-r19 scratch
 *      ar.bsp  translated to new mode
 */
ENTRY_NOPROFILE(ia64_change_mode, 0)
        rsm     psr.i | psr.ic
        mov     r19=ar.rsc              // save rsc while we change mode
        tbit.nz p6,p7=r14,17            // physical or virtual ?
        ;;
        mov     ar.rsc=0                // turn off RSE
(p6)    mov     r15=7                   // RR base for virtual addresses
(p7)    mov     r15=0                   // RR base for physical addresses
        flushrs                         // no dirty registers please
        srlz.i
        ;;
        mov     r16=ar.bsp
        mov     r17=rp
        mov     r18=ar.rnat
        ;;
        dep     r16=r15,r16,61,3        // new address of ar.bsp
        dep     r17=r15,r17,61,3        // new address of rp
        dep     sp=r15,sp,61,3          // new address of sp
        ;;
        mov     ar.bspstore=r16
        mov     rp=r17
        ;;
1:      mov     r16=ip
        mov     ar.rnat=r18
        mov     cr.ipsr=r14             // psr for new mode
        ;;
        add     r16=2f-1b,r16           // address to rfi to
        ;;
        dep     r16=r15,r16,61,3        // new mode address for rfi
        ;;
        mov     cr.iip=r16              // setup for rfi
        mov     cr.ifs=r0
        ;;
        rfi

2:      mov     ar.rsc=r19              // restore ar.rsc
        br.ret.sptk.few rp              // now in new mode
END(ia64_change_mode)

/*
 * ia64_physical_mode:  change mode to physical mode
 *
 * Return:
 *      ret0    psr to restore
 *
 * Modifies:
 *      r15-r18 scratch
 *      ar.bsp  translated to physical mode
 *      psr.i   cleared
 */
ENTRY(ia64_physical_mode, 0)
        mov     r14=psr
        mov     ret0=psr
        movl    r15=(IA64_PSR_I|IA64_PSR_IT|IA64_PSR_DT|IA64_PSR_RT|IA64_PSR_DFL|IA64_PSR_DFH)
        movl    r16=IA64_PSR_BN
        ;;
        andcm   r14=r14,r15             // clear various xT bits
        ;;
        or      r14=r14,r16             // make sure BN=1
        or      ret0=ret0,r16           // make sure BN=1

        br.cond.sptk.many ia64_change_mode
END(ia64_physical_mode)

/*
 * ia64_call_efi_physical:      call an EFI procedure in physical mode
 *
 * Arguments:
 *      in0             Address of EFI procedure descriptor
 *      in1-in5         Arguments to EFI procedure
 *
 * Return:
 *      ret0-ret3       return values from EFI
 *
 */
ENTRY(ia64_call_efi_physical, 6)
        .prologue
        .regstk 6,4,5,0
        .save   ar.pfs,loc0
        alloc   loc0=ar.pfs,6,4,5,0
        ;;
        .save   rp,loc1
        mov     loc1=rp
        ;;
        .body
        br.call.sptk.many rp=ia64_physical_mode
        ;;
        mov     loc2=r8                 // psr to restore mode
        mov     loc3=gp                 // save kernel gp
        ld8     r14=[in0],8             // function address
        ;;
        mov     out0=in1
        mov     out1=in2
        mov     out2=in3
        mov     out3=in4
        mov     out4=in5
        ld8     gp=[in0]                // function gp value
        ;;
        mov     b6=r14
        ;;
        br.call.sptk.many rp=b6         // call EFI procedure
        mov     gp=loc3                 // restore kernel gp
        ;;
        mov     r14=loc2                // psr to restore mode
        br.call.sptk.many rp=ia64_change_mode
        ;;
        mov     rp=loc1
        mov     ar.pfs=loc0
        ;;
        br.ret.sptk.many rp
END(ia64_call_efi_physical)

/**************************************************************************/

ENTRY(fusufault, 0)
{       .mib
        st8.rel         [r15]=r0                // Clear onfault.
        add             ret0=-1,r0
        br.ret.sptk     rp
        ;;
}
END(fusufault)

/*
 * casuptr(intptr_t *p, intptr_t old, intptr_t new)
 *      Perform a compare-exchange in user space.
 */
ENTRY(casuptr, 3)
{       .mlx
        add             r15=PC_CURLWP,r13
        movl            r14=VM_MAX_ADDRESS
        ;;
}
{       .mib
        ld8             r15=[r15]               // r15 = curthread
        cmp.geu         p6,p0=in0,r14
(p6)    br.dpnt.few     1f
        ;;
}
{       .mlx
        add             r15=L_PCB,r15
        movl            r14=fusufault
        ;;
}
{       .mmi
        ld8             r15=[r15]               // r15 = PCB
        ;;
        mov             ar.ccv=in1
        add             r15=PCB_ONFAULT,r15
        ;;
}
{       .mmi
        st8             [r15]=r14               // Set onfault
        ;;
        cmpxchg8.rel    ret0=[in0],in2,ar.ccv
        nop             0
        ;;
}
{       .mfb
        st8.rel         [r15]=r0                // Clear onfault
        nop             0
        br.ret.sptk     rp
        ;;
}
1:
{       .mfb
        add             ret0=-1,r0
        nop             0
        br.ret.sptk     rp
        ;;
}
END(casuptr)

/**************************************************************************/

ENTRY(ufetchstore_fault, 0)
{       .mfb
        st8.rel         [r15]=r0        /* curpcb->pcb_onfault = NULL */
        nop             0               /* ret0 already has error */
        br.ret.sptk     rp
        ;;
}
END(ufetchstore_fault)

#define UFETCH(load_insn, store_insn)                                     \
{       .mlx                                                             ;\
        add             r15=PC_CURLWP,r13                                ;\
        movl            r14=VM_MAX_ADDRESS                              ;;\
}                                                                        ;\
{       .mib                                                             ;\
        ld8             r15=[r15]       /* r15 = curlwp */               ;\
        cmp.geu         p6,p0=in0,r14                                    ;\
(p6)    br.dpnt.few     1f                                              ;;\
}                                                                        ;\
{       .mlx                                                             ;\
        add             r15=L_PCB,r15                                    ;\
        movl            r14=ufetchstore_fault                           ;;\
}                                                                        ;\
{       .mmi                                                             ;\
        ld8             r15=[r15]       /*r15 = curlwp->l_pcb */        ;;\
        nop             0                                                ;\
        add             r15=PCB_ONFAULT,r15                             ;;\
}                                                                        ;\
{       .mmi                                                             ;\
        st8             [r15]=r14       /* curpcb->pcb_onfault = */     ;;\
                                        /*     ufetchstore_fault */       \
        mf                                                               ;\
        nop             0                                               ;;\
}                                                                        ;\
{       .mmi                                                             ;\
        load_insn       r14=[in0]       /* r14 = *uaddr */              ;;\
        st8.rel         [r15]=r0        /* curpcb->pcb_onfault = NULL */ ;\
        nop             0                                               ;;\
}                                                                        ;\
{       .mib                                                             ;\
        store_insn      [in1]=r14       /* *valp = r14 */                ;\
        mov             ret0=r0         /* ret0 = 0 (success!) */        ;\
        br.ret.sptk     rp                                              ;;\
}                                                                        ;\
1:                                                                        \
{       .mfb                                                             ;\
        mov             ret0=EFAULT     /* return EFAULT */              ;\
        nop             0                                                ;\
        br.ret.sptk     rp                                              ;;\
}

/* LINTSTUB: int _ufetch_8(const uint8_t *uaddr, uint8_t *valp); */
ENTRY(_ufetch_8, 2)
        UFETCH(ld1, st1)
END(_ufetch_8)

/* LINTSTUB: int _ufetch_16(const uint16_t *uaddr, uint16_t *valp); */
ENTRY(_ufetch_16, 2)
        UFETCH(ld2, st2)
END(_ufetch_16)

/* LINTSTUB: int _ufetch_32(const uint32_t *uaddr, uint32_t *valp); */
ENTRY(_ufetch_32, 2)
        UFETCH(ld4, st4)
END(_ufetch_32)

/* LINTSTUB: int _ufetch_64(const uint64_t *uaddr, uint64_t *valp); */
ENTRY(_ufetch_64, 2)
        UFETCH(ld8, st8)
END(_ufetch_64)

#define USTORE(store_insn)                                                \
{       .mlx                                                             ;\
        add             r15=PC_CURLWP,r13                                ;\
        movl            r14=VM_MAX_ADDRESS                              ;;\
}                                                                        ;\
{       .mib                                                             ;\
        ld8             r15=[r15]       /* r15 = curlwp */               ;\
        cmp.geu         p6,p0=in0,r14                                    ;\
(p6)    br.dpnt.few     1f                                              ;;\
}                                                                        ;\
{       .mlx                                                             ;\
        add             r15=L_PCB,r15                                    ;\
        movl            r14=ufetchstore_fault                           ;;\
}                                                                        ;\
{       .mmi                                                             ;\
        ld8             r15=[r15]       /*r15 = curlwp->l_pcb */        ;;\
        nop             0                                                ;\
        add             r15=PCB_ONFAULT,r15                             ;;\
}                                                                        ;\
{       .mmi                                                             ;\
        st8             [r15]=r14       /* curpcb->pcb_onfault = */     ;;\
                                        /*     ufetchstore_fault */       \
        store_insn      [in0]=in1       /* *uaddr = val */               ;\
        nop             0                                               ;;\
}                                                                        ;\
{       .mib                                                             ;\
        st8.rel         [r15]=r0        /* curpcb->pcb_onfault = NULL */ ;\
        mov             ret0=r0         /* ret0 = 0 (success!) */        ;\
        br.ret.sptk     rp                                              ;;\
}                                                                        ;\
1:                                                                        \
{       .mfb                                                             ;\
        mov             ret0=EFAULT     /* return EFAULT */              ;\
        nop             0                                                ;\
        br.ret.sptk     rp                                              ;;\
}

/* LINTSTUB: int _ustore_8(uint8_t *uaddr, uint8_t val); */
ENTRY(_ustore_8, 2)
        USTORE(st1.rel)
END(_ustore_8)

/* LINTSTUB: int _ustore_16(uint16_t *uaddr, uint16_t val); */
ENTRY(_ustore_16, 2)
        USTORE(st2.rel)
END(_ustore_16)

/* LINTSTUB: int _ustore_32(uint32_t *uaddr, uint32_t val); */
ENTRY(_ustore_32, 2)
        USTORE(st4.rel)
END(_ustore_32)

/* LINTSTUB: int _ustore_64(uint64_t *uaddr, uint64_t val); */
ENTRY(_ustore_64, 2)
        USTORE(st8.rel)
END(_ustore_64)

/**************************************************************************/

/*
 * XXX XXX XXX: Should be removed?
 */
ENTRY(ia64_copystr, 4)
        mov     r14=in2                 // r14 = i = len
        cmp.eq  p6,p0=r0,in2
(p6)    br.cond.spnt.few 2f             // if (len == 0), bail out

1:      ld1     r15=[in0],1             // read one byte
        ;;
        st1     [in1]=r15,1             // write that byte
        add     in2=-1,in2              // len--
        ;;
        cmp.eq  p6,p0=r0,r15
        cmp.ne  p7,p0=r0,in2
        ;;
(p6)    br.cond.spnt.few 2f             // if (*from == 0), bail out
(p7)    br.cond.sptk.few 1b             // if (len != 0) copy more

2:      cmp.eq  p6,p0=r0,in3
(p6)    br.cond.dpnt.few 3f             // if (lenp != NULL)
        sub     r14=r14,in2             // *lenp = (i - len)
        ;;
        st8     [in3]=r14

3:      cmp.eq  p6,p0=r0,r15
(p6)    br.cond.spnt.few 4f             // *from == '\0'; leave quietly

        mov     ret0=ENAMETOOLONG       // *from != '\0'; error.
        br.ret.sptk.few rp

4:      mov     ret0=0                  // return 0.
        br.ret.sptk.few rp
END(ia64_copystr)

ENTRY(copyinstr, 4)
        .prologue
        .regstk 4, 3, 4, 0
        .save   ar.pfs,loc0
        alloc   loc0=ar.pfs,4,3,4,0
        .save   rp,loc1
        mov     loc1=rp
        .body

        movl    loc2=VM_MAX_ADDRESS             // make sure that src addr
        ;;
        cmp.geu p6,p0=in0,loc2                  // is in user space.
        ;;
(p6)    br.cond.spnt.few copyefault             // if it's not, error out.
        movl    r14=copyerr                     // set up fault handler.
        add     r15=PC_CURLWP,r13               // find curthread
        ;;
        ld8     r15=[r15]
        ;;
        add     r15=L_PCB,r15                   // find pcb
        ;;
        ld8     r15=[r15]
        ;;
        add     loc2=PCB_ONFAULT,r15
        ;;
        st8     [loc2]=r14
        ;;
        mov     out0=in0
        mov     out1=in1
        mov     out2=in2
        mov     out3=in3
        ;;
        br.call.sptk.few rp=ia64_copystr        // do the copy.
        st8     [loc2]=r0                       // kill the fault handler.
        mov     ar.pfs=loc0                     // restore ar.pfs
        mov     rp=loc1                         // restore ra.
        br.ret.sptk.few rp                      // ret0 left over from copystr
END(copyinstr)

ENTRY(copyoutstr, 4)
        .prologue
        .regstk 4, 3, 4, 0
        .save   ar.pfs,loc0
        alloc   loc0=ar.pfs,4,3,4,0
        .save   rp,loc1
        mov     loc1=rp
        .body

        movl    loc2=VM_MAX_ADDRESS             // make sure that dest addr
        ;;
        cmp.geu p6,p0=in1,loc2                  // is in user space.
        ;;
(p6)    br.cond.spnt.few copyefault             // if it's not, error out.
        movl    r14=copyerr                     // set up fault handler.
        add     r15=PC_CURLWP,r13               // find curthread
        ;;
        ld8     r15=[r15]
        ;;
        add     r15=L_PCB,r15                   // find pcb
        ;;
        ld8     r15=[r15]
        ;;
        add     loc2=PCB_ONFAULT,r15
        ;;
        st8     [loc2]=r14
        ;;
        mov     out0=in0
        mov     out1=in1
        mov     out2=in2
        mov     out3=in3
        ;;
        br.call.sptk.few rp=ia64_copystr        // do the copy.
        st8     [loc2]=r0                       // kill the fault handler.
        mov     ar.pfs=loc0                     // restore ar.pfs
        mov     rp=loc1                         // restore ra.
        br.ret.sptk.few rp                      // ret0 left over from copystr
END(copyoutstr)

/*
 * int kcopy(const void *from, void *to, size_t len);
 * Copy len bytes, abort on fault.
 */

ENTRY(kcopy, 3)
        .prologue
        .regstk 3, 3, 3, 0
        .save   ar.pfs,loc0
        alloc   loc0=ar.pfs,3,3,3,0
        .save   rp,loc1
        mov     loc1=rp
        .body

        movl    r14=copyerr                     // set up fault handler.
        add     r15=PC_CURLWP,r13               // find curthread
        ;;
        ld8     r15=[r15]
        ;;
        add     r15=L_PCB,r15                   // find pcb
        ;;
        ld8     r15=[r15]
        ;;
        add     loc2=PCB_ONFAULT,r15
        ;;
        st8     [loc2]=r14
        ;;
        mov     out0=in0
        mov     out1=in1
        mov     out2=in2
        mov     ret0=r0                         // XXX netbsd kcopy same as freebsd?
        ;;
        br.call.sptk.few rp=bcopy               // do the copy.
        st8     [loc2]=r0                       // kill the fault handler.
        mov     ar.pfs=loc0                     // restore ar.pfs
        mov     rp=loc1                         // restore ra.
        br.ret.sptk.few rp
END(kcopy)

ENTRY(copyin, 3)
        .prologue
        .regstk 3, 3, 3, 0
        .save   ar.pfs,loc0
        alloc   loc0=ar.pfs,3,3,3,0
        .save   rp,loc1
        mov     loc1=rp
        .body

        movl    loc2=VM_MAX_ADDRESS             // make sure that src addr
        ;;
        cmp.geu p6,p0=in0,loc2                  // is in user space.
        ;;
(p6)    br.cond.spnt.few copyefault             // if it's not, error out.
        movl    r14=copyerr                     // set up fault handler.
        add     r15=PC_CURLWP,r13               // find curthread
        ;;
        ld8     r15=[r15]
        ;;
        add     r15=L_PCB,r15                   // find pcb
        ;;
        ld8     r15=[r15]
        ;;
        add     loc2=PCB_ONFAULT,r15
        ;;
        st8     [loc2]=r14
        ;;
        mov     out0=in0
        mov     out1=in1
        mov     out2=in2
        mov     ret0=r0                         // return zero for copy{in,out}
        ;;
        br.call.sptk.few rp=bcopy               // do the copy.
        st8     [loc2]=r0                       // kill the fault handler.
        mov     ar.pfs=loc0                     // restore ar.pfs
        mov     rp=loc1                         // restore ra.
        br.ret.sptk.few rp
END(copyin)

ENTRY(copyout, 3)
        .prologue
        .regstk 3, 3, 3, 0
        .save   ar.pfs,loc0
        alloc   loc0=ar.pfs,3,3,3,0
        .save   rp,loc1
        mov     loc1=rp
        .body

        movl    loc2=VM_MAX_ADDRESS             // make sure that dest addr
        ;;
        cmp.geu p6,p0=in1,loc2                  // is in user space.
        ;;
(p6)    br.cond.spnt.few copyefault             // if it's not, error out.
        movl    r14=copyerr                     // set up fault handler.
        add     r15=PC_CURLWP,r13               // find curthread
        ;;
        ld8     r15=[r15]
        ;;
        add     r15=L_PCB,r15                   // find pcb
        ;;
        ld8     r15=[r15]
        ;;
        add     loc2=PCB_ONFAULT,r15
        ;;
        st8     [loc2]=r14
        ;;
        mov     out0=in0
        mov     out1=in1
        mov     out2=in2
        mov     ret0=r0                         // return zero for copy{in,out}
        ;;
        br.call.sptk.few rp=bcopy               // do the copy.
        st8     [loc2]=r0                       // kill the fault handler.
        mov     ar.pfs=loc0                     // restore ar.pfs
        mov     rp=loc1                         // restore ra.
        br.ret.sptk.few rp
END(copyout)

ENTRY(copyerr, 0)
        add     r14=PC_CURLWP,r13 ;;            // find curthread
        ld8     r14=[r14] ;;
        add     r14=L_PCB,r14 ;;                // curthread->td_addr
        ld8     r14=[r14] ;;
        add     r14=PCB_ONFAULT,r14 ;;          // &curthread->td_pcb->pcb_onfault
        st8     [r14]=r0                        // reset fault handler

        br.ret.sptk.few rp
END(copyerr)

ENTRY(copyefault, 0)
        add     r14=PC_CURLWP,r13 ;;            // find curthread
        ld8     r14=[r14] ;;
        add     r14=L_PCB,r14 ;;                // curthread->td_addr
        ld8     r14=[r14] ;;
        add     r14=PCB_ONFAULT,r14 ;;          // &curthread->td_pcb->pcb_onfault
        st8     [r14]=r0                        // reset fault handler

        mov     ret0=EFAULT                     // return EFAULT
        br.ret.sptk.few rp
END(copyefault)

#if defined(GPROF)
/*
 * Important registers:
 *      r8      structure return address
 *      rp      our return address
 *      in0     caller's ar.pfs
 *      in1     caller's gp
 *      in2     caller's rp
 *      in3     GOT entry
 *      ar.pfs  our pfs
 */
ENTRY_NOPROFILE(_mcount, 4)
        alloc           loc0 = ar.pfs, 4, 3, 2, 0
        mov             loc1 = r8
        mov             loc2 = rp
        ;;
        mov             out0 = in2
        mov             out1 = rp
        br.call.sptk    rp = __mcount
        ;;
1:
        mov             gp = in1
        mov             r14 = ip
        mov             b7 = loc2
        ;;
        add             r14 = 2f - 1b, r14
        mov             ar.pfs = loc0
        mov             rp = in2
        ;;
        mov             b7 = r14
        mov             b6 = loc2
        mov             r8 = loc1
        mov             r14 = in0
        br.ret.sptk     b7
        ;;
2:
        mov             ar.pfs = r14
        br.sptk         b6
        ;;
END(_mcount)
#endif