diff options
-rw-r--r-- | sys/ia64/ia64/machdep.c | 24 | ||||
-rw-r--r-- | sys/ia64/ia64/trap.c | 43 |
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: |