/*
 * process.c - process related management
 * 
 * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
 *          Mohamed Lamine Karaoui (2015)
 *          Alain Greiner (2016)
 *
 * 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 <almos_config.h>
#include <hal_types.h>
#include <hal_remote.h>
#include <hal_uspace.h>
#include <errno.h>
#include <printk.h>
#include <memcpy.h>
#include <bits.h>
#include <kmem.h>
#include <page.h>
#include <vmm.h>
#include <vfs.h>
#include <core.h>
#include <thread.h>
#include <list.h>
#include <scheduler.h>
#include <remote_spinlock.h>
#include <dqdt.h>
#include <cluster.h>
#include <ppm.h>
#include <boot_info.h>
#include <process.h>
#include <elf.h>

//////////////////////////////////////////////////////////////////////////////////////////
// Extern global variables
//////////////////////////////////////////////////////////////////////////////////////////

extern process_t process_zero;

//////////////////////////////////////////////////////////////////////////////////////////
// Process initialisation related functions
//////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////
process_t * process_alloc()
{
	kmem_req_t   req;

    req.type  = KMEM_PROCESS;
	req.size  = sizeof(process_t);
	req.flags = AF_KERNEL;

    return (process_t *)kmem_alloc( &req );
}

////////////////////////////////////////
void process_free( process_t * process )
{
    kmem_req_t  req;

	req.type = KMEM_PROCESS;
	req.ptr  = process;
	kmem_free( &req );
}

////////////////////////////////////////////
void process_zero_init( boot_info_t * info )
{
    // reset process descriptor
	memset( &process_zero , 0 , sizeof(process_t) );

    // initialize kernel code & data vsegs base addresses
	process_zero.vmm.code_vpn_base = (vpn_t)(info->kernel_code_start >> CONFIG_PPM_PAGE_SHIFT);
	process_zero.vmm.data_vpn_base = (vpn_t)(info->kernel_data_start >> CONFIG_PPM_PAGE_SHIFT);

    // reset threads and childs number
	process_zero.th_nr       = 0;
	process_zero.children_nr = 0;

    // set PID 
	process_zero.pid            = 0;
}

/////////////////////////////////////////////////
void process_reference_init( process_t * process,
                             pid_t       pid,
                             pid_t       ppid )
{
    // reset signal manager  TODO [AG]

    // reset the file descriptors array 
	process_fd_init( process );

    // reset the process files structures and cd_lock
	process->vfs_root_xp     = XPTR_NULL;
	process->vfs_cwd_xp      = XPTR_NULL;
	process->vfs_bin_xp      = XPTR_NULL;

    spinlock_init( &process->cd_lock );

    // reset children list root
    xlist_root_init( XPTR( local_cxy , &process->children_root ) );
	process->children_nr     = 0;

    // reset semaphore list root
    xlist_root_init( XPTR( local_cxy , &process->sem_root ) );
	process->sem_nr          = 0;

    // register new process in the parent children list
    xptr_t entry = XPTR( local_cxy , &process->brothers_list );
    xptr_t root  = XPTR( local_cxy , &process->children_root );
    xlist_add_first( root , entry );
    
    // reset th_tbl[] array
    uint32_t i;
    for( i = 0 ; i < CONFIG_THREAD_MAX_PER_CLUSTER ; i++ )
	{
        process->th_tbl[i] = NULL;
    }
    process->th_nr  = 0;
    spinlock_init( &process->th_lock );

    // reset process VMM 
	memset( &process->vmm , 0 , sizeof(vmm_t) );

    // initialize PID and PPID
	process->pid   = pid;
    process->ppid  = ppid;

    // set ref_xp field and is_ref flag
    process->is_ref = true;
    process->ref_xp = XPTR( local_cxy , process );

    // register new process descriptor in local cluster manager local_list
    cluster_process_local_link( process );

    // register new process descriptor in owner cluster manager copies_list
    cluster_process_copies_link( process );

	hal_wbflush();

} // end process_reference_init()

/////////////////////////////////////////////////////
error_t process_copy_init( process_t * local_process,
                           xptr_t      reference_process_xp )
{
    // replicate the remote process descriptor in new process descriptor
    xptr_t local_process_xp = XPTR( local_cxy , local_process );
    hal_remote_memcpy( local_process_xp , reference_process_xp , sizeof(process_t) );

    // initalise signal manager TODO [AG]

    // initialise file descriptors array TODO [AG]

    // initialise process files structures TODO [AG]

    // set the ref_xp field and clear the is_ref flag
    local_process->ref_xp = reference_process_xp;
    local_process->is_ref = false;

    // reset children list root (not used in a process descriptor copy)
    xlist_root_init( XPTR( local_cxy , &local_process->children_root ) );
    local_process->children_nr   = 0;           

    // reset brothers list (not used in a process descriptor copy)
    xlist_entry_init( XPTR( local_cxy , &local_process->brothers_list ) );

    // reset semaphores list root (not used in a process descriptor copy)
    xlist_root_init( XPTR( local_cxy , &local_process->sem_root ) );
    local_process->sem_nr        = 0;           

    // initialize th_tbl[] array as empty
    uint32_t i;
    for( i = 0 ; i < CONFIG_THREAD_MAX_PER_CLUSTER ; i++ )
	{
        local_process->th_tbl[i] = NULL;
    }
    local_process->th_nr  = 0;
    spinlock_init( &local_process->th_lock );

    // initialise  process VMM TODO [AG]

    // register new process descriptor in local cluster manager local_list
    cluster_process_local_link( local_process );

    // register new process descriptor in owner cluster manager copies_list
    cluster_process_copies_link( local_process );

	hal_wbflush();

    return 0;

}  // end process_copy_init()

///////////////////////////////////////////
void process_destroy( process_t * process )
{
	if( process->th_nr != 0 )  
    {
	    printk("\n[PANIC] in %s : process %x in cluster %x has still active threads\n",
               __FUNCTION__ , process->pid , local_cxy );
        hal_core_sleep();
    }

    // get local process manager pointer
    pmgr_t * pmgr = &LOCAL_CLUSTER->pmgr;

    // remove the process descriptor from local_list in local cluster manager 
    spinlock_lock( &pmgr->local_lock );
    xlist_unlink( XPTR( local_cxy , &process->local_list ) );
    spinlock_unlock( &pmgr->local_lock );

    // get extended pointer on copies_lock in owner cluster manager
    cxy_t  owner_cxy    = CXY_FROM_PID( process->pid );
	lpid_t lpid         = LPID_FROM_PID( process->pid );
    xptr_t copies_lock  = hal_remote_lwd( XPTR( owner_cxy , &pmgr->copies_lock[lpid] ) );

    // remove the local process descriptor from copies_list
    remote_spinlock_lock( copies_lock );
    xlist_unlink( XPTR( local_cxy , &process->copies_list ) );
    remote_spinlock_unlock( copies_lock );
    
    // synchronize memory
	hal_wbflush();

    // From this point, the process descriptor is unreachable 

    // release signal manager TODO [AG]

    // delete all open file descriptors
    process_fd_destroy( process );

    // Close bin file and decrease refcount for root and cwd
	if( process->vfs_bin_xp != XPTR_NULL ) vfs_close( process->vfs_bin_xp , NULL );
    vfs_file_count_down( process->vfs_root_xp );
    vfs_file_count_down( process->vfs_cwd_xp );

    // Destroy VMM
    vmm_destroy( process );

	process_dmsg("\n[INFO] %s for pid %d / page_faults = %d\n",
	         __FUNCTION__ , process->pid, process->vmm.pgfault_nr );

}  // end process_destroy()

////////////////////////////////////////
void process_kill( process_t * process )
{
    thread_t     * thread;    // pointer on current thead descriptor
    uint32_t       ltid;      // index in process th_tbl
    uint32_t       count;     // thread counter

    // get lock protecting th_tbl[]
    spinlock_lock( &process->th_lock );

    // first loop on threads to send the THREAD_SIG_KILL signal to all process threads
    // we use both "ltid" and "count" indexes, because it can exist "holes" in th_tbl
    for( ltid = 0 , count = 0  ; 
         (ltid < CONFIG_THREAD_MAX_PER_CLUSTER) && (count < process->th_nr) ; 
         ltid++ )
    {
        thread = process->th_tbl[ltid];

        if( thread != NULL )
        {
            thread_kill( thread );
            count++;
        }
    }   
    
    volatile uint32_t ko;

    // second loop on threads to wait acknowledge from scheduler,
    // unlink thread from process and parent thread, and release thread descriptor
    for( ltid = 0 , count = 0  ; 
         (ltid < CONFIG_THREAD_MAX_PER_CLUSTER) && (count < process->th_nr) ;
         ltid++ )
    {
        thread = process->th_tbl[ltid];

        if( thread != NULL )
        {
            // wait scheduler acknowledge
            do { ko = (thread->signals & THREAD_SIG_KILL); } while( ko );

            // unlink thread from brothers list if required
            if( (thread->flags & THREAD_FLAG_DETACHED) == 0 ) 
            xlist_unlink( XPTR( local_cxy , &thread->brothers_list ) );

            // unlink thread from process
            process_remove_thread( thread );

            // release memory for thread descriptor
            thread_destroy( thread );

            count++;
        }
    }

    // release lock protecting th_tbl[]
    spinlock_unlock( &process->th_lock );

    // release memory allocated for process descriptor
    process_destroy( process );

}  // end process_kill()






///////////////////////////////////////////////
process_t * process_get_local_copy( pid_t pid )
{
    error_t        error;
    bool_t         found;
    list_entry_t * iter;
    process_t    * process;     // pointer on local copy

    cluster_t * cluster = LOCAL_CLUSTER;

    // get lock protecting local list of processes
    spinlock_lock( &cluster->pmgr.local_lock );

    // scan the local list of process descriptors to find the process
    found = false;
    LIST_FOREACH( &cluster->pmgr.local_root , iter )
    {
        process = LIST_ELEMENT( iter , process_t , local_list );
        if( process->pid == pid )
        {
            found = true;
            break;
        }
    }

    // release lock protecting local list of processes
    spinlock_unlock( &cluster->pmgr.local_lock );

    // allocate memory for a local process descriptor 
    // and initialise it from reference if required
    if( !found )
    {
        // get extended pointer on reference process descriptor
        xptr_t reference = cluster_get_reference_process_from_pid( pid );

        // allocate memory for local process descriptor
        process = process_alloc();
        if( process == NULL )  return NULL;

        // initialize local process descriptor copy
        error = process_copy_init( process, reference );
        if( error ) return NULL;

        
        // register process in global copies_list
    }

    return process;
} // end process_get_local_copy()


//////////////////////////////////////////////////////////////////////////////////////////
// File descriptor array related functions
//////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////
void process_fd_init( process_t * process )
{
    uint32_t fd;

    remote_spinlock_init( XPTR( local_cxy , &process->fd_array.lock ) );
    process->fd_array.max       = CONFIG_PROCESS_FILE_MAX_NR;
    process->fd_array.current   = 0;

    // initialize array
    for ( fd = 0 ; fd < process->fd_array.max ; fd++ )
    {
        process->fd_array.array[fd] = XPTR_NULL;
    }
}

//////////////////////////////////////////////
void process_fd_destroy( process_t * process )
{
    uint32_t fd;

    // loop on all open file descriptors to close all open files 
    for( fd = 0 ; fd < process->fd_array.max ; fd++ )
    {
        xptr_t file_xp = process->fd_array.array[fd];
        if ( file_xp != XPTR_NULL ) vfs_close( file_xp , NULL );
    }
}

///////////////////////////////////////////////////
bool_t process_fd_array_full( process_t * process )
{
	return ( process->fd_array.current >= process->fd_array.max);  
}

/////////////////////////////////////////////////
error_t process_fd_allocate( process_t * process, 
                             xptr_t      file_xp, 
                             uint32_t   * ret_fd )
{
    bool_t    found;
    uint32_t  fd;

	remote_spinlock_lock( XPTR( local_cxy , &process->fd_array.lock ) );

    found   = false;

    for ( fd = 0; fd < process->fd_array.max; fd++ )
    {
        if ( process->fd_array.array[fd] == XPTR_NULL )
        {
            found = true;
            process->fd_array.array[fd] = file_xp;
	        process->fd_array.current++;
			*ret_fd = fd;
            break;
        }
    }

	remote_spinlock_unlock( XPTR( local_cxy , &process->fd_array.lock ) );

    if ( !found ) return EMFILE;
    else          return 0;
} 

////////////////////////////////////////////////
error_t process_fd_release( process_t * process,
                            uint32_t    fd )
{
    if ( (fd < 0) || (fd > process->fd_array.max) ) return EBADF;

    remote_spinlock_lock( XPTR( local_cxy , &process->fd_array.lock ) );

    process->fd_array.array[fd] = XPTR_NULL;
	process->fd_array.current--;

	remote_spinlock_unlock( XPTR( local_cxy , &process->fd_array.lock ) );

    return 0;
}

///////////////////////////////////////
void process_fd_copy( fd_array_t * dst,
                      fd_array_t * src )
{
    uint32_t fd;
    xptr_t   entry;

    remote_spinlock_lock( XPTR( local_cxy , &src->lock ) );

    // loop on all entries in source process fd_array        
    for( fd = 0 ; fd < src->max ; fd++ )
	{
		entry = src->array[fd];

		if( entry != XPTR_NULL )
		{
            // increment file descriptor ref count
            vfs_file_count_up( entry );

			// copy entry in destination process fd_array
			dst->array[fd] = entry;
		}
	}

    // release lock on source process fd_array
	remote_spinlock_unlock( XPTR( local_cxy , &src->lock ) );
} 

///////////////////////////////////////////
void process_fd_remote_copy( xptr_t dst_xp,
                             xptr_t src_xp )
{
    uint32_t fd;
    xptr_t   entry;

    // get cluster and local pointer for src fd_array
    cxy_t        src_cxy = GET_CXY( src_xp );
    fd_array_t * src_ptr = (fd_array_t *)GET_PTR( src_xp );

    // get cluster and local pointer for dst fd_array
    cxy_t        dst_cxy = GET_CXY( dst_xp );
    fd_array_t * dst_ptr = (fd_array_t *)GET_PTR( dst_xp );

    // get the remote lock protecting the src fd_array
	remote_spinlock_lock( XPTR( src_cxy , &src_ptr->lock ) );

    // get number of entries in fd_array
    uint32_t max = hal_remote_lw( XPTR( src_cxy , &src_ptr->max ) );

    // loop on all entries in source process fd_array        
    for( fd = 0 ; fd < max ; fd++ )
	{
		entry = (xptr_t)hal_remote_lwd( XPTR( src_cxy , &src_ptr->array[fd] ) );

		if( entry != XPTR_NULL )
		{
            // increment file descriptor ref count
            vfs_file_count_up( entry );

			// copy entry in destination process fd_array
			hal_remote_swd( XPTR( dst_cxy , &dst_ptr->array[fd] ) , entry );
		}
	}

    // release lock on source process fd_array
	remote_spinlock_unlock( XPTR( src_cxy , &src_ptr->lock ) );
} 



////////////////////////////////////////////////////////////////////////////////////
//  Thread related functions
////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////
error_t process_register_thread( process_t * process,
                                 thread_t  * thread,
                                 trdid_t   * trdid )
{
    ltid_t   ltid;
    bool_t   found;

    if( process == NULL )
    {
        printk("\n[PANIC] in %s : process argument is NULL\n", __FUNCTION__ );
        hal_core_sleep();
    }
    if( thread == NULL )
    {
        printk("\n[PANIC] in %s : thread argument is NULL\n", __FUNCTION__ );
        hal_core_sleep();
    }

    // search a free slot in th_tbl[]
    found = false;
    for( ltid = 0 ; ltid < CONFIG_THREAD_MAX_PER_CLUSTER ; ltid++ )
    {
        if( process->th_tbl[ltid] == NULL )
        {
            found = true;
            break;
        }
    }

    if( found )
    {
        // register thread in th_tbl[]
        process->th_tbl[ltid] = thread;
        process->th_nr++;

        // returns trdid
        *trdid = TRDID( local_cxy , ltid );
    }

    return (found) ? 0 : ENOMEM;
}
    
///////////////////////////////////////////////
void process_remove_thread( thread_t * thread )
{
    if( thread == NULL )
    {
        printk("\n[PANIC] in %s : thread argument is NULL\n", __FUNCTION__ );
        hal_core_sleep();
    }
    
    process_t * process = thread->process;

    // get thread local index
    ltid_t  ltid = LTID_FROM_TRDID( thread->trdid );

    // remove thread from th_tbl[]
    process->th_tbl[ltid] = NULL;
    process->th_nr--;
}

/////////////////////////////////////////////////////
error_t process_make_exec( exec_info_t  * exec_info )
{
    char           * path;                            // pathname to .elf file
    process_t      * process;                         // pointer on new process 
    pid_t            pid;                             // new process pid
    thread_t       * thread;                          // pointer on new thread
    pthread_attr_t   attr;                            // main thread attributes
    core_t         * core;                            // pointer on selected core
    lid_t            lid;                             // selected core local index
	error_t          error; 

	// get pid and pathname to .elf file  
	path  = exec_info->path;
    pid   = exec_info->pid;
    
    if( CXY_FROM_PID( pid ) != local_cxy )
    {
        printk("\n[PANIC] in %s : illegal process PID %x in cluster %x\n",
               __FUNCTION__ , pid , local_cxy );
        hal_core_sleep();
    }

    exec_dmsg("\n[INFO] %s enters in cluster %x for process %x / path = %s\n",
                __FUNCTION__ , local_cxy , pid , path );

    // create new process descriptor
    process = process_alloc();

    if( process == NULL )
    {
        printk("\n[ERROR] in %s : no memory in cluster %x for process %x / path = %s\n",
               __FUNCTION__ , local_cxy , pid , path );
        return ENOMEM;
    }

    // initialize the process descriptor as the reference
    process_reference_init( process , pid , exec_info->ppid );
                                
    // restore from exec_info the extended pointer on vfs root, cwd, and bin 
    process->vfs_root_xp = exec_info->vfs_root_xp;
    process->vfs_cwd_xp  = exec_info->vfs_cwd_xp;
    process->vfs_bin_xp  = exec_info->vfs_bin_xp;

    // restore from exec_info the embedded fd_array
    process_fd_remote_copy( XPTR( local_cxy , &process->fd_array ), exec_info->fd_array_xp );
                            
    exec_dmsg("\n[INFO] %s restaured fd-array in cluster %x for process %x / path = %s\n",
                __FUNCTION__, local_cxy , pid , path );

	// initialize signal manager TODO ??? [AG]
	// signal_manager_init( process );

    // initialize process VMM
	vmm_init( process );

    exec_dmsg("\n[INFO] %s initialized VMM in cluster %x for process %x / path = %s\n",
                __FUNCTION__ , local_cxy , pid , path );

    // register "code" and "data" vsegs as well a the process entry-point in VMM,
    // using informations contained in the elf file.
	error = elf_load_process( path , process );

	if( error )
	{
		printk("\n[ERROR] in %s : failed to access elf file for process %x / path = %s\n", 
		       __FUNCTION__, pid , path );
        process_destroy( process );
        return error;
	}

    // create "heap" vseg descriptor
    vseg_t * heap_vseg = vmm_create_vseg( process,
                                          CONFIG_VMM_HEAP_BASE,
                                          CONFIG_VMM_HEAP_SIZE,
                                          VSEG_TYPE_HEAP );
    if( heap_vseg == NULL )
	{
		printk("\n[ERROR] in %s : cannot create heap vseg for process %x / path = %s\n",
		       __FUNCTION__, pid , path );
        process_destroy( process );
        return error;
	}

    // create "stack" vseg descriptor for associated main thread
    vseg_t * stack_vseg = vmm_create_vseg( process,
                                           0,             // base defined by VMM
                                           0,             // size defined by VMM
                                           VSEG_TYPE_STACK );
    if( stack_vseg == NULL )
	{
		printk("\n[ERROR] in %s : cannot create stack vseg for process %x / path = %s\n",
		       __FUNCTION__, pid , path );
        process_destroy( process );
        return error;
	}

    // select a core in cluster
    lid  = cluster_select_local_core();
    core = &LOCAL_CLUSTER->core_tbl[lid];

    // initialize pthread attributes for main thread
    attr.pid          = pid;
    attr.entry_func   = (void*)process->vmm.entry_point;
    attr.entry_args   = exec_info->args_pointers; 
    attr.flags        = PT_FLAG_DETACH;             // main thread always detached
    attr.cxy          = local_cxy;
    attr.lid          = lid;

    // create and initialise thread descriptor, link thread & process
	error = thread_user_create( &thread, 
                                &attr,
                                stack_vseg->min,
                                stack_vseg->max - stack_vseg->min );
	if( error )
	{
		printk("\n[ERROR] in %s : cannot create thread for process %x / path = %s\n",
		       __FUNCTION__, pid , path );
        process_destroy( process );
        return error;
	}

	// Register thread in scheduler 
	sched_register_thread( core , thread );

	exec_dmsg("\n[INFO] %s created thread for process %x on core %d in cluster %x\n",
               __FUNCTION__ , process->pid , core->lid , local_cxy );

    // activate new thread 
	thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_GLOBAL );

	return 0;

} // end process_make_exec()

//////////////////////////
void process_init_create()
{
	process_t   * process_init;  // process_init descriptor 
	thread_t    * thread_init;   // process_init main thread descriptor
    pid_t         init_pid;      // process_init pid
    exec_info_t   exec_info;     // structure to be passed to process_make_exec()

	error_t error1 = 0;
	error_t error2 = 0;
	error_t error3 = 0;

	process_dmsg("\n[INFO] %s enters in cluster %x\n", __FUNCTION__ , local_cxy );

    // allocate memory for process descriptor
    process_init = process_alloc();
    if( process_init == NULL )
    {
        printk("\n[PANIC] in %s : no memory for process descriptor in cluster %x\n",
               __FUNCTION__ , local_cxy );
        hal_core_sleep();
    }

    // get a pid from the local cluster 
    xptr_t xp_process = XPTR( local_cxy , process_init );
    error1 = cluster_pid_alloc( xp_process , &init_pid );
    if( error1 )
    {
        printk("\n[PANIC] in %s : cannot get PID in cluster %x\n", 
               __FUNCTION__ , local_cxy );
		hal_core_sleep();
    }
 
    // initializes process_init descriptor as the reference
	process_reference_init( process_init , init_pid , process_zero.pid );

    // initialize process_init VMM
	vmm_init( process_init );

    // initializes vfs_root and vfs_cwd from process_zero
	vfs_file_count_up( process_zero.vfs_root_xp );
	process_init->vfs_root_xp = process_zero.vfs_root_xp;

	vfs_file_count_up( process_zero.vfs_cwd_xp );
	process_init->vfs_cwd_xp  = process_zero.vfs_cwd_xp;

    // update children list in process_zero
	xlist_add_last( XPTR( local_cxy , &process_zero.children_root ),
                    XPTR( local_cxy , &process_init->brothers_list ) );
	process_zero.children_nr = 1;

    // TODO open stdin / stdout / stderr pseudo-files
    xptr_t  stdin_xp;
    xptr_t  stdout_xp;
    xptr_t  stderr_xp; 

	// error1 = vfs_open( process_init->vfs_cwd_xp, NULL, VFS_O_RDONLY, &stdin_xp );
	// error2 = vfs_open( process_init->vfs_cwd_xp, NULL, VFS_O_WRONLY, &stdout_xp );
	// error3 = vfs_open( process_init->vfs_cwd_xp, NULL, VFS_O_WRONLY, &stderr_xp );

	if( error1 || error2 || error3 )
	{
		if( !error1 ) vfs_close( stdin_xp  , NULL );
		if( !error2 ) vfs_close( stdout_xp , NULL );
		if( !error3 ) vfs_close( stderr_xp , NULL );
    
		printk("\n[PANIC] in %s : cannot open stdin/stdout/stderr in cluster %x\n",
               __FUNCTION__ , local_cxy );
        hal_core_sleep();
	}

    // register stdin / stdout / stderr in the fd_array 3 first slots 
	process_init->fd_array.array[0] = stdin_xp;
	process_init->fd_array.array[1] = stdout_xp;
	process_init->fd_array.array[2] = stderr_xp;
    process_init->fd_array.current  = 3;

    // initialize the exec_info structure
    exec_info.pid          = process_init->pid;
    exec_info.ppid         = process_init->ppid;
    exec_info.fd_array_xp  = XPTR( local_cxy , &process_init->fd_array );
    exec_info.vfs_root_xp  = process_init->vfs_root_xp;
    exec_info.vfs_cwd_xp   = process_init->vfs_cwd_xp;
    exec_info.vfs_bin_xp   = process_init->vfs_bin_xp;

    // TODO thread_init ???
    // exec_info.args_nr      = 1;
    // exec_info.envs_nr      = 1;
    // strcpy( exec_info.args[0] , "init" );
    // strcpy( exec_info.envs[0] , "ALMOS-MKH.CONFIG = "CONFIG_ALMOS_VERSION );
    // strcpy( exec_info.path    , INIT_PATHNAME );

    // create process_init and thread_init
	error1 = process_make_exec( &exec_info );
	if( error1 )
	{
        printk("\n[PANIC] in %s : cannot create main thread in cluster %x\n",
               __FUNCTION__ , local_cxy );
		hal_core_sleep();
    }

	process_dmsg("\n[INFO] %s successfully exit in cluster %x\n", __FUNCTION__ , local_cxy );
		
    hal_wbflush();

} // end process_init_create()


