X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fia64%2Fkernel%2Fptrace.c;h=370b51b98b495aacabe2b325bf0c8dee8e8cf1f0;hb=a2f44b27303a5353859d77a3e96a1d3f33f56ab7;hp=4ca33dfb8774ae743411ddae2cc855287882d1a9;hpb=70790a4b5cd6c0291e5b1a2836e2832d46036ac6;p=linux-2.6.git diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 4ca33dfb8..370b51b98 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1,29 +1,36 @@ /* * Kernel support for the ptrace() and syscall tracing interfaces. * - * Copyright (C) 1999-2003 Hewlett-Packard Co + * Copyright (C) 1999-2005 Hewlett-Packard Co * David Mosberger-Tang + * Copyright (C) 2006 Intel Co + * 2006-08-12 - IA64 Native Utrace implementation support added by + * Anil S Keshavamurthy * - * Derived from the x86 and Alpha versions. Most of the code in here - * could actually be factored into a common set of routines. + * Derived from the x86 and Alpha versions. */ -#include #include #include #include #include #include #include +#include #include #include #include +#include +#include +#include +#include #include #include #include #include #include #include +#include #include #ifdef CONFIG_PERFMON #include @@ -31,9 +38,6 @@ #include "entry.h" -#define p4 (1UL << 4) /* for pSys (see entry.h) */ -#define p5 (1UL << 5) /* for pNonSys (see entry.h) */ - /* * Bits in the PSR that we allow ptrace() to change: * be, up, ac, mfl, mfh (the user mask; five bits total) @@ -43,9 +47,11 @@ * ri (restart instruction; two bits) * is (instruction set; one bit) */ -#define IPSR_WRITE_MASK \ - (IA64_PSR_UM | IA64_PSR_DB | IA64_PSR_IS | IA64_PSR_ID | IA64_PSR_DD | IA64_PSR_RI) -#define IPSR_READ_MASK IPSR_WRITE_MASK +#define IPSR_MASK (IA64_PSR_UM | IA64_PSR_DB | IA64_PSR_IS \ + | IA64_PSR_ID | IA64_PSR_DD | IA64_PSR_RI) + +#define MASK(nbits) ((1UL << (nbits)) - 1) /* mask with NBITS bits set */ +#define PFM_MASK MASK(38) #define PTRACE_DEBUG 0 @@ -71,23 +77,24 @@ in_syscall (struct pt_regs *pt) unsigned long ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat) { -# define GET_BITS(first, last, unat) \ - ({ \ - unsigned long bit = ia64_unat_pos(&pt->r##first); \ - unsigned long mask = ((1UL << (last - first + 1)) - 1) << first; \ - unsigned long dist; \ - if (bit < first) \ - dist = 64 + bit - first; \ - else \ - dist = bit - first; \ - ia64_rotr(unat, dist) & mask; \ +# define GET_BITS(first, last, unat) \ + ({ \ + unsigned long bit = ia64_unat_pos(&pt->r##first); \ + unsigned long nbits = (last - first + 1); \ + unsigned long mask = MASK(nbits) << first; \ + unsigned long dist; \ + if (bit < first) \ + dist = 64 + bit - first; \ + else \ + dist = bit - first; \ + ia64_rotr(unat, dist) & mask; \ }) unsigned long val; /* - * Registers that are stored consecutively in struct pt_regs can be handled in - * parallel. If the register order in struct_pt_regs changes, this code MUST be - * updated. + * Registers that are stored consecutively in struct pt_regs + * can be handled in parallel. If the register order in + * struct_pt_regs changes, this code MUST be updated. */ val = GET_BITS( 1, 1, scratch_unat); val |= GET_BITS( 2, 3, scratch_unat); @@ -109,23 +116,24 @@ ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat) unsigned long ia64_put_scratch_nat_bits (struct pt_regs *pt, unsigned long nat) { -# define PUT_BITS(first, last, nat) \ - ({ \ - unsigned long bit = ia64_unat_pos(&pt->r##first); \ - unsigned long mask = ((1UL << (last - first + 1)) - 1) << first; \ - long dist; \ - if (bit < first) \ - dist = 64 + bit - first; \ - else \ - dist = bit - first; \ - ia64_rotl(nat & mask, dist); \ +# define PUT_BITS(first, last, nat) \ + ({ \ + unsigned long bit = ia64_unat_pos(&pt->r##first); \ + unsigned long nbits = (last - first + 1); \ + unsigned long mask = MASK(nbits) << first; \ + long dist; \ + if (bit < first) \ + dist = 64 + bit - first; \ + else \ + dist = bit - first; \ + ia64_rotl(nat & mask, dist); \ }) unsigned long scratch_unat; /* - * Registers that are stored consecutively in struct pt_regs can be handled in - * parallel. If the register order in struct_pt_regs changes, this code MUST be - * updated. + * Registers that are stored consecutively in struct pt_regs + * can be handled in parallel. If the register order in + * struct_pt_regs changes, this code MUST be updated. */ scratch_unat = PUT_BITS( 1, 1, nat); scratch_unat |= PUT_BITS( 2, 3, nat); @@ -152,7 +160,7 @@ ia64_increment_ip (struct pt_regs *regs) ri = 0; regs->cr_iip += 16; } else if (ri == 2) { - get_user(w0, (char *) regs->cr_iip + 0); + get_user(w0, (char __user *) regs->cr_iip + 0); if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) { /* * rfi'ing to slot 2 of an MLX bundle causes @@ -174,7 +182,7 @@ ia64_decrement_ip (struct pt_regs *regs) if (ia64_psr(regs)->ri == 0) { regs->cr_iip -= 16; ri = 2; - get_user(w0, (char *) regs->cr_iip + 0); + get_user(w0, (char __user *) regs->cr_iip + 0); if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) { /* * rfi'ing to slot 2 of an MLX bundle causes @@ -188,10 +196,12 @@ ia64_decrement_ip (struct pt_regs *regs) } /* - * This routine is used to read an rnat bits that are stored on the kernel backing store. - * Since, in general, the alignment of the user and kernel are different, this is not - * completely trivial. In essence, we need to construct the user RNAT based on up to two - * kernel RNAT values and/or the RNAT value saved in the child's pt_regs. + * This routine is used to read an rnat bits that are stored on the + * kernel backing store. Since, in general, the alignment of the user + * and kernel are different, this is not completely trivial. In + * essence, we need to construct the user RNAT based on up to two + * kernel RNAT values and/or the RNAT value saved in the child's + * pt_regs. * * user rbs * @@ -224,29 +234,33 @@ ia64_decrement_ip (struct pt_regs *regs) * +--------+ * <--- child_stack->ar_bspstore * - * The way to think of this code is as follows: bit 0 in the user rnat corresponds to some - * bit N (0 <= N <= 62) in one of the kernel rnat value. The kernel rnat value holding - * this bit is stored in variable rnat0. rnat1 is loaded with the kernel rnat value that + * The way to think of this code is as follows: bit 0 in the user rnat + * corresponds to some bit N (0 <= N <= 62) in one of the kernel rnat + * value. The kernel rnat value holding this bit is stored in + * variable rnat0. rnat1 is loaded with the kernel rnat value that * form the upper bits of the user rnat value. * * Boundary cases: * - * o when reading the rnat "below" the first rnat slot on the kernel backing store, - * rnat0/rnat1 are set to 0 and the low order bits are merged in from pt->ar_rnat. + * o when reading the rnat "below" the first rnat slot on the kernel + * backing store, rnat0/rnat1 are set to 0 and the low order bits are + * merged in from pt->ar_rnat. * - * o when reading the rnat "above" the last rnat slot on the kernel backing store, - * rnat0/rnat1 gets its value from sw->ar_rnat. + * o when reading the rnat "above" the last rnat slot on the kernel + * backing store, rnat0/rnat1 gets its value from sw->ar_rnat. */ static unsigned long get_rnat (struct task_struct *task, struct switch_stack *sw, - unsigned long *krbs, unsigned long *urnat_addr, unsigned long *urbs_end) + unsigned long *krbs, unsigned long *urnat_addr, + unsigned long *urbs_end) { - unsigned long rnat0 = 0, rnat1 = 0, urnat = 0, *slot0_kaddr, umask = 0, mask, m; + unsigned long rnat0 = 0, rnat1 = 0, urnat = 0, *slot0_kaddr; + unsigned long umask = 0, mask, m; unsigned long *kbsp, *ubspstore, *rnat0_kaddr, *rnat1_kaddr, shift; long num_regs, nbits; struct pt_regs *pt; - pt = ia64_task_regs(task); + pt = task_pt_regs(task); kbsp = (unsigned long *) sw->ar_bspstore; ubspstore = (unsigned long *) pt->ar_bspstore; @@ -254,11 +268,12 @@ get_rnat (struct task_struct *task, struct switch_stack *sw, nbits = ia64_rse_num_regs(urnat_addr - 63, urbs_end); else nbits = 63; - mask = (1UL << nbits) - 1; + mask = MASK(nbits); /* - * First, figure out which bit number slot 0 in user-land maps to in the kernel - * rnat. Do this by figuring out how many register slots we're beyond the user's - * backingstore and then computing the equivalent address in kernel space. + * First, figure out which bit number slot 0 in user-land maps + * to in the kernel rnat. Do this by figuring out how many + * register slots we're beyond the user's backingstore and + * then computing the equivalent address in kernel space. */ num_regs = ia64_rse_num_regs(ubspstore, urnat_addr + 1); slot0_kaddr = ia64_rse_skip_regs(krbs, num_regs); @@ -268,7 +283,7 @@ get_rnat (struct task_struct *task, struct switch_stack *sw, if (ubspstore + 63 > urnat_addr) { /* some bits need to be merged in from pt->ar_rnat */ - umask = ((1UL << ia64_rse_slot_num(ubspstore)) - 1) & mask; + umask = MASK(ia64_rse_slot_num(ubspstore)) & mask; urnat = (pt->ar_rnat & umask); mask &= ~umask; if (!mask) @@ -304,9 +319,8 @@ put_rnat (struct task_struct *task, struct switch_stack *sw, long num_regs, nbits; struct pt_regs *pt; unsigned long cfm, *urbs_kargs; - struct unw_frame_info info; - pt = ia64_task_regs(task); + pt = task_pt_regs(task); kbsp = (unsigned long *) sw->ar_bspstore; ubspstore = (unsigned long *) pt->ar_bspstore; @@ -316,11 +330,8 @@ put_rnat (struct task_struct *task, struct switch_stack *sw, * If entered via syscall, don't allow user to set rnat bits * for syscall args. */ - unw_init_from_blocked_task(&info,task); - if (unw_unwind_to_user(&info) == 0) { - unw_get_cfm(&info,&cfm); - urbs_kargs = ia64_rse_skip_regs(urbs_end,-(cfm & 0x7f)); - } + cfm = pt->cr_ifs; + urbs_kargs = ia64_rse_skip_regs(urbs_end, -(cfm & 0x7f)); } if (urbs_kargs >= urnat_addr) @@ -330,12 +341,13 @@ put_rnat (struct task_struct *task, struct switch_stack *sw, return; nbits = ia64_rse_num_regs(urnat_addr - 63, urbs_kargs); } - mask = (1UL << nbits) - 1; + mask = MASK(nbits); /* - * First, figure out which bit number slot 0 in user-land maps to in the kernel - * rnat. Do this by figuring out how many register slots we're beyond the user's - * backingstore and then computing the equivalent address in kernel space. + * First, figure out which bit number slot 0 in user-land maps + * to in the kernel rnat. Do this by figuring out how many + * register slots we're beyond the user's backingstore and + * then computing the equivalent address in kernel space. */ num_regs = ia64_rse_num_regs(ubspstore, urnat_addr + 1); slot0_kaddr = ia64_rse_skip_regs(krbs, num_regs); @@ -345,7 +357,7 @@ put_rnat (struct task_struct *task, struct switch_stack *sw, if (ubspstore + 63 > urnat_addr) { /* some bits need to be place in pt->ar_rnat: */ - umask = ((1UL << ia64_rse_slot_num(ubspstore)) - 1) & mask; + umask = MASK(ia64_rse_slot_num(ubspstore)) & mask; pt->ar_rnat = (pt->ar_rnat & ~umask) | (urnat & umask); mask &= ~umask; if (!mask) @@ -371,25 +383,28 @@ put_rnat (struct task_struct *task, struct switch_stack *sw, } static inline int -on_kernel_rbs (unsigned long addr, unsigned long bspstore, unsigned long urbs_end) +on_kernel_rbs (unsigned long addr, unsigned long bspstore, + unsigned long urbs_end) { - return (addr >= bspstore - && addr <= (unsigned long) ia64_rse_rnat_addr((unsigned long *) urbs_end)); + unsigned long *rnat_addr = ia64_rse_rnat_addr((unsigned long *) + urbs_end); + return (addr >= bspstore && addr <= (unsigned long) rnat_addr); } /* - * Read a word from the user-level backing store of task CHILD. ADDR is the user-level - * address to read the word from, VAL a pointer to the return value, and USER_BSP gives - * the end of the user-level backing store (i.e., it's the address that would be in ar.bsp - * after the user executed a "cover" instruction). + * Read a word from the user-level backing store of task CHILD. ADDR + * is the user-level address to read the word from, VAL a pointer to + * the return value, and USER_BSP gives the end of the user-level + * backing store (i.e., it's the address that would be in ar.bsp after + * the user executed a "cover" instruction). * - * This routine takes care of accessing the kernel register backing store for those - * registers that got spilled there. It also takes care of calculating the appropriate - * RNaT collection words. + * This routine takes care of accessing the kernel register backing + * store for those registers that got spilled there. It also takes + * care of calculating the appropriate RNaT collection words. */ long -ia64_peek (struct task_struct *child, struct switch_stack *child_stack, unsigned long user_rbs_end, - unsigned long addr, long *val) +ia64_peek (struct task_struct *child, struct switch_stack *child_stack, + unsigned long user_rbs_end, unsigned long addr, long *val) { unsigned long *bspstore, *krbs, regnum, *laddr, *urbs_end, *rnat_addr; struct pt_regs *child_regs; @@ -398,13 +413,16 @@ ia64_peek (struct task_struct *child, struct switch_stack *child_stack, unsigned urbs_end = (long *) user_rbs_end; laddr = (unsigned long *) addr; - child_regs = ia64_task_regs(child); + child_regs = task_pt_regs(child); bspstore = (unsigned long *) child_regs->ar_bspstore; krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; - if (on_kernel_rbs(addr, (unsigned long) bspstore, (unsigned long) urbs_end)) { + if (on_kernel_rbs(addr, (unsigned long) bspstore, + (unsigned long) urbs_end)) + { /* - * Attempt to read the RBS in an area that's actually on the kernel RBS => - * read the corresponding bits in the kernel RBS. + * Attempt to read the RBS in an area that's actually + * on the kernel RBS => read the corresponding bits in + * the kernel RBS. */ rnat_addr = ia64_rse_rnat_addr(laddr); ret = get_rnat(child, child_stack, krbs, rnat_addr, urbs_end); @@ -417,18 +435,23 @@ ia64_peek (struct task_struct *child, struct switch_stack *child_stack, unsigned if (((1UL << ia64_rse_slot_num(laddr)) & ret) != 0) { /* - * It is implementation dependent whether the data portion of a - * NaT value gets saved on a st8.spill or RSE spill (e.g., see - * EAS 2.6, 4.4.4.6 Register Spill and Fill). To get consistent - * behavior across all possible IA-64 implementations, we return - * zero in this case. + * It is implementation dependent whether the + * data portion of a NaT value gets saved on a + * st8.spill or RSE spill (e.g., see EAS 2.6, + * 4.4.4.6 Register Spill and Fill). To get + * consistent behavior across all possible + * IA-64 implementations, we return zero in + * this case. */ *val = 0; return 0; } if (laddr < urbs_end) { - /* the desired word is on the kernel RBS and is not a NaT */ + /* + * The desired word is on the kernel RBS and + * is not a NaT. + */ regnum = ia64_rse_num_regs(bspstore, laddr); *val = *ia64_rse_skip_regs(krbs, regnum); return 0; @@ -442,74 +465,75 @@ ia64_peek (struct task_struct *child, struct switch_stack *child_stack, unsigned } long -ia64_poke (struct task_struct *child, struct switch_stack *child_stack, unsigned long user_rbs_end, - unsigned long addr, long val) +ia64_poke (struct task_struct *child, struct switch_stack *child_stack, + unsigned long user_rbs_end, unsigned long addr, long val) { - unsigned long *bspstore, *krbs, regnum, *laddr, *urbs_end = (long *) user_rbs_end; + unsigned long *bspstore, *krbs, regnum, *laddr; + unsigned long *urbs_end = (long *) user_rbs_end; struct pt_regs *child_regs; laddr = (unsigned long *) addr; - child_regs = ia64_task_regs(child); + child_regs = task_pt_regs(child); bspstore = (unsigned long *) child_regs->ar_bspstore; krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; - if (on_kernel_rbs(addr, (unsigned long) bspstore, (unsigned long) urbs_end)) { + if (on_kernel_rbs(addr, (unsigned long) bspstore, + (unsigned long) urbs_end)) + { /* - * Attempt to write the RBS in an area that's actually on the kernel RBS - * => write the corresponding bits in the kernel RBS. + * Attempt to write the RBS in an area that's actually + * on the kernel RBS => write the corresponding bits + * in the kernel RBS. */ if (ia64_rse_is_rnat_slot(laddr)) - put_rnat(child, child_stack, krbs, laddr, val, urbs_end); + put_rnat(child, child_stack, krbs, laddr, val, + urbs_end); else { if (laddr < urbs_end) { regnum = ia64_rse_num_regs(bspstore, laddr); *ia64_rse_skip_regs(krbs, regnum) = val; } } - } else if (access_process_vm(child, addr, &val, sizeof(val), 1) != sizeof(val)) { + } else if (access_process_vm(child, addr, &val, sizeof(val), 1) + != sizeof(val)) return -EIO; - } return 0; } /* - * Calculate the address of the end of the user-level register backing store. This is the - * address that would have been stored in ar.bsp if the user had executed a "cover" - * instruction right before entering the kernel. If CFMP is not NULL, it is used to - * return the "current frame mask" that was active at the time the kernel was entered. + * Calculate the address of the end of the user-level register backing + * store. This is the address that would have been stored in ar.bsp + * if the user had executed a "cover" instruction right before + * entering the kernel. If CFMP is not NULL, it is used to return the + * "current frame mask" that was active at the time the kernel was + * entered. */ unsigned long -ia64_get_user_rbs_end (struct task_struct *child, struct pt_regs *pt, unsigned long *cfmp) +ia64_get_user_rbs_end (struct task_struct *child, struct pt_regs *pt, + unsigned long *cfmp) { - unsigned long *krbs, *bspstore, cfm; - struct unw_frame_info info; + unsigned long *krbs, *bspstore, cfm = pt->cr_ifs; long ndirty; krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; bspstore = (unsigned long *) pt->ar_bspstore; ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19)); - cfm = pt->cr_ifs & ~(1UL << 63); - if (in_syscall(pt)) { - /* - * If bit 63 of cr.ifs is cleared, the kernel was entered via a system - * call and we need to recover the CFM that existed on entry to the - * kernel by unwinding the kernel stack. - */ - unw_init_from_blocked_task(&info, child); - if (unw_unwind_to_user(&info) == 0) { - unw_get_cfm(&info, &cfm); - ndirty += (cfm & 0x7f); - } - } + if (in_syscall(pt)) + ndirty += (cfm & 0x7f); + else + cfm &= ~(1UL << 63); /* clear valid bit */ + if (cfmp) *cfmp = cfm; return (unsigned long) ia64_rse_skip_regs(bspstore, ndirty); } /* - * Synchronize (i.e, write) the RSE backing store living in kernel space to the VM of the - * CHILD task. SW and PT are the pointers to the switch_stack and pt_regs structures, - * respectively. USER_RBS_END is the user-level address at which the backing store ends. + * Synchronize (i.e, write) the RSE backing store living in kernel + * space to the VM of the CHILD task. SW and PT are the pointers to + * the switch_stack and pt_regs structures, respectively. + * USER_RBS_END is the user-level address at which the backing store + * ends. */ long ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw, @@ -523,101 +547,32 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw, ret = ia64_peek(child, sw, user_rbs_end, addr, &val); if (ret < 0) return ret; - if (access_process_vm(child, addr, &val, sizeof(val), 1) != sizeof(val)) + if (access_process_vm(child, addr, &val, sizeof(val), 1) + != sizeof(val)) return -EIO; } return 0; } -static inline int -thread_matches (struct task_struct *thread, unsigned long addr) -{ - unsigned long thread_rbs_end; - struct pt_regs *thread_regs; - - if (ptrace_check_attach(thread, 0) < 0) - /* - * If the thread is not in an attachable state, we'll ignore it. - * The net effect is that if ADDR happens to overlap with the - * portion of the thread's register backing store that is - * currently residing on the thread's kernel stack, then ptrace() - * may end up accessing a stale value. But if the thread isn't - * stopped, that's a problem anyhow, so we're doing as well as we - * can... - */ - return 0; - - thread_regs = ia64_task_regs(thread); - thread_rbs_end = ia64_get_user_rbs_end(thread, thread_regs, NULL); - if (!on_kernel_rbs(addr, thread_regs->ar_bspstore, thread_rbs_end)) - return 0; - - return 1; /* looks like we've got a winner */ -} - -/* - * GDB apparently wants to be able to read the register-backing store of any thread when - * attached to a given process. If we are peeking or poking an address that happens to - * reside in the kernel-backing store of another thread, we need to attach to that thread, - * because otherwise we end up accessing stale data. - * - * task_list_lock must be read-locked before calling this routine! - */ -static struct task_struct * -find_thread_for_addr (struct task_struct *child, unsigned long addr) -{ - struct task_struct *g, *p; - struct mm_struct *mm; - int mm_users; - - if (!(mm = get_task_mm(child))) - return child; - - mm_users = atomic_read(&mm->mm_users) - 1; /* -1 because of our get_task_mm()... */ - if (mm_users <= 1) - goto out; /* not multi-threaded */ - - /* - * First, traverse the child's thread-list. Good for scalability with - * NPTL-threads. - */ - p = child; - do { - if (thread_matches(p, addr)) { - child = p; - goto out; - } - if (mm_users-- <= 1) - goto out; - } while ((p = next_thread(p)) != child); - - do_each_thread(g, p) { - if (child->mm != mm) - continue; - - if (thread_matches(p, addr)) { - child = p; - goto out; - } - } while_each_thread(g, p); - out: - mmput(mm); - return child; -} - /* * Write f32-f127 back to task->thread.fph if it has been modified. */ inline void ia64_flush_fph (struct task_struct *task) { - struct ia64_psr *psr = ia64_psr(ia64_task_regs(task)); + struct ia64_psr *psr = ia64_psr(task_pt_regs(task)); + /* + * Prevent migrating this task while + * we're fiddling with the FPU state + */ + preempt_disable(); if (ia64_is_local_fpu_owner(task) && psr->mfh) { psr->mfh = 0; task->thread.flags |= IA64_THREAD_FPH_VALID; ia64_save_fpu(&task->thread.fph[0]); } + preempt_enable(); } /* @@ -631,7 +586,7 @@ ia64_flush_fph (struct task_struct *task) void ia64_sync_fph (struct task_struct *task) { - struct ia64_psr *psr = ia64_psr(ia64_task_regs(task)); + struct ia64_psr *psr = ia64_psr(task_pt_regs(task)); ia64_flush_fph(task); if (!(task->thread.flags & IA64_THREAD_FPH_VALID)) { @@ -642,8 +597,10 @@ ia64_sync_fph (struct task_struct *task) psr->dfh = 1; } +#if 0 static int -access_fr (struct unw_frame_info *info, int regnum, int hi, unsigned long *data, int write_access) +access_fr (struct unw_frame_info *info, int regnum, int hi, + unsigned long *data, int write_access) { struct ia64_fpreg fpval; int ret; @@ -659,853 +616,1055 @@ access_fr (struct unw_frame_info *info, int regnum, int hi, unsigned long *data, *data = fpval.u.bits[hi]; return ret; } +#endif /* access_fr() */ /* * Change the machine-state of CHILD such that it will return via the normal * kernel exit-path, rather than the syscall-exit path. */ static void -convert_to_non_syscall (struct task_struct *child, struct pt_regs *pt, unsigned long cfm) +convert_to_non_syscall (struct task_struct *child, struct pt_regs *pt, + unsigned long cfm) { struct unw_frame_info info, prev_info; - unsigned long ip, pr; + unsigned long ip, sp, pr; unw_init_from_blocked_task(&info, child); while (1) { prev_info = info; if (unw_unwind(&info) < 0) return; - if (unw_get_rp(&info, &ip) < 0) + + unw_get_sp(&info, &sp); + if ((long)((unsigned long)child + IA64_STK_OFFSET - sp) + < IA64_PT_REGS_SIZE) { + dprintk("ptrace.%s: ran off the top of the kernel " + "stack\n", __FUNCTION__); + return; + } + if (unw_get_pr (&prev_info, &pr) < 0) { + unw_get_rp(&prev_info, &ip); + dprintk("ptrace.%s: failed to read " + "predicate register (ip=0x%lx)\n", + __FUNCTION__, ip); return; - if (ip < FIXADDR_USER_END) + } + if (unw_is_intr_frame(&info) + && (pr & (1UL << PRED_USER_STACK))) break; } + /* + * Note: at the time of this call, the target task is blocked + * in notify_resume_user() and by clearling PRED_LEAVE_SYSCALL + * (aka, "pLvSys") we redirect execution from + * .work_pending_syscall_end to .work_processed_kernel. + */ unw_get_pr(&prev_info, &pr); - pr &= ~pSys; - pr |= pNonSys; + pr &= ~((1UL << PRED_SYSCALL) | (1UL << PRED_LEAVE_SYSCALL)); + pr |= (1UL << PRED_NON_SYSCALL); unw_set_pr(&prev_info, pr); pt->cr_ifs = (1UL << 63) | cfm; + /* + * Clear the memory that is NOT written on syscall-entry to + * ensure we do not leak kernel-state to user when execution + * resumes. + */ + pt->r2 = 0; + pt->r3 = 0; + pt->r14 = 0; + memset(&pt->r16, 0, 16*8); /* clear r16-r31 */ + memset(&pt->f6, 0, 6*16); /* clear f6-f11 */ + pt->b7 = 0; + pt->ar_ccv = 0; + pt->ar_csd = 0; + pt->ar_ssd = 0; } static int -access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data, int write_access) +access_nat_bits (struct task_struct *child, struct pt_regs *pt, + struct unw_frame_info *info, + unsigned long *data, int write_access) { - unsigned long *ptr, regnum, urbs_end, rnat_addr, cfm; - struct switch_stack *sw; - struct pt_regs *pt; - - pt = ia64_task_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - - if ((addr & 0x7) != 0) { - dprintk("ptrace: unaligned register address 0x%lx\n", addr); - return -1; - } + unsigned long regnum, nat_bits, scratch_unat, dummy = 0; + char nat = 0; - if (addr < PT_F127 + 16) { - /* accessing fph */ - if (write_access) - ia64_sync_fph(child); - else - ia64_flush_fph(child); - ptr = (unsigned long *) ((unsigned long) &child->thread.fph + addr); - } else if ((addr >= PT_F10) && (addr < PT_F11 + 16)) { - /* scratch registers untouched by kernel (saved in pt_regs) */ - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, f10) + addr - PT_F10); - } else if (addr >= PT_F12 && addr < PT_F15 + 16) { - /* scratch registers untouched by kernel (saved in switch_stack) */ - ptr = (unsigned long *) ((long) sw + (addr - PT_NAT_BITS - 32)); - } else if (addr < PT_AR_LC + 8) { - /* preserved state: */ - unsigned long nat_bits, scratch_unat, dummy = 0; - struct unw_frame_info info; - char nat = 0; - int ret; - - unw_init_from_blocked_task(&info, child); - if (unw_unwind_to_user(&info) < 0) + if (write_access) { + nat_bits = *data; + scratch_unat = ia64_put_scratch_nat_bits(pt, nat_bits); + if (unw_set_ar(info, UNW_AR_UNAT, scratch_unat) < 0) { + dprintk("ptrace: failed to set ar.unat\n"); return -1; - - switch (addr) { - case PT_NAT_BITS: - if (write_access) { - nat_bits = *data; - scratch_unat = ia64_put_scratch_nat_bits(pt, nat_bits); - if (unw_set_ar(&info, UNW_AR_UNAT, scratch_unat) < 0) { - dprintk("ptrace: failed to set ar.unat\n"); - return -1; - } - for (regnum = 4; regnum <= 7; ++regnum) { - unw_get_gr(&info, regnum, &dummy, &nat); - unw_set_gr(&info, regnum, dummy, (nat_bits >> regnum) & 1); - } - } else { - if (unw_get_ar(&info, UNW_AR_UNAT, &scratch_unat) < 0) { - dprintk("ptrace: failed to read ar.unat\n"); - return -1; - } - nat_bits = ia64_get_scratch_nat_bits(pt, scratch_unat); - for (regnum = 4; regnum <= 7; ++regnum) { - unw_get_gr(&info, regnum, &dummy, &nat); - nat_bits |= (nat != 0) << regnum; - } - *data = nat_bits; - } - return 0; - - case PT_R4: case PT_R5: case PT_R6: case PT_R7: - if (write_access) { - /* read NaT bit first: */ - unsigned long dummy; - - ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4, &dummy, &nat); - if (ret < 0) - return ret; - } - return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data, &nat, - write_access); - - case PT_B1: case PT_B2: case PT_B3: case PT_B4: case PT_B5: - return unw_access_br(&info, (addr - PT_B1)/8 + 1, data, write_access); - - case PT_AR_EC: - return unw_access_ar(&info, UNW_AR_EC, data, write_access); - - case PT_AR_LC: - return unw_access_ar(&info, UNW_AR_LC, data, write_access); - - default: - if (addr >= PT_F2 && addr < PT_F5 + 16) - return access_fr(&info, (addr - PT_F2)/16 + 2, (addr & 8) != 0, - data, write_access); - else if (addr >= PT_F16 && addr < PT_F31 + 16) - return access_fr(&info, (addr - PT_F16)/16 + 16, (addr & 8) != 0, - data, write_access); - else { - dprintk("ptrace: rejecting access to register address 0x%lx\n", - addr); - return -1; - } } - } else if (addr < PT_F9+16) { - /* scratch state */ - switch (addr) { - case PT_AR_BSP: - /* - * By convention, we use PT_AR_BSP to refer to the end of the user-level - * backing store. Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) to get - * the real value of ar.bsp at the time the kernel was entered. - * - * Furthermore, when changing the contents of PT_AR_BSP (or - * PT_CFM) we MUST copy any users-level stacked registers that are - * stored on the kernel stack back to user-space because - * otherwise, we might end up clobbering kernel stacked registers. - * Also, if this happens while the task is blocked in a system - * call, which convert the state such that the non-system-call - * exit path is used. This ensures that the proper state will be - * picked up when resuming execution. However, it *also* means - * that once we write PT_AR_BSP/PT_CFM, it won't be possible to - * modify the syscall arguments of the pending system call any - * longer. This shouldn't be an issue because modifying - * PT_AR_BSP/PT_CFM generally implies that we're either abandoning - * the pending system call or that we defer it's re-execution - * (e.g., due to GDB doing an inferior function call). - */ - urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); - if (write_access) { - if (*data != urbs_end) { - if (ia64_sync_user_rbs(child, sw, - pt->ar_bspstore, urbs_end) < 0) - return -1; - if (in_syscall(pt)) - convert_to_non_syscall(child, pt, cfm); - /* simulate user-level write of ar.bsp: */ - pt->loadrs = 0; - pt->ar_bspstore = *data; - } - } else - *data = urbs_end; - return 0; - - case PT_CFM: - urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); - if (write_access) { - if (((cfm ^ *data) & 0x3fffffffffU) != 0) { - if (ia64_sync_user_rbs(child, sw, - pt->ar_bspstore, urbs_end) < 0) - return -1; - if (in_syscall(pt)) - convert_to_non_syscall(child, pt, cfm); - pt->cr_ifs = ((pt->cr_ifs & ~0x3fffffffffUL) - | (*data & 0x3fffffffffUL)); - } - } else - *data = cfm; - return 0; - - case PT_CR_IPSR: - if (write_access) - pt->cr_ipsr = ((*data & IPSR_WRITE_MASK) - | (pt->cr_ipsr & ~IPSR_WRITE_MASK)); - else - *data = (pt->cr_ipsr & IPSR_READ_MASK); - return 0; - - case PT_AR_RNAT: - urbs_end = ia64_get_user_rbs_end(child, pt, NULL); - rnat_addr = (long) ia64_rse_rnat_addr((long *) urbs_end); - if (write_access) - return ia64_poke(child, sw, urbs_end, rnat_addr, *data); - else - return ia64_peek(child, sw, urbs_end, rnat_addr, data); - - case PT_R1: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, r1)); - break; - - case PT_R2: case PT_R3: - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, r2) + addr - PT_R2); - break; - case PT_R8: case PT_R9: case PT_R10: case PT_R11: - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, r8)+ addr - PT_R8); - break; - case PT_R12: case PT_R13: - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, r12)+ addr - PT_R12); - break; - case PT_R14: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, r14)); - break; - case PT_R15: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, r15)); - break; - case PT_R16: case PT_R17: case PT_R18: case PT_R19: - case PT_R20: case PT_R21: case PT_R22: case PT_R23: - case PT_R24: case PT_R25: case PT_R26: case PT_R27: - case PT_R28: case PT_R29: case PT_R30: case PT_R31: - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, r16) + addr - PT_R16); - break; - case PT_B0: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, b0)); - break; - case PT_B6: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, b6)); - break; - case PT_B7: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, b7)); - break; - case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8: - case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8: - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, f6) + addr - PT_F6); - break; - case PT_AR_BSPSTORE: - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, ar_bspstore)); - break; - case PT_AR_RSC: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, ar_rsc)); - break; - case PT_AR_UNAT: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, ar_unat)); - break; - case PT_AR_PFS: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, ar_pfs)); - break; - case PT_AR_CCV: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, ar_ccv)); - break; - case PT_AR_FPSR: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, ar_fpsr)); - break; - case PT_CR_IIP: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, cr_iip)); - break; - case PT_PR: - ptr = (unsigned long *) ((long) pt + offsetof(struct pt_regs, pr)); - break; - /* scratch register */ - - default: - /* disallow accessing anything else... */ - dprintk("ptrace: rejecting access to register address 0x%lx\n", - addr); - return -1; + for (regnum = 4; regnum <= 7; ++regnum) { + unw_get_gr(info, regnum, &dummy, &nat); + unw_set_gr(info, regnum, dummy, + (nat_bits >> regnum) & 1); } - } else if (addr <= PT_AR_SSD) { - ptr = (unsigned long *) - ((long) pt + offsetof(struct pt_regs, ar_csd) + addr - PT_AR_CSD); } else { - /* access debug registers */ - - if (addr >= PT_IBR) { - regnum = (addr - PT_IBR) >> 3; - ptr = &child->thread.ibr[0]; - } else { - regnum = (addr - PT_DBR) >> 3; - ptr = &child->thread.dbr[0]; - } - - if (regnum >= 8) { - dprintk("ptrace: rejecting access to register address 0x%lx\n", addr); + if (unw_get_ar(info, UNW_AR_UNAT, &scratch_unat) < 0) { + dprintk("ptrace: failed to read ar.unat\n"); return -1; } -#ifdef CONFIG_PERFMON - /* - * Check if debug registers are used by perfmon. This test must be done - * once we know that we can do the operation, i.e. the arguments are all - * valid, but before we start modifying the state. - * - * Perfmon needs to keep a count of how many processes are trying to - * modify the debug registers for system wide monitoring sessions. - * - * We also include read access here, because they may cause the - * PMU-installed debug register state (dbr[], ibr[]) to be reset. The two - * arrays are also used by perfmon, but we do not use - * IA64_THREAD_DBG_VALID. The registers are restored by the PMU context - * switch code. - */ - if (pfm_use_debug_registers(child)) return -1; -#endif - - if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { - child->thread.flags |= IA64_THREAD_DBG_VALID; - memset(child->thread.dbr, 0, sizeof(child->thread.dbr)); - memset(child->thread.ibr, 0, sizeof(child->thread.ibr)); + nat_bits = ia64_get_scratch_nat_bits(pt, scratch_unat); + for (regnum = 4; regnum <= 7; ++regnum) { + unw_get_gr(info, regnum, &dummy, &nat); + nat_bits |= (nat != 0) << regnum; } - - ptr += regnum; - - if (write_access) - /* don't let the user set kernel-level breakpoints... */ - *ptr = *data & ~(7UL << 56); - else - *data = *ptr; - return 0; + *data = nat_bits; } - if (write_access) - *ptr = *data; - else - *data = *ptr; return 0; } -static long -ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr) -{ - struct switch_stack *sw; - struct pt_regs *pt; - long ret, retval; - struct unw_frame_info info; - char nat = 0; - int i; - - retval = verify_area(VERIFY_WRITE, ppr, sizeof(struct pt_all_user_regs)); - if (retval != 0) { - return -EIO; - } - - pt = ia64_task_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - unw_init_from_blocked_task(&info, child); - if (unw_unwind_to_user(&info) < 0) { - return -EIO; - } - if (((unsigned long) ppr & 0x7) != 0) { - dprintk("ptrace:unaligned register address %p\n", ppr); - return -EIO; - } +/* "asmlinkage" so the input arguments are preserved... */ - retval = 0; +asmlinkage void +syscall_trace_enter (long arg0, long arg1, long arg2, long arg3, + long arg4, long arg5, long arg6, long arg7, + struct pt_regs regs) +{ + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(®s, 0); - /* control regs */ + if (unlikely(current->audit_context)) { + long syscall; + int arch; - retval |= __put_user(pt->cr_iip, &ppr->cr_iip); - retval |= access_uarea(child, PT_CR_IPSR, &ppr->cr_ipsr, 0); + if (IS_IA32_PROCESS(®s)) { + syscall = regs.r1; + arch = AUDIT_ARCH_I386; + } else { + syscall = regs.r15; + arch = AUDIT_ARCH_IA64; + } - /* app regs */ + audit_syscall_entry(arch, syscall, arg0, arg1, arg2, arg3); + } - retval |= __put_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]); - retval |= __put_user(pt->ar_rsc, &ppr->ar[PT_AUR_RSC]); - retval |= __put_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]); - retval |= __put_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]); - retval |= __put_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]); - retval |= __put_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]); +} - retval |= access_uarea(child, PT_AR_EC, &ppr->ar[PT_AUR_EC], 0); - retval |= access_uarea(child, PT_AR_LC, &ppr->ar[PT_AUR_LC], 0); - retval |= access_uarea(child, PT_AR_RNAT, &ppr->ar[PT_AUR_RNAT], 0); - retval |= access_uarea(child, PT_AR_BSP, &ppr->ar[PT_AUR_BSP], 0); - retval |= access_uarea(child, PT_CFM, &ppr->cfm, 0); +/* "asmlinkage" so the input arguments are preserved... */ - /* gr1-gr3 */ +asmlinkage void +syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, + long arg4, long arg5, long arg6, long arg7, + struct pt_regs regs) +{ + if (unlikely(current->audit_context)) { + int success = AUDITSC_RESULT(regs.r10); + long result = regs.r8; - retval |= __copy_to_user(&ppr->gr[1], &pt->r1, sizeof(long)); - retval |= __copy_to_user(&ppr->gr[2], &pt->r2, sizeof(long) *2); + if (success != AUDITSC_SUCCESS) + result = -result; + audit_syscall_exit(success, result); + } - /* gr4-gr7 */ + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(®s, 1); - for (i = 4; i < 8; i++) { - retval |= unw_access_gr(&info, i, &ppr->gr[i], &nat, 0); + if (test_thread_flag(TIF_SINGLESTEP)) { + force_sig(SIGTRAP, current); /* XXX */ + tracehook_report_syscall_step(®s); } +} - /* gr8-gr11 */ - - retval |= __copy_to_user(&ppr->gr[8], &pt->r8, sizeof(long) * 4); - /* gr12-gr15 */ +#ifdef CONFIG_UTRACE - retval |= __copy_to_user(&ppr->gr[12], &pt->r12, sizeof(long) * 2); - retval |= __copy_to_user(&ppr->gr[14], &pt->r14, sizeof(long)); - retval |= __copy_to_user(&ppr->gr[15], &pt->r15, sizeof(long)); +/* Utrace implementation starts here */ - /* gr16-gr31 */ +typedef struct utrace_get { + void *kbuf; + void __user *ubuf; +} utrace_get_t; - retval |= __copy_to_user(&ppr->gr[16], &pt->r16, sizeof(long) * 16); +typedef struct utrace_set { + const void *kbuf; + const void __user *ubuf; +} utrace_set_t; - /* b0 */ +typedef struct utrace_getset { + struct task_struct *target; + const struct utrace_regset *regset; + union { + utrace_get_t get; + utrace_set_t set; + } u; + unsigned int pos; + unsigned int count; + int ret; +} utrace_getset_t; - retval |= __put_user(pt->b0, &ppr->br[0]); +static int +access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info, + unsigned long addr, unsigned long *data, int write_access) +{ + struct pt_regs *pt; + unsigned long *ptr = NULL; + int ret; + char nat=0; - /* b1-b5 */ + pt = task_pt_regs(target); + switch (addr) { + case ELF_GR_OFFSET(1): + ptr = &pt->r1; + break; + case ELF_GR_OFFSET(2): + case ELF_GR_OFFSET(3): + ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2)); + break; + case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7): + if (write_access) { + /* read NaT bit first: */ + unsigned long dummy; - for (i = 1; i < 6; i++) { - retval |= unw_access_br(&info, i, &ppr->br[i], 0); + ret = unw_get_gr(info, addr/8, &dummy, &nat); + if (ret < 0) + return ret; + } + return unw_access_gr(info, addr/8, data, &nat, write_access); + case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11): + ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8); + break; + case ELF_GR_OFFSET(12): + case ELF_GR_OFFSET(13): + ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12); + break; + case ELF_GR_OFFSET(14): + ptr = &pt->r14; + break; + case ELF_GR_OFFSET(15): + ptr = &pt->r15; } + if (write_access) + *ptr = *data; + else + *data = *ptr; + return 0; +} - /* b6-b7 */ - - retval |= __put_user(pt->b6, &ppr->br[6]); - retval |= __put_user(pt->b7, &ppr->br[7]); - - /* fr2-fr5 */ +static int +access_elf_breg(struct task_struct *target, struct unw_frame_info *info, + unsigned long addr, unsigned long *data, int write_access) +{ + struct pt_regs *pt; + unsigned long *ptr = NULL; - for (i = 2; i < 6; i++) { - retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 0); - retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 0); + pt = task_pt_regs(target); + switch (addr) { + case ELF_BR_OFFSET(0): + ptr = &pt->b0; + break; + case ELF_BR_OFFSET(1) ... ELF_BR_OFFSET(5): + return unw_access_br(info, (addr - ELF_BR_OFFSET(0))/8, + data, write_access); + case ELF_BR_OFFSET(6): + ptr = &pt->b6; + break; + case ELF_BR_OFFSET(7): + ptr = &pt->b7; } + if (write_access) + *ptr = *data; + else + *data = *ptr; + return 0; +} + +static int +access_elf_areg(struct task_struct *target, struct unw_frame_info *info, + unsigned long addr, unsigned long *data, int write_access) +{ + struct pt_regs *pt; + unsigned long cfm, urbs_end, rnat_addr; + unsigned long *ptr = NULL; - /* fr6-fr11 */ + pt = task_pt_regs(target); + if (addr >= ELF_AR_RSC_OFFSET && addr <= ELF_AR_SSD_OFFSET) { + switch (addr) { + case ELF_AR_RSC_OFFSET: + /* force PL3 */ + if (write_access) + pt->ar_rsc = *data | (3 << 2); + else + *data = pt->ar_rsc; + return 0; + case ELF_AR_BSP_OFFSET: + /* + * By convention, we use PT_AR_BSP to refer to + * the end of the user-level backing store. + * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) + * to get the real value of ar.bsp at the time + * the kernel was entered. + * + * Furthermore, when changing the contents of + * PT_AR_BSP (or PT_CFM) we MUST copy any + * users-level stacked registers that are + * stored on the kernel stack back to + * user-space because otherwise, we might end + * up clobbering kernel stacked registers. + * Also, if this happens while the task is + * blocked in a system call, which convert the + * state such that the non-system-call exit + * path is used. This ensures that the proper + * state will be picked up when resuming + * execution. However, it *also* means that + * once we write PT_AR_BSP/PT_CFM, it won't be + * possible to modify the syscall arguments of + * the pending system call any longer. This + * shouldn't be an issue because modifying + * PT_AR_BSP/PT_CFM generally implies that + * we're either abandoning the pending system + * call or that we defer it's re-execution + * (e.g., due to GDB doing an inferior + * function call). + */ + urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); + if (write_access) { + if (*data != urbs_end) { + if (ia64_sync_user_rbs(target, info->sw, + pt->ar_bspstore, + urbs_end) < 0) + return -1; + if (in_syscall(pt)) + convert_to_non_syscall(target, + pt, + cfm); + /* + * Simulate user-level write + * of ar.bsp: + */ + pt->loadrs = 0; + pt->ar_bspstore = *data; + } + } else + *data = urbs_end; + return 0; + case ELF_AR_BSPSTORE_OFFSET: // ar_bsp_store + ptr = &pt->ar_bspstore; + break; + case ELF_AR_RNAT_OFFSET: // ar_rnat + urbs_end = ia64_get_user_rbs_end(target, pt, NULL); + rnat_addr = (long) ia64_rse_rnat_addr((long *) + urbs_end); + if (write_access) + return ia64_poke(target, info->sw, urbs_end, + rnat_addr, *data); + else + return ia64_peek(target, info->sw, urbs_end, + rnat_addr, data); + case ELF_AR_CCV_OFFSET: // ar_ccv + ptr = &pt->ar_ccv; + break; + case ELF_AR_UNAT_OFFSET: // ar_unat + ptr = &pt->ar_unat; + break; + case ELF_AR_FPSR_OFFSET: // ar_fpsr + ptr = &pt->ar_fpsr; + break; + case ELF_AR_PFS_OFFSET: // ar_pfs + ptr = &pt->ar_pfs; + break; + case ELF_AR_LC_OFFSET: // ar_lc + return unw_access_ar(info, UNW_AR_LC, data, + write_access); + case ELF_AR_EC_OFFSET: // ar_ec + return unw_access_ar(info, UNW_AR_EC, data, + write_access); + case ELF_AR_CSD_OFFSET: // ar_csd + ptr = &pt->ar_csd; + break; + case ELF_AR_SSD_OFFSET: // ar_ssd + ptr = &pt->ar_ssd; + } + } else if (addr >= ELF_CR_IIP_OFFSET && addr <= ELF_CR_IPSR_OFFSET) { + switch (addr) { + case ELF_CR_IIP_OFFSET: + ptr = &pt->cr_iip; + break; + case ELF_CFM_OFFSET: + urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); + if (write_access) { + if (((cfm ^ *data) & PFM_MASK) != 0) { + if (ia64_sync_user_rbs(target, info->sw, + pt->ar_bspstore, + urbs_end) < 0) + return -1; + if (in_syscall(pt)) + convert_to_non_syscall(target, + pt, + cfm); + pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) + | (*data & PFM_MASK)); + } + } else + *data = cfm; + return 0; + case ELF_CR_IPSR_OFFSET: + if (write_access) + pt->cr_ipsr = ((*data & IPSR_MASK) + | (pt->cr_ipsr & ~IPSR_MASK)); + else + *data = (pt->cr_ipsr & IPSR_MASK); + return 0; + } + } else if (addr == ELF_NAT_OFFSET) + return access_nat_bits(target, pt, info, + data, write_access); + else if (addr == ELF_PR_OFFSET) + ptr = &pt->pr; + else + return -1; - retval |= __copy_to_user(&ppr->fr[6], &pt->f6, sizeof(struct ia64_fpreg) * 6); + if (write_access) + *ptr = *data; + else + *data = *ptr; - /* fp scratch regs(12-15) */ + return 0; +} - retval |= __copy_to_user(&ppr->fr[12], &sw->f12, sizeof(struct ia64_fpreg) * 4); +static int +access_elf_reg(struct task_struct *target, struct unw_frame_info *info, + unsigned long addr, unsigned long *data, int write_access) +{ + if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15)) + return access_elf_gpreg(target, info, addr, data, write_access); + else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7)) + return access_elf_breg(target, info, addr, data, write_access); + else + return access_elf_areg(target, info, addr, data, write_access); +} - /* fr16-fr31 */ +void do_gpregs_get(struct unw_frame_info *info, void *arg) +{ + struct pt_regs *pt; + utrace_getset_t *dst = arg; + elf_greg_t tmp[16]; + unsigned int i, index, min_copy; - for (i = 16; i < 32; i++) { - retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 0); - retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 0); - } + if (unw_unwind_to_user(info) < 0) + return; - /* fph */ + /* + * coredump format: + * r0-r31 + * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT) + * predicate registers (p0-p63) + * b0-b7 + * ip cfm user-mask + * ar.rsc ar.bsp ar.bspstore ar.rnat + * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec + */ - ia64_flush_fph(child); - retval |= __copy_to_user(&ppr->fr[32], &child->thread.fph, sizeof(ppr->fr[32]) * 96); - /* preds */ + /* Skip r0 */ + if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { + dst->ret = utrace_regset_copyout_zero(&dst->pos, &dst->count, + &dst->u.get.kbuf, + &dst->u.get.ubuf, + 0, ELF_GR_OFFSET(1)); + if (dst->ret || dst->count == 0) + return; + } - retval |= __put_user(pt->pr, &ppr->pr); + /* gr1 - gr15 */ + if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { + index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); + min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ? + (dst->pos + dst->count) : ELF_GR_OFFSET(16); + for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++) + if (access_elf_reg(dst->target, info, i, + &tmp[index], 0) < 0) { + dst->ret = -EIO; + return; + } + dst->ret = utrace_regset_copyout(&dst->pos, &dst->count, + &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, + ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); + if (dst->ret || dst->count == 0) + return; + } - /* nat bits */ + /* r16-r31 */ + if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { + pt = task_pt_regs(dst->target); + dst->ret = utrace_regset_copyout(&dst->pos, &dst->count, + &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16, + ELF_GR_OFFSET(16), ELF_NAT_OFFSET); + if (dst->ret || dst->count == 0) + return; + } - retval |= access_uarea(child, PT_NAT_BITS, &ppr->nat, 0); + /* nat, pr, b0 - b7 */ + if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { + index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); + min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ? + (dst->pos + dst->count) : ELF_CR_IIP_OFFSET; + for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++) + if (access_elf_reg(dst->target, info, i, + &tmp[index], 0) < 0) { + dst->ret = -EIO; + return; + } + dst->ret = utrace_regset_copyout(&dst->pos, &dst->count, + &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, + ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); + if (dst->ret || dst->count == 0) + return; + } - ret = retval ? -EIO : 0; - return ret; + /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat + * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd + */ + if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { + index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); + min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ? + (dst->pos + dst->count) : ELF_AR_END_OFFSET; + for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), index++) + if (access_elf_reg(dst->target, info, i, + &tmp[index], 0) < 0) { + dst->ret = -EIO; + return; + } + dst->ret = utrace_regset_copyout(&dst->pos, &dst->count, + &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, + ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); + } } -static long -ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr) +void do_gpregs_set(struct unw_frame_info *info, void *arg) { - struct switch_stack *sw; struct pt_regs *pt; - long ret, retval; - struct unw_frame_info info; - char nat = 0; - int i; + utrace_getset_t *dst = arg; + elf_greg_t tmp[16]; + unsigned int i, index; - retval = verify_area(VERIFY_READ, ppr, sizeof(struct pt_all_user_regs)); - if (retval != 0) { - return -EIO; - } + if (unw_unwind_to_user(info) < 0) + return; - pt = ia64_task_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - unw_init_from_blocked_task(&info, child); - if (unw_unwind_to_user(&info) < 0) { - return -EIO; + /* Skip r0 */ + if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { + dst->ret = utrace_regset_copyin_ignore(&dst->pos, &dst->count, + &dst->u.set.kbuf, + &dst->u.set.ubuf, + 0, ELF_GR_OFFSET(1)); + if (dst->ret || dst->count == 0) + return; } - if (((unsigned long) ppr & 0x7) != 0) { - dprintk("ptrace:unaligned register address %p\n", ppr); - return -EIO; + /* gr1-gr15 */ + if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { + i = dst->pos; + index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); + dst->ret = utrace_regset_copyin(&dst->pos, &dst->count, + &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, + ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); + if (dst->ret) + return; + for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) + if (access_elf_reg(dst->target, info, i, + &tmp[index], 1) < 0) { + dst->ret = -EIO; + return; + } + if (dst->count == 0) + return; } - retval = 0; - - /* control regs */ - - retval |= __get_user(pt->cr_iip, &ppr->cr_iip); - retval |= access_uarea(child, PT_CR_IPSR, &ppr->cr_ipsr, 1); - - /* app regs */ - - retval |= __get_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]); - retval |= __get_user(pt->ar_rsc, &ppr->ar[PT_AUR_RSC]); - retval |= __get_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]); - retval |= __get_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]); - retval |= __get_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]); - retval |= __get_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]); - - retval |= access_uarea(child, PT_AR_EC, &ppr->ar[PT_AUR_EC], 1); - retval |= access_uarea(child, PT_AR_LC, &ppr->ar[PT_AUR_LC], 1); - retval |= access_uarea(child, PT_AR_RNAT, &ppr->ar[PT_AUR_RNAT], 1); - retval |= access_uarea(child, PT_AR_BSP, &ppr->ar[PT_AUR_BSP], 1); - retval |= access_uarea(child, PT_CFM, &ppr->cfm, 1); - - /* gr1-gr3 */ - - retval |= __copy_from_user(&pt->r1, &ppr->gr[1], sizeof(long)); - retval |= __copy_from_user(&pt->r2, &ppr->gr[2], sizeof(long) * 2); - - /* gr4-gr7 */ - - for (i = 4; i < 8; i++) { - long ret = unw_get_gr(&info, i, &ppr->gr[i], &nat); - if (ret < 0) { - return ret; - } - retval |= unw_access_gr(&info, i, &ppr->gr[i], &nat, 1); + /* gr16-gr31 */ + if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { + pt = task_pt_regs(dst->target); + dst->ret = utrace_regset_copyin(&dst->pos, &dst->count, + &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16, + ELF_GR_OFFSET(16), ELF_NAT_OFFSET); + if (dst->ret || dst->count == 0) + return; } - /* gr8-gr11 */ - - retval |= __copy_from_user(&pt->r8, &ppr->gr[8], sizeof(long) * 4); - - /* gr12-gr15 */ - - retval |= __copy_from_user(&pt->r12, &ppr->gr[12], sizeof(long) * 2); - retval |= __copy_from_user(&pt->r14, &ppr->gr[14], sizeof(long)); - retval |= __copy_from_user(&pt->r15, &ppr->gr[15], sizeof(long)); - - /* gr16-gr31 */ + /* nat, pr, b0 - b7 */ + if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { + i = dst->pos; + index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); + dst->ret = utrace_regset_copyin(&dst->pos, &dst->count, + &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, + ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); + if (dst->ret) + return; + for (; i < dst->pos; i += sizeof(elf_greg_t), index++) + if (access_elf_reg(dst->target, info, i, + &tmp[index], 1) < 0) { + dst->ret = -EIO; + return; + } + if (dst->count == 0) + return; + } - retval |= __copy_from_user(&pt->r16, &ppr->gr[16], sizeof(long) * 16); + /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat + * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd + */ + if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { + i = dst->pos; + index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); + dst->ret = utrace_regset_copyin(&dst->pos, &dst->count, + &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, + ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); + if (dst->ret) + return; + for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) + if (access_elf_reg(dst->target, info, i, + &tmp[index], 1) < 0) { + dst->ret = -EIO; + return; + } + } +} - /* b0 */ +#define ELF_FP_OFFSET(i) (i * sizeof(elf_fpreg_t)) - retval |= __get_user(pt->b0, &ppr->br[0]); +void do_fpregs_get(struct unw_frame_info *info, void *arg) +{ + utrace_getset_t *dst = arg; + struct task_struct *task = dst->target; + elf_fpreg_t tmp[30]; + int index, min_copy, i; - /* b1-b5 */ + if (unw_unwind_to_user(info) < 0) + return; - for (i = 1; i < 6; i++) { - retval |= unw_access_br(&info, i, &ppr->br[i], 1); + /* Skip pos 0 and 1 */ + if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { + dst->ret = utrace_regset_copyout_zero(&dst->pos, &dst->count, + &dst->u.get.kbuf, + &dst->u.get.ubuf, + 0, ELF_FP_OFFSET(2)); + if (dst->count == 0 || dst->ret) + return; } - /* b6-b7 */ - - retval |= __get_user(pt->b6, &ppr->br[6]); - retval |= __get_user(pt->b7, &ppr->br[7]); - - /* fr2-fr5 */ + /* fr2-fr31 */ + if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { + index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t); + min_copy = min(((unsigned int)ELF_FP_OFFSET(32)), + dst->pos + dst->count); + for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t), index++) + if (unw_get_fr(info, i / sizeof(elf_fpreg_t), + &tmp[index])) { + dst->ret = -EIO; + return; + } + dst->ret = utrace_regset_copyout(&dst->pos, &dst->count, + &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, + ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); + if (dst->count == 0 || dst->ret) + return; + } - for (i = 2; i < 6; i++) { - retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 1); - retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 1); + /* fph */ + if (dst->count > 0) { + ia64_flush_fph(dst->target); + if (task->thread.flags & IA64_THREAD_FPH_VALID) + dst->ret = utrace_regset_copyout( + &dst->pos, &dst->count, + &dst->u.get.kbuf, &dst->u.get.ubuf, + &dst->target->thread.fph, + ELF_FP_OFFSET(32), -1); + else + /* Zero fill instead. */ + dst->ret = utrace_regset_copyout_zero( + &dst->pos, &dst->count, + &dst->u.get.kbuf, &dst->u.get.ubuf, + ELF_FP_OFFSET(32), -1); } +} - /* fr6-fr11 */ +void do_fpregs_set(struct unw_frame_info *info, void *arg) +{ + utrace_getset_t *dst = arg; + elf_fpreg_t fpreg, tmp[30]; + int index, start, end; - retval |= __copy_from_user(&pt->f6, &ppr->fr[6], sizeof(ppr->fr[6]) * 6); + if (unw_unwind_to_user(info) < 0) + return; - /* fp scratch regs(12-15) */ + /* Skip pos 0 and 1 */ + if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { + dst->ret = utrace_regset_copyin_ignore(&dst->pos, &dst->count, + &dst->u.set.kbuf, + &dst->u.set.ubuf, + 0, ELF_FP_OFFSET(2)); + if (dst->count == 0 || dst->ret) + return; + } - retval |= __copy_from_user(&sw->f12, &ppr->fr[12], sizeof(ppr->fr[12]) * 4); + /* fr2-fr31 */ + if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { + start = dst->pos; + end = min(((unsigned int)ELF_FP_OFFSET(32)), + dst->pos + dst->count); + dst->ret = utrace_regset_copyin(&dst->pos, &dst->count, + &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, + ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); + if (dst->ret) + return; - /* fr16-fr31 */ + if (start & 0xF) { // only write high part + if (unw_get_fr(info, start / sizeof(elf_fpreg_t), + &fpreg)) { + dst->ret = -EIO; + return; + } + tmp[start / sizeof(elf_fpreg_t) - 2].u.bits[0] + = fpreg.u.bits[0]; + start &= ~0xFUL; + } + if (end & 0xF) { // only write low part + if (unw_get_fr(info, end / sizeof(elf_fpreg_t), &fpreg)) { + dst->ret = -EIO; + return; + } + tmp[end / sizeof(elf_fpreg_t) -2].u.bits[1] + = fpreg.u.bits[1]; + end = (end + 0xF) & ~0xFUL; + } - for (i = 16; i < 32; i++) { - retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 1); - retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 1); + for ( ; start < end ; start += sizeof(elf_fpreg_t)) { + index = start / sizeof(elf_fpreg_t); + if (unw_set_fr(info, index, tmp[index - 2])){ + dst->ret = -EIO; + return; + } + } + if (dst->ret || dst->count == 0) + return; } /* fph */ + if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(128)) { + ia64_sync_fph(dst->target); + dst->ret = utrace_regset_copyin(&dst->pos, &dst->count, + &dst->u.set.kbuf, + &dst->u.set.ubuf, + &dst->target->thread.fph, + ELF_FP_OFFSET(32), -1); + } +} - ia64_sync_fph(child); - retval |= __copy_from_user(&child->thread.fph, &ppr->fr[32], sizeof(ppr->fr[32]) * 96); - - /* preds */ +static int +do_regset_call(void (*call)(struct unw_frame_info *, void *), + struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + utrace_getset_t info = { .target = target, .regset = regset, + .pos = pos, .count = count, + .u.set = { .kbuf = kbuf, .ubuf = ubuf }, + .ret = 0 }; - retval |= __get_user(pt->pr, &ppr->pr); + if (target == current) + unw_init_running(call, &info); + else { + struct unw_frame_info ufi; + memset(&ufi, 0, sizeof(ufi)); + unw_init_from_blocked_task(&ufi, target); + (*call)(&ufi, &info); + } - /* nat bits */ + return info.ret; +} - retval |= access_uarea(child, PT_NAT_BITS, &ppr->nat, 1); +static int +gpregs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + return do_regset_call(do_gpregs_get, target, regset, pos, count, kbuf, ubuf); +} - ret = retval ? -EIO : 0; - return ret; +static int gpregs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + return do_regset_call(do_gpregs_set, target, regset, pos, count, kbuf, ubuf); } +static void do_gpregs_writeback(struct unw_frame_info *info, void *arg) +{ + struct pt_regs *pt; + utrace_getset_t *dst = arg; + unsigned long urbs_end; + + if (unw_unwind_to_user(info) < 0) + return; + pt = task_pt_regs(dst->target); + urbs_end = ia64_get_user_rbs_end(dst->target, pt, NULL); + dst->ret = ia64_sync_user_rbs(dst->target, info->sw, pt->ar_bspstore, urbs_end); +} /* - * Called by kernel/ptrace.c when detaching.. - * - * Make sure the single step bit is not set. + * This is called to write back the register backing store. + * ptrace does this before it stops, so that a tracer reading the user + * memory after the thread stops will get the current register data. */ -void -ptrace_disable (struct task_struct *child) +static int +gpregs_writeback(struct task_struct *target, + const struct utrace_regset *regset, + int now) { - struct ia64_psr *child_psr = ia64_psr(ia64_task_regs(child)); - - /* make sure the single step/take-branch tra bits are not set: */ - child_psr->ss = 0; - child_psr->tb = 0; + return do_regset_call(do_gpregs_writeback, target, regset, 0, 0, NULL, NULL); } -asmlinkage long -sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, - long arg4, long arg5, long arg6, long arg7, long stack) +static int +fpregs_active(struct task_struct *target, const struct utrace_regset *regset) { - struct pt_regs *pt, *regs = (struct pt_regs *) &stack; - unsigned long urbs_end, peek_or_poke; - struct task_struct *child; - struct switch_stack *sw; - long ret; - - lock_kernel(); - ret = -EPERM; - if (request == PTRACE_TRACEME) { - /* are we already being traced? */ - if (current->ptrace & PT_PTRACED) - goto out; - ret = security_ptrace(current->parent, current); - if (ret) - goto out; - current->ptrace |= PT_PTRACED; - ret = 0; - goto out; - } - - peek_or_poke = (request == PTRACE_PEEKTEXT || request == PTRACE_PEEKDATA - || request == PTRACE_POKETEXT || request == PTRACE_POKEDATA); - ret = -ESRCH; - read_lock(&tasklist_lock); - { - child = find_task_by_pid(pid); - if (child) { - if (peek_or_poke) - child = find_thread_for_addr(child, addr); - get_task_struct(child); - } - } - read_unlock(&tasklist_lock); - if (!child) - goto out; - if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT)) - goto out_tsk; - - ret = -EPERM; - if (pid == 1) /* no messing around with init! */ - goto out_tsk; - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; - - pt = ia64_task_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - - switch (request) { - case PTRACE_PEEKTEXT: - case PTRACE_PEEKDATA: /* read word at location addr */ - urbs_end = ia64_get_user_rbs_end(child, pt, NULL); - ret = ia64_peek(child, sw, urbs_end, addr, &data); - if (ret == 0) { - ret = data; - regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */ - } - goto out_tsk; - - case PTRACE_POKETEXT: - case PTRACE_POKEDATA: /* write the word at location addr */ - urbs_end = ia64_get_user_rbs_end(child, pt, NULL); - ret = ia64_poke(child, sw, urbs_end, addr, data); - goto out_tsk; - - case PTRACE_PEEKUSR: /* read the word at addr in the USER area */ - if (access_uarea(child, addr, &data, 0) < 0) { - ret = -EIO; - goto out_tsk; - } - ret = data; - regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */ - goto out_tsk; - - case PTRACE_POKEUSR: /* write the word at addr in the USER area */ - if (access_uarea(child, addr, &data, 1) < 0) { - ret = -EIO; - goto out_tsk; - } - ret = 0; - goto out_tsk; - - case PTRACE_OLD_GETSIGINFO: /* for backwards-compatibility */ - ret = ptrace_request(child, PTRACE_GETSIGINFO, addr, data); - goto out_tsk; - - case PTRACE_OLD_SETSIGINFO: /* for backwards-compatibility */ - ret = ptrace_request(child, PTRACE_SETSIGINFO, addr, data); - goto out_tsk; - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: /* restart after signal. */ - ret = -EIO; - if (data > _NSIG) - goto out_tsk; - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - child->exit_code = data; - - /* make sure the single step/taken-branch trap bits are not set: */ - ia64_psr(pt)->ss = 0; - ia64_psr(pt)->tb = 0; - - wake_up_process(child); - ret = 0; - goto out_tsk; - - case PTRACE_KILL: - /* - * 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 exit. - */ - if (child->state == TASK_ZOMBIE) /* already dead */ - goto out_tsk; - child->exit_code = SIGKILL; - - /* make sure the single step/take-branch tra bits are not set: */ - ia64_psr(pt)->ss = 0; - ia64_psr(pt)->tb = 0; - - wake_up_process(child); - ret = 0; - goto out_tsk; - - case PTRACE_SINGLESTEP: /* let child execute for one instruction */ - case PTRACE_SINGLEBLOCK: - ret = -EIO; - if (data > _NSIG) - goto out_tsk; - - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - if (request == PTRACE_SINGLESTEP) { - ia64_psr(pt)->ss = 1; - } else { - ia64_psr(pt)->tb = 1; - } - child->exit_code = data; + return (target->thread.flags & IA64_THREAD_FPH_VALID) ? 128 : 32; +} - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; - goto out_tsk; +static int fpregs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + return do_regset_call(do_fpregs_get, target, regset, pos, count, kbuf, ubuf); +} - case PTRACE_DETACH: /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - goto out_tsk; +static int fpregs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + return do_regset_call(do_fpregs_set, target, regset, pos, count, kbuf, ubuf); +} - case PTRACE_GETREGS: - ret = ptrace_getregs(child, (struct pt_all_user_regs*) data); - goto out_tsk; +static int dbregs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; - case PTRACE_SETREGS: - ret = ptrace_setregs(child, (struct pt_all_user_regs*) data); - goto out_tsk; +#ifdef CONFIG_PERFMON + /* + * Check if debug registers are used by perfmon. This + * test must be done once we know that we can do the + * operation, i.e. the arguments are all valid, but + * before we start modifying the state. + * + * Perfmon needs to keep a count of how many processes + * are trying to modify the debug registers for system + * wide monitoring sessions. + * + * We also include read access here, because they may + * cause the PMU-installed debug register state + * (dbr[], ibr[]) to be reset. The two arrays are also + * used by perfmon, but we do not use + * IA64_THREAD_DBG_VALID. The registers are restored + * by the PMU context switch code. + */ + if (pfm_use_debug_registers(target)) + return -EIO; +#endif - default: - ret = ptrace_request(child, request, addr, data); - goto out_tsk; + if (!(target->thread.flags & IA64_THREAD_DBG_VALID)) + ret = utrace_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 0, -1); + else { + preempt_disable(); + if (target == current) + ia64_load_debug_regs(&target->thread.dbr[0]); + preempt_enable_no_resched(); + ret = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.dbr, 0, -1); } - out_tsk: - put_task_struct(child); - out: - unlock_kernel(); + return ret; } - -void -syscall_trace (void) +static int dbregs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - if (!(current->ptrace & PT_PTRACED)) - return; - /* - * The 0x80 provides a way for the tracing parent to distinguish between a syscall - * stop and SIGTRAP delivery. - */ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); + int i, ret; - /* - * This isn't the same as continuing with a signal, but it will do for normal use. - * strace only continues with a signal if the stopping signal is not SIGTRAP. - * -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; +#ifdef CONFIG_PERFMON + if (pfm_use_debug_registers(target)) + return -EIO; +#endif + + ret = 0; + if (!(target->thread.flags & IA64_THREAD_DBG_VALID)){ + target->thread.flags |= IA64_THREAD_DBG_VALID; + memset(target->thread.dbr, 0, 2 * sizeof(target->thread.dbr)); + } else if (target == current){ + preempt_disable(); + ia64_save_debug_regs(&target->thread.dbr[0]); + preempt_enable_no_resched(); } -} -/* "asmlinkage" so the input arguments are preserved... */ + ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.dbr, 0, -1); -asmlinkage void -syscall_trace_enter (long arg0, long arg1, long arg2, long arg3, - long arg4, long arg5, long arg6, long arg7, long stack) -{ - struct pt_regs *regs = (struct pt_regs *) &stack; - long syscall; + for (i = 1; i < IA64_NUM_DBG_REGS; i += 2) { + target->thread.dbr[i] &= ~(7UL << 56); + target->thread.ibr[i] &= ~(7UL << 56); + } - if (unlikely(current->audit_context)) { - if (IS_IA32_PROCESS(regs)) - syscall = regs->r1; - else - syscall = regs->r15; + if (ret) + return ret; - audit_syscall_entry(current, syscall, arg0, arg1, arg2, arg3); + if (target == current){ + preempt_disable(); + ia64_load_debug_regs(&target->thread.dbr[0]); + preempt_enable_no_resched(); } - - if (test_thread_flag(TIF_SYSCALL_TRACE) && (current->ptrace & PT_PTRACED)) - syscall_trace(); + return 0; } -/* "asmlinkage" so the input arguments are preserved... */ - -asmlinkage void -syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, - long arg4, long arg5, long arg6, long arg7, long stack) +static const struct utrace_regset native_regsets[] = { + { + .n = ELF_NGREG, + .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), + .get = gpregs_get, .set = gpregs_set, + .writeback = gpregs_writeback + }, + { + .n = ELF_NFPREG, + .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), + .get = fpregs_get, .set = fpregs_set, .active = fpregs_active + }, + { + .n = 2 * IA64_NUM_DBG_REGS, .size = sizeof(long), + .align = sizeof(long), + .get = dbregs_get, .set = dbregs_set + } +}; + +const struct utrace_regset_view utrace_ia64_native = { + .name = "ia64", + .e_machine = EM_IA_64, + .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) +}; +EXPORT_SYMBOL_GPL(utrace_ia64_native); + +#endif /* CONFIG_UTRACE */ + + +#ifdef CONFIG_PTRACE + +#define WORD(member, num) \ + offsetof(struct pt_all_user_regs, member), \ + offsetof(struct pt_all_user_regs, member) + num * sizeof(long) +static const struct ptrace_layout_segment pt_all_user_regs_layout[] = { + {WORD(nat, 1), 0, ELF_NAT_OFFSET}, + {WORD(cr_iip, 1), 0, ELF_CR_IIP_OFFSET}, + {WORD(cfm, 1), 0, ELF_CFM_OFFSET}, + {WORD(cr_ipsr, 1), 0, ELF_CR_IPSR_OFFSET}, + {WORD(pr, 1), 0, ELF_PR_OFFSET}, + {WORD(gr[0], 1), -1, -1}, + {WORD(gr[1], 31), 0, ELF_GR_OFFSET(1)}, + {WORD(br[0], 8), 0, ELF_BR_OFFSET(0)}, + {WORD(ar[0], 16), -1, -1}, + {WORD(ar[PT_AUR_RSC], 4), 0, ELF_AR_RSC_OFFSET}, + {WORD(ar[PT_AUR_RNAT+1], 12), -1, -1}, + {WORD(ar[PT_AUR_CCV], 1), 0, ELF_AR_CCV_OFFSET}, + {WORD(ar[PT_AUR_CCV+1], 3), -1, -1}, + {WORD(ar[PT_AUR_UNAT], 1), 0, ELF_AR_UNAT_OFFSET}, + {WORD(ar[PT_AUR_UNAT+1], 3), -1, -1}, + {WORD(ar[PT_AUR_FPSR], 1), 0, ELF_AR_FPSR_OFFSET}, + {WORD(ar[PT_AUR_FPSR+1], 23), -1, -1}, + {WORD(ar[PT_AUR_PFS], 3), 0, ELF_AR_PFS_OFFSET}, + {WORD(ar[PT_AUR_EC+1], 62), -1, -1}, + {offsetof(struct pt_all_user_regs, fr[0]), + offsetof(struct pt_all_user_regs, fr[2]), + -1, -1}, + {offsetof(struct pt_all_user_regs, fr[2]), + offsetof(struct pt_all_user_regs, fr[128]), + 1, 2 * sizeof(elf_fpreg_t)}, + {0, 0, -1, 0} +}; +#undef WORD + +#define NEXT(addr, sum) (addr + sum * sizeof(long)) +static const struct ptrace_layout_segment pt_uarea_layout[] = { + {PT_F32, PT_NAT_BITS, 1, ELF_FP_OFFSET(32)}, + {PT_NAT_BITS, NEXT(PT_NAT_BITS, 1), 0, ELF_NAT_OFFSET}, + {PT_F2, PT_F10, 1, ELF_FP_OFFSET(2)}, + {PT_F10, PT_R4, 1, ELF_FP_OFFSET(10)}, + {PT_R4, PT_B1, 0, ELF_GR_OFFSET(4)}, + {PT_B1, PT_AR_EC, 0, ELF_BR_OFFSET(1)}, + {PT_AR_EC, PT_AR_LC, 0, ELF_AR_EC_OFFSET}, + {PT_AR_LC, NEXT(PT_AR_LC, 1), 0, ELF_AR_LC_OFFSET}, + {PT_CR_IPSR, PT_CR_IIP, 0, ELF_CR_IPSR_OFFSET}, + {PT_CR_IIP, PT_AR_UNAT, 0, ELF_CR_IIP_OFFSET}, + {PT_AR_UNAT, PT_AR_PFS, 0, ELF_AR_UNAT_OFFSET}, + {PT_AR_PFS, PT_AR_RSC, 0, ELF_AR_PFS_OFFSET}, + {PT_AR_RSC, PT_AR_RNAT, 0, ELF_AR_RSC_OFFSET}, + {PT_AR_RNAT, PT_AR_BSPSTORE, 0, ELF_AR_RNAT_OFFSET}, + {PT_AR_BSPSTORE,PT_PR, 0, ELF_AR_BSPSTORE_OFFSET}, + {PT_PR, PT_B6, 0, ELF_PR_OFFSET}, + {PT_B6, PT_AR_BSP, 0, ELF_BR_OFFSET(6)}, + {PT_AR_BSP, PT_R1, 0, ELF_AR_BSP_OFFSET}, + {PT_R1, PT_R12, 0, ELF_GR_OFFSET(1)}, + {PT_R12, PT_R8, 0, ELF_GR_OFFSET(12)}, + {PT_R8, PT_R16, 0, ELF_GR_OFFSET(8)}, + {PT_R16, PT_AR_CCV, 0, ELF_GR_OFFSET(16)}, + {PT_AR_CCV, PT_AR_FPSR, 0, ELF_AR_CCV_OFFSET}, + {PT_AR_FPSR, PT_B0, 0, ELF_AR_FPSR_OFFSET}, + {PT_B0, PT_B7, 0, ELF_BR_OFFSET(0)}, + {PT_B7, PT_F6, 0, ELF_BR_OFFSET(7)}, + {PT_F6, PT_AR_CSD, 1, ELF_FP_OFFSET(6)}, + {PT_AR_CSD, NEXT(PT_AR_CSD, 2), 0, ELF_AR_CSD_OFFSET}, + {PT_DBR, NEXT(PT_DBR, 8), 2, 0}, + {PT_IBR, NEXT(PT_IBR, 8), 2, 8 * sizeof(long)}, + {0, 0, -1, 0} +}; +#undef NEXT + +int arch_ptrace(long *request, struct task_struct *child, + struct utrace_attached_engine *engine, + unsigned long addr, unsigned long data, long *val) { - if (unlikely(current->audit_context)) - audit_syscall_exit(current, ((struct pt_regs *) &stack)->r8); - - if (test_thread_flag(TIF_SYSCALL_TRACE) && (current->ptrace & PT_PTRACED)) - syscall_trace(); + int ret = -ENOSYS; + switch (*request) { + case PTRACE_OLD_GETSIGINFO: + *request = PTRACE_GETSIGINFO; + break; + case PTRACE_OLD_SETSIGINFO: + *request = PTRACE_SETSIGINFO; + break; + + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + ret = access_process_vm(child, addr, val, sizeof(*val), 0); + ret = ret == sizeof(*val) ? 0 : -EIO; + break; + + case PTRACE_PEEKUSR: + return ptrace_layout_access(child, engine, + utrace_native_view(current), + pt_uarea_layout, + addr, sizeof(long), + NULL, val, 0); + case PTRACE_POKEUSR: + return ptrace_pokeusr(child, engine, + pt_uarea_layout, addr, data); + + case PTRACE_GETREGS: + case PTRACE_SETREGS: + return ptrace_layout_access(child, engine, + utrace_native_view(current), + pt_all_user_regs_layout, + 0, sizeof(struct pt_all_user_regs), + (void __user *) data, NULL, + *request == PTRACE_SETREGS); + } + return ret; } + +#endif /* CONFIG_PTRACE */