#include "assym.h"
#include <sys/asm_linkage.h>
#include <sys/machthread.h>
#include <sys/machcpuvar.h>
#include <sys/intreg.h>
#include <sys/cmn_err.h>
#include <sys/ftrace.h>
#include <sys/machasi.h>
#include <sys/scb.h>
#include <sys/error.h>
#include <sys/mmu.h>
#include <vm/hat_sfmmu.h>
#define INTR_REPORT_SIZE 64
#ifdef TRAPTRACE
#include <sys/traptrace.h>
#endif
ENTRY_NP(cpu_mondo)
!
! Register Usage:-
! %g5 PC for fasttrap TL>0 handler
! %g1 arg 1
! %g2 arg 2
! %g3 queue base VA
! %g4 queue size mask
! %g6 head ptr
! %g7 tail ptr
mov CPU_MONDO_Q_HD, %g3
ldxa [%g3]ASI_QUEUE, %g6 ! %g6 = head ptr
mov CPU_MONDO_Q_TL, %g4
ldxa [%g4]ASI_QUEUE, %g7 ! %g7 = tail ptr
cmp %g6, %g7
be,pn %xcc, 3f ! head == tail
nop
CPU_ADDR(%g1,%g2)
add %g1, CPU_MCPU, %g2
ldx [%g2 + MCPU_CPU_Q_BASE], %g3 ! %g3 = queue base PA
ldx [%g2 + MCPU_CPU_Q_SIZE], %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.
!
! XXX - 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_MEM, %g5 ! get PC from q base + head
add %g6, 0x8, %g6 ! inc head
ldxa [%g3 + %g6]ASI_MEM, %g1 ! read data word 1
add %g6, 0x8, %g6 ! inc head
ldxa [%g3 + %g6]ASI_MEM, %g2 ! read data word 2
add %g6, (INTR_REPORT_SIZE - 16) , %g6 ! inc head to next record
and %g6, %g4, %g6 ! and size mask for wrap around
mov CPU_MONDO_Q_HD, %g3
stxa %g6, [%g3]ASI_QUEUE ! store head pointer
membar #Sync
#ifdef TRAPTRACE
TRACE_PTR(%g4, %g6)
GET_TRACE_TICK(%g6, %g3)
stxa %g6, [%g4 + TRAP_ENT_TICK]%asi
TRACE_SAVE_TL_GL_REGS(%g4, %g6)
rdpr %tt, %g6
stha %g6, [%g4 + TRAP_ENT_TT]%asi
rdpr %tpc, %g6
stna %g6, [%g4 + TRAP_ENT_TPC]%asi
rdpr %tstate, %g6
stxa %g6, [%g4 + TRAP_ENT_TSTATE]%asi
stna %sp, [%g4 + TRAP_ENT_SP]%asi
stna %g5, [%g4 + TRAP_ENT_TR]%asi ! pc of the TL>0 handler
stna %g1, [%g4 + TRAP_ENT_F1]%asi ! arg1
stna %g2, [%g4 + TRAP_ENT_F3]%asi ! arg2
mov CPU_MONDO_Q_HD, %g6
ldxa [%g6]ASI_QUEUE, %g6 ! new head offset
stna %g6, [%g4 + TRAP_ENT_F2]%asi
stna %g7, [%g4 + TRAP_ENT_F4]%asi ! tail offset
TRACE_NEXT(%g4, %g6, %g3)
#endif
set KERNELBASE, %g4
cmp %g5, %g4
bl,pn %xcc, 2f ! branch if bad %pc
nop
.global sfmmu_shctx_cpu_mondo_patch
sfmmu_shctx_cpu_mondo_patch:
set OFW_START_ADDR, %g4 ! Check if this a call into OBP?
cmp %g5, %g4
bl,pt %xcc, 1f
nop
set OFW_END_ADDR, %g4
cmp %g5, %g4
bg,pn %xcc, 1f
nop
mov MMU_PCONTEXT, %g3
ldxa [%g3]ASI_MMU_CTX, %g4
cmp %g4, INVALID_CONTEXT ! Check if we are in kernel mode
ble,pn %xcc, 1f ! or the primary context is invalid
nop
set INVALID_CONTEXT, %g4 ! Invalidate contexts - compatability
stxa %g4, [%g3]ASI_MMU_CTX ! mode ensures shared contexts are also
mov MMU_SCONTEXT, %g3 ! invalidated.
stxa %g4, [%g3]ASI_MMU_CTX
membar #Sync
mov %o0, %g3 ! save output regs
mov %o1, %g4
mov %o5, %g6
clr %o0 ! Invalidate tsbs, set ntsb = 0
clr %o1 ! and HV_TSB_INFO_PA = 0
mov MMU_TSB_CTXNON0, %o5
ta FAST_TRAP ! set TSB info for user process
brnz,a,pn %o0, ptl1_panic
mov PTL1_BAD_HCALL, %g1
mov %g3, %o0 ! restore output regs
mov %g4, %o1
mov %g6, %o5
1:
jmp %g5 ! jump to traphandler
nop
2:
! invalid trap handler, discard it for now
set cpu_mondo_inval, %g4
ldx [%g4], %g5
inc %g5
stx %g5, [%g4]
3:
retry
SET_SIZE(cpu_mondo)
ENTRY_NP(dev_mondo)
!
! Register Usage:-
! %g5 PC for fasttrap TL>0 handler
! %g1 arg 1
! %g2 arg 2
! %g3 queue base PA
! %g4 queue size mask
! %g6 head ptr
! %g7 tail ptr
mov DEV_MONDO_Q_HD, %g3
ldxa [%g3]ASI_QUEUE, %g6 ! %g6 = head ptr
mov DEV_MONDO_Q_TL, %g4
ldxa [%g4]ASI_QUEUE, %g7 ! %g7 = tail ptr
cmp %g6, %g7
be,pn %xcc, 0f ! head == tail
nop
CPU_ADDR(%g1,%g2)
add %g1, CPU_MCPU, %g2
ldx [%g2 + MCPU_DEV_Q_BASE], %g3 ! %g3 = queue base PA
! Register usage:
! %g5 - inum
! %g1 - cpu struct pointer used below in TRAPTRACE
!
ldxa [%g3 + %g6]ASI_MEM, %g5 ! get inum from q base + head
!
! We verify that inum is valid ( < MAXVNUM). If it is greater
! than MAXVNUM, we let setvecint_tl1 take care of it.
!
set MAXIVNUM, %g4
cmp %g5, %g4
bgeu,a,pn %xcc, 1f
ldx [%g2 + MCPU_DEV_Q_SIZE], %g4 ! queue size - delay slot
!
! Copy 64-byte payload to the *iv_payload if it is not NULL
!
set intr_vec_table, %g1 ! %g1 = intr_vec_table
sll %g5, CPTRSHIFT, %g7 ! %g7 = offset to inum entry
! in the intr_vec_table
add %g1, %g7, %g7 ! %g7 = &intr_vec_table[inum]
ldn [%g7], %g1 ! %g1 = ptr to intr_vec_t (iv)
!
! Verify the pointer to first intr_vec_t for a given inum and
! it should not be NULL. If this pointer is NULL, then it is a
! spurious interrupt. In this case, just call setvecint_tl1 and
! it will handle this spurious interrupt.
!
brz,a,pn %g1, 1f ! if %g1 is NULL
ldx [%g2 + MCPU_DEV_Q_SIZE], %g4 ! queue size - delay slot
ldx [%g1 + IV_PAYLOAD_BUF], %g1 ! %g1 = iv->iv_payload_buf
brz,a,pt %g1, 1f ! if it is NULL
ldx [%g2 + MCPU_DEV_Q_SIZE], %g4 ! queue size - delay slot
!
! Now move 64 byte payload from mondo queue to buf
!
mov %g6, %g7 ! %g7 = head ptr
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 0] ! byte 0 - 7
add %g7, 8, %g7
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 8] ! byte 8 - 15
add %g7, 8, %g7
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 16] ! byte 16 - 23
add %g7, 8, %g7
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 24] ! byte 24 - 31
add %g7, 8, %g7
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 32] ! byte 32 - 39
add %g7, 8, %g7
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 40] ! byte 40 - 47
add %g7, 8, %g7
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 48] ! byte 48 - 55
add %g7, 8, %g7
ldxa [%g3 + %g7]ASI_MEM, %g4
stx %g4, [%g1 + 56] ! byte 56 - 63
ldx [%g2 + MCPU_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_Q_HD, %g3
stxa %g6, [%g3]ASI_QUEUE ! increment head offset
membar #Sync
#ifdef TRAPTRACE
TRACE_PTR(%g4, %g6)
GET_TRACE_TICK(%g6, %g3)
stxa %g6, [%g4 + TRAP_ENT_TICK]%asi
TRACE_SAVE_TL_GL_REGS(%g4, %g6)
rdpr %tt, %g6
stha %g6, [%g4 + TRAP_ENT_TT]%asi
rdpr %tpc, %g6
stna %g6, [%g4 + TRAP_ENT_TPC]%asi
rdpr %tstate, %g6
stxa %g6, [%g4 + TRAP_ENT_TSTATE]%asi
! move head to sp
ldx [%g2 + MCPU_DEV_Q_BASE], %g6
stna %g6, [%g4 + TRAP_ENT_SP]%asi ! Device Queue Base PA
stna %g5, [%g4 + TRAP_ENT_TR]%asi ! Inum
mov DEV_MONDO_Q_HD, %g6
ldxa [%g6]ASI_QUEUE, %g6 ! New head offset
stna %g6, [%g4 + TRAP_ENT_F1]%asi
ldx [%g2 + MCPU_DEV_Q_SIZE], %g6
stna %g6, [%g4 + TRAP_ENT_F2]%asi ! Q Size
stna %g7, [%g4 + TRAP_ENT_F3]%asi ! tail offset
stna %g0, [%g4 + TRAP_ENT_F4]%asi
TRACE_NEXT(%g4, %g6, %g3)
#endif
!
! setvecint_tl1 will do all the work, and finish with a retry
!
ba,pt %xcc, setvecint_tl1
mov %g5, %g1 ! setvecint_tl1 expects inum in %g1
0: retry
SET_SIZE(dev_mondo)
.seg ".data"
.global cpu_mondo_inval
.align 8
cpu_mondo_inval:
.skip 8
.seg ".text"
ENTRY_NP(resumable_error)
mov CPU_RQ_HD, %g4
ldxa [%g4]ASI_QUEUE, %g2 ! %g2 = Q head offset
mov CPU_RQ_TL, %g4
ldxa [%g4]ASI_QUEUE, %g3 ! %g3 = Q tail offset
mov %g2, %g6 ! save head in %g2
cmp %g6, %g3
be,pn %xcc, 0f ! head == tail
nop
CPU_ADDR(%g1, %g4) ! %g1 = cpu struct addr
2: set CPU_RQ_BASE_OFF, %g4
ldx [%g1 + %g4], %g4 ! %g4 = queue base PA
add %g6, %g4, %g4 ! %g4 = PA of ER in Q
set CPU_RQ_SIZE, %g7
add %g4, %g7, %g7 ! %g7=PA of ER in kernel buf
ldxa [%g7]ASI_MEM, %g5 ! %g5=first 8 byte of ER buf
cmp 0, %g5
bne,pn %xcc, 1f ! first 8 byte is not 0
nop
set 0, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 0 - 7
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 8 - 15
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 16 - 23
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 24 - 31
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 32 - 39
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 40 - 47
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 48 - 55
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 56 - 63
set CPU_RQ_SIZE, %g5 ! %g5 = queue size
sub %g5, 1, %g5 ! %g5 = queu size mask
add %g6, Q_ENTRY_SIZE, %g6 ! increment q head to next
and %g6, %g5, %g6 ! size mask for warp around
cmp %g6, %g3 ! head == tail ??
bne,pn %xcc, 2b ! still have more to process
nop
mov CPU_RQ_HD, %g4
stxa %g6, [%g4]ASI_QUEUE ! update head offset
membar #Sync
set process_resumable_error, %g1
rdpr %pil, %g4
cmp %g4, PIL_14
ba sys_trap
movl %icc, PIL_14, %g4
1: mov CPU_RQ_HD, %g4
stxa %g3, [%g4]ASI_QUEUE ! set head equal to tail
membar #Sync
mov %g6, %g2
set rq_overflow, %g1
rdpr %pil, %g4
cmp %g4, PIL_14
ba sys_trap
movl %icc, PIL_14, %g4
0: retry
SET_SIZE(resumable_error)
ENTRY_NP(nonresumable_error)
mov CPU_NRQ_HD, %g4
ldxa [%g4]ASI_QUEUE, %g2 ! %g2 = Q head offset
mov CPU_NRQ_TL, %g4
ldxa [%g4]ASI_QUEUE, %g3 ! %g3 = Q tail offset
cmp %g2, %g3
be,pn %xcc, 0f ! head == tail
nop
wrpr %g0, 1, %gl
mov CPU_NRQ_HD, %g4
ldxa [%g4]ASI_QUEUE, %g2 ! %g2 = Q head offset
mov CPU_NRQ_TL, %g4
ldxa [%g4]ASI_QUEUE, %g3 ! %g3 = Q tail offset
mov %g2, %g6 ! save head in %g2
CPU_PADDR(%g1, %g4) ! %g1 = cpu struct paddr
2: set CPU_NRQ_BASE_OFF, %g4
ldxa [%g1 + %g4]ASI_MEM, %g4 ! %g4 = queue base PA
add %g6, %g4, %g4 ! %g4 = PA of ER in Q
set CPU_NRQ_SIZE, %g7
add %g4, %g7, %g7 ! %g7 = PA of ER in kernel buf
ldxa [%g7]ASI_MEM, %g5 ! %g5 = first 8 byte of ER buf
cmp 0, %g5
bne,pn %xcc, 1f ! first 8 byte is not 0
nop
set 0, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 0 - 7
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 8 - 15
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 16 - 23
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 24 - 31
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 32 - 39
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 40 - 47
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 48 - 55
add %g5, 8, %g5
ldxa [%g4 + %g5]ASI_MEM, %g1
stxa %g1, [%g7 + %g5]ASI_MEM ! byte 56 - 63
set CPU_NRQ_SIZE, %g5 ! %g5 = queue size
sub %g5, 1, %g5 ! %g5 = queu size mask
add %g6, Q_ENTRY_SIZE, %g6 ! increment q head to next
and %g6, %g5, %g6 ! size mask for warp around
cmp %g6, %g3 ! head == tail ??
bne,pn %xcc, 2b ! still have more to process
nop
mov CPU_NRQ_HD, %g4
stxa %g6, [%g4]ASI_QUEUE ! update head offset
membar #Sync
sllx %g3, 32, %g3 ! %g3.h = tail offset
or %g3, %g2, %g3 ! %g3.l = head offset
rdpr %tl, %g2 ! %g2 = current tl
cmp %g2, 2
bne,pt %xcc, 3f ! if tl != 2
nop
rdpr %tpc, %g4
set trap_table, %g5
cmp %g4, %g5
blu,pt %xcc, 3f
nop
set etrap_table, %g5
cmp %g4, %g5
bgeu,pt %xcc, 3f
nop
wrpr %g0, 1, %tl
rdpr %tt, %g4 ! %g4 = tt[1]
and %g4, WTRAP_TTMASK, %g4
cmp %g4, WTRAP_TYPE
bne,pt %xcc, 3f
nop
rdpr %tstate, %g5 ! %g5 = tstate[1]
btst TSTATE_PRIV, %g5
bnz %xcc, 3f ! Is it from user code?
nop
mov 1, %g4
sllx %g4, ERRH_U_SPILL_FILL_SHIFT, %g4
or %g2, %g4, %g2 ! turn on flag in %g2
3: sub %g2, 1, %g2 ! %g2.l = previous tl
set process_nonresumable_error, %g1
rdpr %pil, %g4
cmp %g4, PIL_14
ba sys_trap
movl %icc, PIL_14, %g4
1: set nrq_overflow, %g1
rdpr %pil, %g4
cmp %g4, PIL_14
ba sys_trap
movl %icc, PIL_14, %g4
0: retry
SET_SIZE(nonresumable_error)