Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
1f8870ed
Commit
1f8870ed
authored
May 27, 2003
by
Miles Bader
Committed by
Arnaldo Carvalho de Melo
May 27, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] Add v850 support for hardware single-step (via ptrace)
parent
7d136361
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
310 additions
and
94 deletions
+310
-94
arch/v850/kernel/entry.S
arch/v850/kernel/entry.S
+177
-50
arch/v850/kernel/ptrace.c
arch/v850/kernel/ptrace.c
+133
-44
No files found.
arch/v850/kernel/entry.S
View file @
1f8870ed
...
@@ -235,25 +235,30 @@
...
@@ -235,25 +235,30 @@
sst.w
syscall_num
,
PTO
+
PT_CUR_SYSCALL
[
ep
]
sst.w
syscall_num
,
PTO
+
PT_CUR_SYSCALL
[
ep
]
/*
Save
register
state
not
normally
saved
by
PUSH_STATE
for
TYPE
.
*/
/*
Save
register
state
not
normally
saved
by
PUSH_STATE
for
TYPE
,
to
the
state
-
save
-
frame
on
the
stack
; also copies SP to EP. r19 may be trashed. */
#define SAVE_EXTRA_STATE(type) \
#define SAVE_EXTRA_STATE(type) \
mov
sp
,
ep
; \
mov
sp
,
ep
; \
type
##
_EXTRA_STATE_SAVER
type
##
_EXTRA_STATE_SAVER
/*
Restore
register
state
not
normally
restored
by
POP_STATE
for
TYPE
.
*/
/*
Restore
register
state
not
normally
restored
by
POP_STATE
for
TYPE
,
from
the
state
-
save
-
frame
on
the
stack
; also copies SP to EP.
r19
may
be
trashed
.
*/
#define RESTORE_EXTRA_STATE(type) \
#define RESTORE_EXTRA_STATE(type) \
mov
sp
,
ep
; \
mov
sp
,
ep
; \
type
##
_EXTRA_STATE_RESTORER
type
##
_EXTRA_STATE_RESTORER
/*
Save
any
call
-
clobbered
registers
not
normally
saved
by
PUSH_STATE
/*
Save
any
call
-
clobbered
registers
not
normally
saved
by
PUSH_STATE
for
for
TYPE
.
*/
TYPE
,
to
the
state
-
save
-
frame
on
the
stack
.
#define SAVE_EXTRA_STATE_FOR_FUNCALL(type) \
EP
may
be
trashed
,
but
is
not
guaranteed
to
contain
a
copy
of
SP
mov
sp
,
ep
; \
(
unlike
after
most
SAVE_
...
macros
)
.
r19
may
be
trashed
.
*/
type
##
_FUNCALL_EXTRA_STATE_SAVER
#define SAVE_EXTRA_STATE_FOR_SCHEDULE(type) \
/*
Restore
any
call
-
clobbered
registers
not
normally
restored
by
POP_STATE
for
type
##
_SCHEDULE_EXTRA_STATE_SAVER
TYPE
.
*/
/*
Restore
any
call
-
clobbered
registers
not
normally
restored
by
#define RESTORE_EXTRA_STATE_FOR_FUNCALL(type) \
POP_STATE
for
TYPE
,
to
the
state
-
save
-
frame
on
the
stack
.
mov
sp
,
ep
; \
EP
may
be
trashed
,
but
is
not
guaranteed
to
contain
a
copy
of
SP
type
##
_FUNCALL_EXTRA_STATE_RESTORER
(
unlike
after
most
RESTORE_
...
macros
)
.
r19
may
be
trashed
.
*/
#define RESTORE_EXTRA_STATE_FOR_SCHEDULE(type) \
type
##
_SCHEDULE_EXTRA_STATE_RESTORER
/*
These
are
extra_state_saver
/
restorer
values
for
a
user
trap
.
Note
/*
These
are
extra_state_saver
/
restorer
values
for
a
user
trap
.
Note
...
@@ -264,36 +269,48 @@
...
@@ -264,36 +269,48 @@
caller
of
the
syscall
function
should
have
saved
them
.
*/
caller
of
the
syscall
function
should
have
saved
them
.
*/
#define TRAP_RET reti
#define TRAP_RET reti
/*
Traps
don
't save call-clobbered registers (but do still save arg regs). */
/*
Traps
don
't save call-clobbered registers (but do still save arg regs).
We
preserve
PSw
to
keep
long
-
term
state
,
namely
interrupt
status
(
for
traps
from
kernel
-
mode
),
and
the
single
-
step
flag
(
for
user
traps
)
.
*/
#define TRAP_STATE_SAVER \
#define TRAP_STATE_SAVER \
SAVE_ARG_REGS
; \
SAVE_ARG_REGS
; \
SAVE_PC
(
EIPC
)
SAVE_PC
(
EIPC
)
; \
SAVE_PSW
(
EIPSW
)
/*
When
traps
return
,
they
just
leave
call
-
clobbered
registers
(
except
for
arg
/*
When
traps
return
,
they
just
leave
call
-
clobbered
registers
(
except
for
arg
regs
)
with
whatever
value
they
have
from
the
kernel
.
*/
regs
)
with
whatever
value
they
have
from
the
kernel
.
Traps
don
't preserve
the
PSW
,
but
we
zero
EIPSW
to
ensure
it
doesn
't contain anything dangerous
(
in
particular
,
the
single
-
step
flag
)
.
*/
#define TRAP_STATE_RESTORER \
#define TRAP_STATE_RESTORER \
RESTORE_ARG_REGS
; \
RESTORE_ARG_REGS
; \
RESTORE_PC
(
EIPC
)
RESTORE_PC
(
EIPC
)
; \
RESTORE_PSW
(
EIPSW
)
/*
Save
registers
not
normally
saved
by
traps
.
We
need
to
save
r12
,
even
/*
Save
registers
not
normally
saved
by
traps
.
We
need
to
save
r12
,
even
though
it
's nominally call-clobbered, because it'
s
used
when
restarting
though
it
's nominally call-clobbered, because it'
s
used
when
restarting
a
system
call
(
the
signal
-
handling
path
uses
SAVE_EXTRA_STATE
,
and
a
system
call
(
the
signal
-
handling
path
uses
SAVE_EXTRA_STATE
,
and
expects
r12
to
be
restored
when
the
trap
returns
)
.
Similarly
,
we
must
expects
r12
to
be
restored
when
the
trap
returns
)
.
*/
save
the
PSW
,
so
that
it
's at least in a known state in the the pt_regs
structure
.
*/
#define TRAP_EXTRA_STATE_SAVER \
#define TRAP_EXTRA_STATE_SAVER \
SAVE_RVAL_REGS
; \
SAVE_RVAL_REGS
; \
sst.w
r12
,
PTO
+
PT_GPR
(
12
)[
ep
]
; \
sst.w
r12
,
PTO
+
PT_GPR
(
12
)[
ep
]
; \
SAVE_CALL_SAVED_REGS
; \
SAVE_CALL_SAVED_REGS
; \
SAVE_PSW
(
EIPSW
)
; \
SAVE_CT_REGS
SAVE_CT_REGS
#define TRAP_EXTRA_STATE_RESTORER \
#define TRAP_EXTRA_STATE_RESTORER \
RESTORE_RVAL_REGS
; \
RESTORE_RVAL_REGS
; \
sld.w
PTO
+
PT_GPR
(
12
)[
ep
],
r12
; \
sld.w
PTO
+
PT_GPR
(
12
)[
ep
],
r12
; \
RESTORE_CALL_SAVED_REGS
; \
RESTORE_CALL_SAVED_REGS
; \
RESTORE_PSW
(
EIPSW
)
; \
RESTORE_CT_REGS
RESTORE_CT_REGS
#define TRAP_FUNCALL_EXTRA_STATE_SAVER \
/*
Save
registers
prior
to
calling
scheduler
(
just
before
trap
returns
)
.
We
have
to
save
the
return
-
value
registers
to
preserve
the
trap
's return
value
.
Note
that
...
_SCHEDULE_EXTRA_STATE_SAVER
,
unlike
most
...
_SAVER
macros
,
is
required
to
setup
EP
itself
if
EP
is
needed
(
this
is
because
in
many
cases
,
the
macro
is
empty
)
.
*/
#define TRAP_SCHEDULE_EXTRA_STATE_SAVER \
mov
sp
,
ep
; \
SAVE_RVAL_REGS
SAVE_RVAL_REGS
#define TRAP_FUNCALL_EXTRA_STATE_RESTORER \
/*
Note
that
...
_SCHEDULE_EXTRA_STATE_RESTORER
,
unlike
most
...
_RESTORER
macros
,
is
required
to
setup
EP
itself
if
EP
is
needed
(
this
is
because
in
many
cases
,
the
macro
is
empty
)
.
*/
#define TRAP_SCHEDULE_EXTRA_STATE_RESTORER \
mov
sp
,
ep
; \
RESTORE_RVAL_REGS
RESTORE_RVAL_REGS
/*
Register
saving
/
restoring
for
maskable
interrupts
.
*/
/*
Register
saving
/
restoring
for
maskable
interrupts
.
*/
...
@@ -391,33 +408,34 @@
...
@@ -391,33 +408,34 @@
ld.w
TI_FLAGS
[
r18
],
r19
; \
ld.w
TI_FLAGS
[
r18
],
r19
; \
andi
_TIF_NEED_RESCHED
,
r19
,
r0
; \
andi
_TIF_NEED_RESCHED
,
r19
,
r0
; \
bnz
3
f
; /* Call the scheduler. */ \
bnz
3
f
; /* Call the scheduler. */ \
andi
_TIF_SIGPENDING
,
r19
,
r0
; \
5
:
andi
_TIF_SIGPENDING
,
r19
,
r18
; \
bnz
4
f
; /* Signals to handle, handle them */ \
ld.w
TASK_PTRACE
[
CURRENT_TASK
],
r19
; /* ptrace flags */ \
or
r18
,
r19
; /* see if either is non-zero */ \
bnz
4
f
; /* if so, handle them */ \
\
\
/*
Return
to
user
state
.
*/
\
/*
Return
to
user
state
.
*/
\
1
:
st.b
r0
,
KM
; /* Now officially in user state. */ \
1
:
st.b
r0
,
KM
; /* Now officially in user state. */ \
\
\
/*
Final
return
.
The
stack
-
pointer
fiddling
is
not
needed
when
returning
\
/*
Final
return
.
The
stack
-
pointer
fiddling
is
not
needed
when
returning
\
to
kernel
-
mode
,
but
they
don
't hurt, and this way we can share the \
to
kernel
-
mode
,
but
they
don
't hurt, and this way we can share the \
(
somtimes
rather
lengthy
)
POP_STATE
macro
.
*/
\
(
som
e
times
rather
lengthy
)
POP_STATE
macro
.
*/
\
2
:
POP_STATE
(
type
)
; \
2
:
POP_STATE
(
type
)
; \
st.w
sp
,
KSP
; /* Save the kernel stack pointer. */ \
st.w
sp
,
KSP
; /* Save the kernel stack pointer. */ \
ld.w
PT_GPR
(
GPR_SP
)-
PT_SIZE
[
sp
],
sp
; /* Restore stack pointer. */ \
ld.w
PT_GPR
(
GPR_SP
)-
PT_SIZE
[
sp
],
sp
; /* Restore stack pointer. */ \
type
##
_RET
; /* Return from the trap/interrupt. */ \
type
##
_RET
; /* Return from the trap/interrupt. */ \
\
\
/*
Call
the
scheduler
before
returning
from
a
syscall
/
trap
.
*/
\
/*
Call
the
scheduler
before
returning
from
a
syscall
/
trap
.
*/
\
3
:
SAVE_EXTRA_STATE_FOR_
FUNCALL
(
type
)
; /* Prepare for funcall. */
\
3
:
SAVE_EXTRA_STATE_FOR_
SCHEDULE
(
type
)
; /* Prepare to call scheduler. */
\
jarl
CSYM
(
schedule
)
,
lp
; /* Call scheduler */ \
jarl
call_scheduler
,
lp
; /* Call scheduler */ \
di
; /* The scheduler enables interrupts */\
di
; /* The scheduler enables interrupts */\
RESTORE_EXTRA_STATE_FOR_
FUNCALL
(
type
)
; \
RESTORE_EXTRA_STATE_FOR_
SCHEDULE
(
type
)
; \
GET_CURRENT_THREAD
(
r18
)
; \
GET_CURRENT_THREAD
(
r18
)
; \
ld.w
TI_FLAGS
[
r18
],
r19
; \
ld.w
TI_FLAGS
[
r18
],
r19
; \
andi
_TIF_SIGPENDING
,
r19
,
r0
; \
br
5
b
; /* Continue with return path. */ \
bz
1
b
; /* No signals, return. */ \
/
*
Signals
to
handle
,
fall
through
to
handle
them
.
*/
\
\
\
/*
Handle
a
signal
return
.
*/
\
/*
Handle
a
signal
or
ptraced
process
return
.
\
4
:
/
*
Not
all
registers
are
saved
by
the
normal
trap
/
interrupt
entry
\
r18
should
be
non
-
zero
if
there
are
pending
signals
.
*/
\
4
:
/
*
Not
all
registers
are
saved
by
the
normal
trap
/
interrupt
entry
\
points
(
for
instance
,
call
-
saved
registers
(
because
the
normal
\
points
(
for
instance
,
call
-
saved
registers
(
because
the
normal
\
C
-
compiler
calling
sequence
in
the
kernel
makes
sure
they
're \
C
-
compiler
calling
sequence
in
the
kernel
makes
sure
they
're \
preserved
),
and
call
-
clobbered
registers
in
the
case
of
\
preserved
),
and
call
-
clobbered
registers
in
the
case
of
\
...
@@ -425,12 +443,9 @@
...
@@ -425,12 +443,9 @@
complete
register
state
.
Here
we
save
anything
not
saved
by
\
complete
register
state
.
Here
we
save
anything
not
saved
by
\
the
normal
entry
sequence
,
so
that
it
may
be
safely
restored
\
the
normal
entry
sequence
,
so
that
it
may
be
safely
restored
\
(
in
a
possibly
modified
form
)
after
do_signal
returns
.
*/
\
(
in
a
possibly
modified
form
)
after
do_signal
returns
.
*/
\
SAVE_EXTRA_STATE
(
type
)
; /* Save state not saved by entry. */ \
SAVE_EXTRA_STATE
(
type
)
; /* Save state not saved by entry. */ \
movea
PTO
,
sp
,
r6
; /* Arg 1: struct pt_regs *regs */ \
jarl
handle_signal_or_ptrace_return
,
lp
; \
mov
r0
,
r7
; /* Arg 2: sigset_t *oldset */ \
RESTORE_EXTRA_STATE
(
type
)
; /* Restore extra regs. */ \
jarl
CSYM
(
do_signal
),
lp
; /* Handle any signals */ \
di
; /* sig handling enables interrupts */ \
RESTORE_EXTRA_STATE
(
type
)
; /* Restore extra regs. */ \
br
1
b
br
1
b
...
@@ -678,22 +693,50 @@ END(irq)
...
@@ -678,22 +693,50 @@ END(irq)
*
indirect
jump
)
.
*
indirect
jump
)
.
*/
*/
G_ENTRY
(
dbtrap
):
G_ENTRY
(
dbtrap
):
SAVE_STATE
(
DBTRAP
,
r0
,
ENTRY_SP
)//
Save
registers
.
SAVE_STATE
(
DBTRAP
,
r0
,
ENTRY_SP
)//
Save
registers
.
/
*
First
see
if
we
came
from
kernel
mode
; if so, the dbtrap
instruction
has
a
special
meaning
,
to
set
the
DIR
(
`
debug
information
register
') register. This is because the DIR register
can
_only_
be
manipulated
/
read
while
in
`
debug
mode
,
' and debug
mode
is
only
active
while
we
're inside the dbtrap handler. The
exact
functionality
is
:
{
DIR
=
(
DIR
|
r6
)
&
~
r7
; return DIR; }. */
ld.b
PTO
+
PT_KERNEL_MODE
[
sp
],
r19
cmp
r19
,
r0
bz
1
f
stsr
SR_DIR
,
r10
or
r6
,
r10
not
r7
,
r7
and
r7
,
r10
ldsr
r10
,
SR_DIR
stsr
SR_DIR
,
r10
//
Confirm
the
value
we
set
st.w
r10
,
PTO
+
PT_GPR
(
10
)[
sp
]
//
return
it
br
3
f
1
:
ei
//
Enable
interrupts
.
/
*
The
default
signal
type
we
raise
.
*/
mov
SIGTRAP
,
r6
/
*
See
if
it
's a single-step trap. */
stsr
SR_DBPSW
,
r19
andi
0x0800
,
r19
,
r19
bnz
2
f
/
*
Look
to
see
if
the
preceding
instruction
was
is
a
dbtrap
or
not
,
/
*
Look
to
see
if
the
preceding
instruction
was
is
a
dbtrap
or
not
,
to
decide
which
signal
we
should
use
.
*/
to
decide
which
signal
we
should
use
.
*/
stsr
SR_DBPC
,
r19
//
PC
following
trapping
insn
stsr
SR_DBPC
,
r19
//
PC
following
trapping
insn
ld.hu
-
2
[
r19
],
r19
ld.hu
-
2
[
r19
],
r19
mov
SIGTRAP
,
r6
ori
0xf840
,
r0
,
r20
//
DBTRAP
insn
ori
0xf840
,
r0
,
r20
//
DBTRAP
insn
cmp
r19
,
r20
//
Was
this
trap
caused
by
DBTRAP
?
cmp
r19
,
r20
//
Was
this
trap
caused
by
DBTRAP
?
cmov
ne
,
SIGILL
,
r6
,
r6
//
Choose
signal
appropriately
cmov
ne
,
SIGILL
,
r6
,
r6
//
Choose
signal
appropriately
/
*
Raise
the
desired
signal
.
*/
/
*
Raise
the
desired
signal
.
*/
mov
CURRENT_TASK
,
r7
//
Arg
1
:
task
2
:
mov
CURRENT_TASK
,
r7
//
Arg
1
:
task
jarl
CSYM
(
force
_sig
),
lp
//
tail
call
jarl
CSYM
(
send
_sig
),
lp
//
tail
call
RETURN
(
DBTRAP
)
3
:
RETURN
(
DBTRAP
)
END
(
dbtrap
)
END
(
dbtrap
)
...
@@ -734,10 +777,94 @@ L_ENTRY(bad_trap_wrapper):
...
@@ -734,10 +777,94 @@ L_ENTRY(bad_trap_wrapper):
END
(
bad_trap_wrapper
)
END
(
bad_trap_wrapper
)
/*
*
Invoke
the
scheduler
,
called
from
the
trap
/
irq
kernel
exit
path
.
*
*
This
basically
just
calls
`
schedule
', but also arranges for extra
*
registers
to
be
saved
for
ptrace
'd processes, so ptrace can modify them.
*/
L_ENTRY
(
call_scheduler
):
ld.w
TASK_PTRACE
[
CURRENT_TASK
],
r19
//
See
if
task
is
ptrace
'd
cmp
r19
,
r0
bnz
1
f
//
...
yes
,
do
special
stuff
jr
CSYM
(
schedule
)
//
...
no
,
just
tail
-
call
scheduler
//
Save
extra
regs
for
ptrace
'd task. We want to save anything
//
that
would
otherwise
only
be
`
implicitly
' saved by the normal
//
compiler
calling
-
convention
.
1
:
mov
sp
,
ep
//
Setup
EP
for
SAVE_CALL_SAVED_REGS
SAVE_CALL_SAVED_REGS
//
Save
call
-
saved
registers
to
stack
mov
lp
,
r20
//
Save
LP
in
a
callee
-
saved
register
jarl
CSYM
(
schedule
),
lp
//
Call
scheduler
mov
r20
,
lp
mov
sp
,
ep
//
We
can
't rely on EP after return
RESTORE_CALL_SAVED_REGS
//
Restore
(
possibly
modified
)
regs
jmp
[
lp
]
//
Return
to
the
return
path
END
(
call_scheduler
)
/*
*
This
is
an
out
-
of
-
line
handler
for
two
special
cases
during
the
kernel
*
trap
/
irq
exit
sequence
:
*
*
(
1
)
If
r18
is
non
-
zero
then
a
signal
needs
to
be
handled
,
which
is
*
done
,
and
then
the
caller
returned
to
.
*
*
(
2
)
If
r18
is
non
-
zero
then
we
're returning to a ptraced process, which
*
has
several
special
cases
--
single
-
stepping
and
trap
tracing
,
both
*
of
which
require
using
the
`
dbret
' instruction to exit the kernel
*
instead
of
the
normal
`
reti
' (this is because the CPU not correctly
*
single
-
step
after
a
reti
)
.
In
this
case
,
of
course
,
this
handler
*
never
returns
to
the
caller
.
*
*
In
either
case
,
all
registers
should
have
been
saved
to
the
current
*
state
-
save
-
frame
on
the
stack
,
except
for
callee
-
saved
registers
.
*
*
[
These
two
different
cases
are
combined
merely
to
avoid
bloating
the
*
macro
-
inlined
code
,
not
because
they
really
make
much
sense
together
!]
*/
L_ENTRY
(
handle_signal_or_ptrace_return
):
cmp
r18
,
r0
//
See
if
handling
a
signal
bz
1
f
//
...
nope
,
go
do
ptrace
return
//
Handle
a
signal
mov
lp
,
r20
//
Save
link
-
pointer
mov
r10
,
r21
//
Save
return
-
values
(
for
trap
)
mov
r11
,
r22
movea
PTO
,
sp
,
r6
//
Arg
1
:
struct
pt_regs
*
regs
mov
r0
,
r7
//
Arg
2
:
sigset_t
*
oldset
jarl
CSYM
(
do_signal
),
lp
//
Handle
the
signal
di
//
sig
handling
enables
interrupts
mov
r20
,
lp
//
Restore
link
-
pointer
mov
r21
,
r10
//
Restore
return
-
values
(
for
trap
)
mov
r22
,
r11
ld.w
TASK_PTRACE
[
CURRENT_TASK
],
r19
//
check
ptrace
flags
too
cmp
r19
,
r0
bnz
1
f
//
...
some
set
,
so
look
more
2
:
jmp
[
lp
]
//
...
none
set
,
so
return
normally
//
ptrace
return
1
:
ld.w
PTO
+
PT_PSW
[
sp
],
r19
//
Look
at
user
-
processes
's flags
andi
0x0800
,
r19
,
r19
//
See
if
single
-
step
flag
is
set
bz
2
b
//
...
nope
,
return
normally
//
Return
as
if
from
a
dbtrap
insn
st.b
r0
,
KM
//
Now
officially
in
user
state
.
POP_STATE
(
DBTRAP
)
//
Restore
regs
st.w
sp
,
KSP
//
Save
the
kernel
stack
pointer
.
ld.w
PT_GPR
(
GPR_SP
)-
PT_SIZE
[
sp
],
sp
//
Restore
user
stack
pointer
.
DBTRAP_RET
//
Return
from
the
trap
/
interrupt
.
END
(
handle_signal_or_ptrace_return
)
/*
/*
*
This
is
where
we
switch
between
two
threads
.
The
arguments
are
:
*
This
is
where
we
switch
between
two
threads
.
The
arguments
are
:
*
r6
--
pointer
to
the
struct
thread
for
the
`
current
' process
*
r6
--
pointer
to
the
struct
thread
for
the
`
current
' process
*
r7
--
pointer
to
the
struct
thread
for
the
`
new
' process.
*
r7
--
pointer
to
the
struct
thread
for
the
`
new
' process.
*
when
this
function
returns
,
it
will
return
to
the
new
thread
.
*
when
this
function
returns
,
it
will
return
to
the
new
thread
.
*/
*/
C_ENTRY
(
switch_thread
):
C_ENTRY
(
switch_thread
):
...
...
arch/v850/kernel/ptrace.c
View file @
1f8870ed
/*
/*
* arch/v850/kernel/ptrace.c -- `ptrace' system call
* arch/v850/kernel/ptrace.c -- `ptrace' system call
*
*
* Copyright (C) 2002
NEC
Corporation
* Copyright (C) 2002
,03 NEC Electronics
Corporation
* Copyright (C) 2002 Miles Bader <miles@gnu.org>
* Copyright (C) 2002
,03
Miles Bader <miles@gnu.org>
*
*
* Derived from arch/mips/kernel/ptrace.c:
* Derived from arch/mips/kernel/ptrace.c:
*
*
...
@@ -29,6 +29,89 @@
...
@@ -29,6 +29,89 @@
#include <asm/processor.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
/* Returns the address where the register at REG_OFFS in P is stashed away. */
static
v850_reg_t
*
reg_save_addr
(
unsigned
reg_offs
,
struct
task_struct
*
t
)
{
struct
pt_regs
*
regs
;
/* Three basic cases:
(1) A register normally saved before calling the scheduler, is
available in the kernel entry pt_regs structure at the top
of the kernel stack. The kernel trap/irq exit path takes
care to save/restore almost all registers for ptrace'd
processes.
(2) A call-clobbered register, where the process P entered the
kernel via [syscall] trap, is not stored anywhere; that's
OK, because such registers are not expected to be preserved
when the trap returns anyway (so we don't actually bother to
test for this case).
(3) A few registers not used at all by the kernel, and so
normally never saved except by context-switches, are in the
context switch state. */
if
(
reg_offs
==
PT_CTPC
||
reg_offs
==
PT_CTPSW
||
reg_offs
==
PT_CTBP
)
/* Register saved during context switch. */
regs
=
thread_saved_regs
(
t
);
else
/* Register saved during kernel entry (or not available). */
regs
=
task_regs
(
t
);
return
(
v850_reg_t
*
)((
char
*
)
regs
+
reg_offs
);
}
/* Set the bits SET and clear the bits CLEAR in the v850e DIR
(`debug information register'). Returns the new value of DIR. */
static
inline
v850_reg_t
set_dir
(
v850_reg_t
set
,
v850_reg_t
clear
)
{
register
v850_reg_t
rval
asm
(
"r10"
);
register
v850_reg_t
arg0
asm
(
"r6"
)
=
set
;
register
v850_reg_t
arg1
asm
(
"r7"
)
=
clear
;
/* The dbtrap handler has exactly this functionality when called
from kernel mode. 0xf840 is a `dbtrap' insn. */
asm
(
".short 0xf840"
:
"=r"
(
rval
)
:
"r"
(
arg0
),
"r"
(
arg1
));
return
rval
;
}
/* Makes sure hardware single-stepping is (globally) enabled.
Returns true if successful. */
static
inline
int
enable_single_stepping
(
void
)
{
static
int
enabled
=
0
;
/* Remember whether we already did it. */
if
(
!
enabled
)
{
/* Turn on the SE (`single-step enable') bit, 0x100, in the
DIR (`debug information register'). This may fail if a
processor doesn't support it or something. We also try
to clear bit 0x40 (`INI'), which is necessary to use the
debug stuff on the v850e2; on the v850e, clearing 0x40
shouldn't cause any problem. */
v850_reg_t
dir
=
set_dir
(
0x100
,
0x40
);
/* Make sure it really got set. */
if
(
dir
&
0x100
)
enabled
=
1
;
}
return
enabled
;
}
/* Try to set CHILD's single-step flag to VAL. Returns true if successful. */
static
int
set_single_step
(
struct
task_struct
*
t
,
int
val
)
{
v850_reg_t
*
psw_addr
=
reg_save_addr
(
PT_PSW
,
t
);
if
(
val
)
{
/* Make sure single-stepping is enabled. */
if
(
!
enable_single_stepping
())
return
0
;
/* Set T's single-step flag. */
*
psw_addr
|=
0x800
;
}
else
*
psw_addr
&=
~
0x800
;
return
1
;
}
int
sys_ptrace
(
long
request
,
long
pid
,
long
addr
,
long
data
)
int
sys_ptrace
(
long
request
,
long
pid
,
long
addr
,
long
data
)
{
{
struct
task_struct
*
child
;
struct
task_struct
*
child
;
...
@@ -36,12 +119,6 @@ int sys_ptrace(long request, long pid, long addr, long data)
...
@@ -36,12 +119,6 @@ int sys_ptrace(long request, long pid, long addr, long data)
lock_kernel
();
lock_kernel
();
#if 0
printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
(int) request, (int) pid, (unsigned long) addr,
(unsigned long) data);
#endif
if
(
request
==
PTRACE_TRACEME
)
{
if
(
request
==
PTRACE_TRACEME
)
{
/* are we already being traced? */
/* are we already being traced? */
if
(
current
->
ptrace
&
PT_PTRACED
)
{
if
(
current
->
ptrace
&
PT_PTRACED
)
{
...
@@ -81,31 +158,15 @@ int sys_ptrace(long request, long pid, long addr, long data)
...
@@ -81,31 +158,15 @@ int sys_ptrace(long request, long pid, long addr, long data)
goto
out_tsk
;
goto
out_tsk
;
switch
(
request
)
{
switch
(
request
)
{
case
PTRACE_PEEKTEXT
:
/* read word at location addr. */
unsigned
long
val
,
copied
;
case
PTRACE_PEEKDATA
:{
unsigned
long
tmp
;
int
copied
;
copied
=
access_process_vm
(
child
,
addr
,
&
tmp
,
sizeof
(
tmp
),
0
);
case
PTRACE_PEEKTEXT
:
/* read word at location addr. */
case
PTRACE_PEEKDATA
:
copied
=
access_process_vm
(
child
,
addr
,
&
val
,
sizeof
(
val
),
0
);
rval
=
-
EIO
;
rval
=
-
EIO
;
if
(
copied
!=
sizeof
(
tmp
))
if
(
copied
!=
sizeof
(
val
))
break
;
break
;
rval
=
put_user
(
tmp
,(
unsigned
long
*
)
data
);
rval
=
put_user
(
val
,
(
unsigned
long
*
)
data
);
goto
out
;
}
/* Read the word at location addr in the USER area. */
case
PTRACE_PEEKUSR
:
if
(
addr
>=
0
&&
addr
<
PT_SIZE
&&
(
addr
&
0x3
)
==
0
)
{
struct
pt_regs
*
regs
=
task_regs
(
child
);
unsigned
long
val
=
*
(
unsigned
long
*
)((
char
*
)
regs
+
addr
);
rval
=
put_user
(
val
,
(
unsigned
long
*
)
data
);
}
else
{
rval
=
0
;
rval
=
-
EIO
;
}
goto
out
;
goto
out
;
case
PTRACE_POKETEXT
:
/* write the word at location addr. */
case
PTRACE_POKETEXT
:
/* write the word at location addr. */
...
@@ -117,35 +178,62 @@ int sys_ptrace(long request, long pid, long addr, long data)
...
@@ -117,35 +178,62 @@ int sys_ptrace(long request, long pid, long addr, long data)
rval
=
-
EIO
;
rval
=
-
EIO
;
goto
out
;
goto
out
;
/* Read/write the word at location ADDR in the registers. */
case
PTRACE_PEEKUSR
:
case
PTRACE_POKEUSR
:
case
PTRACE_POKEUSR
:
if
(
addr
>=
0
&&
addr
<
PT_SIZE
&&
(
addr
&
0x3
)
==
0
)
{
rval
=
0
;
struct
pt_regs
*
regs
=
task_regs
(
child
);
if
(
addr
>=
PT_SIZE
&&
request
==
PTRACE_PEEKUSR
)
{
unsigned
long
*
loc
=
/* Special requests that don't actually correspond
(
unsigned
long
*
)((
char
*
)
regs
+
addr
);
to offsets in struct pt_regs. */
*
loc
=
data
;
if
(
addr
==
PT_TEXT_ADDR
)
}
else
{
val
=
child
->
mm
->
start_code
;
rval
=
0
;
else
if
(
addr
==
PT_DATA_ADDR
)
rval
=
-
EIO
;
val
=
child
->
mm
->
start_data
;
}
else
if
(
addr
==
PT_TEXT_LEN
)
val
=
child
->
mm
->
end_code
-
child
->
mm
->
start_code
;
else
rval
=
-
EIO
;
}
else
if
(
addr
>=
0
&&
addr
<
PT_SIZE
&&
(
addr
&
0x3
)
==
0
)
{
v850_reg_t
*
reg_addr
=
reg_save_addr
(
addr
,
child
);
if
(
request
==
PTRACE_PEEKUSR
)
val
=
*
reg_addr
;
else
*
reg_addr
=
data
;
}
else
rval
=
-
EIO
;
if
(
rval
==
0
&&
request
==
PTRACE_PEEKUSR
)
rval
=
put_user
(
val
,
(
unsigned
long
*
)
data
);
goto
out
;
goto
out
;
case
PTRACE_SYSCALL
:
/* continue and stop at next (return from) syscall */
/* Continue and stop at next (return from) syscall */
case
PTRACE_CONT
:
/* rvaltart after signal. */
case
PTRACE_SYSCALL
:
/* Restart after a signal. */
case
PTRACE_CONT
:
/* Execute a single instruction. */
case
PTRACE_SINGLESTEP
:
rval
=
-
EIO
;
rval
=
-
EIO
;
if
((
unsigned
long
)
data
>
_NSIG
)
if
((
unsigned
long
)
data
>
_NSIG
)
break
;
break
;
/* Turn CHILD's single-step flag on or off. */
if
(
!
set_single_step
(
child
,
request
==
PTRACE_SINGLESTEP
))
break
;
if
(
request
==
PTRACE_SYSCALL
)
if
(
request
==
PTRACE_SYSCALL
)
set_tsk_thread_flag
(
child
,
TIF_SYSCALL_TRACE
);
set_tsk_thread_flag
(
child
,
TIF_SYSCALL_TRACE
);
else
else
clear_tsk_thread_flag
(
child
,
TIF_SYSCALL_TRACE
);
clear_tsk_thread_flag
(
child
,
TIF_SYSCALL_TRACE
);
child
->
exit_code
=
data
;
child
->
exit_code
=
data
;
wake_up_process
(
child
);
wake_up_process
(
child
);
rval
=
0
;
rval
=
0
;
break
;
break
;
/*
/*
* make the child exit. Best I can do is send it a sigkill.
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* perhaps it should be put in the status that it wants to
* exit.
* exit.
*/
*/
case
PTRACE_KILL
:
case
PTRACE_KILL
:
...
@@ -157,6 +245,7 @@ int sys_ptrace(long request, long pid, long addr, long data)
...
@@ -157,6 +245,7 @@ int sys_ptrace(long request, long pid, long addr, long data)
break
;
break
;
case
PTRACE_DETACH
:
/* detach a process that was attached. */
case
PTRACE_DETACH
:
/* detach a process that was attached. */
set_single_step
(
child
,
0
);
/* Clear single-step flag */
rval
=
ptrace_detach
(
child
,
data
);
rval
=
ptrace_detach
(
child
,
data
);
break
;
break
;
...
@@ -181,7 +270,7 @@ asmlinkage void syscall_trace(void)
...
@@ -181,7 +270,7 @@ asmlinkage void syscall_trace(void)
/* The 0x80 provides a way for the tracing parent to distinguish
/* The 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
between a syscall stop and SIGTRAP delivery */
current
->
exit_code
=
SIGTRAP
|
((
current
->
ptrace
&
PT_TRACESYSGOOD
)
current
->
exit_code
=
SIGTRAP
|
((
current
->
ptrace
&
PT_TRACESYSGOOD
)
?
0x80
:
0
);
?
0x80
:
0
);
current
->
state
=
TASK_STOPPED
;
current
->
state
=
TASK_STOPPED
;
notify_parent
(
current
,
SIGCHLD
);
notify_parent
(
current
,
SIGCHLD
);
schedule
();
schedule
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment