root/sys/arch/mips/mips/spl.S
/*      $NetBSD: spl.S,v 1.22 2026/06/27 12:51:38 skrll Exp $   */

/*-
 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Matt Thomas <matt@3am-software.com>.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

#include "opt_multiprocessor.h" /* MP kernel? */
#include "opt_cputype.h"        /* which mips CPU levels do we support? */
#include "opt_ddb.h"

#include <sys/cdefs.h>

#include <mips/asm.h>
#include <mips/cpuregs.h>

RCSID("$NetBSD: spl.S,v 1.22 2026/06/27 12:51:38 skrll Exp $")

#include "assym.h"

        .data
        .globl  _C_LABEL(ipl_sr_map)
        .type   _C_LABEL(ipl_sr_map),@object
        .p2align INT_SCALESHIFT
_C_LABEL(ipl_sr_map):
        .word   0                       /* IPL_NONE */
        .word   MIPS_SOFT_INT_MASK_0    /* IPL_SOFT{CLOCK,BIO} */
        .word   MIPS_SOFT_INT_MASK      /* IPL_SOFT{NET,SERIAL} */
        .word   MIPS_INT_MASK           /* IPL_VM */
        .word   MIPS_INT_MASK           /* IPL_SCHED */
        .word   MIPS_INT_MASK           /* IPL_DDB */
        .word   MIPS_INT_MASK           /* IPL_HIGH */

        .text
        .set    noreorder
/*
 * MIPS processor interrupt control
 *
 * Used as building blocks for spl(9) kernel interface.
 */
_splraise:
        /*
         * a0 = SR bits to be cleared for this IPL
         * a1 = this IPL (IPL_*)
         * Can only use a0-a3 and v0-v1
         */
        PTR_L   a3, L_CPU(MIPS_CURLWP)
        NOP_L                                   # load delay
        INT_L   v0, CPU_INFO_CPL(a3)            # get current IPL from cpu_info
        NOP_L                                   # load delay
        sltu    v1, v0, a1                      # curipl < newipl
        beqz    v1, 1f                          # no, already at or above target
         nop                                    #  branch delay
        mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
        MFC0_HAZARD                             # load delay
        or      v1, MIPS_INT_MASK               # enable all interrupts
        xor     a0, v1                          # disable ipl's masked bits
        DYNAMIC_STATUS_MASK(a0,v0)              # machine dependent masking
#if !defined(__mips_o32)
        or      v1, MIPS_SR_INT_IE              #
        xor     v1, MIPS_SR_INT_IE              # clear interrupt enable bit
        mtc0    v1, MIPS_COP_0_STATUS           # disable interrupts
#else
        mtc0    zero, MIPS_COP_0_STATUS         # disable interrupts
#endif
        COP0_SYNC
#ifdef MULTIPROCESSOR
        PTR_L   a3, L_CPU(MIPS_CURLWP)          # make sure curcpu is correct
        NOP_L                                   # load delay
#endif
        INT_S   a1, CPU_INFO_CPL(a3)            # save IPL in cpu_info
        mtc0    a0, MIPS_COP_0_STATUS           # store back
        COP0_SYNC
#ifdef PARANOIA
        jr      ra
         nop                                    #  branch delay
#endif /* PARANOIA */
1:
#ifdef PARANOIA
        mfc0    v1, MIPS_COP_0_STATUS
        MFC0_HAZARD                             # load delay
        and     a0, v1                          # a1 contains bit that MBZ
3:      bnez    a0, 3b                          # loop forever
         nop                                    #  branch delay
#endif /* PARANOIA */
        jr      ra
         nop                                    #  branch delay

STATIC_LEAF(_splsw_splx)
STATIC_XLEAF(_splsw_splx_noprof)                # does not get mcount hooks
#ifdef PARANOIA
        sltiu   v0, a0, IPL_HIGH+1              # v0 = a0 <= IPL_HIGH
98:     beqz    v0, 98b
         nop
#endif
        PTR_L   a3, L_CPU(MIPS_CURLWP)          # get cpu_info
        NOP_L                                   # load delay
        INT_L   a2, CPU_INFO_CPL(a3)            # get IPL from cpu_info
        NOP_L                                   # load delay
        beq     a0, a2, 2f                      # if same, nothing to do
         nop                                    #  branch delay
#ifdef PARANOIA
        sltu    v0, a0, a2                      # v0 = a0 < a2
99:     beqz    v0, 99b                         # loop forever if false
         nop                                    #  branch delay
#endif /* PARANOIA */
        PTR_LA  v1, _C_LABEL(ipl_sr_map)        # get address of table
        sll     a2, a0, INT_SCALESHIFT          # convert IPL to array offset
        PTR_ADDU v1, a2                         # add to table addr
        INT_L   a1, (v1)                        # load SR bits for this IPL
1:
        mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
        xor     a1, MIPS_INT_MASK               # invert SR bits
        or      v1, a1                          # set any bits for this IPL
        DYNAMIC_STATUS_MASK(v1,t0)              # machine dependent masking
#if !defined(__mips_o32)
        or      v0, v1, MIPS_SR_INT_IE          #
        xor     v0, MIPS_SR_INT_IE              # clear interrupt enable bit
        mtc0    v0, MIPS_COP_0_STATUS           # disable interrupts
#else
        mtc0    zero, MIPS_COP_0_STATUS         # disable interrupts
#endif
        COP0_SYNC
        INT_S   a0, CPU_INFO_CPL(a3)            # save IPL in cpu_info (KSEG0)
        mtc0    v1, MIPS_COP_0_STATUS           # store back
        COP0_SYNC
#ifdef PARANOIA
        jr      ra
         nop                                    #  branch delay
#endif /* PARANOIA */
2:
#ifdef PARANOIA
        PTR_LA  v1, _C_LABEL(ipl_sr_map)        # get address of table
        sll     a2, a0, INT_SCALESHIFT          # convert IPL to array offset
        PTR_ADDU v1, a2                         # add to table addr
        INT_L   a1, (v1)                        # load SR bits for this IPL
        mfc0    v1, MIPS_COP_0_STATUS
        MFC0_HAZARD                             # load delay
        and     v1, MIPS_INT_MASK
        xor     a1, MIPS_INT_MASK
3:      bne     a1, v1, 3b
         nop                                    #  branch delay
#endif /* PARANOIA */
        jr      ra
         nop                                    #  branch delay
END(_splsw_splx)

STATIC_LEAF(_splsw_spl0)
        INT_L   v1, _C_LABEL(ipl_sr_map) + 4*IPL_NONE
        PTR_L   a3, L_CPU(MIPS_CURLWP)
        or      v1, MIPS_SR_INT_IE              # make sure interrupts are on
        xor     v1, MIPS_INT_MASK               # invert
        mfc0    a0, MIPS_COP_0_STATUS
        MFC0_HAZARD                             # load delay
        or      v0, a0, v1
        DYNAMIC_STATUS_MASK(v0,t0)              # machine dependent masking
#if !defined(__mips_o32)
        or      v1, v0, MIPS_SR_INT_IE          #
        xor     v1, MIPS_SR_INT_IE              # clear interrupt enable bit
        mtc0    v1, MIPS_COP_0_STATUS           # disable interrupts
#else
        mtc0    zero, MIPS_COP_0_STATUS         # disable interrupts
#endif
        COP0_SYNC
#if IPL_NONE == 0
        INT_S   zero, CPU_INFO_CPL(a3)          # set ipl to 0
#else
#error IPL_NONE != 0
#endif
        mtc0    v0, MIPS_COP_0_STATUS           # enable all sources
        JR_HB_RA                                # return (clear hazards)
END(_splsw_spl0)

STATIC_LEAF(_splsw_setsoftintr)
        mfc0    v1, MIPS_COP_0_STATUS           # save status register
#if !defined(__mips_o32)
        MFC0_HAZARD                             # load delay
        or      v0, v1, MIPS_SR_INT_IE          #
        xor     v0, MIPS_SR_INT_IE              # clear interrupt enable bit
        mtc0    v0, MIPS_COP_0_STATUS           # disable interrupts
#else
        mtc0    zero, MIPS_COP_0_STATUS         # disable interrupts
#endif
        COP0_SYNC
        mfc0    v0, MIPS_COP_0_CAUSE            # fetch cause register
        MFC0_HAZARD                             # load delay
        or      v0, v0, a0                      # set soft intr. bits
        mtc0    v0, MIPS_COP_0_CAUSE            # store back
        COP0_SYNC
        mtc0    v1, MIPS_COP_0_STATUS           # enable interrupts
        JR_HB_RA                                # return (clear hazards)
END(_splsw_setsoftintr)

STATIC_LEAF(_splsw_clrsoftintr)
        mfc0    v1, MIPS_COP_0_STATUS           # save status register
#if !defined(__mips_o32)
        MFC0_HAZARD                             # load delay
        or      v0, v1, MIPS_SR_INT_IE          #
        xor     v0, MIPS_SR_INT_IE              # clear interrupt enable bit
        mtc0    v0, MIPS_COP_0_STATUS           # disable interrupts
#else
        mtc0    zero, MIPS_COP_0_STATUS         # disable interrupts
#endif
        COP0_SYNC
        mfc0    v0, MIPS_COP_0_CAUSE            # fetch cause register
        nor     a0, zero, a0                    # bitwise inverse of A0
        and     v0, v0, a0                      # clear soft intr. bits
        mtc0    v0, MIPS_COP_0_CAUSE            # store back
        COP0_SYNC
        mtc0    v1, MIPS_COP_0_STATUS           # enable interrupts
        JR_HB_RA                                # return (clear hazards)
END(_splsw_clrsoftintr)

STATIC_LEAF(_splsw_splraise)
#if defined(DDB) && __mips >= 32
        tgeiu   a0, IPL_HIGH+1
#endif
        move    a1, a0
        PTR_LA  v1, _C_LABEL(ipl_sr_map)
        sll     a2, a0, INT_SCALESHIFT
        PTR_ADDU v1, a2
        b       _splraise
         INT_L  a0, (v1)
END(_splsw_splraise)

STATIC_LEAF(_splsw_splhigh)
STATIC_XLEAF(_splsw_splhigh_noprof)
        PTR_L   a3, L_CPU(MIPS_CURLWP)
        NOP_L                                   # load delay
        INT_L   v0, CPU_INFO_CPL(a3)            # get current IPL from cpu_info
        li      a1, IPL_HIGH                    #
        beq     v0, a1, 1f                      # don't do anything if IPL_HIGH
         nop                                    # branch delay
        mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
        MFC0_HAZARD                             # load delay
        and     a0, v1, MIPS_INT_MASK           # select all interrupts
        xor     a0, v1                          # clear all interrupts
        DYNAMIC_STATUS_MASK(a0,a2)              # machine dependent masking
        mtc0    a0, MIPS_COP_0_STATUS           # store back
        COP0_SYNC
#ifdef MULTIPROCESSOR
        PTR_L   a3, L_CPU(MIPS_CURLWP)          # make sure curcpu is correct
        NOP_L                                   # load delay
#endif
        INT_S   a1, CPU_INFO_CPL(a3)            # save IPL in cpu_info
#ifdef PARANOIA
        jr      ra                              # return
         nop                                    #  branch delay
#endif /* PARANOIA */
1:
#ifdef PARANOIA
        mfc0    v1, MIPS_COP_0_STATUS           # fetch status register
        MFC0_HAZARD                             # load delay
        and     v1, MIPS_INT_MASK               # any int bits set?
2:      bnez    v1, 2b                          # loop forever.
         nop                                    # branch delay
#endif /* PARANOIA */
        jr      ra                              # return
         nop                                    # branch delay
END(_splsw_splhigh)

        .p2align 4
STATIC_LEAF(_splsw_splddb)
        INT_L   a0, _C_LABEL(ipl_sr_map) + 4*IPL_DDB
        b       _splraise
         li     a1, IPL_DDB
        nop
END(_splsw_splddb)

STATIC_LEAF(_splsw_splsched)
        INT_L   a0, _C_LABEL(ipl_sr_map) + 4*IPL_SCHED
        b       _splraise
         li     a1, IPL_SCHED
        nop
END(_splsw_splsched)

STATIC_LEAF(_splsw_splvm)
        INT_L   a0, _C_LABEL(ipl_sr_map) + 4*IPL_VM
        b       _splraise
         li     a1, IPL_VM
        nop
END(_splsw_splvm)

STATIC_LEAF(_splsw_splsoftserial)
        INT_L   a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTSERIAL
        b       _splraise
         li     a1, IPL_SOFTSERIAL
        nop
END(_splsw_splsoftserial)

STATIC_LEAF(_splsw_splsoftnet)
        INT_L   a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTNET
        b       _splraise
         li     a1, IPL_SOFTNET
        nop
END(_splsw_splsoftnet)

STATIC_LEAF(_splsw_splsoftbio)
        INT_L   a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTBIO
        b       _splraise
         li     a1, IPL_SOFTBIO
        nop
END(_splsw_splsoftbio)

STATIC_LEAF(_splsw_splsoftclock)
        INT_L   a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTCLOCK
        b       _splraise
         li     a1, IPL_SOFTCLOCK
        nop
END(_splsw_splsoftclock)

STATIC_LEAF(_splsw_splintr)
        mfc0    ta1, MIPS_COP_0_CAUSE           # get active interrupts
        MFC0_HAZARD                             # load delay
                                                # restrict to hard int bits
        and     v1, ta1, MIPS_HARD_INT_MASK     # now have pending interrupts
        li      v0, IPL_NONE                    #  return IPL_NONE
        beq     v1, zero, 2f                    # quick exit if nothing pending
         nop                                    #  branch delay

        li      v0, IPL_VM                      # start at IPL_VM
        PTR_LA  ta3, _C_LABEL(ipl_sr_map) + 4*IPL_VM
        INT_L   ta2, -4(ta3)                    # load mask for IPL_SOFTSERIAL
        NOP_L                                   # load delay
        xor     ta2, MIPS_INT_MASK              # invert
        and     v1, ta2                         # apply to pending bits

1:
        INT_L   ta2, (ta3)                      # get SR bits for ipl in ta2
        NOP_L                                   # load delay
        xor     ta2, MIPS_INT_MASK              # invert
        and     ta2, v1                         # any match to pending intrs?
        beq     ta2, zero, 2f                   #  no, return ipl
         nop                                    #  branch delay

        PTR_ADDU ta3, 1 << INT_SCALESHIFT       # point to next entry
        addiu   v0, 1                           #  increase ipl by 1
        b       1b                              # and check it
         move   v1, ta2                         # whittle down pending intrs

2:
        jr      ra
         INT_S  v1, (a0)                        # return a new pending mask
END(_splsw_splintr)

STATIC_LEAF(_splsw_splcheck)
#ifdef PARANOIA
        PTR_L   t0, L_CPU(MIPS_CURLWP)
        NOP_L                                   # load delay
        INT_L   t1, CPU_INFO_CPL(t0)            # get current priority level

        mfc0    t0, MIPS_COP_0_STATUS           # get current status
        MFC0_HAZARD                             # load delay
        and     t0, MIPS_INT_MASK               # just want INT bits

        PTR_LA  t2, _C_LABEL(ipl_sr_map)
        sll     t1, INT_SCALESHIFT              # shift cpl to array index
        PTR_ADDU t2, t1
        INT_L   t3, (t2)                        # load value
        NOP_L                                   # load delay
        xor     t3, MIPS_INT_MASK               # invert
1:      bne     t0, t3, 1b                      # loop forever if not equal
         nop                                    #  branch delay
#endif /* PARANOIA */
        jr      ra
         nop                                    #  branch delay
END(_splsw_splcheck)

        .rdata
        .globl _C_LABEL(std_splsw)
_C_LABEL(std_splsw):
        PTR_WORD _C_LABEL(_splsw_splhigh)
        PTR_WORD _C_LABEL(_splsw_splsched)
        PTR_WORD _C_LABEL(_splsw_splvm)
        PTR_WORD _C_LABEL(_splsw_splsoftserial)
        PTR_WORD _C_LABEL(_splsw_splsoftnet)
        PTR_WORD _C_LABEL(_splsw_splsoftbio)
        PTR_WORD _C_LABEL(_splsw_splsoftclock)
        PTR_WORD _C_LABEL(_splsw_splraise)
        PTR_WORD _C_LABEL(_splsw_spl0)
        PTR_WORD _C_LABEL(_splsw_splx)
        PTR_WORD _C_LABEL(_splsw_splhigh_noprof)
        PTR_WORD _C_LABEL(_splsw_splx_noprof)
        PTR_WORD _C_LABEL(_splsw_setsoftintr)
        PTR_WORD _C_LABEL(_splsw_clrsoftintr)
        PTR_WORD _C_LABEL(_splsw_splintr)
        PTR_WORD _C_LABEL(_splsw_splcheck)