#include "assym.h"
#define FAST_TRAP_DONE \
ba,a fast_trap_done
#if defined(CHEETAH) || defined(OLYMPUS_C)
#define GET_NATIVE_TIME(out, scr1, scr2) \
rd STICK, out
#define DELTA_NATIVE_TIME(delta, reg, scr1, scr2, scr3) \
rd STICK, reg; \
add reg, delta, reg; \
wr reg, STICK
#define RD_TICKCMPR(out, scr) \
rd STICK_COMPARE, out
#define WR_TICKCMPR(in, scr1, scr2, label) \
wr in, STICK_COMPARE
#elif defined(HUMMINGBIRD)
#include <sys/spitregs.h>
#define HSTC_LOW 0x60
#define HSTC_HIGH 0x68
#define HST_LOW 0x70
#define HST_HIGH 0x78
#define HST_DIFF 0x08
#define SETL41(reg, byte) \
sethi %hi(0x1FE00000), reg; \
or reg, 0xF, reg; \
sllx reg, 12, reg; \
or reg, byte, reg;
#define SETL41_OFF 16
#define GET_NATIVE_TIME(out, scr, tmp) \
SETL41(scr, HST_LOW); \
ldxa [scr]ASI_IO, tmp; \
inc HST_DIFF, scr; \
ldxa [scr]ASI_IO, out; \
dec HST_DIFF, scr; \
ldxa [scr]ASI_IO, scr; \
sub scr, tmp, tmp; \
brlz,pn tmp, .-(SETL41_OFF+24); \
sllx out, 32, out; \
or out, scr, out
#define DELTA_NATIVE_TIME(delta, addr, high, low, tmp) \
SETL41(addr, HST_LOW); \
ldxa [addr]ASI_IO, tmp; \
inc HST_DIFF, addr; \
ldxa [addr]ASI_IO, high; \
dec HST_DIFF, addr; \
ldxa [addr]ASI_IO, low; \
sub low, tmp, tmp; \
brlz,pn tmp, .-24; \
sllx high, 32, high; \
or high, low, high; \
add high, delta, high; \
srl high, 0, low; \
srlx high, 32, high; \
stxa %g0, [addr]ASI_IO; \
inc HST_DIFF, addr; \
stxa high, [addr]ASI_IO; \
dec HST_DIFF, addr; \
stxa low, [addr]ASI_IO
#define RD_TICKCMPR(out, scr) \
SETL41(scr, HSTC_LOW); \
ldxa [scr]ASI_IO, out; \
inc HST_DIFF, scr; \
ldxa [scr]ASI_IO, scr; \
sllx scr, 32, scr; \
or scr, out, out
#define WR_TICKCMPR(in, scra, scrd, label) \
SETL41(scra, HSTC_HIGH); \
srlx in, 32, scrd; \
stxa scrd, [scra]ASI_IO; \
dec HST_DIFF, scra; \
stxa in, [scra]ASI_IO
#else
#define GET_NATIVE_TIME(out, scr1, scr2) \
rdpr %tick, out
#define DELTA_NATIVE_TIME(delta, reg, scr1, scr2, scr3) \
rdpr %tick, reg; \
add reg, delta, reg; \
wrpr reg, %tick
#define RD_TICKCMPR(out, scr) \
rd TICK_COMPARE, out
#ifdef BB_ERRATA_1
#define WR_TICKCMPR(cmpr,scr1,scr2,label) \
ba,a .bb_errata_1.label ;\
.align 64 ;\
.bb_errata_1.label: ;\
wr cmpr, TICK_COMPARE ;\
rd TICK_COMPARE, %g0
#else
#define WR_TICKCMPR(in,scr1,scr2,label) \
wr in, TICK_COMPARE
#endif
#endif
#include <sys/clock.h>
#include <sys/asm_linkage.h>
#include <sys/privregs.h>
#include <sys/machparam.h>
#include <sys/machthread.h>
#include <sys/clock.h>
#include <sys/intreg.h>
#include <sys/psr_compat.h>
#include <sys/isa_defs.h>
#include <sys/dditypes.h>
#include <sys/intr.h>
#include "assym.h"
ENTRY(get_impl)
GET_CPU_IMPL(%o0)
retl
nop
SET_SIZE(get_impl)
ENTRY_NP(tickcmpr_set)
! get 64-bit clock_cycles interval
mov %o0, %o2
mov 8, %o3 ! A reasonable initial step size
1:
WR_TICKCMPR(%o2,%o4,%o5,__LINE__) ! Write to TICK_CMPR
GET_NATIVE_TIME(%o0, %o4, %o5) ! Read %tick to confirm the
sllx %o0, 1, %o0 ! value we wrote was in the future.
srlx %o0, 1, %o0
cmp %o2, %o0 ! If the value we wrote was in the
bg,pt %xcc, 2f ! future, then blow out of here.
sllx %o3, 1, %o3 ! If not, then double our step size,
ba,pt %xcc, 1b ! and take another lap.
add %o0, %o3, %o2 !
2:
retl
nop
SET_SIZE(tickcmpr_set)
ENTRY_NP(tickcmpr_disable)
mov 1, %g1
sllx %g1, TICKINT_DIS_SHFT, %o0
WR_TICKCMPR(%o0,%o4,%o5,__LINE__) ! Write to TICK_CMPR
retl
nop
SET_SIZE(tickcmpr_disable)
#ifdef DEBUG
.seg ".text"
tick_write_panic:
.asciz "tick_write_delta: interrupts already disabled on entry"
#endif
ENTRY_NP(tick_write_delta)
rdpr %pstate, %g1
#ifdef DEBUG
andcc %g1, PSTATE_IE, %g0 ! If DEBUG, check that interrupts
bnz 0f ! aren't already disabled.
sethi %hi(tick_write_panic), %o1
save %sp, -SA(MINFRAME), %sp ! get a new window to preserve caller
call panic
or %i1, %lo(tick_write_panic), %o0
#endif
0: wrpr %g1, PSTATE_IE, %pstate ! Disable interrupts
mov %o0, %o2
ba 0f ! Branch to cache line-aligned instr.
nop
.align 16
0: nop ! The next 3 instructions are now hot.
DELTA_NATIVE_TIME(%o2, %o3, %o4, %o5, %g2) ! read/inc/write %tick
retl ! Return
wrpr %g0, %g1, %pstate ! delay: Re-enable interrupts
ENTRY_NP(tickcmpr_disabled)
RD_TICKCMPR(%g1, %o0)
retl
srlx %g1, TICKINT_DIS_SHFT, %o0
SET_SIZE(tickcmpr_disabled)
ENTRY(gettick)
ALTENTRY(randtick)
GET_NATIVE_TIME(%o0, %o2, %o3)
retl
nop
SET_SIZE(randtick)
SET_SIZE(gettick)
ENTRY_NP(gettick_counter)
rdpr %tick, %o0
sllx %o0, 1, %o0
retl
srlx %o0, 1, %o0 ! shake off npt bit
SET_SIZE(gettick_counter)
ENTRY_NP(gethrtime)
GET_HRTIME(%g1, %o0, %o1, %o2, %o3, %o4, %o5, %g2)
! %g1 = hrtime
retl
mov %g1, %o0
SET_SIZE(gethrtime)
ENTRY_NP(gethrtime_unscaled)
GET_NATIVE_TIME(%g1, %o2, %o3) ! %g1 = native time
retl
mov %g1, %o0
SET_SIZE(gethrtime_unscaled)
ENTRY_NP(gethrtime_waitfree)
ALTENTRY(dtrace_gethrtime)
GET_NATIVE_TIME(%g1, %o2, %o3) ! %g1 = native time
NATIVE_TIME_TO_NSEC(%g1, %o2, %o3)
retl
mov %g1, %o0
SET_SIZE(dtrace_gethrtime)
SET_SIZE(gethrtime_waitfree)
ENTRY(gethrtime_max)
NATIVE_TIME_MAX(%g1)
NATIVE_TIME_TO_NSEC(%g1, %o0, %o1)
! hrtime_t's are signed, max hrtime_t must be positive
mov -1, %o2
brlz,a %g1, 1f
srlx %o2, 1, %g1
1:
retl
mov %g1, %o0
SET_SIZE(gethrtime_max)
ENTRY(scalehrtime)
ldx [%o0], %o1
NATIVE_TIME_TO_NSEC(%o1, %o2, %o3)
retl
stx %o1, [%o0]
SET_SIZE(scalehrtime)
ENTRY_NP(get_timestamp)
GET_HRTIME(%g1, %g2, %g3, %g4, %g5, %o0, %o1, %o2) ! %g1 = hrtime
srlx %g1, 32, %o0 ! %o0 = hi32(%g1)
srl %g1, 0, %o1 ! %o1 = lo32(%g1)
FAST_TRAP_DONE
SET_SIZE(get_timestamp)
#define CONV_HRESTIME(hrestsec, hrestnsec, adj, nslt, nano) \
brz,pt adj, 3f; \
add hrestnsec, nslt, hrestnsec; \
brlz,pn adj, 2f; \
srlx nslt, ADJ_SHIFT, nslt; \
subcc adj, nslt, %g0; \
movg %xcc, nslt, adj; \
ba 3f; \
add hrestnsec, adj, hrestnsec; \
2: addcc adj, nslt, %g0; \
bge,a,pt %xcc, 3f; \
add hrestnsec, adj, hrestnsec; \
sub hrestnsec, nslt, hrestnsec; \
3: cmp hrestnsec, nano; \
bl,pt %xcc, 4f; \
nop; \
add hrestsec, 1, hrestsec; \
sub hrestnsec, nano, hrestnsec; \
ba,a 3b; \
4:
ENTRY_NP(gethrestime)
GET_HRESTIME(%o1, %o2, %o3, %o4, %o5, %g1, %g2, %g3, %g4)
CONV_HRESTIME(%o1, %o2, %o3, %o4, %o5)
stn %o1, [%o0]
retl
stn %o2, [%o0 + CLONGSIZE]
SET_SIZE(gethrestime)
ENTRY_NP(gethrestime_sec)
GET_HRESTIME(%o0, %o2, %o3, %o4, %o5, %g1, %g2, %g3, %g4)
CONV_HRESTIME(%o0, %o2, %o3, %o4, %o5)
retl ! %o0 current hrestime seconds
nop
SET_SIZE(gethrestime_sec)
ENTRY_NP(gethrestime_lasttick)
sethi %hi(hres_lock), %o1
0:
lduw [%o1 + %lo(hres_lock)], %o2 ! Load lock value
membar #LoadLoad ! Load of lock must complete
andn %o2, 1, %o2 ! Mask off lowest bit
ldn [%o1 + %lo(hrestime)], %g1 ! Seconds.
add %o1, %lo(hrestime), %o4
ldn [%o4 + CLONGSIZE], %g2 ! Nanoseconds.
membar #LoadLoad ! All loads must complete
lduw [%o1 + %lo(hres_lock)], %o3 ! Reload lock value
cmp %o3, %o2 ! If lock is locked or has
bne 0b ! changed, retry.
stn %g1, [%o0] ! Delay: store seconds
retl
stn %g2, [%o0 + CLONGSIZE] ! Delay: store nanoseconds
SET_SIZE(gethrestime_lasttick)
ENTRY_NP(get_hrestime)
GET_HRESTIME(%o0, %o1, %g1, %g2, %g3, %g4, %g5, %o2, %o3)
CONV_HRESTIME(%o0, %o1, %g1, %g2, %g3)
FAST_TRAP_DONE
SET_SIZE(get_hrestime)
ENTRY_NP(get_virtime)
GET_NATIVE_TIME(%g5, %g1, %g2) ! %g5 = native time in ticks
CPU_ADDR(%g2, %g3) ! CPU struct ptr to %g2
ldn [%g2 + CPU_THREAD], %g2 ! thread pointer to %g2
ldn [%g2 + T_LWP], %g3 ! lwp pointer to %g3
ldx [%g3 + LWP_STATE_START], %g1 ! ms_state_start
sub %g5, %g1, %g5
ldx [%g3 + LWP_ACCT_USER], %g1 ! ms_acct[LMS_USER]
add %g5, %g1, %g5
NATIVE_TIME_TO_NSEC(%g5, %g1, %o0)
srl %g5, 0, %o1 ! %o1 = lo32(%g5)
srlx %g5, 32, %o0 ! %o0 = hi32(%g5)
FAST_TRAP_DONE
SET_SIZE(get_virtime)
.seg ".text"
hrtime_base_panic:
.asciz "hrtime_base stepping back"
ENTRY_NP(hres_tick)
save %sp, -SA(MINFRAME), %sp ! get a new window
sethi %hi(hrestime), %l4
ldstub [%l4 + %lo(hres_lock + HRES_LOCK_OFFSET)], %l5 ! try locking
7: tst %l5
bz,pt %xcc, 8f ! if we got it, drive on
ld [%l4 + %lo(nsec_scale)], %l5 ! delay: %l5 = scaling factor
ldub [%l4 + %lo(hres_lock + HRES_LOCK_OFFSET)], %l5
9: tst %l5
bz,a,pn %xcc, 7b
ldstub [%l4 + %lo(hres_lock + HRES_LOCK_OFFSET)], %l5
ba,pt %xcc, 9b
ldub [%l4 + %lo(hres_lock + HRES_LOCK_OFFSET)], %l5
8:
membar #StoreLoad|#StoreStore
!
! update hres_last_tick. %l5 has the scaling factor (nsec_scale).
!
ldx [%l4 + %lo(hrtime_base)], %g1 ! load current hrtime_base
GET_NATIVE_TIME(%l0, %l3, %l6) ! current native time
stx %l0, [%l4 + %lo(hres_last_tick)]! prev = current
! convert native time to nsecs
NATIVE_TIME_TO_NSEC_SCALE(%l0, %l5, %l2, NSEC_SHIFT)
sub %l0, %g1, %i1 ! get accurate nsec delta
ldx [%l4 + %lo(hrtime_base)], %l1
cmp %l1, %l0
bg,pn %xcc, 9f
nop
stx %l0, [%l4 + %lo(hrtime_base)] ! update hrtime_base
!
! apply adjustment, if any
!
ldx [%l4 + %lo(hrestime_adj)], %l0 ! %l0 = hrestime_adj
brz %l0, 2f
! hrestime_adj == 0 ?
! yes, skip adjustments
clr %l5 ! delay: set adj to zero
tst %l0 ! is hrestime_adj >= 0 ?
bge,pt %xcc, 1f ! yes, go handle positive case
srl %i1, ADJ_SHIFT, %l5 ! delay: %l5 = adj
addcc %l0, %l5, %g0 ! hrestime_adj < -adj ?
bl,pt %xcc, 2f ! yes, use current adj
neg %l5 ! delay: %l5 = -adj
ba,pt %xcc, 2f
mov %l0, %l5 ! no, so set adj = hrestime_adj
1:
subcc %l0, %l5, %g0 ! hrestime_adj < adj ?
bl,a,pt %xcc, 2f ! yes, set adj = hrestime_adj
mov %l0, %l5 ! delay: adj = hrestime_adj
2:
ldx [%l4 + %lo(timedelta)], %l0 ! %l0 = timedelta
sub %l0, %l5, %l0 ! timedelta -= adj
stx %l0, [%l4 + %lo(timedelta)] ! store new timedelta
stx %l0, [%l4 + %lo(hrestime_adj)] ! hrestime_adj = timedelta
or %l4, %lo(hrestime), %l2
ldn [%l2], %i2 ! %i2:%i3 = hrestime sec:nsec
ldn [%l2 + CLONGSIZE], %i3
add %i3, %l5, %i3 ! hrestime.nsec += adj
add %i3, %i1, %i3 ! hrestime.nsec += nslt
set NANOSEC, %l5 ! %l5 = NANOSEC
cmp %i3, %l5
bl,pt %xcc, 5f ! if hrestime.tv_nsec < NANOSEC
sethi %hi(one_sec), %i1 ! delay
add %i2, 0x1, %i2 ! hrestime.tv_sec++
sub %i3, %l5, %i3 ! hrestime.tv_nsec - NANOSEC
mov 0x1, %l5
st %l5, [%i1 + %lo(one_sec)]
5:
stn %i2, [%l2]
stn %i3, [%l2 + CLONGSIZE] ! store the new hrestime
membar #StoreStore
ld [%l4 + %lo(hres_lock)], %i1
inc %i1 ! release lock
st %i1, [%l4 + %lo(hres_lock)] ! clear hres_lock
ret
restore
9:
!
! release hres_lock
!
ld [%l4 + %lo(hres_lock)], %i1
inc %i1
st %i1, [%l4 + %lo(hres_lock)]
sethi %hi(hrtime_base_panic), %o0
call panic
or %o0, %lo(hrtime_base_panic), %o0
SET_SIZE(hres_tick)
.seg ".text"
kstat_q_panic_msg:
.asciz "kstat_q_exit: qlen == 0"
ENTRY(kstat_q_panic)
save %sp, -SA(MINFRAME), %sp
sethi %hi(kstat_q_panic_msg), %o0
call panic
or %o0, %lo(kstat_q_panic_msg), %o0
SET_SIZE(kstat_q_panic)
#define BRZPN brz,pn
#define BRZPT brz,pt
#define KSTAT_Q_UPDATE(QOP, QBR, QZERO, QRETURN, QTYPE) \
ld [%o0 + QTYPE##CNT], %o1; \
QOP %o1, 1, %o2; \
QBR %o1, QZERO; \
st %o2, [%o0 + QTYPE##CNT]; \
ldx [%o0 + QTYPE##LASTUPDATE], %o3; \
ldx [%o0 + QTYPE##TIME], %o4; \
ldx [%o0 + QTYPE##LENTIME], %o5; \
sub %g1, %o3, %o2; \
mulx %o1, %o2, %o3; \
add %o4, %o2, %o4; \
add %o5, %o3, %o5; \
stx %o4, [%o0 + QTYPE##TIME]; \
stx %o5, [%o0 + QTYPE##LENTIME]; \
QRETURN; \
stx %g1, [%o0 + QTYPE##LASTUPDATE];
#if !defined(DEBUG)
#define KSTAT_Q_UPDATE_ND(QOP, QRETURN, QTYPE) \
ld [%o0 + QTYPE##CNT], %o1; \
QOP %o1, 1, %o2; \
st %o2, [%o0 + QTYPE##CNT]; \
ldx [%o0 + QTYPE##LASTUPDATE], %o3; \
ldx [%o0 + QTYPE##TIME], %o4; \
ldx [%o0 + QTYPE##LENTIME], %o5; \
sub %g1, %o3, %o2; \
mulx %o1, %o2, %o3; \
add %o4, %o2, %o4; \
add %o5, %o3, %o5; \
stx %o4, [%o0 + QTYPE##TIME]; \
stx %o5, [%o0 + QTYPE##LENTIME]; \
QRETURN; \
stx %g1, [%o0 + QTYPE##LASTUPDATE];
#endif
.align 16
ENTRY(kstat_waitq_enter)
GET_NATIVE_TIME(%g1, %g2, %g3)
KSTAT_Q_UPDATE(add, BRZPT, 1f, 1:retl, KSTAT_IO_W)
SET_SIZE(kstat_waitq_enter)
.align 16
ENTRY(kstat_waitq_exit)
GET_NATIVE_TIME(%g1, %g2, %g3)
#if defined(DEBUG)
KSTAT_Q_UPDATE(sub, BRZPN, kstat_q_panic, retl, KSTAT_IO_W)
#else
KSTAT_Q_UPDATE_ND(sub, retl, KSTAT_IO_W)
#endif
SET_SIZE(kstat_waitq_exit)
.align 16
ENTRY(kstat_runq_enter)
GET_NATIVE_TIME(%g1, %g2, %g3)
KSTAT_Q_UPDATE(add, BRZPT, 1f, 1:retl, KSTAT_IO_R)
SET_SIZE(kstat_runq_enter)
.align 16
ENTRY(kstat_runq_exit)
GET_NATIVE_TIME(%g1, %g2, %g3)
#if defined(DEBUG)
KSTAT_Q_UPDATE(sub, BRZPN, kstat_q_panic, retl, KSTAT_IO_R)
#else
KSTAT_Q_UPDATE_ND(sub, retl, KSTAT_IO_R)
#endif
SET_SIZE(kstat_runq_exit)
.align 16
ENTRY(kstat_waitq_to_runq)
GET_NATIVE_TIME(%g1, %g2, %g3)
#if defined(DEBUG)
KSTAT_Q_UPDATE(sub, BRZPN, kstat_q_panic, 1:, KSTAT_IO_W)
#else
KSTAT_Q_UPDATE_ND(sub, 1:, KSTAT_IO_W)
#endif
KSTAT_Q_UPDATE(add, BRZPT, 1f, 1:retl, KSTAT_IO_R)
SET_SIZE(kstat_waitq_to_runq)
.align 16
ENTRY(kstat_runq_back_to_waitq)
GET_NATIVE_TIME(%g1, %g2, %g3)
#if defined(DEBUG)
KSTAT_Q_UPDATE(sub, BRZPN, kstat_q_panic, 1:, KSTAT_IO_R)
#else
KSTAT_Q_UPDATE_ND(sub, 1:, KSTAT_IO_R)
#endif
KSTAT_Q_UPDATE(add, BRZPT, 1f, 1:retl, KSTAT_IO_W)
SET_SIZE(kstat_runq_back_to_waitq)
.seg ".data"
.global timedelta, hres_last_tick, hrestime, hrestime_adj
.global hres_lock, nsec_scale, hrtime_base, traptrace_use_stick
.global nsec_shift, adj_shift
.align 64
timedelta:
.word 0, 0
hres_last_tick:
.word 0, 0
hrestime:
.nword 0, 0
hrestime_adj:
.word 0, 0
hres_lock:
.word 0
nsec_scale:
.word 0
hrtime_base:
.word 0, 0
traptrace_use_stick:
.word 0
nsec_shift:
.word NSEC_SHIFT
adj_shift:
.word ADJ_SHIFT
ENTRY(drv_usecwait)
ALTENTRY(usec_delay)
brlez,a,pn %o0, 0f
mov 1, %o0
0:
sethi %hi(sticks_per_usec), %o1
lduw [%o1 + %lo(sticks_per_usec)], %o1
mulx %o1, %o0, %o1 ! Scale usec to ticks
inc %o1 ! We don't start on a tick edge
GET_NATIVE_TIME(%o2, %o3, %o4)
add %o1, %o2, %o1
1:
#ifdef _OPL
.word 0x81b01060 ! insert "sleep" instruction
#endif ! use byte code for now
cmp %o1, %o2
GET_NATIVE_TIME(%o2, %o3, %o4)
bgeu,pt %xcc, 1b
nop
retl
nop
SET_SIZE(usec_delay)
SET_SIZE(drv_usecwait)
ENTRY_NP(pil14_interrupt)
CPU_ADDR(%g1, %g2)
rdpr %pil, %g6 ! %g6 = interrupted PIL
stn %g6, [%g1 + CPU_PROFILE_PIL] ! record interrupted PIL
rdpr %tstate, %g6
rdpr %tpc, %g5
btst TSTATE_PRIV, %g6 ! trap from supervisor mode?
bnz,a,pt %xcc, 1f
stn %g5, [%g1 + CPU_PROFILE_PC] ! if so, record kernel PC
stn %g5, [%g1 + CPU_PROFILE_UPC] ! if not, record user PC
ba pil_interrupt_common ! must be large-disp branch
stn %g0, [%g1 + CPU_PROFILE_PC] ! zero kernel PC
1: ba pil_interrupt_common ! must be large-disp branch
stn %g0, [%g1 + CPU_PROFILE_UPC] ! zero user PC
SET_SIZE(pil14_interrupt)
ENTRY_NP(tick_rtt)
!
! Load TICK_COMPARE into %o5; if bit 63 is set, then TICK_COMPARE is
! disabled. If TICK_COMPARE is enabled, we know that we need to
! reenqueue the interrupt request structure. We'll then check TICKINT
! in SOFTINT; if it's set, then we know that we were in a TICK_COMPARE
! interrupt. In this case, TICK_COMPARE may have been rewritten
! recently; we'll compare %o5 to the current time to verify that it's
! in the future.
!
! Note that %o5 is live until after 1f.
! XXX - there is a subroutine call while %o5 is live!
!
RD_TICKCMPR(%o5, %g1)
srlx %o5, TICKINT_DIS_SHFT, %g1
brnz,pt %g1, 2f
nop
rdpr %pstate, %g5
andn %g5, PSTATE_IE, %g1
wrpr %g0, %g1, %pstate ! Disable vec interrupts
sethi %hi(cbe_level14_inum), %o1
ldx [%o1 + %lo(cbe_level14_inum)], %o1
call intr_enqueue_req ! preserves %o5 and %g5
mov PIL_14, %o0
! Check SOFTINT for TICKINT/STICKINT
rd SOFTINT, %o4
set (TICK_INT_MASK | STICK_INT_MASK), %o0
andcc %o4, %o0, %g0
bz,a,pn %icc, 2f
wrpr %g0, %g5, %pstate ! Enable vec interrupts
! clear TICKINT/STICKINT
wr %o0, CLEAR_SOFTINT
!
! Now that we've cleared TICKINT, we can reread %tick and confirm
! that the value we programmed is still in the future. If it isn't,
! we need to reprogram TICK_COMPARE to fire as soon as possible.
!
GET_NATIVE_TIME(%o0, %g1, %g2) ! %o0 = tick
sllx %o0, 1, %o0 ! Clear the DIS bit
srlx %o0, 1, %o0
cmp %o5, %o0 ! In the future?
bg,a,pt %xcc, 2f ! Yes, drive on.
wrpr %g0, %g5, %pstate ! delay: enable vec intr
!
! If we're here, then we have programmed TICK_COMPARE with a %tick
! which is in the past; we'll now load an initial step size, and loop
! until we've managed to program TICK_COMPARE to fire in the future.
!
mov 8, %o4 ! 8 = arbitrary inital step
1: add %o0, %o4, %o5 ! Add the step
WR_TICKCMPR(%o5,%g1,%g2,__LINE__) ! Write to TICK_CMPR
GET_NATIVE_TIME(%o0, %g1, %g2) ! %o0 = tick
sllx %o0, 1, %o0 ! Clear the DIS bit
srlx %o0, 1, %o0
cmp %o5, %o0 ! In the future?
bg,a,pt %xcc, 2f ! Yes, drive on.
wrpr %g0, %g5, %pstate ! delay: enable vec intr
ba 1b ! No, try again.
sllx %o4, 1, %o4 ! delay: double step size
2: ba current_thread_complete
nop
SET_SIZE(tick_rtt)
ENTRY_NP(pil15_interrupt)
CPU_ADDR(%g1, %g2)
rdpr %tstate, %g6
rdpr %tpc, %g5
btst TSTATE_PRIV, %g6 ! trap from supervisor mode?
bnz,a,pt %xcc, 1f
stn %g5, [%g1 + CPU_CPCPROFILE_PC] ! if so, record kernel PC
stn %g5, [%g1 + CPU_CPCPROFILE_UPC] ! if not, record user PC
ba pil15_epilogue ! must be large-disp branch
stn %g0, [%g1 + CPU_CPCPROFILE_PC] ! zero kernel PC
1: ba pil15_epilogue ! must be large-disp branch
stn %g0, [%g1 + CPU_CPCPROFILE_UPC] ! zero user PC
SET_SIZE(pil15_interrupt)
#ifdef DEBUG
.seg ".text"
find_cpufreq_panic:
.asciz "find_cpufrequency: interrupts already disabled on entry"
#endif
ENTRY_NP(find_cpufrequency)
rdpr %pstate, %g1
#ifdef DEBUG
andcc %g1, PSTATE_IE, %g0 ! If DEBUG, check that interrupts
bnz 0f ! are currently enabled
sethi %hi(find_cpufreq_panic), %o1
call panic
or %o1, %lo(find_cpufreq_panic), %o0
#endif
0:
wrpr %g1, PSTATE_IE, %pstate ! Disable interrupts
3:
ldub [%o0], %o1 ! Read the number of seconds
mov %o1, %o2 ! remember initial value in %o2
1:
GET_NATIVE_TIME(%o3, %g4, %g5)
cmp %o1, %o2 ! did the seconds register roll over?
be,pt %icc, 1b ! branch back if unchanged
ldub [%o0], %o2 ! delay: load the new seconds val
brz,pn %o2, 3b ! if the minutes just rolled over,
! the last second could have been
! inaccurate; try again.
mov %o2, %o4 ! delay: store init. val. in %o2
2:
GET_NATIVE_TIME(%o5, %g4, %g5)
cmp %o2, %o4 ! did the seconds register roll over?
be,pt %icc, 2b ! branch back if unchanged
ldub [%o0], %o4 ! delay: load the new seconds val
brz,pn %o4, 0b ! if the minutes just rolled over,
! the last second could have been
! inaccurate; try again.
wrpr %g0, %g1, %pstate ! delay: re-enable interrupts
retl
sub %o5, %o3, %o0 ! return the difference in ticks
SET_SIZE(find_cpufrequency)
#if defined(CHEETAH) || defined(CHEETAH_PLUS) || defined(JALAPENO) || \
defined(SERRANO)
!
! On US-III, the prefetch instruction queue is 8 entries deep.
! Also, prefetches for write put data in the E$, which has
! lines of 512 bytes for an 8MB cache. Each E$ line is further
! subblocked into 64 byte chunks.
!
! Since prefetch can only bring in 64 bytes at a time (See Sparc
! v9 Architecture Manual pp.204) and a page_t is 128 bytes,
! then 2 prefetches are required in order to bring an entire
! page into the E$.
!
! Since the prefetch queue is 8 entries deep, we currently can
! only have 4 prefetches for page_t's outstanding. Thus, we
! prefetch n+4 ahead of where we are now:
!
! 4 * sizeof(page_t) -> 512
! 4 * sizeof(page_t) +64 -> 576
!
! Example
! =======
! contiguous page array in memory...
!
! |AAA1|AAA2|BBB1|BBB2|CCC1|CCC2|DDD1|DDD2|XXX1|XXX2|YYY1|YYY2|...
! ^ ^ ^ ^ ^ ^
! pp | pp+4*sizeof(page)+64
! |
! pp+4*sizeof(page)
!
! Prefetch
! Queue
! +-------+<--- In this iteration, we're working with pp (AAA1),
! |Preftch| but we enqueue prefetch for addr = XXX1
! | XXX1 |
! +-------+<--- this queue slot will be a prefetch instruction for
! |Preftch| for addr = pp + 4*sizeof(page_t) + 64 (or second
! | XXX2 | half of page XXX)
! +-------+
! |Preftch|<-+- The next time around this function, we'll be
! | YYY1 | | working with pp = BBB1, but will be enqueueing
! +-------+ | prefetches to for both halves of page YYY,
! |Preftch| | while both halves of page XXX are in transit
! | YYY2 |<-+ make their way into the E$.
! +-------+
! |Preftch|
! | ZZZ1 |
! +-------+
! . .
! : :
!
! E$
! +============================================...
! | XXX1 | XXX2 | YYY1 | YYY2 | ZZZ1 | ZZZ2 |
! +============================================...
! | | | | | | |
! +============================================...
! .
! :
!
! So we should expect the first four page accesses to stall
! while we warm up the cache, afterwhich, most of the pages
! will have their pp ready in the E$.
!
! Also note that if sizeof(page_t) grows beyond 128, then
! we'll need an additional prefetch to get an entire page
! into the E$, thus reducing the number of outstanding page
! prefetches to 2 (ie. 3 prefetches/page = 6 queue slots)
! etc.
!
! Cheetah+
! ========
! On Cheetah+ we use "#n_write" prefetches as these avoid
! unnecessary RTS->RTO bus transaction state change, and
! just issues RTO transaction. (See pp.77 of Cheetah+ Delta
! PRM). On Cheetah, #n_write prefetches are reflected with
! RTS->RTO state transition regardless.
!
#define STRIDE1 512
#define STRIDE2 576
#if STRIDE1 != (PAGE_SIZE * 4)
#error "STRIDE1 != (PAGE_SIZE * 4)"
#endif
ENTRY(prefetch_page_w)
prefetch [%o0+STRIDE1], #n_writes
retl
prefetch [%o0+STRIDE2], #n_writes
SET_SIZE(prefetch_page_w)
!
! Note on CHEETAH to prefetch for read, we really use #one_write.
! This fetches to E$ (general use) rather than P$ (floating point use).
!
ENTRY(prefetch_page_r)
prefetch [%o0+STRIDE1], #one_write
retl
prefetch [%o0+STRIDE2], #one_write
SET_SIZE(prefetch_page_r)
#elif defined(SPITFIRE) || defined(HUMMINGBIRD)
!
! UltraSparcII can have up to 3 prefetches outstanding.
! A page_t is 128 bytes (2 prefetches of 64 bytes each)
! So prefetch for pp + 1, which is
!
! pp + sizeof(page_t)
! and
! pp + sizeof(page_t) + 64
!
#define STRIDE1 128
#define STRIDE2 192
#if STRIDE1 != PAGE_SIZE
#error "STRIDE1 != PAGE_SIZE"
#endif
ENTRY(prefetch_page_w)
prefetch [%o0+STRIDE1], #n_writes
retl
prefetch [%o0+STRIDE2], #n_writes
SET_SIZE(prefetch_page_w)
ENTRY(prefetch_page_r)
prefetch [%o0+STRIDE1], #n_reads
retl
prefetch [%o0+STRIDE2], #n_reads
SET_SIZE(prefetch_page_r)
#elif defined(OLYMPUS_C)
!
! Prefetch strides for Olympus-C
!
#define STRIDE1 0x440
#define STRIDE2 0x640
ENTRY(prefetch_page_w)
prefetch [%o0+STRIDE1], #n_writes
retl
prefetch [%o0+STRIDE2], #n_writes
SET_SIZE(prefetch_page_w)
ENTRY(prefetch_page_r)
prefetch [%o0+STRIDE1], #n_writes
retl
prefetch [%o0+STRIDE2], #n_writes
SET_SIZE(prefetch_page_r)
#else
#error "You need to fix this for your new cpu type."
#endif
#if defined(CHEETAH) || defined(CHEETAH_PLUS) || defined(JALAPENO) || \
defined(SERRANO)
#define PREFETCH_Q_LEN 8
#elif defined(SPITFIRE) || defined(HUMMINGBIRD)
#define PREFETCH_Q_LEN 3
#elif defined(OLYMPUS_C)
!
! Use length of one for now.
!
#define PREFETCH_Q_LEN 1
#else
#error You need to fix this for your new cpu type.
#endif
#include <vm/kpm.h>
#ifdef SEGKPM_SUPPORT
#define SMAP_SIZE 72
#define SMAP_STRIDE (((PREFETCH_Q_LEN * 64) / SMAP_SIZE) * 64)
#else
!
! The hardware will prefetch the 64 byte cache aligned block
! that contains the address specified in the prefetch instruction.
! Since the size of the smap struct is 48 bytes, issuing 1 prefetch
! per pass will suffice as long as we prefetch far enough ahead to
! make sure we don't stall for the cases where the smap object
! spans multiple hardware prefetch blocks. Let's prefetch as far
! ahead as the hardware will allow.
!
! The smap array is processed with decreasing address pointers.
!
#define SMAP_SIZE 48
#define SMAP_STRIDE (PREFETCH_Q_LEN * SMAP_SIZE)
#endif
ENTRY(prefetch_smap_w)
retl
prefetch [%o0-SMAP_STRIDE], #n_writes
SET_SIZE(prefetch_smap_w)
ENTRY_NP(getidsr)
retl
ldxa [%g0]ASI_INTR_DISPATCH_STATUS, %o0
SET_SIZE(getidsr)