summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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