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

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

diff -u --recursive --new-file v2.4.7/linux/arch/ia64/kernel/ptrace.c linux/arch/ia64/kernel/ptrace.c
@@ -144,12 +144,10 @@
 }
 
 /*
- * 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
  *
@@ -182,20 +180,18 @@
  *					+--------+
  *						  <--- 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 pt_regs *pt, struct switch_stack *sw,
@@ -289,57 +285,82 @@
 	}
 }
 
+/*
+ * 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.
+ */
 long
-ia64_peek (struct task_struct *child, unsigned long user_bsp, 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, *ubsp = (long *) user_bsp;
-	struct switch_stack *child_stack;
+	unsigned long *bspstore, *krbs, regnum, *laddr, *urbs_end, *rnat_addr;
 	struct pt_regs *child_regs;
 	size_t copied;
 	long ret;
 
+	urbs_end = (long *) user_rbs_end;
 	laddr = (unsigned long *) addr;
 	child_regs = ia64_task_regs(child);
-	child_stack = (struct switch_stack *) (child->thread.ksp + 16);
 	bspstore = (unsigned long *) child_regs->ar_bspstore;
 	krbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
-	if (laddr >= bspstore && laddr <= ia64_rse_rnat_addr(ubsp)) {
+	if (laddr >= bspstore && laddr <= ia64_rse_rnat_addr(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.
 		 */
-		if (ia64_rse_is_rnat_slot(laddr))
-			ret = get_rnat(child_regs, child_stack, krbs, laddr);
-		else {
-			if (laddr >= ubsp)
-				ret = 0;
-			else {
-				regnum = ia64_rse_num_regs(bspstore, laddr);
-				ret = *ia64_rse_skip_regs(krbs, regnum);
-			}
+		rnat_addr = ia64_rse_rnat_addr(laddr);
+		ret = get_rnat(child_regs, child_stack, krbs, rnat_addr);
+
+		if (laddr == rnat_addr) {
+			/* return NaT collection word itself */
+			*val = ret;
+			return 0;
+		}
+
+		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.
+			 */
+			*val = 0;
+			return 0;
+		}
+
+		if (laddr < urbs_end) {
+			/* 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;
 		}
-	} else {
-		copied = access_process_vm(child, addr, &ret, sizeof(ret), 0);
-		if (copied != sizeof(ret))
-			return -EIO;
 	}
+	copied = access_process_vm(child, addr, &ret, sizeof(ret), 0);
+	if (copied != sizeof(ret))
+		return -EIO;
 	*val = ret;
 	return 0;
 }
 
 long
-ia64_poke (struct task_struct *child, unsigned long user_bsp, 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, *ubsp = (long *) user_bsp;
-	struct switch_stack *child_stack;
+	unsigned long *bspstore, *krbs, regnum, *laddr, *urbs_end = (long *) user_rbs_end;
 	struct pt_regs *child_regs;
 
 	laddr = (unsigned long *) addr;
 	child_regs = ia64_task_regs(child);
-	child_stack = (struct switch_stack *) (child->thread.ksp + 16);
 	bspstore = (unsigned long *) child_regs->ar_bspstore;
 	krbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
-	if (laddr >= bspstore && laddr <= ia64_rse_rnat_addr(ubsp)) {
+	if (laddr >= bspstore && laddr <= ia64_rse_rnat_addr(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.
@@ -347,7 +368,7 @@
 		if (ia64_rse_is_rnat_slot(laddr))
 			put_rnat(child_regs, child_stack, krbs, laddr, val);
 		else {
-			if (laddr < ubsp) {
+			if (laddr < urbs_end) {
 				regnum = ia64_rse_num_regs(bspstore, laddr);
 				*ia64_rse_skip_regs(krbs, regnum) = val;
 			}
@@ -359,11 +380,13 @@
 }
 
 /*
- * Calculate the user-level address that would have been in ar.bsp had the user executed a
- * "cover" instruction right before entering the kernel.
+ * 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_bsp (struct task_struct *child, struct pt_regs *pt)
+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;
@@ -372,6 +395,7 @@
 	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 ((long) pt->cr_ifs >= 0) {
 		/*
@@ -385,81 +409,102 @@
 			ndirty += (cfm & 0x7f);
 		}
 	}
+	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
- * indicated child process.
- *
- * If new_bsp is non-zero, the bsp will (effectively) be updated to the new value upon
- * resumption of the child process.  This is accomplished by setting the loadrs value to
- * zero and the bspstore value to the new bsp value.
- *
- * When new_bsp and flush_user_rbs are both 0, the register backing store in kernel space
- * is written to user space and the loadrs and bspstore values are left alone.
- *
- * When new_bsp is zero and flush_user_rbs is 1 (non-zero), loadrs is set to 0, and the
- * bspstore value is set to the old bsp value.  This will cause the stacked registers (r32
- * and up) to be obtained entirely from the child's memory space rather than from the
- * kernel.  (This makes it easier to write code for modifying the stacked registers in
- * multi-threaded programs.)
- *
- * Note: I had originally written this function without the flush_user_rbs parameter; it
- * was written so that loadrs would always be set to zero.  But I had problems with
- * certain system calls apparently causing a portion of the RBS to be zeroed.  (I still
- * don't understand why this was happening.) Anyway, it'd definitely less intrusive to
- * leave loadrs and bspstore alone if possible.
+ * 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.
  */
-static long
-sync_kernel_register_backing_store (struct task_struct *child, long user_bsp, long new_bsp,
-				    int flush_user_rbs)
+long
+ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw,
+		    unsigned long user_rbs_start, unsigned long user_rbs_end)
 {
-	struct pt_regs *child_regs = ia64_task_regs(child);
 	unsigned long addr, val;
 	long ret;
 
-	/*
-	 * Return early if nothing to do.  Note that new_bsp will be zero if the caller
-	 * wants to force synchronization without changing bsp.
-	 */
-	if (user_bsp == new_bsp)
-		return 0;
-
-	/* Write portion of backing store living on kernel stack to the child's VM. */
-	for (addr = child_regs->ar_bspstore; addr < user_bsp; addr += 8) {
-		ret = ia64_peek(child, user_bsp, addr, &val);
-		if (ret != 0)
+	/* now copy word for word from kernel rbs to user rbs: */
+	for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) {
+		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))
 			return -EIO;
 	}
+	return 0;
+}
 
-	if (new_bsp != 0) {
-		flush_user_rbs = 1;
-		user_bsp = new_bsp;
-	}
+/*
+ * Simulate user-level "flushrs".  Note: we can't just add pt->loadrs>>16 to
+ * pt->ar_bspstore because the kernel backing store and the user-level backing store may
+ * have different alignments (and therefore a different number of intervening rnat slots).
+ */
+static void
+user_flushrs (struct task_struct *task, struct pt_regs *pt)
+{
+	unsigned long *krbs;
+	long ndirty;
 
-	if (flush_user_rbs) {
-		child_regs->loadrs = 0;
-		child_regs->ar_bspstore = user_bsp;
-	}
-	return 0;
+	krbs = (unsigned long *) task + IA64_RBS_OFFSET/8;
+	ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19));
+
+	pt->ar_bspstore = (unsigned long) ia64_rse_skip_regs((unsigned long *) pt->ar_bspstore,
+							     ndirty);
+	pt->loadrs = 0;
 }
 
+/*
+ * Synchronize the RSE backing store of CHILD and all tasks that share the address space
+ * with it.  CHILD_URBS_END is the address of the end of the register backing store of
+ * CHILD.  If MAKE_WRITABLE is set, a user-level "flushrs" is simulated such that the VM
+ * can be written via ptrace() and the tasks will pick up the newly written values.  It
+ * would be OK to unconditionally simulate a "flushrs", but this would be more intrusive
+ * than strictly necessary (e.g., it would make it impossible to obtain the original value
+ * of ar.bspstore).
+ */
 static void
-sync_thread_rbs (struct task_struct *child, long bsp, struct mm_struct *mm, int make_writable)
+threads_sync_user_rbs (struct task_struct *child, unsigned long child_urbs_end, int make_writable)
 {
+	struct switch_stack *sw;
+	unsigned long urbs_end;
 	struct task_struct *p;
-	read_lock(&tasklist_lock);
+	struct mm_struct *mm;
+	struct pt_regs *pt;
+	long multi_threaded;
+
+	task_lock(child);
 	{
-		for_each_task(p) {
-			if (p->mm == mm && p->state != TASK_RUNNING)
-				sync_kernel_register_backing_store(p, bsp, 0, make_writable);
+		mm = child->mm;
+		multi_threaded = mm && (atomic_read(&mm->mm_users) > 1);
+	}
+	task_unlock(child);
+
+	if (!multi_threaded) {
+		sw = (struct switch_stack *) (child->thread.ksp + 16);
+		pt = ia64_task_regs(child);
+		ia64_sync_user_rbs(child, sw, pt->ar_bspstore, child_urbs_end);
+		if (make_writable)
+			user_flushrs(child, pt);
+	} else {
+		read_lock(&tasklist_lock);
+		{
+			for_each_task(p) {
+				if (p->mm == mm && p->state != TASK_RUNNING) {
+					sw = (struct switch_stack *) (p->thread.ksp + 16);
+					pt = ia64_task_regs(p);
+					urbs_end = ia64_get_user_rbs_end(p, pt, NULL);
+					ia64_sync_user_rbs(p, sw, pt->ar_bspstore, urbs_end);
+					if (make_writable)
+						user_flushrs(p, pt);
+				}
+			}
 		}
+		read_unlock(&tasklist_lock);
 	}
-	read_unlock(&tasklist_lock);
-	child->thread.flags |= IA64_THREAD_KRBS_SYNCED;
+	child->thread.flags |= IA64_THREAD_KRBS_SYNCED;	/* set the flag in the child thread only */
 }
 
 /*
@@ -528,7 +573,7 @@
 static int
 access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data, int write_access)
 {
-	unsigned long *ptr, regnum, bsp, rnat_addr;
+	unsigned long *ptr, regnum, urbs_end, rnat_addr;
 	struct switch_stack *sw;
 	struct unw_frame_info info;
 	struct pt_regs *pt;
@@ -625,13 +670,24 @@
 		/* scratch state */
 		switch (addr) {
 		      case PT_AR_BSP:
-			bsp = ia64_get_user_bsp(child, pt);
-			if (write_access)
-				return sync_kernel_register_backing_store(child, bsp, *data, 1);
-			else {
-				*data = bsp;
-				return 0;
-			}
+			/*
+			 * 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.
+			 */
+			urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
+			if (write_access) {
+				if (*data != urbs_end) {
+					if (ia64_sync_user_rbs(child, sw,
+							       pt->ar_bspstore, urbs_end) < 0)
+						return -1;
+					/* simulate user-level write of ar.bsp: */
+					pt->loadrs = 0;
+					pt->ar_bspstore = *data;
+				}
+			} else
+				*data = urbs_end;
+			return 0;
 
 		      case PT_CFM:
 			if ((long) pt->cr_ifs < 0) {
@@ -666,12 +722,12 @@
 			return 0;
 
 		      case PT_AR_RNAT:
-			bsp = ia64_get_user_bsp(child, pt);
-			rnat_addr = (long) ia64_rse_rnat_addr((long *) bsp - 1);
+			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, bsp, rnat_addr, *data);
+				return ia64_poke(child, sw, urbs_end, rnat_addr, *data);
 			else
-				return ia64_peek(child, bsp, rnat_addr, data);
+				return ia64_peek(child, sw, urbs_end, rnat_addr, data);
 
 				   case PT_R1:  case PT_R2:  case PT_R3:
 		      case PT_R8:  case PT_R9:  case PT_R10: case PT_R11:
@@ -738,8 +794,9 @@
 	    long arg4, long arg5, long arg6, long arg7, long stack)
 {
 	struct pt_regs *pt, *regs = (struct pt_regs *) &stack;
+	unsigned long flags, urbs_end;
 	struct task_struct *child;
-	unsigned long flags, bsp;
+	struct switch_stack *sw;
 	long ret;
 
 	lock_kernel();
@@ -784,25 +841,17 @@
 		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 */
-		bsp = ia64_get_user_bsp(child, pt);
-		if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)) {
-			struct mm_struct *mm;
-			long do_sync;
-
-			task_lock(child);
-			{
-				mm = child->mm;
-				do_sync = mm && (atomic_read(&mm->mm_users) > 1);
-			}
-			task_unlock(child);
-			if (do_sync)
-				sync_thread_rbs(child, bsp, mm, 0);
-		}
-		ret = ia64_peek(child, bsp, addr, &data);
+		urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
+
+		if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED))
+			threads_sync_user_rbs(child, urbs_end, 0);
+
+		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 */
@@ -811,21 +860,11 @@
 
 	      case PTRACE_POKETEXT:
 	      case PTRACE_POKEDATA:		/* write the word at location addr */
-		bsp = ia64_get_user_bsp(child, pt);
-		if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)) {
-			struct mm_struct *mm;
-			long do_sync;
-
-			task_lock(child);
-			{
-				mm = child->mm;
-				do_sync = mm && (atomic_read(&child->mm->mm_users) > 1);
-			}
-			task_unlock(child);
-			if (do_sync)
-				sync_thread_rbs(child, bsp, mm, 1);
-		}
-		ret = ia64_poke(child, bsp, addr, data);
+		urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
+		if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED))
+			threads_sync_user_rbs(child, urbs_end, 1);
+
+		ret = ia64_poke(child, sw, urbs_end, addr, data);
 		goto out_tsk;
 
 	      case PTRACE_PEEKUSR:		/* read the word at addr in the USER area */

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