/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#if defined(lint) || defined(__lint)
#include <sys/dtrace_impl.h>
#else
#include <sys/asm_linkage.h>
#include <sys/privregs.h>
#include <sys/fsr.h>
#include <sys/asi.h>
#include "assym.h"
#endif
#if defined(lint) || defined(__lint)
int
dtrace_getipl(void)
{ return (0); }
#else /* lint */
ENTRY_NP(dtrace_getipl)
retl
rdpr %pil, %o0
SET_SIZE(dtrace_getipl)
#endif /* lint */
#if defined(lint) || defined(__lint)
uint_t
dtrace_getotherwin(void)
{ return (0); }
#else /* lint */
ENTRY_NP(dtrace_getotherwin)
retl
rdpr %otherwin, %o0
SET_SIZE(dtrace_getotherwin)
#endif /* lint */
#if defined(lint) || defined(__lint)
uint_t
dtrace_getfprs(void)
{ return (0); }
#else /* lint */
ENTRY_NP(dtrace_getfprs)
retl
rd %fprs, %o0
SET_SIZE(dtrace_getfprs)
#endif /* lint */
#if defined(lint) || defined(__lint)
/*ARGSUSED*/
void
dtrace_getfsr(uint64_t *val)
{}
#else /* lint */
ENTRY_NP(dtrace_getfsr)
rdpr %pstate, %o1
andcc %o1, PSTATE_PEF, %g0
bz,pn %xcc, 1f
nop
rd %fprs, %o1
andcc %o1, FPRS_FEF, %g0
bz,pn %xcc, 1f
nop
retl
stx %fsr, [%o0]
1:
retl
stx %g0, [%o0]
SET_SIZE(dtrace_getfsr)
#endif /* lint */
#if defined(lint) || defined(__lint)
greg_t
dtrace_getfp(void)
{ return (0); }
#else /* lint */
ENTRY_NP(dtrace_getfp)
retl
mov %fp, %o0
SET_SIZE(dtrace_getfp)
#endif /* lint */
#if defined(lint) || defined(__lint)
void
dtrace_flush_user_windows(void)
{}
#else
ENTRY_NP(dtrace_flush_user_windows)
rdpr %otherwin, %g1
brz %g1, 3f
clr %g2
1:
save %sp, -WINDOWSIZE, %sp
rdpr %otherwin, %g1
brnz %g1, 1b
add %g2, 1, %g2
2:
sub %g2, 1, %g2 ! restore back to orig window
brnz %g2, 2b
restore
3:
retl
nop
SET_SIZE(dtrace_flush_user_windows)
#endif /* lint */
#if defined(lint) || defined(__lint)
uint32_t
dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
{
uint32_t old;
if ((old = *target) == cmp)
*target = new;
return (old);
}
void *
dtrace_casptr(void *target, void *cmp, void *new)
{
void *old;
if ((old = *(void **)target) == cmp)
*(void **)target = new;
return (old);
}
#else /* lint */
ENTRY(dtrace_cas32)
cas [%o0], %o1, %o2
retl
mov %o2, %o0
SET_SIZE(dtrace_cas32)
ENTRY(dtrace_casptr)
casn [%o0], %o1, %o2
retl
mov %o2, %o0
SET_SIZE(dtrace_casptr)
#endif /* lint */
#if defined(lint)
/*ARGSUSED*/
uintptr_t
dtrace_caller(int aframes)
{
return (0);
}
#else /* lint */
ENTRY(dtrace_caller)
sethi %hi(nwin_minus_one), %g4
ld [%g4 + %lo(nwin_minus_one)], %g4
rdpr %canrestore, %g2
cmp %g2, %o0
bl %icc, 1f
rdpr %cwp, %g1
sub %g1, %o0, %g3
brgez,a,pt %g3, 0f
wrpr %g3, %cwp
!
! CWP minus the number of frames is negative; we must perform the
! arithmetic modulo MAXWIN.
!
add %g4, %g3, %g3
inc %g3
wrpr %g3, %cwp
0:
mov %i7, %g4
wrpr %g1, %cwp
retl
mov %g4, %o0
1:
!
! The caller has been flushed to the stack. This is unlikely
! (interrupts are disabled in dtrace_probe()), but possible (the
! interrupt inducing the spill may have been taken before the
! call to dtrace_probe()).
!
retl
mov -1, %o0
SET_SIZE(dtrace_caller)
#endif
#if defined(lint)
/*ARGSUSED*/
int
dtrace_fish(int aframes, int reg, uintptr_t *regval)
{
return (0);
}
#else /* lint */
ENTRY(dtrace_fish)
rd %pc, %g5
ba 0f
add %g5, 12, %g5
mov %l0, %g4
mov %l1, %g4
mov %l2, %g4
mov %l3, %g4
mov %l4, %g4
mov %l5, %g4
mov %l6, %g4
mov %l7, %g4
mov %i0, %g4
mov %i1, %g4
mov %i2, %g4
mov %i3, %g4
mov %i4, %g4
mov %i5, %g4
mov %i6, %g4
mov %i7, %g4
0:
sub %o1, 16, %o1 ! Can only retrieve %l's and %i's
sll %o1, 2, %o1 ! Multiply by instruction size
add %g5, %o1, %g5 ! %g5 now contains the instr. to pick
sethi %hi(nwin_minus_one), %g4
ld [%g4 + %lo(nwin_minus_one)], %g4
!
! First we need to see if the frame that we're fishing in is still
! contained in the register windows.
!
rdpr %canrestore, %g2
cmp %g2, %o0
bl %icc, 2f
rdpr %cwp, %g1
sub %g1, %o0, %g3
brgez,a,pt %g3, 0f
wrpr %g3, %cwp
!
! CWP minus the number of frames is negative; we must perform the
! arithmetic modulo MAXWIN.
!
add %g4, %g3, %g3
inc %g3
wrpr %g3, %cwp
0:
jmp %g5
ba 1f
1:
wrpr %g1, %cwp
stn %g4, [%o2]
retl
clr %o0 ! Success; return 0.
2:
!
! The frame that we're looking for has been flushed to the stack; the
! caller will be forced to
!
retl
add %g2, 1, %o0 ! Failure; return deepest frame + 1
SET_SIZE(dtrace_fish)
#endif
#if defined(lint)
/*ARGSUSED*/
void
dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
volatile uint16_t *flags)
{}
#else
ENTRY(dtrace_copyin)
tst %o2
bz 2f
clr %g1
lduba [%o0 + %g1]ASI_USER, %g2
0:
! check for an error if the count is 4k-aligned
andcc %g1, 0xfff, %g0
bnz,pt %icc, 1f
stub %g2, [%o1 + %g1]
lduh [%o3], %g3
andcc %g3, CPU_DTRACE_BADADDR, %g0
bnz,pn %icc, 2f
nop
1:
inc %g1
cmp %g1, %o2
bl,a 0b
lduba [%o0 + %g1]ASI_USER, %g2
2:
retl
nop
SET_SIZE(dtrace_copyin)
#endif
#if defined(lint)
/*ARGSUSED*/
void
dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
volatile uint16_t *flags)
{}
#else
ENTRY(dtrace_copyinstr)
tst %o2
bz 2f
clr %g1
lduba [%o0 + %g1]ASI_USER, %g2
0:
stub %g2, [%o1 + %g1] ! Store byte
! check for an error if the count is 4k-aligned
andcc %g1, 0xfff, %g0
bnz,pt %icc, 1f
inc %g1
lduh [%o3], %g3
andcc %g3, CPU_DTRACE_BADADDR, %g0
bnz,pn %icc, 2f
nop
1:
cmp %g2, 0 ! Was that '\0'?
be 2f ! If so, we're done
cmp %g1, %o2 ! Compare to limit
bl,a 0b ! If less, take another lap
lduba [%o0 + %g1]ASI_USER, %g2 ! delay: load user byte
2:
retl
nop
SET_SIZE(dtrace_copyinstr)
#endif
#if defined(lint)
/*ARGSUSED*/
void
dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
volatile uint16_t *flags)
{}
#else
ENTRY(dtrace_copyout)
tst %o2
bz 2f
clr %g1
ldub [%o0 + %g1], %g2
0:
! check for an error if the count is 4k-aligned
andcc %g1, 0xfff, %g0
bnz,pt %icc, 1f
stba %g2, [%o1 + %g1]ASI_USER
lduh [%o3], %g3
andcc %g3, CPU_DTRACE_BADADDR, %g0
bnz,pn %icc, 2f
nop
1:
inc %g1
cmp %g1, %o2
bl,a 0b
ldub [%o0 + %g1], %g2
2:
retl
nop
SET_SIZE(dtrace_copyout)
#endif
#if defined(lint)
/*ARGSUSED*/
void
dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
volatile uint16_t *flags)
{}
#else
ENTRY(dtrace_copyoutstr)
tst %o2
bz 2f
clr %g1
ldub [%o0 + %g1], %g2
0:
stba %g2, [%o1 + %g1]ASI_USER
! check for an error if the count is 4k-aligned
andcc %g1, 0xfff, %g0
bnz,pt %icc, 1f
inc %g1
lduh [%o3], %g3
andcc %g3, CPU_DTRACE_BADADDR, %g0
bnz,pn %icc, 2f
nop
1:
cmp %g2, 0
be 2f
cmp %g1, %o2
bl,a 0b
ldub [%o0 + %g1], %g2
2:
retl
nop
SET_SIZE(dtrace_copyoutstr)
#endif
#if defined(lint)
/*ARGSUSED*/
uintptr_t
dtrace_fulword(void *addr)
{ return (0); }
#else
ENTRY(dtrace_fulword)
clr %o1
ldna [%o0]ASI_USER, %o1
retl
mov %o1, %o0
SET_SIZE(dtrace_fulword)
#endif
#if defined(lint)
/*ARGSUSED*/
uint8_t
dtrace_fuword8(void *addr)
{ return (0); }
#else
ENTRY(dtrace_fuword8)
clr %o1
lduba [%o0]ASI_USER, %o1
retl
mov %o1, %o0
SET_SIZE(dtrace_fuword8)
#endif
#if defined(lint)
/*ARGSUSED*/
uint16_t
dtrace_fuword16(void *addr)
{ return (0); }
#else
ENTRY(dtrace_fuword16)
clr %o1
lduha [%o0]ASI_USER, %o1
retl
mov %o1, %o0
SET_SIZE(dtrace_fuword16)
#endif
#if defined(lint)
/*ARGSUSED*/
uint32_t
dtrace_fuword32(void *addr)
{ return (0); }
#else
ENTRY(dtrace_fuword32)
clr %o1
lda [%o0]ASI_USER, %o1
retl
mov %o1, %o0
SET_SIZE(dtrace_fuword32)
#endif
#if defined(lint)
/*ARGSUSED*/
uint64_t
dtrace_fuword64(void *addr)
{ return (0); }
#else
ENTRY(dtrace_fuword64)
clr %o1
ldxa [%o0]ASI_USER, %o1
retl
mov %o1, %o0
SET_SIZE(dtrace_fuword64)
#endif
#if defined(lint)
/*ARGSUSED*/
int
dtrace_getupcstack_top(uint64_t *pcstack, int pcstack_limit, uintptr_t *sp)
{ return (0); }
#else
/*
* %g1 pcstack
* %g2 current window
* %g3 maxwin (nwindows - 1)
* %g4 saved %cwp (so we can get back to the original window)
* %g5 iteration count
* %g6 saved %fp
*
* %o0 pcstack / return value (iteration count)
* %o1 pcstack_limit
* %o2 last_fp
*/
ENTRY(dtrace_getupcstack_top)
mov %o0, %g1 ! we need the pcstack pointer while
! we're visiting other windows
rdpr %otherwin, %g5 ! compute the number of iterations
cmp %g5, %o1 ! (windows to observe) by taking the
movg %icc, %o1, %g5 ! min of %otherwin and pcstack_limit
brlez,a,pn %g5, 2f ! return 0 if count <= 0
clr %o0
sethi %hi(nwin_minus_one), %g3 ! hang onto maxwin since we'll need
ld [%g3 + %lo(nwin_minus_one)], %g3 ! it for our modular arithmetic
rdpr %cwp, %g4 ! remember our window so we can return
rdpr %canrestore, %g2 ! compute the first non-user window
subcc %g4, %g2, %g2 ! current = %cwp - %canrestore
bge,pt %xcc, 1f ! good to go if current is >= 0
mov %g5, %o0 ! we need to return the count
add %g2, %g3, %g2 ! normalize our current window if it's
add %g2, 1, %g2 ! less than zero
! note that while it's tempting, we can't execute restore to decrement
! the %cwp by one (mod nwindows) because we're in the user's windows
1:
deccc %g2 ! decrement the current window
movl %xcc, %g3, %g2 ! normalize if it's negative (-1)
wrpr %g2, %cwp ! change windows
stx %i7, [%g1] ! stash the return address in pcstack
deccc %g5 ! decrement the count
bnz,pt %icc, 1b ! we iterate until the count reaches 0
add %g1, 8, %g1 ! increment the pcstack pointer
mov %i6, %g6 ! stash the last frame pointer we
! encounter so the caller can
! continue the stack walk in memory
wrpr %g4, %cwp ! change back to the original window
stn %g6, [%o2] ! return the last frame pointer
2:
retl
nop
SET_SIZE(dtrace_getupcstack_top)
#endif
#if defined(lint)
/*ARGSUSED*/
int
dtrace_getustackdepth_top(uintptr_t *sp)
{ return (0); }
#else
ENTRY(dtrace_getustackdepth_top)
mov %o0, %o2
rdpr %otherwin, %o0
brlez,a,pn %o0, 2f ! return 0 if there are no user wins
clr %o0
rdpr %cwp, %g4 ! remember our window so we can return
rdpr %canrestore, %g2 ! compute the first user window
sub %g4, %g2, %g2 ! current = %cwp - %canrestore -
subcc %g2, %o0, %g2 ! %otherwin
bge,pt %xcc, 1f ! normalize the window if necessary
sethi %hi(nwin_minus_one), %g3
ld [%g3 + %lo(nwin_minus_one)], %g3
add %g2, %g3, %g2
add %g2, 1, %g2
1:
wrpr %g2, %cwp ! change to the first user window
mov %i6, %g6 ! stash the frame pointer
wrpr %g4, %cwp ! change back to the original window
stn %g6, [%o2] ! return the frame pointer
2:
retl
nop
SET_SIZE(dtrace_getustackdepth_top)
#endif
#if defined(lint) || defined(__lint)
/* ARGSUSED */
ulong_t
dtrace_getreg_win(uint_t reg, uint_t depth)
{ return (0); }
#else /* lint */
ENTRY(dtrace_getreg_win)
sub %o0, 16, %o0
cmp %o0, 16 ! %o0 must begin in the range [16..32)
blu,pt %xcc, 1f
nop
retl
clr %o0
1:
set dtrace_getreg_win_table, %g3
sll %o0, 2, %o0
add %g3, %o0, %g3
rdpr %canrestore, %o3
rdpr %cwp, %g2
! Set %cwp to be (%cwp - %canrestore - %o1) mod NWINDOWS
sub %g2, %o3, %o2 ! %o2 is %cwp - %canrestore
subcc %o2, %o1, %o4
bge,a,pn %xcc, 2f
wrpr %o4, %cwp
sethi %hi(nwin_minus_one), %o3
ld [%o3 + %lo(nwin_minus_one)], %o3
add %o2, %o3, %o4
wrpr %o4, %cwp
2:
jmp %g3
ba 3f
3:
wrpr %g2, %cwp
retl
mov %g1, %o0
dtrace_getreg_win_table:
mov %l0, %g1
mov %l1, %g1
mov %l2, %g1
mov %l3, %g1
mov %l4, %g1
mov %l5, %g1
mov %l6, %g1
mov %l7, %g1
mov %i0, %g1
mov %i1, %g1
mov %i2, %g1
mov %i3, %g1
mov %i4, %g1
mov %i5, %g1
mov %i6, %g1
mov %i7, %g1
SET_SIZE(dtrace_getreg_win)
#endif /* lint */
#if defined(lint) || defined(__lint)
/* ARGSUSED */
void
dtrace_putreg_win(uint_t reg, ulong_t value)
{}
#else /* lint */
ENTRY(dtrace_putreg_win)
sub %o0, 16, %o0
cmp %o0, 16 ! %o0 must be in the range [16..32)
blu,pt %xcc, 1f
nop
retl
nop
1:
mov %o1, %g1 ! move the value into a global register
set dtrace_putreg_table, %g3
sll %o0, 2, %o0
add %g3, %o0, %g3
rdpr %canrestore, %o3
rdpr %cwp, %g2
! Set %cwp to be (%cwp - %canrestore - 1) mod NWINDOWS
sub %g2, %o3, %o2 ! %o2 is %cwp - %canrestore
subcc %o2, 1, %o4
bge,a,pn %xcc, 2f
wrpr %o4, %cwp
sethi %hi(nwin_minus_one), %o3
ld [%o3 + %lo(nwin_minus_one)], %o3
add %o2, %o3, %o4
wrpr %o4, %cwp
2:
jmp %g3
ba 3f
3:
wrpr %g2, %cwp
retl
nop
dtrace_putreg_table:
mov %g1, %l0
mov %g1, %l1
mov %g1, %l2
mov %g1, %l3
mov %g1, %l4
mov %g1, %l5
mov %g1, %l6
mov %g1, %l7
mov %g1, %i0
mov %g1, %i1
mov %g1, %i2
mov %g1, %i3
mov %g1, %i4
mov %g1, %i5
mov %g1, %i6
mov %g1, %i7
SET_SIZE(dtrace_putreg_win)
#endif /* lint */
#if defined(lint) || defined(__lint)
/*ARGSUSED*/
void
dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
int fault, int fltoffs, uintptr_t illval)
{}
#else /* lint */
ENTRY(dtrace_probe_error)
save %sp, -SA(MINFRAME), %sp
sethi %hi(dtrace_probeid_error), %l0
ld [%l0 + %lo(dtrace_probeid_error)], %o0
mov %i0, %o1
mov %i1, %o2
mov %i2, %o3
mov %i3, %o4
call dtrace_probe
mov %i4, %o5
ret
restore
SET_SIZE(dtrace_probe_error)
#endif