/**********************************************************************
ptrace.c

Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
terms and conditions.

Jeff Dike (jdike@karaya.com) : Modified for integration into uml
**********************************************************************/

#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>

#include "ptproxy.h"
#include "debug.h"
#include "kern_util.h"
#include "ptrace_user.h"
#include "tt.h"
#include "os.h"

long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2,
		  long arg3, long arg4, pid_t child, int *ret)
{
	sigset_t relay;
	long result;
	int status;

	*ret = 0;
	if(debugger->debugee->died) return(-ESRCH);

	switch(arg1){
	case PTRACE_ATTACH:
		if(debugger->debugee->traced) return(-EPERM);

		debugger->debugee->pid = arg2;
		debugger->debugee->traced = 1;

		if(is_valid_pid(arg2) && (arg2 != child)){
			debugger->debugee->in_context = 0;
			kill(arg2, SIGSTOP);
			debugger->debugee->event = 1;
			debugger->debugee->wait_status = W_STOPCODE(SIGSTOP);
		}
		else {
			debugger->debugee->in_context = 1;
			if(debugger->debugee->stopped) 
				child_proxy(child, W_STOPCODE(SIGSTOP));
			else kill(child, SIGSTOP);
		}

		return(0);

	case PTRACE_DETACH:
		if(!debugger->debugee->traced) return(-EPERM);
		
		debugger->debugee->traced = 0;
		debugger->debugee->pid = 0;
		if(!debugger->debugee->in_context)
			kill(child, SIGCONT);

		return(0);

	case PTRACE_CONT:
		if(!debugger->debugee->in_context) return(-EPERM);
		*ret = PTRACE_CONT;
		return(ptrace(PTRACE_CONT, child, arg3, arg4));

#ifdef UM_HAVE_GETFPREGS
	case PTRACE_GETFPREGS:
	{
		long regs[FP_FRAME_SIZE];
		int i, result;

		result = ptrace(PTRACE_GETFPREGS, child, 0, regs);
		if(result == -1) return(-errno);
		
		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
			ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
			       regs[i]);
		return(result);
	}
#endif

#ifdef UM_HAVE_GETFPXREGS
	case PTRACE_GETFPXREGS:
	{
		long regs[FPX_FRAME_SIZE];
		int i, result;

		result = ptrace(PTRACE_GETFPXREGS, child, 0, regs);
		if(result == -1) return(-errno);
		
		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
			ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
			       regs[i]);
		return(result);
	}
#endif

#ifdef UM_HAVE_GETREGS
	case PTRACE_GETREGS:
	{
		long regs[FRAME_SIZE];
		int i, result;

		result = ptrace(PTRACE_GETREGS, child, 0, regs);
		if(result == -1) return(-errno);

		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
			ptrace (PTRACE_POKEDATA, debugger->pid,
				arg4 + 4 * i, regs[i]);
		return(result);
	}
	break;
#endif

	case PTRACE_KILL:
		result = ptrace(PTRACE_KILL, child, arg3, arg4);
		if(result == -1) return(-errno);

		return(result);

	case PTRACE_PEEKDATA:
	case PTRACE_PEEKTEXT:
	case PTRACE_PEEKUSR:
		/* The value being read out could be -1, so we have to 
		 * check errno to see if there's an error, and zero it
		 * beforehand so we're not faked out by an old error
		 */

		errno = 0;
		result = ptrace(arg1, child, arg3, 0);
		if((result == -1) && (errno != 0)) return(-errno);

		result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result);
		if(result == -1) return(-errno);
			
		return(result);

	case PTRACE_POKEDATA:
	case PTRACE_POKETEXT:
	case PTRACE_POKEUSR:
		result = ptrace(arg1, child, arg3, arg4);
		if(result == -1) return(-errno);

		if(arg1 == PTRACE_POKEUSR) ptrace_pokeuser(arg3, arg4);
		return(result);

#ifdef UM_HAVE_SETFPREGS
	case PTRACE_SETFPREGS:
	{
		long regs[FP_FRAME_SIZE];
		int i;

		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
			regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
					  arg4 + 4 * i, 0);
		result = ptrace(PTRACE_SETFPREGS, child, 0, regs);
		if(result == -1) return(-errno);

		return(result);
	}
#endif

#ifdef UM_HAVE_SETFPXREGS
	case PTRACE_SETFPXREGS:
	{
		long regs[FPX_FRAME_SIZE];
		int i;

		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
			regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
					  arg4 + 4 * i, 0);
		result = ptrace(PTRACE_SETFPXREGS, child, 0, regs);
		if(result == -1) return(-errno);

		return(result);
	}
#endif

#ifdef UM_HAVE_SETREGS
	case PTRACE_SETREGS:
	{
		long regs[FRAME_SIZE];
		int i;

		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
			regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid,
					 arg4 + 4 * i, 0);
		result = ptrace(PTRACE_SETREGS, child, 0, regs);
		if(result == -1) return(-errno);

		return(result);
	}
#endif

	case PTRACE_SINGLESTEP:
		if(!debugger->debugee->in_context) return(-EPERM);
		sigemptyset(&relay);
		sigaddset(&relay, SIGSEGV);
		sigaddset(&relay, SIGILL);
		sigaddset(&relay, SIGBUS);
		result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4);
		if(result == -1) return(-errno);
		
		status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP,
				       &relay);
		child_proxy(child, status);
		return(result);

	case PTRACE_SYSCALL:
		if(!debugger->debugee->in_context) return(-EPERM);
		result = ptrace(PTRACE_SYSCALL, child, arg3, arg4);
		if(result == -1) return(-errno);

		*ret = PTRACE_SYSCALL;
		return(result);

	case PTRACE_TRACEME:
	default:
		return(-EINVAL);
	}
}

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-file-style: "linux"
 * End:
 */
