aboutsummaryrefslogtreecommitdiff
path: root/sys/sun4v/sun4v/interrupt.S
diff options
context:
space:
mode:
Diffstat (limited to 'sys/sun4v/sun4v/interrupt.S')
-rw-r--r--sys/sun4v/sun4v/interrupt.S477
1 files changed, 477 insertions, 0 deletions
diff --git a/sys/sun4v/sun4v/interrupt.S b/sys/sun4v/sun4v/interrupt.S
new file mode 100644
index 000000000000..9fc67f15a14c
--- /dev/null
+++ b/sys/sun4v/sun4v/interrupt.S
@@ -0,0 +1,477 @@
+/*-
+ * Copyright (c) 2002 Jake Burkholder.
+ * Copyright (c) 2006 Kip Macy kmacy@FreeBSD.org
+ * 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.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_simulator.h"
+#include "opt_trap_trace.h"
+#include <machine/hypervisorvar.h>
+#include <machine/asi.h>
+#include <machine/asmacros.h>
+#include <machine/ktr.h>
+#include <machine/pstate.h>
+
+#include "assym.s"
+
+
+#define PUTCHAR(x) \
+ mov x, %o0 ; \
+ mov CONS_WRITE, %o5 ; \
+ ta FAST_TRAP
+
+ENTRY(intr_fast)
+ save %sp, -CCFSZ, %sp
+1: ldx [PCPU(IRHEAD)], %l0
+ brnz,a,pt %l0, 2f
+ nop
+
+ ret
+ restore
+
+2: wrpr %g0, PSTATE_NORMAL, %pstate
+
+ ldx [%l0 + IR_NEXT], %l1
+ brnz,pt %l1, 3f
+ stx %l1, [PCPU(IRHEAD)]
+ PCPU_ADDR(IRHEAD, %l1)
+ stx %l1, [PCPU(IRTAIL)]
+
+3: ldx [%l0 + IR_FUNC], %o0
+ ldx [%l0 + IR_ARG], %o1
+ lduw [%l0 + IR_VEC], %o2
+
+ /* intrcnt[intr_countp[%o2]]++ */
+ SET(intrcnt, %l7, %l2) /* %l2 = intrcnt */
+ prefetcha [%l2] ASI_N, 1
+ SET(intr_countp, %l7, %l3) /* %l3 = intr_countp */
+ sllx %o2, 1, %l4 /* %l4 = vec << 1 */
+ lduh [%l4 + %l3], %l5 /* %l5 = intr_countp[%o2] */
+ sllx %l5, 3, %l6 /* %l6 = intr_countp[%o2] << 3 */
+ add %l6, %l2, %l7 /* %l7 = intrcnt[intr_countp[%o2]] */
+ ldx [%l7], %l2
+ inc %l2
+ stx %l2, [%l7]
+
+ ldx [PCPU(IRFREE)], %l1
+ stx %l1, [%l0 + IR_NEXT]
+ stx %l0, [PCPU(IRFREE)]
+
+ wrpr %g0, PSTATE_KERNEL, %pstate
+
+ call %o0
+ mov %o1, %o0
+
+ lduw [%l0 + IR_VEC], %o0
+ call hvio_intr_setstate
+ mov HV_INTR_IDLE_STATE, %o1
+ ba,a %xcc, 1b
+ nop
+END(intr_fast)
+
+/*
+ * Running tally of invalid CPU mondo interrupts
+ */
+#if defined(lint)
+uint64_t cpu_mondo_invalid;
+#else /* lint */
+ .data
+ .globl cpu_mondo_invalid
+ .align 8
+cpu_mondo_invalid:
+ .skip 8
+
+ .text
+#endif /* lint */
+
+#if defined(lint)
+void
+cpu_mondo(void)
+{}
+#else /* lint */
+
+/*
+ * (TT 0x7c, TL>0) CPU Mondo Queue Handler
+ * Globals are the Interrupt Globals.
+ *
+ * Interrupts in sun4v are delivered to privileged code in the form
+ * of interrupt reports. Each interrupt report is 64-bytes long,
+ * consisting of 8 64-bit words. Each interrupt report is appended
+ * onto the appropriate interrupt queue of which there are currently two,
+ * one for CPU mondos (formerly referred to as cross trap function
+ * requests) and one for device mondos. Each queue has its own
+ * trap vector.
+ *
+ * New reports are appended onto the tail of the queue, and privileged
+ * code reads the report from the queue's head. The head pointer is stored
+ * in a register and is equal to the current offset from the base address
+ * of its associated queue.
+ *
+ * The structure of the cpu mondo report follows sun4u conventions:
+ *
+ * word 0: address of cross-trap handler
+ * word 1: first argument to handler
+ * word 2: second argument to handler
+ * word 3-7: unused
+ *
+ * Privileged code is responsible for incrementing the head pointer
+ * to remove each entry. The pointers are updated using modulo
+ * arithmetic such that the queue is empty when head == tail and is full
+ * when the addition of an entry would make head == tail.
+ *
+ * This trap handler is called when the cpu mondo queue becomes non-empty
+ * and will continue to be called while interrupts enabled until the
+ * queue becomes empty.
+ *
+ */
+ENTRY(cpu_mondo)
+ !
+ ! Register Usage:-
+ ! %g5 PC for fasttrap TL>0 handler
+ ! %g1 arg 1
+ ! %g2 arg 2
+ ! %g3 queue base RA, ackmask
+ ! %g4 queue size mask
+ ! %g6 head ptr
+ mov CPU_MONDO_QUEUE_HEAD, %g1
+ ldxa [%g1]ASI_QUEUE, %g6 ! %g6 = head ptr
+ mov CPU_MONDO_QUEUE_TAIL, %g2
+ ldxa [%g2]ASI_QUEUE, %g4 ! %g4 = tail ptr
+ cmp %g6, %g4
+ be,pn %xcc, 0f ! head == tail
+ nop
+
+ /*
+ * Get the address of the current CPU and index into
+ * the pcpu structure for the Q values.
+ */
+ GET_PCPU_SCRATCH
+ ldx [PCPU(CPU_Q_RA)], %g3 ! %g3 = queue base PA
+ ldx [PCPU(CPU_Q_SIZE)], %g4 ! %g4 = queue size
+ sub %g4, 1, %g4 ! %g4 = queue size mask
+
+ ! Load interrupt receive data registers 1 and 2 to fetch
+ ! the arguments for the fast trap handler.
+ !
+ ! Since the data words in the interrupt report are not defined yet
+ ! we assume that the consective words contain valid data and preserve
+ ! sun4u's xcall mondo arguments.
+ ! Register usage:
+ ! %g5 PC for fasttrap TL>0 handler
+ ! %g1 arg 1
+ ! %g2 arg 2
+
+ ldxa [%g3 + %g6]ASI_REAL, %g5 ! get PC from q base + head
+ add %g6, 0x8, %g6 ! inc head 8 bytes
+ ldxa [%g3 + %g6]ASI_REAL, %g1 ! read data word 1
+ add %g6, 0x8, %g6 ! inc head 8 bytes
+ ldxa [%g3 + %g6]ASI_REAL, %g2 ! read data word 2
+ add %g6, 0x8, %g6 ! inc head 8 bytes
+ ldxa [%g3 + %g6]ASI_REAL, %g7 ! read data word 3
+ add %g6, (INTR_REPORT_SIZE - 24) , %g6 ! inc head to next record
+
+ and %g6, %g4, %g6 ! and size mask for wrap around
+ mov CPU_MONDO_QUEUE_HEAD, %g3
+ stxa %g6, [%g3]ASI_QUEUE ! store head pointer
+ membar #Sync
+
+ mov %g7, %g3 ! ackmask
+ /*
+ * For now catch invalid PC being passed via cpu_mondo queue
+ */
+ set KERNBASE, %g4
+ cmp %g5, %g4
+ bl,a,pn %xcc, 1f ! branch if bad %pc
+ nop
+
+ jmp %g5 ! jump to traphandler
+ nop
+1:
+ ! invalid trap handler, discard it for now
+ set cpu_mondo_invalid, %g4
+ ldx [%g4], %g5
+ inc %g5
+ stx %g5, [%g4]
+0:
+ retry
+ /* NOTREACHED */
+END(cpu_mondo)
+
+#endif /* lint */
+
+
+#if defined(lint)
+void
+dev_mondo(void)
+{}
+#else /* lint */
+
+/*
+ * (TT 0x7d, TL>0) Dev Mondo Queue Handler
+ * Globals are the Interrupt Globals.
+ *
+ * Device interrupt reports take the following form:
+ * Bits: 63 11 10 6 5 0
+ * word 0: | zero | | bus port | | device ID |
+ * word 1-7: unused
+ *
+ */
+ENTRY(dev_mondo)
+ /*
+ * Register Usage:-
+ * %g5 inum
+ * %g1 ptr to intrerrupt vector entry for inum
+ * %g3 queue base PA
+ * %g4 queue size mask
+ * %g6 head ptr
+ */
+! mov %o0, %g1
+! mov %o5, %g6
+! PUTCHAR(0x2b)
+! PUTCHAR(0x2b)
+! mov %g1, %o0
+! mov %g6, %o5
+
+ mov DEV_MONDO_QUEUE_HEAD, %g1
+ ldxa [%g1]ASI_QUEUE, %g6 ! %g6 = head ptr
+ mov DEV_MONDO_QUEUE_TAIL, %g2
+ ldxa [%g2]ASI_QUEUE, %g4 ! %g4 = tail ptr
+ cmp %g6, %g4
+ be,pn %xcc, 0f ! head == tail
+ nop
+
+ /*
+ * Get CPU address and index to Q base
+ */
+ GET_PCPU_SCRATCH
+ ldx [PCPU(DEV_Q_RA)], %g3 ! %g3 = queue base PA
+
+ /*
+ * Register usage:
+ * %g5 - inum
+ */
+ ldxa [%g3 + %g6]ASI_REAL, %g5 ! %g5 = inum from q base + head
+
+ /*
+ * We verify that inum is valid ( < IV_MAX). If it is greater
+ * than IV_MAX, we let the software interrupt handler take care
+ * of it.
+ */
+ set IV_MAX, %g4
+ cmp %g5, %g4
+ bgeu,a,pn %xcc, 1f
+ ldx [PCPU(DEV_Q_SIZE)], %g4 ! queue size (delay slot)
+
+ /*
+ * Find the function, argument and desired priority from the
+ * intr_vectors table
+ */
+ set intr_vectors, %g1
+ sll %g5, IV_SHIFT, %g2
+ add %g1, %g2, %g1 ! %g1 = &intr_vectors[inum]
+
+ /*
+ * Get an intr_request from the free list. There should always be one
+ * unless we are getting an interrupt storm from stray interrupts, in
+ * which case the we will dereference a NULL pointer and panic.
+ */
+ ldx [PCPU(IRFREE)], %g2 ! %g2 = ptr to tail element
+
+ /*
+ * Sun4v-fixme: It might be better to have the software interrupt
+ * handler deal with this scenario rather than just panic.
+ */
+ !brz,a,pt %g2, 1f
+ !ldx [PCPU(DEV_Q_SIZE)], %g4 ! queue size (delay slot)
+
+ ldx [%g2 + IR_NEXT], %g3 ! %g3 = next element in list
+ stx %g3, [PCPU(IRFREE)] ! %g3 is now the tail
+
+ /*
+ * Store the vector number, function, argument, and priority.
+ */
+ stw %g5, [%g2 + IR_VEC]
+ ldx [%g1 + IV_FUNC], %g3 ! %g3 = function ptr
+ stx %g3, [%g2 + IR_FUNC]
+ ldx [%g1 + IV_ARG], %g3 ! %g3 = argument ptr
+ stx %g3, [%g2 + IR_ARG]
+ lduw [%g1 + IV_PRI], %g1 ! %g1 = priority
+ stw %g1, [%g2 + IR_PRI]
+
+ /*
+ * Link it onto the end of the active list
+ */
+ stx %g0, [%g2 + IR_NEXT]
+ ldx [PCPU(IRTAIL)], %g3 ! %g3 = ptr to tail
+ stx %g2, [%g3]
+ add %g2, IR_NEXT, %g2
+ stx %g2, [PCPU(IRTAIL)]
+
+ /*
+ * Unlike Solaris, FreeBSD throws away the rest of the
+ * 64-byte packet. We may want to revisit this philosophy
+ * at some point since information in the packet is defined by
+ * the device and MAY be meaningful.
+ *
+ * For now, we retain FreeBSD's sun4u semantics.
+ */
+ ldx [PCPU(DEV_Q_SIZE)], %g4 ! queue size
+
+1: sub %g4, 1, %g4 ! %g4 = queue size mask
+ add %g6, INTR_REPORT_SIZE, %g6 ! inc head to next record
+ and %g6, %g4, %g6 ! and mask for wrap around
+ mov DEV_MONDO_QUEUE_HEAD, %g3
+ stxa %g6, [%g3]ASI_QUEUE ! increment head offset
+ membar #Sync
+
+ /*
+ * Trigger a softint at the level indicated by the priority
+ */
+ mov 1, %g6
+ sllx %g6, %g1, %g6
+ wr %g6, 0, %set_softint
+#if 0
+ mov %o0, %g1
+ mov %o5, %g6
+ PUTCHAR(0x2b)
+ PUTCHAR(0x2b)
+ mov %g1, %o0
+ mov %g6, %o5
+#endif
+ /*
+ * Done, retry the instruction
+ */
+0: retry
+
+
+ /* NOTREACHED */
+END(dev_mondo)
+#endif /* lint */
+
+ENTRY(tl_invlctx)
+ mov %o0, %g2
+ mov %o1, %g4
+ mov %o2, %g5
+ mov %o3, %g6
+ mov %o5, %g7
+ mov %g0, %o0
+ mov %g0, %o1
+ mov %g1, %o2
+ mov MAP_ITLB|MAP_DTLB, %o3
+ mov MMU_DEMAP_CTX, %o5
+ ta FAST_TRAP
+ brz,pt %o0, 1f
+ nop
+ ba panic_bad_hcall
+ mov MMU_DEMAP_CTX, %o1
+1:
+ mov %g2, %o0
+ mov %g4, %o1
+ mov %g5, %o2
+ mov %g6, %o3
+ mov %g7, %o5
+ ba,a,pt %xcc, set_ackmask
+ nop
+END(tl_invlctx)
+
+ENTRY(tl_invltlb)
+ mov %o0, %g1
+ mov %o1, %g2
+ mov %o2, %g4
+ mov %o5, %g5
+
+ mov %g0, %o0
+ mov %g0, %o1
+ mov MAP_ITLB | MAP_DTLB, %o2
+ mov MMU_DEMAP_ALL, %o5
+ ta FAST_TRAP
+ brz,pt %o0, 1f
+ nop
+ ba panic_bad_hcall
+ mov MMU_DEMAP_ALL, %o1
+1:
+ mov %g1, %o0
+ mov %g2, %o1
+ mov %g4, %o2
+ mov %g5, %o5
+
+ ba,a,pt %xcc, set_ackmask
+ nop
+END(tl_invltlb)
+
+ENTRY(tl_invlpg)
+ mov %o0, %g5
+ mov %o1, %g6
+ mov %o2, %g7
+ mov MAP_ITLB|MAP_DTLB, %o2
+ mov %g1, %o0
+ mov %g2, %o1
+ ta MMU_UNMAP_ADDR
+ brz,pt %o0, 1f
+ nop
+ ba panic_bad_hcall
+ mov MMU_UNMAP_ADDR, %o1
+1:
+ mov %g5, %o0
+ mov %g6, %o1
+ mov %g7, %o2
+ ba,a,pt %xcc, set_ackmask
+ nop
+END(tl_invlpg)
+
+ENTRY(set_ackmask)
+ membar #Sync
+ GET_PCPU_PHYS_SCRATCH(%g6)
+ wr %g0, ASI_REAL, %asi
+
+#ifdef TRAP_TRACING
+ /* pcpu->pad[0] = %pc */
+ rd %pc, %g4
+ stxa %g4, [PCPU(PAD)]%asi
+
+ /* pcpu->pad[1] = %tick */
+ rdpr %tick, %g4
+ stxa %g4, [PCPU(PAD) + 8]%asi
+
+ /*
+ * Increment a counter which might help us notice if we're
+ * stuck in a loop. pcpu->pad[2] = count
+ */
+ ldxa [PCPU(PAD) + 16]%asi, %g4
+ add %g4, 1, %g4
+ stxa %g4, [PCPU(PAD) + 16]%asi
+#endif
+
+ lda [PCPU(CPUMASK)]%asi, %g4
+ lda [%g3]%asi, %g1
+1: or %g1, %g4, %g2
+ casa [%g3]%asi, %g1, %g2
+ cmp %g1, %g2
+ bne,a,pn %icc, 1b
+ lda [%g3]%asi, %g1
+ retry
+END(set_ackmask)