patch-2.1.9 linux/arch/sparc/kernel/ptrace.c

Next file: linux/arch/sparc/kernel/rirq.S
Previous file: linux/arch/sparc/kernel/process.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
@@ -4,6 +4,9 @@
  *
  * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
  * and David Mosberger.
+ *
+ * Added Linux support -miguel (wierd, eh?, the orignal code was meant
+ * to emulate SunOS).
  */
 
 #include <linux/kernel.h>
@@ -15,6 +18,9 @@
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define MAGIC_CONSTANT 0x80000000
 
 /* change a pid into a task struct. */
 static inline struct task_struct * get_task(int pid)
@@ -70,7 +76,7 @@
 	}
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
-	if (page >= high_memory)
+	if (MAP_NR(page) >= max_mapnr)
 		return 0;
 	page += addr & ~PAGE_MASK;
 	retval = *(unsigned long *) page;
@@ -127,15 +133,15 @@
 		goto repeat;
 	}
 /* this is a hack for non-kernel-mapped video buffers and similar */
-	flush_cache_page(vma, page);
-	if (page < high_memory) {
+	flush_cache_page(vma, addr);
+	if (MAP_NR(page) < max_mapnr) {
 		*(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
 		flush_page_to_ram(page);
 	}
 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
 /* this should also re-instate whatever read-only mode there was before */
 	set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
-	flush_tlb_page(vma, page);
+	flush_tlb_page(vma, addr);
 }
 
 static struct vm_area_struct * find_extend_vma(struct task_struct * tsk,
@@ -144,7 +150,7 @@
 	struct vm_area_struct * vma;
 
 	addr &= PAGE_MASK;
-	vma = find_vma(tsk,addr);
+	vma = find_vma(tsk->mm,addr);
 	if (!vma)
 		return NULL;
 	if (vma->vm_start <= addr)
@@ -250,7 +256,8 @@
  * is a valid errno will mean setting the condition codes to indicate
  * an error return.  This doesn't work, so we have this hook.
  */
-static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
+static inline void
+pt_error_return(struct pt_regs *regs, unsigned long error)
 {
 	regs->u_regs[UREG_I0] = error;
 	regs->psr |= PSR_C;
@@ -258,7 +265,8 @@
 	regs->npc += 4;
 }
 
-static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
+static inline void
+pt_succ_return(struct pt_regs *regs, unsigned long value)
 {
 	regs->u_regs[UREG_I0] = value;
 	regs->psr &= ~PSR_C;
@@ -266,13 +274,34 @@
 	regs->npc += 4;
 }
 
+static void
+pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr)
+{
+	if(put_user(value, addr))
+		return pt_error_return(regs, EFAULT);
+	regs->u_regs[UREG_I0] = 0;
+	regs->psr &= ~PSR_C;
+	regs->pc = regs->npc;
+	regs->npc += 4;
+}
+
+static void
+pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr)
+{
+	if (current->personality & PER_BSD)
+		pt_succ_return (regs, val);
+	else
+		pt_succ_return_linux (regs, val, addr);
+}
+
 /* Fuck me gently with a chainsaw... */
 static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
-				   struct task_struct *tsk)
+				   struct task_struct *tsk, long *addr)
 {
 	struct pt_regs *cregs = tsk->tss.kregs;
 	struct thread_struct *t = &tsk->tss;
-
+	int v;
+	
 	if(offset >= 1024)
 		offset -= 1024; /* whee... */
 	if(offset & ((sizeof(unsigned long) - 1))) {
@@ -281,82 +310,83 @@
 	}
 	if(offset >= 16 && offset < 784) {
 		offset -= 16; offset >>= 2;
-		pt_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset));	
+		pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
 		return;
 	}
 	if(offset >= 784 && offset < 832) {
 		offset -= 784; offset >>= 2;
-		pt_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset));
+		pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
 		return;
 	}
 	switch(offset) {
 	case 0:
-		regs->u_regs[UREG_I0] = t->ksp;
+		v = t->ksp;
 		break;
 	case 4:
-		regs->u_regs[UREG_I0] = t->kpc;
+		v = t->kpc;
 		break;
 	case 8:
-		regs->u_regs[UREG_I0] = t->kpsr;
+		v = t->kpsr;
 		break;
 	case 12:
-		regs->u_regs[UREG_I0] = t->uwinmask;
+		v = t->uwinmask;
 		break;
 	case 832:
-		regs->u_regs[UREG_I0] = t->w_saved;
+		v = t->w_saved;
 		break;
 	case 896:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I0];
+		v = cregs->u_regs[UREG_I0];
 		break;
 	case 900:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I1];
+		v = cregs->u_regs[UREG_I1];
 		break;
 	case 904:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I2];
+		v = cregs->u_regs[UREG_I2];
 		break;
 	case 908:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I3];
+		v = cregs->u_regs[UREG_I3];
 		break;
 	case 912:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I4];
+		v = cregs->u_regs[UREG_I4];
 		break;
 	case 916:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I5];
+		v = cregs->u_regs[UREG_I5];
 		break;
 	case 920:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I6];
+		v = cregs->u_regs[UREG_I6];
 		break;
 	case 924:
-		if(tsk->tss.flags & 0x80000000)
-			regs->u_regs[UREG_I0] = cregs->u_regs[UREG_G1];
+		if(tsk->tss.flags & MAGIC_CONSTANT)
+			v = cregs->u_regs[UREG_G1];
 		else
-			regs->u_regs[UREG_I0] = 0;
+			v = 0;
 		break;
 	case 940:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I0];
+		v = cregs->u_regs[UREG_I0];
 		break;
 	case 944:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I1];
+		v = cregs->u_regs[UREG_I1];
 		break;
 
 	case 948:
 		/* Isn't binary compatibility _fun_??? */
 		if(cregs->psr & PSR_C)
-			regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I0] << 24;
+			v = cregs->u_regs[UREG_I0] << 24;
 		else
-			regs->u_regs[UREG_I0] = 0;
+			v = 0;
 		break;
 
 		/* Rest of them are completely unsupported. */
 	default:
-		printk("%s [%d]: Wants to read user offset %d\n",
+		printk("%s [%d]: Wants to read user offset %ld\n",
 		       current->comm, current->pid, offset);
 		pt_error_return(regs, EIO);
 		return;
 	}
-	regs->psr &= ~PSR_C;
-	regs->pc = regs->npc;
-	regs->npc += 4;
+	if (current->personality & PER_BSD)
+		pt_succ_return (regs, v);
+	else
+		pt_succ_return_linux (regs, v, addr);
 	return;
 }
 
@@ -415,7 +445,7 @@
 
 		/* Rest of them are completely unsupported or "no-touch". */
 	default:
-		printk("%s [%d]: Wants to write user offset %d\n",
+		printk("%s [%d]: Wants to write user offset %ld\n",
 		       current->comm, current->pid, offset);
 		goto failure;
 	}
@@ -430,6 +460,34 @@
 /* #define ALLOW_INIT_TRACING */
 /* #define DEBUG_PTRACE */
 
+#ifdef DEBUG_PTRACE
+char *pt_rq [] = {
+"TRACEME",
+"PEEKTEXT",
+"PEEKDATA",
+"PEEKUSR",
+"POKETEXT",
+"POKEDATA",
+"POKEUSR",
+"CONT",
+"KILL",
+"SINGLESTEP",
+"SUNATTACH",
+"SUNDETACH",
+"GETREGS",
+"SETREGS",
+"GETFPREGS",
+"SETFPREGS",
+"READDATA",
+"WRITEDATA",
+"READTEXT",
+"WRITETEXT",
+"GETFPAREGS",
+"SETFPAREGS",
+""
+};
+#endif
+
 asmlinkage void do_ptrace(struct pt_regs *regs)
 {
 	unsigned long request = regs->u_regs[UREG_I0];
@@ -440,8 +498,21 @@
 	struct task_struct *child;
 
 #ifdef DEBUG_PTRACE
-	printk("do_ptrace: rq=%d pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
-	       (int) request, (int) pid, addr, data, addr2);
+	{
+		char *s;
+
+		if ((request > 0) && (request < 21))
+			s = pt_rq [request];
+		else
+			s = "unknown";
+
+		if (request == PTRACE_POKEDATA && data == 0x91d02001){
+			printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n",
+				pid, addr, addr2);
+		} else 
+			printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
+			       s, (int) request, (int) pid, addr, data, addr2);
+	}
 #endif
 	if(request == PTRACE_TRACEME) {
 		/* are we already being traced? */
@@ -527,12 +598,12 @@
 			pt_error_return(regs, -res);
 			return;
 		}
-		pt_succ_return(regs, tmp);
+		pt_os_succ_return(regs, tmp, (long *) data);
 		return;
 	}
 
 	case PTRACE_PEEKUSR:
-		read_sunos_user(regs, addr, child);
+		read_sunos_user(regs, addr, child, (long *) data);
 		return;
 
 	case PTRACE_POKEUSR:
@@ -550,10 +621,6 @@
 			return;
 		}
 		vma = find_extend_vma(child, addr);
-		if(vma && request == PTRACE_POKEDATA && (vma->vm_flags & VM_EXEC)) {
-			pt_error_return(regs, EIO);
-			return;
-		}
 		res = write_long(child, addr, data);
 		if(res < 0)
 			pt_error_return(regs, -res);
@@ -567,45 +634,48 @@
 		struct pt_regs *cregs = child->tss.kregs;
 		int rval;
 
-		rval = verify_area(VERIFY_WRITE, pregs, sizeof(struct pt_regs) - 4);
-		if(rval) {
-			pt_error_return(regs, rval);
-			return;
-		}
-		pregs->psr = cregs->psr;
-		pregs->pc = cregs->pc;
-		pregs->npc = cregs->npc;
-		pregs->y = cregs->y;
+		rval = verify_area(VERIFY_WRITE, pregs, sizeof(struct pt_regs));
+		if(rval)
+			return pt_error_return(regs, -rval);
+		__put_user(cregs->psr, (&pregs->psr));
+		__put_user(cregs->pc, (&pregs->pc));
+		__put_user(cregs->npc, (&pregs->npc));
+		__put_user(cregs->y, (&pregs->y));
 		for(rval = 1; rval < 16; rval++)
-			pregs->u_regs[rval - 1] = cregs->u_regs[rval];
+			__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
 		pt_succ_return(regs, 0);
+#ifdef DEBUG_PTRACE
+		printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]);
+#endif
 		return;
 	}
 
 	case PTRACE_SETREGS: {
 		struct pt_regs *pregs = (struct pt_regs *) addr;
 		struct pt_regs *cregs = child->tss.kregs;
-		unsigned long psr;
-		int rval, i;
+		unsigned long psr, pc, npc, y;
+		int i;
 
-		rval = verify_area(VERIFY_READ, pregs, sizeof(struct pt_regs) - 4);
-		if(rval) {
-			pt_error_return(regs, rval);
-			return;
-		}
 		/* Must be careful, tracing process can only set certain
 		 * bits in the psr.
 		 */
-		psr = (pregs->psr) & PSR_ICC;
+		i = verify_area(VERIFY_READ, pregs, sizeof(struct pt_regs));
+		if(i)
+			return pt_error_return(regs, -i);
+		__get_user(psr, (&pregs->psr));
+		__get_user(pc, (&pregs->pc));
+		__get_user(npc, (&pregs->npc));
+		__get_user(y, (&pregs->y));
+		psr &= PSR_ICC;
 		cregs->psr &= ~PSR_ICC;
 		cregs->psr |= psr;
-		if(!((pregs->pc | pregs->npc) & 3)) {
-			cregs->pc = pregs->pc;
-			cregs->npc = pregs->npc;
+		if(!((pc | npc) & 3)) {
+			cregs->pc = pc;
+			cregs->npc =npc;
 		}
-		cregs->y = pregs->y;
+		cregs->y = y;
 		for(i = 1; i < 16; i++)
-			cregs->u_regs[i] = pregs->u_regs[i-1];
+			__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
 		pt_succ_return(regs, 0);
 		return;
 	}
@@ -622,18 +692,21 @@
 				unsigned long insn;
 			} fpq[16];
 		} *fps = (struct fps *) addr;
-		int rval, i;
+		int i;
 
-		rval = verify_area(VERIFY_WRITE, fps, sizeof(struct fps));
-		if(rval) { pt_error_return(regs, rval); return; }
+		i = verify_area(VERIFY_WRITE, fps, sizeof(struct fps));
+		if(i)
+			return pt_error_return(regs, -i);
 		for(i = 0; i < 32; i++)
-			fps->regs[i] = child->tss.float_regs[i];
-		fps->fsr = child->tss.fsr;
-		fps->fpqd = child->tss.fpqdepth;
-		fps->flags = fps->extra = 0;
+			__put_user(child->tss.float_regs[i], (&fps->regs[i]));
+		__put_user(child->tss.fsr, (&fps->fsr));
+		__put_user(child->tss.fpqdepth, (&fps->fpqd));
+		__put_user(0, (&fps->flags));
+		__put_user(0, (&fps->extra));
 		for(i = 0; i < 16; i++) {
-			fps->fpq[i].insnaddr = child->tss.fpqueue[i].insn_addr;
-			fps->fpq[i].insn = child->tss.fpqueue[i].insn;
+			__put_user(child->tss.fpqueue[i].insn_addr,
+				   (&fps->fpq[i].insnaddr));
+			__put_user(child->tss.fpqueue[i].insn, (&fps->fpq[i].insn));
 		}
 		pt_succ_return(regs, 0);
 		return;
@@ -651,17 +724,18 @@
 				unsigned long insn;
 			} fpq[16];
 		} *fps = (struct fps *) addr;
-		int rval, i;
+		int i;
 
-		rval = verify_area(VERIFY_READ, fps, sizeof(struct fps));
-		if(rval) { pt_error_return(regs, rval); return; }
-		for(i = 0; i < 32; i++)
-			child->tss.float_regs[i] = fps->regs[i];
-		child->tss.fsr = fps->fsr;
-		child->tss.fpqdepth = fps->fpqd;
+		i = verify_area(VERIFY_READ, fps, sizeof(struct fps));
+		if(i)
+			return pt_error_return(regs, -i);
+		copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (32 * 4));
+		__get_user(child->tss.fsr, (&fps->fsr));
+		__get_user(child->tss.fpqdepth, (&fps->fpqd));
 		for(i = 0; i < 16; i++) {
-			child->tss.fpqueue[i].insn_addr = fps->fpq[i].insnaddr;
-			child->tss.fpqueue[i].insn = fps->fpq[i].insn;
+			__get_user(child->tss.fpqueue[i].insn_addr,
+				   (&fps->fpq[i].insnaddr));
+			__get_user(child->tss.fpqueue[i].insn, (&fps->fpq[i].insn));
 		}
 		pt_succ_return(regs, 0);
 		return;
@@ -674,18 +748,14 @@
 		unsigned char tmp;
 		int res, len = data;
 
-		res = verify_area(VERIFY_WRITE, (void *) dest, len);
-		if(res) {
-			pt_error_return(regs, -res);
-			return;
-		}
+		res = verify_area(VERIFY_WRITE, dest, len);
+		if(res)
+			return pt_error_return(regs, -res);
 		while(len) {
 			res = read_byte(child, src, &tmp);
-			if(res < 0) {
-				pt_error_return(regs, -res);
-				return;
-			}
-			*dest = tmp;
+			if(res < 0)
+				return pt_error_return(regs, -res);
+			__put_user(tmp, dest);
 			src++; dest++; len--;
 		}
 		pt_succ_return(regs, 0);
@@ -698,17 +768,16 @@
 		unsigned long dest = addr;
 		int res, len = data;
 
-		res = verify_area(VERIFY_READ, (void *) src, len);
-		if(res) {
-			pt_error_return(regs, -res);
-			return;
-		}
+		res = verify_area(VERIFY_READ, src, len);
+		if(res)
+			return pt_error_return(regs, -res);
 		while(len) {
-			res = write_byte(child, dest, *src);
-			if(res < 0) {
-				pt_error_return(regs, -res);
-				return;
-			}
+			unsigned long tmp;
+
+			__get_user(tmp, src);
+			res = write_byte(child, dest, tmp);
+			if(res < 0)
+				return pt_error_return(regs, -res);
 			src++; dest++; len--;
 		}
 		pt_succ_return(regs, 0);
@@ -716,7 +785,6 @@
 	}
 
 	case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
-		data = 0;
 		addr = 1;
 
 	case PTRACE_CONT: { /* restart after signal. */
@@ -724,15 +792,32 @@
 			pt_error_return(regs, EIO);
 			return;
 		}
+		if (addr != 1) {
+			if (addr & 3) {
+				pt_error_return(regs, EINVAL);
+				return;
+			}
+#ifdef DEBUG_PTRACE
+			printk ("Original: %08lx %08lx\n", child->tss.kregs->pc, child->tss.kregs->npc);
+			printk ("Continuing with %08lx %08lx\n", addr, addr+4);
+#endif
+			child->tss.kregs->pc = addr;
+			child->tss.kregs->npc = addr + 4;
+		}
+
 		if (request == PTRACE_SYSCALL)
 			child->flags |= PF_TRACESYS;
 		else
 			child->flags &= ~PF_TRACESYS;
+
 		child->exit_code = data;
-		if((addr != 1) & !(addr & 3)) {
-			child->tss.kregs->pc = addr;
-			child->tss.kregs->npc = addr + 4;
-		}
+#ifdef DEBUG_PTRACE
+		printk("CONT: %s [%d]: set exit_code = %x %x %x\n", child->comm,
+			child->pid, child->exit_code,
+			child->tss.kregs->pc,
+			child->tss.kregs->npc);
+		       
+#endif
 		wake_up_process(child);
 		pt_succ_return(regs, 0);
 		return;
@@ -787,7 +872,7 @@
 		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
-	current->tss.flags ^= 0x80000000;
+	current->tss.flags ^= MAGIC_CONSTANT;
 	notify_parent(current);
 	schedule();
 	/*
@@ -795,7 +880,12 @@
 	 * for normal use.  strace only continues with a signal if the
 	 * stopping signal is not SIGTRAP.  -brl
 	 */
-	if (current->exit_code)
+#ifdef DEBUG_PTRACE
+	printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
+		current->pid, current->exit_code);
+#endif
+	if (current->exit_code) {
 		current->signal |= (1 << (current->exit_code - 1));
+	}
 	current->exit_code = 0;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov