/*
 * @OSF_FREE_FREE_COPYRIGHT@
 * 
 */
/*
 * HISTORY
 * $Log: parent_linux.c,v $
 * Revision 1.1.2.2  1996/11/18  18:16:14  barbou
 * 	Changed the syscall exception mask for second servers from
 * 	EXC_MASK_BAD_INSTRUCTION to EXC_MASK_SYSCALL.
 * 	Use the ioperm() syscall instead of iopl() to get Mach privileges.
 * 	In parent_linux_exit(), terminate the Mach task instead of using exit()
 * 	because exit() terminates only the current thread.
 * 	[1996/11/18  18:00:04  barbou]
 *
 * Revision 1.1.2.1  1996/09/09  17:18:43  barbou
 * 	Created.
 * 	[1996/08/30  17:01:30  barbou]
 * 
 * $EndLog$
 */

#include <mach/exception.h>
#include <mach/mach_interface.h>

#include <osfmach3/mach3_debug.h>
#include <osfmach3/parent_server.h>
#include <osfmach3/mach_init.h>

#include <linux/unistd.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/termios.h>

extern int errno;

#define __NR_parent_linux_getpid __NR_getpid
#define __NR_parent_linux_sigaction __NR_sigaction
#define __NR_parent_linux_write __NR_write
#define __NR_parent_linux_read __NR_read
#define __NR_parent_linux_ioctl __NR_ioctl
#define __NR_parent_linux__exit __NR_exit
#define __NR_parent_linux_ioperm __NR_ioperm


_syscall0(int, parent_linux_getpid)

_syscall3(int, parent_linux_sigaction,
	  int, sig,
	  const struct sigaction *, nsa,
	  struct sigaction *, osa)

_syscall3(int, parent_linux_write,
	  int, fd,
	  char *, buf,
	  int, count);

_syscall3(int, parent_linux_read,
	  int, fd,
	  char *, buf,
	  int, count);

_syscall3(int, parent_linux_ioctl,
	  int, fd,
	  int, request,
	  char *, data);

_syscall1(int, parent_linux__exit,
	  int, error_code);

_syscall3(int, parent_linux_ioperm,
	  unsigned long, from,
	  unsigned long, num,
	  int, on);


void
parent_linux_sig_handler(
	int sig)
{
	printk("** SERVER RECEIVED SIGNAL %d. Calling Debugger...\n", sig);
	Debugger("signal");
}


void
parent_linux_catchall_signals(void)
{
	struct sigaction sa;
	int sig;

	sa.sa_handler = parent_linux_sig_handler;
	sa.sa_mask = 0;
	sa.sa_flags = 0;

	for (sig = 1; sig < NSIG; sig++) {
		(void) parent_linux_sigaction(sig, &sa, (struct sigaction *) 0);
	}
}

struct termios parent_linux_console_termios;


int
parent_linux_grab_console(void)
{
	struct termios termios;

	if (parent_server_ioctl(0, TCGETS,
				(char *) &parent_linux_console_termios) < 0) {
		printk("parent_linux_grab_console:ioctl(TCGETS) -> %d\n",
		       errno);
	}
	termios = parent_linux_console_termios;

	termios.c_iflag = IGNBRK;
	termios.c_oflag = 0;
	termios.c_cflag = CS8|CREAD|PARENB;
	termios.c_lflag = 0;
	termios.c_cc[VMIN] = 1;
	termios.c_cc[VTIME] = 0;

	if (parent_server_ioctl(0, TCSETS, (char *) &termios) < 0) {
		printk("parent_linux_grab_console: ioctl(TCSETS) -> %d\n",
		       errno);
	}

	return 0;
}


int
parent_linux_release_console(void)
{
	if (parent_server_ioctl(0, TCSETS,
				(char *) &parent_linux_console_termios) < 0) {
		printk("parent_linux_release_console: ioctl(TCSETS) -> %d\n",
		       errno);
	}

	return 0;
}


exception_mask_t
parent_linux_syscall_exc_mask(void)
{
	return EXC_MASK_SYSCALL;
}


int
parent_linux_get_mach_privilege(void)
{
	return parent_linux_ioperm(0, 0, 1);	/* see sys_ioperm() */
}


int parent_linux_exit(
	int code)
{
	kern_return_t	kr;

	/*
	 * exit() doesn't work very well for multi-threaded processes
	 * in Linux: it exits only the calling thread.
	 * Terminate the Mach task: this should cause the Linux processes
	 * to exit thanks to the dead-name notifications.
	 */
	kr = task_terminate(mach_task_self());
	if (kr != KERN_SUCCESS) {
		MACH3_DEBUG(1, kr, ("parent_linux_server: task_terminate"));
	}
	/* try an exit if this fails... but I doubt it fails ! */
	parent_linux__exit(code);
	/*NOTREACHED*/
	return code;
}