summaryrefslogtreecommitdiffstats
path: root/sys/ia64
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2004-08-08 00:28:07 +0000
committermarcel <marcel@FreeBSD.org>2004-08-08 00:28:07 +0000
commitee3caa0d62d47ad27c287d6311aa5899c9e9aaf9 (patch)
tree7bb6c5891d36bcca258b453e41e88329c47e74c3 /sys/ia64
parent04b83a573c862a789f0b6f20788b4f78aa3504f6 (diff)
downloadFreeBSD-src-ee3caa0d62d47ad27c287d6311aa5899c9e9aaf9.zip
FreeBSD-src-ee3caa0d62d47ad27c287d6311aa5899c9e9aaf9.tar.gz
Implement single stepping when we leave the kernel through the EPC syscall
path. The basic problem is that we cannot set the single stepping flag directly, because we don't leave the kernel via an interrupt return. So, we need another way to set the single stepping flag. The way we do this is by enabling the lower-privilege transfer trap, which gets raised when we drop the privilege level. However, since we're still running in kernel space (sec), we're not yet done. We clear the lower- privilege transfer trap, enable the taken-branch trap and continue exiting the kernel until we branch into user space. Given the current code, there's a total of two traps this way before we can raise SIGTRAP.
Diffstat (limited to 'sys/ia64')
-rw-r--r--sys/ia64/ia64/machdep.c24
-rw-r--r--sys/ia64/ia64/trap.c43
2 files changed, 63 insertions, 4 deletions
diff --git a/sys/ia64/ia64/machdep.c b/sys/ia64/ia64/machdep.c
index 1dfab47..64fc1c0 100644
--- a/sys/ia64/ia64/machdep.c
+++ b/sys/ia64/ia64/machdep.c
@@ -1249,16 +1249,36 @@ ptrace_set_pc(struct thread *td, unsigned long addr)
int
ptrace_single_step(struct thread *td)
{
+ struct trapframe *tf;
- td->td_frame->tf_special.psr |= IA64_PSR_SS;
+ /*
+ * There's no way to set single stepping when we're leaving the
+ * kernel through the EPC syscall path. The way we solve this is
+ * by enabling the lower-privilege trap so that we re-enter the
+ * kernel as soon as the privilege level changes. See trap.c for
+ * how we proceed from there.
+ */
+ tf = td->td_frame;
+ if (tf->tf_flags & FRAME_SYSCALL)
+ tf->tf_special.psr |= IA64_PSR_LP;
+ else
+ tf->tf_special.psr |= IA64_PSR_SS;
return (0);
}
int
ptrace_clear_single_step(struct thread *td)
{
+ struct trapframe *tf;
- td->td_frame->tf_special.psr &= ~IA64_PSR_SS;
+ /*
+ * Clear any and all status bits we may use to implement single
+ * stepping.
+ */
+ tf = td->td_frame;
+ tf->tf_special.psr &= ~IA64_PSR_SS;
+ tf->tf_special.psr &= ~IA64_PSR_LP;
+ tf->tf_special.psr &= ~IA64_PSR_TB;
return (0);
}
diff --git a/sys/ia64/ia64/trap.c b/sys/ia64/ia64/trap.c
index 9c5c615..3a67cff 100644
--- a/sys/ia64/ia64/trap.c
+++ b/sys/ia64/ia64/trap.c
@@ -591,7 +591,6 @@ trap(int vector, struct trapframe *tf)
case IA64_VEC_NAT_CONSUMPTION:
case IA64_VEC_SPECULATION:
case IA64_VEC_UNSUPP_DATA_REFERENCE:
- case IA64_VEC_LOWER_PRIVILEGE_TRANSFER:
if (user) {
ucode = vector;
sig = SIGILL;
@@ -660,7 +659,6 @@ trap(int vector, struct trapframe *tf)
}
case IA64_VEC_DEBUG:
- case IA64_VEC_TAKEN_BRANCH_TRAP:
case IA64_VEC_SINGLE_STEP_TRAP:
tf->tf_special.psr &= ~IA64_PSR_SS;
if (!user) {
@@ -784,6 +782,47 @@ trap(int vector, struct trapframe *tf)
}
}
+ case IA64_VEC_LOWER_PRIVILEGE_TRANSFER:
+ /*
+ * The lower-privilege transfer trap is used by the EPC
+ * syscall code to trigger re-entry into the kernel when the
+ * process should be single stepped. The problem is that
+ * there's no way to set single stepping directly without
+ * using the rfi instruction. So instead we enable the
+ * lower-privilege transfer trap and when we get here we
+ * know that the process is about to enter userland (and
+ * has already lowered its privilege).
+ * However, there's another gotcha. When the process has
+ * lowered it's privilege it's still running in the gateway
+ * page. If we enable single stepping, we'll be stepping
+ * the code in the gateway page. In and by itself this is
+ * not a problem, but it's an address debuggers won't know
+ * anything about. Hence, it can only cause confusion.
+ * We know that we need to branch to get out of the gateway
+ * page, so what we do here is enable the taken branch
+ * trap and just let the process continue. When we branch
+ * out of the gateway page we'll get back into the kernel
+ * and then we enable single stepping.
+ * Since this a rather round-about way of enabling single
+ * stepping, don't make things complicated even more by
+ * calling userret() and do_ast(). We do that later...
+ */
+ tf->tf_special.psr &= ~IA64_PSR_LP;
+ tf->tf_special.psr |= IA64_PSR_TB;
+ return;
+
+ case IA64_VEC_TAKEN_BRANCH_TRAP:
+ /*
+ * Don't assume there aren't any branches other than the
+ * branch that takes us out of the gateway page. Check the
+ * iip and raise SIGTRAP only when it's an user address.
+ */
+ if (tf->tf_special.iip >= VM_MAX_ADDRESS)
+ return;
+ tf->tf_special.psr &= ~IA64_PSR_TB;
+ sig = SIGTRAP;
+ break;
+
case IA64_VEC_IA32_EXCEPTION:
switch ((tf->tf_special.isr >> 16) & 0xffff) {
case IA32_EXCEPTION_DIVIDE:
OpenPOWER on IntegriCloud