1/*        $NetBSD: spl.S,v 1.20 2023/05/22 06:50:52 skrll Exp $       */
2
3/*-
4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas <matt@3am-software.com>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "opt_multiprocessor.h"         /* MP kernel? */
33#include "opt_cputype.h"      /* which mips CPU levels do we support? */
34#include "opt_ddb.h"
35
36#include <sys/cdefs.h>
37
38#include <mips/asm.h>
39#include <mips/cpuregs.h>
40
41RCSID("$NetBSD: spl.S,v 1.20 2023/05/22 06:50:52 skrll Exp $")
42
43#include "assym.h"
44
45          .data
46          .globl    _C_LABEL(ipl_sr_map)
47          .type     _C_LABEL(ipl_sr_map),@object
48          .p2align INT_SCALESHIFT
49_C_LABEL(ipl_sr_map):
50          .word     0                             /* IPL_NONE */
51          .word     MIPS_SOFT_INT_MASK_0          /* IPL_SOFT{CLOCK,BIO} */
52          .word     MIPS_SOFT_INT_MASK  /* IPL_SOFT{NET,SERIAL} */
53          .word     MIPS_INT_MASK                 /* IPL_VM */
54          .word     MIPS_INT_MASK                 /* IPL_SCHED */
55          .word     MIPS_INT_MASK                 /* IPL_DDB */
56          .word     MIPS_INT_MASK                 /* IPL_HIGH */
57
58          .text
59          .set      noreorder
60/*
61 * MIPS processor interrupt control
62 *
63 * Used as building blocks for spl(9) kernel interface.
64 */
65_splraise:
66          /*
67           * a0 = SR bits to be cleared for this IPL
68           * a1 = this IPL (IPL_*)
69           * Can only use a0-a3 and v0-v1
70           */
71          PTR_L     a3, L_CPU(MIPS_CURLWP)
72          NOP_L                                             # load delay
73          INT_L     v0, CPU_INFO_CPL(a3)                    # get current IPL from cpu_info
74          NOP_L                                             # load delay
75          sltu      v1, a1, v0                              # newipl < curipl
76          bnez      v1, 1f                                  # yes, don't change.
77           nop                                              #  branch delay
78          mfc0      v1, MIPS_COP_0_STATUS                   # fetch status register
79          MFC0_HAZARD                                       # load delay
80          or        v1, MIPS_INT_MASK             # enable all interrupts
81          xor       a0, v1                                  # disable ipl's masked bits
82          DYNAMIC_STATUS_MASK(a0,v0)              # machine dependent masking
83#if !defined(__mips_o32)
84          or        v1, MIPS_SR_INT_IE            #
85          xor       v1, MIPS_SR_INT_IE            # clear interrupt enable bit
86          mtc0      v1, MIPS_COP_0_STATUS                   # disable interrupts
87#else
88          mtc0      zero, MIPS_COP_0_STATUS                 # disable interrupts
89#endif
90          COP0_SYNC
91#ifdef MULTIPROCESSOR
92          PTR_L     a3, L_CPU(MIPS_CURLWP)                  # make sure curcpu is correct
93          NOP_L                                             # load delay
94#endif
95          INT_S     a1, CPU_INFO_CPL(a3)                    # save IPL in cpu_info
96          mtc0      a0, MIPS_COP_0_STATUS                   # store back
97          COP0_SYNC
98#ifdef PARANOIA
99          jr        ra
100           nop                                              #  branch delay
101#endif /* PARANOIA */
1021:
103#ifdef PARANOIA
104          mfc0      v1, MIPS_COP_0_STATUS
105          MFC0_HAZARD                                       # load delay
106          and       a0, v1                                  # a1 contains bit that MBZ
1073:        bnez      a0, 3b                                  # loop forever
108           nop                                              #  branch delay
109#endif /* PARANOIA */
110          jr        ra
111           nop                                              #  branch delay
112
113STATIC_LEAF(_splsw_splx)
114STATIC_XLEAF(_splsw_splx_noprof)                  # does not get mcount hooks
115#ifdef PARANOIA
116          sltiu     v0, a0, IPL_HIGH+1            # v0 = a0 <= IPL_HIGH
11798:       beqz      v0, 98b
118           nop
119#endif
120          PTR_L     a3, L_CPU(MIPS_CURLWP)                  # get cpu_info
121          NOP_L                                             # load delay
122          INT_L     a2, CPU_INFO_CPL(a3)                    # get IPL from cpu_info
123          NOP_L                                             # load delay
124          beq       a0, a2, 2f                              # if same, nothing to do
125           nop                                              #  branch delay
126#ifdef PARANOIA
127          sltu      v0, a0, a2                              # v0 = a0 < a2
12899:       beqz      v0, 99b                                 # loop forever if false
129           nop                                              #  branch delay
130#endif /* PARANOIA */
131          PTR_LA    v1, _C_LABEL(ipl_sr_map)      # get address of table
132          sll       a2, a0, INT_SCALESHIFT                  # convert IPL to array offset
133          PTR_ADDU v1, a2                                   # add to table addr
134          INT_L     a1, (v1)                      # load SR bits for this IPL
1351:
136          mfc0      v1, MIPS_COP_0_STATUS                   # fetch status register
137          xor       a1, MIPS_INT_MASK             # invert SR bits
138          or        v1, a1                                  # set any bits for this IPL
139          DYNAMIC_STATUS_MASK(v1,t0)              # machine dependent masking
140#if !defined(__mips_o32)
141          or        v0, v1, MIPS_SR_INT_IE                  #
142          xor       v0, MIPS_SR_INT_IE            # clear interrupt enable bit
143          mtc0      v0, MIPS_COP_0_STATUS                   # disable interrupts
144#else
145          mtc0      zero, MIPS_COP_0_STATUS                 # disable interrupts
146#endif
147          COP0_SYNC
148          INT_S     a0, CPU_INFO_CPL(a3)                    # save IPL in cpu_info (KSEG0)
149          mtc0      v1, MIPS_COP_0_STATUS                   # store back
150          COP0_SYNC
151#ifdef PARANOIA
152          jr        ra
153           nop                                              #  branch delay
154#endif /* PARANOIA */
1552:
156#ifdef PARANOIA
157          PTR_LA    v1, _C_LABEL(ipl_sr_map)      # get address of table
158          sll       a2, a0, INT_SCALESHIFT                  # convert IPL to array offset
159          PTR_ADDU v1, a2                                   # add to table addr
160          INT_L     a1, (v1)                      # load SR bits for this IPL
161          mfc0      v1, MIPS_COP_0_STATUS
162          MFC0_HAZARD                                       # load delay
163          and       v1, MIPS_INT_MASK
164          xor       a1, MIPS_INT_MASK
1653:        bne       a1, v1, 3b
166           nop                                              #  branch delay
167#endif /* PARANOIA */
168          jr        ra
169           nop                                              #  branch delay
170END(_splsw_splx)
171
172STATIC_LEAF(_splsw_spl0)
173          INT_L     v1, _C_LABEL(ipl_sr_map) + 4*IPL_NONE
174          PTR_L     a3, L_CPU(MIPS_CURLWP)
175          or        v1, MIPS_SR_INT_IE            # make sure interrupts are on
176          xor       v1, MIPS_INT_MASK             # invert
177          mfc0      a0, MIPS_COP_0_STATUS
178          MFC0_HAZARD                                       # load delay
179          or        v0, a0, v1
180          DYNAMIC_STATUS_MASK(v0,t0)              # machine dependent masking
181#if !defined(__mips_o32)
182          or        v1, v0, MIPS_SR_INT_IE                  #
183          xor       v1, MIPS_SR_INT_IE            # clear interrupt enable bit
184          mtc0      v1, MIPS_COP_0_STATUS                   # disable interrupts
185#else
186          mtc0      zero, MIPS_COP_0_STATUS                 # disable interrupts
187#endif
188          COP0_SYNC
189#if IPL_NONE == 0
190          INT_S     zero, CPU_INFO_CPL(a3)                  # set ipl to 0
191#else
192#error IPL_NONE != 0
193#endif
194          mtc0      v0, MIPS_COP_0_STATUS                   # enable all sources
195          JR_HB_RA                                # return (clear hazards)
196END(_splsw_spl0)
197
198STATIC_LEAF(_splsw_setsoftintr)
199          mfc0      v1, MIPS_COP_0_STATUS                   # save status register
200#if !defined(__mips_o32)
201          MFC0_HAZARD                                       # load delay
202          or        v0, v1, MIPS_SR_INT_IE                  #
203          xor       v0, MIPS_SR_INT_IE            # clear interrupt enable bit
204          mtc0      v0, MIPS_COP_0_STATUS                   # disable interrupts
205#else
206          mtc0      zero, MIPS_COP_0_STATUS                 # disable interrupts
207#endif
208          COP0_SYNC
209          mfc0      v0, MIPS_COP_0_CAUSE                    # fetch cause register
210          MFC0_HAZARD                                       # load delay
211          or        v0, v0, a0                              # set soft intr. bits
212          mtc0      v0, MIPS_COP_0_CAUSE                    # store back
213          COP0_SYNC
214          mtc0      v1, MIPS_COP_0_STATUS                   # enable interrupts
215          JR_HB_RA                                # return (clear hazards)
216END(_splsw_setsoftintr)
217
218STATIC_LEAF(_splsw_clrsoftintr)
219          mfc0      v1, MIPS_COP_0_STATUS                   # save status register
220#if !defined(__mips_o32)
221          MFC0_HAZARD                                       # load delay
222          or        v0, v1, MIPS_SR_INT_IE                  #
223          xor       v0, MIPS_SR_INT_IE            # clear interrupt enable bit
224          mtc0      v0, MIPS_COP_0_STATUS                   # disable interrupts
225#else
226          mtc0      zero, MIPS_COP_0_STATUS                 # disable interrupts
227#endif
228          COP0_SYNC
229          mfc0      v0, MIPS_COP_0_CAUSE                    # fetch cause register
230          nor       a0, zero, a0                            # bitwise inverse of A0
231          and       v0, v0, a0                              # clear soft intr. bits
232          mtc0      v0, MIPS_COP_0_CAUSE                    # store back
233          COP0_SYNC
234          mtc0      v1, MIPS_COP_0_STATUS                   # enable interrupts
235          JR_HB_RA                                # return (clear hazards)
236END(_splsw_clrsoftintr)
237
238STATIC_LEAF(_splsw_splraise)
239#if defined(DDB) && __mips >= 32
240          tgeiu     a0, IPL_HIGH+1
241#endif
242          move      a1, a0
243          PTR_LA    v1, _C_LABEL(ipl_sr_map)
244          sll       a2, a0, INT_SCALESHIFT
245          PTR_ADDU v1, a2
246          b         _splraise
247           INT_L    a0, (v1)
248END(_splsw_splraise)
249
250STATIC_LEAF(_splsw_splhigh)
251STATIC_XLEAF(_splsw_splhigh_noprof)
252          PTR_L     a3, L_CPU(MIPS_CURLWP)
253          NOP_L                                             # load delay
254          INT_L     v0, CPU_INFO_CPL(a3)                    # get current IPL from cpu_info
255          li        a1, IPL_HIGH                            #
256          beq       v0, a1, 1f                              # don't do anything if IPL_HIGH
257           nop                                              # branch delay
258          mfc0      v1, MIPS_COP_0_STATUS                   # fetch status register
259          MFC0_HAZARD                                       # load delay
260          and       a0, v1, MIPS_INT_MASK                   # select all interrupts
261          xor       a0, v1                                  # clear all interrupts
262          DYNAMIC_STATUS_MASK(a0,a2)              # machine dependent masking
263          mtc0      a0, MIPS_COP_0_STATUS                   # store back
264          COP0_SYNC
265#ifdef MULTIPROCESSOR
266          PTR_L     a3, L_CPU(MIPS_CURLWP)                  # make sure curcpu is correct
267          NOP_L                                             # load delay
268#endif
269          INT_S     a1, CPU_INFO_CPL(a3)                    # save IPL in cpu_info
270#ifdef PARANOIA
271          jr        ra                                      # return
272           nop                                              #  branch delay
273#endif /* PARANOIA */
2741:
275#ifdef PARANOIA
276          mfc0      v1, MIPS_COP_0_STATUS                   # fetch status register
277          MFC0_HAZARD                                       # load delay
278          and       v1, MIPS_INT_MASK             # any int bits set?
2792:        bnez      v1, 2b                                  # loop forever.
280           nop                                              # branch delay
281#endif /* PARANOIA */
282          jr        ra                                      # return
283           nop                                              # branch delay
284END(_splsw_splhigh)
285
286          .p2align 4
287STATIC_LEAF(_splsw_splddb)
288          INT_L     a0, _C_LABEL(ipl_sr_map) + 4*IPL_DDB
289          b         _splraise
290           li       a1, IPL_DDB
291          nop
292END(_splsw_splddb)
293
294STATIC_LEAF(_splsw_splsched)
295          INT_L     a0, _C_LABEL(ipl_sr_map) + 4*IPL_SCHED
296          b         _splraise
297           li       a1, IPL_SCHED
298          nop
299END(_splsw_splsched)
300
301STATIC_LEAF(_splsw_splvm)
302          INT_L     a0, _C_LABEL(ipl_sr_map) + 4*IPL_VM
303          b         _splraise
304           li       a1, IPL_VM
305          nop
306END(_splsw_splvm)
307
308STATIC_LEAF(_splsw_splsoftserial)
309          INT_L     a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTSERIAL
310          b         _splraise
311           li       a1, IPL_SOFTSERIAL
312          nop
313END(_splsw_splsoftserial)
314
315STATIC_LEAF(_splsw_splsoftnet)
316          INT_L     a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTNET
317          b         _splraise
318           li       a1, IPL_SOFTNET
319          nop
320END(_splsw_splsoftnet)
321
322STATIC_LEAF(_splsw_splsoftbio)
323          INT_L     a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTBIO
324          b         _splraise
325           li       a1, IPL_SOFTBIO
326          nop
327END(_splsw_splsoftbio)
328
329STATIC_LEAF(_splsw_splsoftclock)
330          INT_L     a0, _C_LABEL(ipl_sr_map) + 4*IPL_SOFTCLOCK
331          b         _splraise
332           li       a1, IPL_SOFTCLOCK
333          nop
334END(_splsw_splsoftclock)
335
336STATIC_LEAF(_splsw_splintr)
337          mfc0      ta1, MIPS_COP_0_CAUSE                   # get active interrupts
338          MFC0_HAZARD                                       # load delay
339                                                            # restrict to hard int bits
340          and       v1, ta1, MIPS_HARD_INT_MASK   # now have pending interrupts
341          li        v0, IPL_NONE                            #  return IPL_NONE
342          beq       v1, zero, 2f                            # quick exit if nothing pending
343           nop                                              #  branch delay
344
345          li        v0, IPL_VM                              # start at IPL_VM
346          PTR_LA    ta3, _C_LABEL(ipl_sr_map) + 4*IPL_VM
347          INT_L     ta2, -4(ta3)                            # load mask for IPL_SOFTSERIAL
348          NOP_L                                             # load delay
349          xor       ta2, MIPS_INT_MASK            # invert
350          and       v1, ta2                                 # apply to pending bits
351
3521:
353          INT_L     ta2, (ta3)                              # get SR bits for ipl in ta2
354          NOP_L                                             # load delay
355          xor       ta2, MIPS_INT_MASK            # invert
356          and       ta2, v1                                 # any match to pending intrs?
357          beq       ta2, zero, 2f                           #  no, return ipl
358           nop                                              #  branch delay
359
360          PTR_ADDU ta3, 1 << INT_SCALESHIFT       # point to next entry
361          addiu     v0, 1                                   #  increase ipl by 1
362          b         1b                                      # and check it
363           move     v1, ta2                                 # whittle down pending intrs
364
3652:
366          jr        ra
367           INT_S    v1, (a0)                      # return a new pending mask
368END(_splsw_splintr)
369
370STATIC_LEAF(_splsw_splcheck)
371#ifdef PARANOIA
372          PTR_L     t0, L_CPU(MIPS_CURLWP)
373          NOP_L                                             # load delay
374          INT_L     t1, CPU_INFO_CPL(t0)                    # get current priority level
375
376          mfc0      t0, MIPS_COP_0_STATUS                   # get current status
377          MFC0_HAZARD                                       # load delay
378          and       t0, MIPS_INT_MASK             # just want INT bits
379
380          PTR_LA    t2, _C_LABEL(ipl_sr_map)
381          sll       t1, INT_SCALESHIFT            # shift cpl to array index
382          PTR_ADDU t2, t1
383          INT_L     t3, (t2)                      # load value
384          NOP_L                                             # load delay
385          xor       t3, MIPS_INT_MASK             # invert
3861:        bne       t0, t3, 1b                              # loop forever if not equal
387           nop                                              #  branch delay
388#endif /* PARANOIA */
389          jr        ra
390           nop                                              #  branch delay
391END(_splsw_splcheck)
392
393          .rdata
394          .globl _C_LABEL(std_splsw)
395_C_LABEL(std_splsw):
396        PTR_WORD _C_LABEL(_splsw_splhigh)
397        PTR_WORD _C_LABEL(_splsw_splsched)
398        PTR_WORD _C_LABEL(_splsw_splvm)
399        PTR_WORD _C_LABEL(_splsw_splsoftserial)
400        PTR_WORD _C_LABEL(_splsw_splsoftnet)
401        PTR_WORD _C_LABEL(_splsw_splsoftbio)
402        PTR_WORD _C_LABEL(_splsw_splsoftclock)
403        PTR_WORD _C_LABEL(_splsw_splraise)
404        PTR_WORD _C_LABEL(_splsw_spl0)
405        PTR_WORD _C_LABEL(_splsw_splx)
406        PTR_WORD _C_LABEL(_splsw_splhigh_noprof)
407        PTR_WORD _C_LABEL(_splsw_splx_noprof)
408          PTR_WORD _C_LABEL(_splsw_setsoftintr)
409          PTR_WORD _C_LABEL(_splsw_clrsoftintr)
410          PTR_WORD _C_LABEL(_splsw_splintr)
411          PTR_WORD _C_LABEL(_splsw_splcheck)
412