patch-2.4.19 linux-2.4.19/arch/ia64/kernel/ptrace.c

Next file: linux-2.4.19/arch/ia64/kernel/sal.c
Previous file: linux-2.4.19/arch/ia64/kernel/process.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/arch/ia64/kernel/ptrace.c linux-2.4.19/arch/ia64/kernel/ptrace.c
@@ -1,7 +1,7 @@
 /*
  * Kernel support for the ptrace() and syscall tracing interfaces.
  *
- * Copyright (C) 1999-2001 Hewlett-Packard Co
+ * Copyright (C) 1999-2002 Hewlett-Packard Co
  *	David Mosberger-Tang <davidm@hpl.hp.com>
  *
  * Derived from the x86 and Alpha versions.  Most of the code in here
@@ -23,6 +23,9 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/unwind.h>
+#ifdef CONFIG_PERFMON
+# include <asm/perfmon.h>
+#endif
 
 /*
  * Bits in the PSR that we allow ptrace() to change:
@@ -755,11 +758,6 @@
 	} else {
 		/* access debug registers */
 
-		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));
-		}
 		if (addr >= PT_IBR) {
 			regnum = (addr - PT_IBR) >> 3;
 			ptr = &child->thread.ibr[0];
@@ -772,6 +770,30 @@
 			dprintk("ptrace: rejecting access to register address 0x%lx\n", addr);
 			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));
+		}
 
 		ptr += regnum;
 
@@ -789,6 +811,260 @@
 	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;
+	}
+
+	retval = 0;
+
+	/* control regs */
+
+	retval |= __put_user(pt->cr_iip, &ppr->cr_iip);
+	retval |= access_uarea(child, PT_CR_IPSR, &ppr->cr_ipsr, 0);
+
+	/* app regs */
+
+	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);
+
+	/* gr1-gr3 */
+
+	retval |= __copy_to_user(&ppr->gr[1], &pt->r1, sizeof(long) * 3);
+
+	/* gr4-gr7 */
+
+	for (i = 4; i < 8; i++) {
+		retval |= unw_access_gr(&info, i, &ppr->gr[i], &nat, 0);
+	}
+
+	/* gr8-gr11 */
+
+	retval |= __copy_to_user(&ppr->gr[8], &pt->r8, sizeof(long) * 4);
+
+	/* gr12-gr15 */
+
+	retval |= __copy_to_user(&ppr->gr[12], &pt->r12, sizeof(long) * 4);
+
+	/* gr16-gr31 */
+
+	retval |= __copy_to_user(&ppr->gr[16], &pt->r16, sizeof(long) * 16);
+
+	/* b0 */
+
+	retval |= __put_user(pt->b0, &ppr->br[0]);
+
+	/* b1-b5 */
+
+	for (i = 1; i < 6; i++) {
+		retval |= unw_access_br(&info, i, &ppr->br[i], 0);
+	}
+
+	/* b6-b7 */
+
+	retval |= __put_user(pt->b6, &ppr->br[6]);
+	retval |= __put_user(pt->b7, &ppr->br[7]);
+
+	/* fr2-fr5 */
+
+	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);
+	}
+
+	/* fr6-fr9 */
+
+	retval |= __copy_to_user(&ppr->fr[6], &pt->f6, sizeof(struct ia64_fpreg) * 4);
+
+	/* fp scratch regs(10-15) */
+
+	retval |= __copy_to_user(&ppr->fr[10], &sw->f10, sizeof(struct ia64_fpreg) * 6);
+
+	/* fr16-fr31 */
+
+	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);
+	}
+
+	/* fph */
+
+	ia64_flush_fph(child);
+	retval |= __copy_to_user(&ppr->fr[32], &child->thread.fph, sizeof(ppr->fr[32]) * 96);
+
+	/* preds */
+
+	retval |= __put_user(pt->pr, &ppr->pr);
+
+	/* nat bits */
+
+	retval |= access_uarea(child, PT_NAT_BITS, &ppr->nat, 0);
+
+	ret = retval ? -EIO : 0;
+	return ret;
+}
+
+static long
+ptrace_setregs (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_READ, 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;
+	}
+
+	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) * 3);
+
+	/* 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);
+	}
+
+	/* 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) * 4);
+
+	/* gr16-gr31 */
+
+	retval |= __copy_from_user(&pt->r16, &ppr->gr[16], sizeof(long) * 16);
+
+	/* b0 */
+
+	retval |= __get_user(pt->b0, &ppr->br[0]);
+
+	/* b1-b5 */
+
+	for (i = 1; i < 6; i++) {
+		retval |= unw_access_br(&info, i, &ppr->br[i], 1);
+	}
+
+	/* b6-b7 */
+
+	retval |= __get_user(pt->b6, &ppr->br[6]);
+	retval |= __get_user(pt->b7, &ppr->br[7]);
+
+	/* fr2-fr5 */
+
+	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);
+	}
+
+	/* fr6-fr9 */
+
+	retval |= __copy_from_user(&pt->f6, &ppr->fr[6], sizeof(ppr->fr[6]) * 4);
+
+	/* fp scratch regs(10-15) */
+
+	retval |= __copy_from_user(&sw->f10, &ppr->fr[10], sizeof(ppr->fr[10]) * 6);
+
+	/* fr16-fr31 */
+
+	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);
+	}
+
+	/* fph */
+
+	ia64_sync_fph(child);
+	retval |= __copy_from_user(&child->thread.fph, &ppr->fr[32], sizeof(ppr->fr[32]) * 96);
+
+	/* preds */
+
+	retval |= __get_user(pt->pr, &ppr->pr);
+
+	/* nat bits */
+
+	retval |= access_uarea(child, PT_NAT_BITS, &ppr->nat, 1);
+
+	ret = retval ? -EIO : 0;
+	return ret;
+}
+
 /*
  * Called by kernel/ptrace.c when detaching..
  *
@@ -979,6 +1255,14 @@
 		ret = ptrace_detach(child, data);
 		goto out_tsk;
 
+	      case PTRACE_GETREGS:
+		ret = ptrace_getregs(child, (struct pt_all_user_regs*) data);
+		goto out_tsk;
+
+	      case PTRACE_SETREGS:
+		ret = ptrace_setregs(child, (struct pt_all_user_regs*) data);
+		goto out_tsk;
+
 	      default:
 		ret = -EIO;
 		goto out_tsk;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)