/*
 * syscall.c - kernel unified syscall entry-point
 * 
 * Author Ghassan Almaless (2008,2009,2010,2011,2012)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH.is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH.is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH.; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <syscall.h>
#include <errno.h>
#include <time.h>
#include <cpu.h>
#include <dma.h>
#include <thread.h>
#include <process.h>
#include <scheduler.h>
#include <kmem.h>
#include <kdmsg.h>
#include <sys-vfs.h>
#include <cpu-trace.h>
#include <semaphore.h>
#include <cond_var.h>
#include <barrier.h>
#include <rwlock.h>
#include <vmm.h>
#include <signal.h>
#include <page.h>

/////////////////////////////////
static error_t sys_notAvailable()
{
	printk(WARNING, "WARNING: Undefined system call\n");
	return ENOSYS;
}

//////////////////////////////////////////////////////////////////////////
// Syscall vector definition
//////////////////////////////////////////////////////////////////////////

typedef int (*sys_func_t) ();

static const sys_func_t sys_call_tbl[SYSCALLS_NR] = 
{
	sys_thread_exit,      // 0
	sys_mmap,             // 1
	sys_thread_create,    // 2
	sys_thread_join,      // 3
	sys_thread_detach,    // 4
	sys_thread_yield,     // 5
	sys_sem,              // 6
	sys_cond_var,         // 7
	sys_barrier,          // 8
	sys_rwlock,           // 9
	sys_thread_sleep,     // 10
	sys_thread_wakeup,    // 11
	sys_open,             // 12
	sys_creat,            // 13
	sys_read,             // 14
	sys_write,            // 15
	sys_lseek,            // 16
	sys_close,            // 17
	sys_unlink,           // 18
	sys_pipe,             // 19
	sys_chdir,            // 20
	sys_mkdir,            // 21
	sys_mkfifo,           // 22
	sys_opendir,          // 23
	sys_readdir,          // 24
	sys_closedir,         // 25
	sys_getcwd,           // 26
	sys_clock,            // 27
	sys_alarm,            // 28
	sys_dma_memcpy,       // 29
	sys_utls,             // 30
	sys_notAvailable,	  // 31	 reserved for sigreturn TODO ??? [AG]
	sys_signal,           // 32
	sys_sigreturn_setup,  // 33
	sys_kill,             // 34
	sys_getpid,           // 35
	sys_fork,             // 36
	sys_exec,             // 37
	sys_thread_getattr,   // 38
	sys_ps,               // 39
	sys_madvise,          // 40
	sys_mcntl,            // 41
	sys_stat,             // 42
	sys_thread_migrate,   // 43
	sys_sbrk,             // 44
	sys_rmdir,            // 45
	sys_ftime,            // 46
	sys_chmod,            // 47
	sys_fsync,            // 48
	sys_gettimeofday,     // 49
	sys_times             // 50
};

/////////////////////////////
reg_t do_syscall ( reg_t arg0,
		           reg_t arg1,
		           reg_t arg2,
		           reg_t arg3,
		           reg_t service_num )
  
{
	int32_t    return_val = 0;                        // default value 
    thread_t * this       = CURRENT_THREAD;
	cpu_t    * cpu        = thread_current_cpu( this );
	
    // change thread state
	this->state = S_KERNEL;
	
    // prepare kernel time computation
	tm_usr_compute( this );

/*  TODO Pourqoi cette sauvegarde ? [AG]
	return_val = cpu_context_save( &this->info.pss );
   
	if(return_val != 0)
	{
		// Reload these pointers as the process may be forked
		this = CURRENT_THREAD;
		cpu  = current_cpu;
		cpu_wbflush();
		
		return_val = this->info.retval;
		goto END_DO_SYSCALL;
	}
*/

    // check syscall index
	if( service_num >= SYSCALLS_NR )
	{
		printk(INFO, "INFO %s : Undefined sysccall %d / thread = %x in cluster %x\n",
		       __FUNCTION__, service_num, this, cluster_cxy );

		this->info.errno = ENOSYS;
		goto END_DO_SYSCALL;
	}

    // debug message before syscall execution
	if( this->info.isTraced == true )
	{
		printk(DEBUG, "DEBUG %s : Pid %d / Tid %d / CPU %d in cluster %x\n"
                      "           Service %d / Arg0 %x / Arg1 %x / Arg2 %x / Arg3 %x\n",
		       __FUNCTION__, this->process->pid, this->info.order, cpu->lid, cluster_cxy,
		       service_num, arg0, arg1, arg2, arg3);
	}

    // reset errno
	this->info.errno = 0;

    // enable interrupts
	cpu_enable_all_irq( NULL );
  
    // call the proper kernel function
	return_val = sys_call_tbl[service_num] (arg0,arg1,arg2,arg3);

    // disable interrupts	
	cpu_disable_all_irq(NULL);
   
    // debug message after syscall execution
	if(this->info.isTraced == true)
	{
		printk(DEBUG, "DEBUG %s : Pid %d / Tid %d / CPU %d in cluster %x\n"
		              "           Service %d / Return = %x / Error = %d\n",
		       __FUNCTION__, this->process->pid, this->info.order, cpu->lid, cluster_cxy,
		       service_num, return_val, this->info.errno );
	}

END_DO_SYSCALL:

    // compute kernel time
	tm_sys_compute(this);

    // update thread state
	this->info.retval = return_val;
	this->state = S_USER;

    // notify syscall completion
	signal_notify( this );

	return return_val;
} // end do_syscall()
