Index: trunk/kernel/kern/process.c
===================================================================
--- trunk/kernel/kern/process.c	(revision 416)
+++ trunk/kernel/kern/process.c	(revision 428)
@@ -39,4 +39,5 @@
 #include <core.h>
 #include <thread.h>
+#include <chdev.h>
 #include <list.h>
 #include <string.h>
@@ -56,5 +57,6 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
-extern process_t process_zero;
+extern process_t           process_zero;     // allocated in kernel_init.c
+extern chdev_directory_t   chdev_dir;        // allocated in kernel_init.c
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -84,39 +86,14 @@
 }
 
-/////////////////////////////////////////////
-void process_zero_init( process_t * process )
-{
-    // initialize PID, PPID anf PREF
-    process->pid    = 0;
-    process->ppid   = 0;
-    process->ref_xp = XPTR( local_cxy , process );
-
-    // reset th_tbl[] array as empty
-    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 );
-
-	hal_fence();
-
-process_dmsg("\n[DBG] %s : core[%x,%d] exit for process %x\n",
-__FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , process->pid );
-
-}  // end process_zero_init()
-
 /////////////////////////////////////////////////
 void process_reference_init( process_t * process,
                              pid_t       pid,
-                             pid_t       ppid,
+                             xptr_t      parent_xp,
                              xptr_t      model_xp )
 {
+    cxy_t       parent_cxy;
+    process_t * parent_ptr;
     cxy_t       model_cxy;
     process_t * model_ptr;
-	error_t     error1;
-	error_t     error2;
-	error_t     error3;
     xptr_t      stdin_xp;
     xptr_t      stdout_xp;
@@ -126,7 +103,12 @@
     uint32_t    stderr_id;
     error_t     error;
-
-process_dmsg("\n[DBG] %s : core[%x,%d] enters for process %x / ppid = %x\n",
-__FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , pid , ppid );
+    uint32_t    txt_id;
+    char        rx_path[40];
+    char        tx_path[40];
+    xptr_t      chdev_xp;
+    chdev_t *   chdev_ptr;
+    cxy_t       chdev_cxy;
+    pid_t       model_pid;
+    pid_t       parent_pid;
 
     // get model process cluster and local pointer
@@ -134,8 +116,20 @@
     model_ptr = (process_t *)GET_PTR( model_xp );
 
-    // initialize PID, PPID, and REF
-	process->pid    = pid;
-    process->ppid   = ppid;
-    process->ref_xp = XPTR( local_cxy , process );
+    // get parent process cluster and local pointer
+    parent_cxy = GET_CXY( parent_xp );
+    parent_ptr = (process_t *)GET_PTR( parent_xp );
+
+    // get model_pid and parent_pid
+    parent_pid = hal_remote_lw( XPTR( parent_cxy , &parent_ptr->pid ) );
+    model_pid  = hal_remote_lw( XPTR( model_cxy  , &model_ptr->pid ) );
+
+process_dmsg("\n[DBG] %s : core[%x,%d] enters / pid = %x / ppid = %x / model_pid = %x\n",
+__FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , pid , parent_pid , model_pid );
+
+    // initialize PID, REF_XP, PARENT_XP, and STATE
+	process->pid       = pid;
+    process->ref_xp    = XPTR( local_cxy , process );
+    process->parent_xp = parent_xp;
+    process->state     = PROCESS_STATE_RUNNING;
 
     // initialize vmm as empty 
@@ -143,6 +137,5 @@
     assert( (error == 0) , __FUNCTION__ , "cannot initialize VMM\n" );
  
-
-process_dmsg("\n[DBG] %s : core[%x,%d] / vmm empty for process %x\n", 
+process_dmsg("\n[DBG] %s : core[%x,%d] / vmm inialised as empty for process %x\n", 
 __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , pid );
 
@@ -150,9 +143,24 @@
     process_fd_init( process );
 
-    // create stdin / stdout / stderr pseudo-files
-    if( ppid == 0 )                                       // process_init
-    {
-        error1 = vfs_open( process,
-                           CONFIG_INIT_STDIN,
+    // define the stdin/stdout/stderr pseudo files <=> select a TXT terminal.
+    // - if INIT (pid == 1)         => link to kernel TXT[0] 
+    // - if KSH[i] (model_pid == 1) => allocate a free TXT[i] 
+    // - if USER process            => same terminal as model
+
+    if( (pid == 1) || (model_pid == 1)) // INIT or KSH process
+    {
+        if (pid == 1 )  txt_id = 0;                    // INIT
+        else            txt_id = process_txt_alloc();  // KSH[i]
+
+        // attach process to TXT[txt_id]
+        process_txt_attach( process , txt_id ); 
+
+        // build path to TXT_RX[i] and TXT_TX[i] chdevs
+        snprintf( rx_path , 40 , "/dev/external/txt%d_rx", txt_id );
+        snprintf( tx_path , 40 , "/dev/external/txt%d_tx", txt_id );
+
+        // create stdin pseudo file          
+        error = vfs_open( process,
+                           rx_path,
                            O_RDONLY, 
                            0,                // FIXME chmod
@@ -160,6 +168,10 @@
                            &stdin_id );
 
-        error2 = vfs_open( process,
-                           CONFIG_INIT_STDOUT,
+        assert( (error == 0) , __FUNCTION__ , "cannot open stdin pseudo file" );
+        assert( (stdin_id == 0) , __FUNCTION__ , "stdin index must be 0" );
+
+        // create stdout pseudo file          
+        error = vfs_open( process,
+                           tx_path,
                            O_WRONLY, 
                            0,                // FIXME chmod
@@ -167,40 +179,38 @@
                            &stdout_id );
 
-        error3 = vfs_open( process,
-                           CONFIG_INIT_STDERR,
+        assert( (error == 0) , __FUNCTION__ , "cannot open stdout pseudo file" );
+        assert( (stdout_id == 1) , __FUNCTION__ , "stdout index must be 1" );
+
+        // create stderr pseudo file          
+        error = vfs_open( process,
+                           tx_path,
                            O_WRONLY, 
                            0,                // FIXME chmod
                            &stderr_xp, 
                            &stderr_id );
-    }
-    else                                                  // any other process
-    {
-        error1 = vfs_open( process,
-                           CONFIG_USER_STDIN,
-                           O_RDONLY, 
-                           0,                // FIXME chmod
-                           &stdin_xp, 
-                           &stdin_id );
-
-        error2 = vfs_open( process,
-                           CONFIG_USER_STDOUT,
-                           O_WRONLY, 
-                           0,                // FIXME chmod
-                           &stdout_xp, 
-                           &stdout_id );
-
-        error3 = vfs_open( process,
-                           CONFIG_USER_STDERR,
-                           O_WRONLY, 
-                           0,                // FIXME chmod
-                           &stderr_xp, 
-                           &stderr_id );
-    }
-
-    assert( ((error1 == 0) && (error2 == 0) && (error3 == 0)) , __FUNCTION__ ,
-    "cannot open stdin/stdout/stderr pseudo files\n");
-
-    assert( ((stdin_id == 0) && (stdout_id == 1) && (stderr_id == 2)) , __FUNCTION__ ,
-    "bad indexes : stdin %d / stdout %d / stderr %d \n", stdin_id , stdout_id , stderr_id );
+
+        assert( (error == 0) , __FUNCTION__ , "cannot open stderr pseudo file" );
+        assert( (stderr_id == 2) , __FUNCTION__ , "stderr index must be 2" );
+
+    }
+    else                                            // normal user process 
+    {
+        // get extended pointer on model process TXT chdev
+        chdev_xp = chdev_from_file( model_ptr->fd_array.array[0] );
+ 
+        // get cluster and local pointer on chdev
+        chdev_cxy = GET_CXY( chdev_xp );
+        chdev_ptr = (chdev_t *)GET_PTR( chdev_xp );
+ 
+        // get TXT terminal index
+        txt_id = hal_remote_lw( XPTR( chdev_cxy , &chdev_ptr->channel ) );
+
+        // attach process to TXT[txt_id]
+        process_txt_attach( process , txt_id ); 
+
+        // copy all open files from model process fd_array to this process
+        process_fd_remote_copy( XPTR( local_cxy , &process->fd_array ),
+                                XPTR( model_cxy , &model_ptr->fd_array ) );
+    }
 
     // initialize specific inodes root and cwd
@@ -214,9 +224,5 @@
     remote_rwlock_init( XPTR( local_cxy , &process->cwd_lock ) );
 
-    // copy all open file descriptors (other than stdin / stdout / stderr) 
-    process_fd_remote_copy( XPTR( local_cxy , &process->fd_array ),
-                            XPTR( model_cxy , &model_ptr->fd_array ) );
-
-process_dmsg("\n[DBG] %s : core[%x,%d] / fd array for process %x\n", 
+process_dmsg("\n[DBG] %s : core[%x,%d] / fd array initialised for process %x\n", 
 __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , pid );
 
@@ -224,4 +230,5 @@
     xlist_root_init( XPTR( local_cxy , &process->children_root ) );
     process->children_nr     = 0;
+    remote_spinlock_init( XPTR( local_cxy , &process->children_lock ) );
 
     // reset semaphore / mutex / barrier / condvar list roots 
@@ -256,5 +263,5 @@
 __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , pid );
 
-}  // process_reference init()
+}  // process_reference_init()
 
 /////////////////////////////////////////////////////
@@ -268,10 +275,11 @@
     process_t * ref_ptr = (process_t *)GET_PTR( reference_process_xp );
 
-    // set the pid, ppid, ref_xp fields in local process
-    local_process->pid    = hal_remote_lw( XPTR( ref_cxy , &ref_ptr->pid ) );
-    local_process->ppid   = hal_remote_lw( XPTR( ref_cxy , &ref_ptr->ppid ) );
-    local_process->ref_xp = reference_process_xp;
-
-process_dmsg("\n[DBG] %s : core[%x,%d] enters for process %x in cluster %x\n",
+    // initialize PID, REF_XP, PARENT_XP, and STATE
+    local_process->pid       = hal_remote_lw(  XPTR( ref_cxy , &ref_ptr->pid ) );
+    local_process->parent_xp = hal_remote_lwd( XPTR( ref_cxy , &ref_ptr->parent_xp ) );
+    local_process->ref_xp    = reference_process_xp;
+    local_process->state     = PROCESS_STATE_RUNNING;
+
+process_dmsg("\n[DBG] %s : core[%x,%d] enters for process %x\n",
 __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , local_process->pid );
 
@@ -291,7 +299,8 @@
     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 ) );
+    remote_spinlock_init( XPTR( local_cxy , &local_process->children_lock ) );
+
+    // reset children_list (not used in a process descriptor copy)
+    xlist_entry_init( XPTR( local_cxy , &local_process->children_list ) );
 
     // reset semaphores list root (not used in a process descriptor copy)
@@ -318,5 +327,5 @@
 	hal_fence();
 
-process_dmsg("\n[DBG] %s : core[%x,%d] exit for process %x in cluster %x\n",
+process_dmsg("\n[DBG] %s : core[%x,%d] exit for process %x\n",
 __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , local_process->pid );
 
@@ -328,14 +337,21 @@
 void process_destroy( process_t * process )
 {
-	if( process->th_nr != 0 )
-    {
-        panic("process %x in cluster %x has still active threads",
-              process->pid , local_cxy );
-    }
+    xptr_t      parent_xp;
+    process_t * parent_ptr;
+    cxy_t       parent_cxy;
+    xptr_t      parent_thread_xp;
+    xptr_t      children_lock_xp;
+    xptr_t      copies_lock_xp;
+
+	assert( (process->th_nr == 0) , __FUNCTION__ ,
+    "process %x in cluster %x has still active threads", process->pid , local_cxy );
+
+process_dmsg("\n[DBG] %s : core[%x,%d] enter for process %x\n",
+__FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , process->pid );
 
     // get local process manager pointer
     pmgr_t * pmgr = &LOCAL_CLUSTER->pmgr;
 
-    // remove the process descriptor from local_list in cluster manager
+    // remove process from local_list in cluster manager
     remote_spinlock_lock( XPTR( local_cxy , &pmgr->local_lock ) );
     xlist_unlink( XPTR( local_cxy , &process->local_list ) );
@@ -343,23 +359,46 @@
 
     // 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  = XPTR( owner_cxy , &pmgr->copies_lock[lpid] );
-
-    // remove the local process descriptor from copies_list
-    remote_spinlock_lock( copies_lock );
+    cxy_t  owner_cxy = CXY_FROM_PID( process->pid );
+	lpid_t lpid      = LPID_FROM_PID( process->pid );
+    copies_lock_xp   = XPTR( owner_cxy , &pmgr->copies_lock[lpid] );
+
+    // remove local process from copies_list
+    remote_spinlock_lock( copies_lock_xp );
     xlist_unlink( XPTR( local_cxy , &process->copies_list ) );
-    remote_spinlock_unlock( copies_lock );
+    remote_spinlock_unlock( copies_lock_xp );
+
+    // for reference process only
+    if( XPTR( local_cxy , process ) == process->ref_xp )
+    {
+        // remove reference process from txt_list
+        process_txt_detach( process );
+
+        // get pointers on parent process
+        parent_xp  = process->parent_xp;
+        parent_cxy = GET_CXY( parent_xp );
+        parent_ptr = GET_PTR( parent_xp );
+
+        // get extended pointer on children_lock in parent process
+        children_lock_xp = XPTR( parent_cxy , &parent_ptr->children_lock );
+
+        // remove process from children_list
+        remote_spinlock_lock( children_lock_xp );
+        xlist_unlink( XPTR( local_cxy , &process->children_list ) );
+        remote_spinlock_unlock( children_lock_xp );
+
+        // get extende pointer on parent main thread
+        parent_thread_xp = XPTR( parent_cxy ,
+                           hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->th_tbl[1] )));
+        
+        // unblock parent process main thread
+        thread_unblock( parent_thread_xp , THREAD_BLOCKED_WAIT );
+    }
 
     // release the process PID to cluster manager
     cluster_pid_release( process->pid );
 
-	hal_fence();
-
-    // From this point, the process descriptor is unreachable
-
     // FIXME close all open files and update dirty [AG]
 
-    // Decrease refcount for bin file, root file and cwd file
+    // decrease refcount for bin file, root file and cwd file
 	if( process->vfs_bin_xp  != XPTR_NULL ) vfs_file_count_down( process->vfs_bin_xp );
 	if( process->vfs_root_xp != XPTR_NULL ) vfs_file_count_down( process->vfs_root_xp );
@@ -371,4 +410,7 @@
     // release memory allocated to process descriptor
     process_free( process );
+
+process_dmsg("\n[DBG] %s : core[%x,%d] exit for process %x\n",
+__FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , process->pid );
 
 }  // end process_destroy()
@@ -401,5 +443,5 @@
     rpc_desc_t         rpc;               // rpc descriptor allocated in stack
 
-sigaction_dmsg("\n[DBG] %s : enter to %s process %x in cluster %x\n",
+process_dmsg("\n[DBG] %s : enter to %s process %x in cluster %x\n",
 __FUNCTION__ , process_action_str( action_type ) , process->pid , local_cxy );
 
@@ -415,6 +457,5 @@
 
     // check owner cluster
-    assert( (owner_cxy == local_cxy) , __FUNCTION__ ,
-    "must be executed in the owner cluster\n" ); 
+    assert( (owner_cxy == local_cxy) , __FUNCTION__ , "must be executed in owner cluster\n" ); 
    
     // get number of remote copies
@@ -425,6 +466,5 @@
     assert( ((action_type == DELETE_ALL_THREADS ) ||
              (action_type == BLOCK_ALL_THREADS )  ||
-             (action_type == UNBLOCK_ALL_THREADS )),
-             __FUNCTION__ , "illegal action type" );
+             (action_type == UNBLOCK_ALL_THREADS )), __FUNCTION__ , "illegal action type" );
              
     // initialise rpc descriptor
@@ -434,5 +474,5 @@
     rpc.thread   = client;
 
-    // get extended pointers on copies root, copies lock, and number of copies
+    // get extended pointers on copies root and lock
     root_xp   = XPTR( local_cxy , &cluster->pmgr.copies_root[lpid] );
     lock_xp   = XPTR( local_cxy , &cluster->pmgr.copies_lock[lpid] );
@@ -452,5 +492,5 @@
         {
 
-sigaction_dmsg("\n[DBG] %s : send RPC to remote cluster %x\n",
+process_dmsg("\n[DBG] %s : send RPC to remote cluster %x\n",
 __FUNCTION__ , process_cxy );
 
@@ -477,21 +517,21 @@
     }
 
-sigaction_dmsg("\n[DBG] %s : make action in owner cluster %x\n",
+process_dmsg("\n[DBG] %s : make action in owner cluster %x\n",
 __FUNCTION__ , local_cxy );
 
 
     // call directly the relevant function in local owner cluster
-    if      (action_type == DELETE_ALL_THREADS  ) process_delete ( process , client_xp ); 
-    else if (action_type == BLOCK_ALL_THREADS   ) process_block  ( process , client_xp ); 
-    else if (action_type == UNBLOCK_ALL_THREADS ) process_unblock( process             );
-
-sigaction_dmsg("\n[DBG] %s : exit after %s process %x in cluster %x\n",
+    if      (action_type == DELETE_ALL_THREADS  ) process_delete_threads ( process , client_xp ); 
+    else if (action_type == BLOCK_ALL_THREADS   ) process_block_threads  ( process , client_xp ); 
+    else if (action_type == UNBLOCK_ALL_THREADS ) process_unblock_threads( process             );
+
+process_dmsg("\n[DBG] %s : exit after %s process %x in cluster %x\n",
 __FUNCTION__ , process_action_str( action_type ) , process->pid , local_cxy );
 
 }  // end process_sigaction()
 
-////////////////////////////////////////
-void process_block( process_t * process,
-                    xptr_t      client_xp )
+////////////////////////////////////////////////
+void process_block_threads( process_t * process,
+                            xptr_t      client_xp )
 {
     thread_t          * target;         // pointer on target thread 
@@ -524,8 +564,8 @@
 
             // - if the target thread is the client thread, we do nothing,
-            //   and simply decrement the responses counter.
+            //   and we simply decrement the responses counter.
             // - if the calling thread and the target thread are on the same core,
-            //   we block the target thread, we don't ask ask anything to the scheduler,
-            //   and simply decrement the responses counter.
+            //   we block the target thread, we don't need confirmation from scheduler,
+            //   and we simply decrement the responses counter.
             // - if the calling thread and the target thread are not running on the same
             //   core, we ask the target scheduler to acknowlege the blocking
@@ -559,5 +599,5 @@
     }
 
-    // get lock protecting process th_tbl[]
+    // release lock protecting process th_tbl[]
     spinlock_unlock( &process->th_lock );
 
@@ -575,8 +615,8 @@
 __FUNCTION__ , process->pid , local_cxy , count );
 
-}  // end process_block()
-
-///////////////////////////////////////////
-void process_unblock( process_t * process )
+}  // end process_block_threads()
+
+///////////////////////////////////////////////////
+void process_unblock_threads( process_t * process )
 {
     thread_t          * target;        // pointer on target thead 
@@ -605,5 +645,5 @@
     }
 
-    // get lock protecting process th_tbl[]
+    // release lock protecting process th_tbl[]
     spinlock_unlock( &process->th_lock );
 
@@ -611,20 +651,16 @@
 __FUNCTION__ , process->pid , local_cxy , count );
 
-}  // end process_unblock()
-
-/////////////////////////////////////////
-void process_delete( process_t * process,
-                     xptr_t      client_xp )
+}  // end process_unblock_threads()
+
+/////////////////////////////////////////////////
+void process_delete_threads( process_t * process,
+                             xptr_t      client_xp )
 {
     thread_t          * target;        // pointer on target thread 
     uint32_t            ltid;          // index in process th_tbl
     uint32_t            count;         // request counter
-    thread_t          * requester;     // pointer on calling thread 
 
 sigaction_dmsg("\n[DBG] %s : enter for process %x in cluster %x at cycle %d\n",
 __FUNCTION__ , process->pid , local_cxy , (uint32_t)hal_get_cycles() );
-
-    // get calling thread pointer
-    requester = CURRENT_THREAD;
 
     // get lock protecting process th_tbl[]
@@ -649,5 +685,5 @@
     }
 
-    // get lock protecting process th_tbl[]
+    // release lock protecting process th_tbl[]
     spinlock_unlock( &process->th_lock );
 
@@ -655,5 +691,5 @@
 __FUNCTION__ , process->pid , local_cxy , (uint32_t)hal_get_cycles() );
 
-}  // end process_delete()
+}  // end process_delete_threads()
 
 ///////////////////////////////////////////////
@@ -779,5 +815,5 @@
 	remote_spinlock_unlock( XPTR( ref_cxy , &ref_ptr->fd_array.lock ) );
 
-    if ( !found ) return EMFILE;
+    if ( !found ) return -1;
     else          return 0;
 }
@@ -831,7 +867,6 @@
 	remote_spinlock_lock( XPTR( src_cxy , &src_ptr->lock ) );
 
-    // loop on all entries other than
-    // the three first entries: stdin/stdout/stderr
-    for( fd = 3 ; fd < CONFIG_PROCESS_FILE_MAX_NR ; fd++ )
+    // loop on all fd_array entries
+    for( fd = 0 ; fd < CONFIG_PROCESS_FILE_MAX_NR ; fd++ )
 	{
 		entry = (xptr_t)hal_remote_lwd( XPTR( src_cxy , &src_ptr->array[fd] ) );
@@ -862,5 +897,5 @@
 {
     ltid_t   ltid;
-    bool_t   found;
+    bool_t   found = false;
 
     assert( (process != NULL) , __FUNCTION__ , "process argument is NULL" );
@@ -868,8 +903,9 @@
     assert( (thread != NULL) , __FUNCTION__ , "thread argument is NULL" );
 
+    // take lock protecting th_tbl
+    spinlock_lock( &process->th_lock );
+
     // search a free slot in th_tbl[] 
-    // 0 is not a valid ltid value
-    found = false;
-    for( ltid = 1 ; ltid < CONFIG_THREAD_MAX_PER_CLUSTER ; ltid++ )
+    for( ltid = 0 ; ltid < CONFIG_THREAD_MAX_PER_CLUSTER ; ltid++ )
     {
         if( process->th_tbl[ltid] == NULL )
@@ -890,4 +926,9 @@
     }
 
+
+    // release lock protecting th_tbl
+    hal_fence();
+    spinlock_unlock( &process->th_lock );
+
     return (found) ? 0 : ENOMEM;
 
@@ -903,8 +944,18 @@
     // get thread local index
     ltid_t  ltid = LTID_FROM_TRDID( thread->trdid );
+
+    // take lock protecting th_tbl
+    spinlock_lock( &process->th_lock );
+
+    assert( (process->th_nr) , __FUNCTION__ , "process th_nr cannot be 0\n" );
 
     // remove thread from th_tbl[]
     process->th_tbl[ltid] = NULL;
     process->th_nr--;
+
+    hal_fence();
+
+    // release lock protecting th_tbl
+    spinlock_unlock( &process->th_lock );
 
 }  // process_remove_thread()
@@ -921,4 +972,5 @@
     pid_t       parent_pid;      // process identifier for parent process
     xptr_t      ref_xp;          // extended pointer on reference process
+    xptr_t      vfs_bin_xp;      // extended pointer on .elf file
     error_t     error;
 
@@ -927,7 +979,8 @@
     process_t * parent_process_ptr = (process_t *)GET_PTR( parent_process_xp );
 
-    // get parent process PID
-    parent_pid = hal_remote_lw( XPTR( parent_process_cxy , &parent_process_ptr->pid ) );
-    
+    // get parent process PID and extended pointer on .elf file
+    parent_pid = hal_remote_lw (XPTR( parent_process_cxy , &parent_process_ptr->pid));
+    vfs_bin_xp = hal_remote_lwd(XPTR( parent_process_cxy , &parent_process_ptr->vfs_bin_xp));
+
     // check parent process is the reference
     ref_xp = hal_remote_lwd( XPTR( parent_process_cxy , &parent_process_ptr->ref_xp ) );
@@ -947,10 +1000,10 @@
     }
 
-fork_dmsg("\n[DBG] %s : core[%x,%d] child process descriptor allocated at cycle %d\n",
- __FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
+fork_dmsg("\n[DBG] %s : core[%x,%d] created child process %x at cycle %d\n",
+ __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, process, (uint32_t)hal_get_cycles() );
 
     // allocate a child PID from local cluster
     error = cluster_pid_alloc( process , &new_pid );
-    if( (error != 0) || (new_pid == 0) )
+    if( error ) 
     {
         printk("\n[ERROR] in %s : cannot get PID in cluster %x\n", 
@@ -960,5 +1013,5 @@
     }
 
-fork_dmsg("\n[DBG] %s : core[%x, %d] child process PID allocated = %x at cycle %d\n",
+fork_dmsg("\n[DBG] %s : core[%x, %d] child process PID = %x at cycle %d\n",
  __FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, new_pid , (uint32_t)hal_get_cycles() );
 
@@ -966,5 +1019,5 @@
     process_reference_init( process,
                             new_pid,
-                            parent_pid,
+                            parent_process_xp,
                             parent_process_xp );
 
@@ -987,4 +1040,7 @@
 __FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
 
+    // update extended pointer on .elf file
+    process->vfs_bin_xp = vfs_bin_xp;
+
     // create child thread descriptor from parent thread descriptor
     error = thread_user_fork( parent_thread_xp,
@@ -1000,4 +1056,7 @@
     }
 
+    // check main thread index
+    assert( (thread->trdid == 0) , __FUNCTION__ , "main thread must have index 0\n" );
+
 fork_dmsg("\n[DBG] %s : core[%x,%d] child thread created at cycle %d\n", 
 __FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
@@ -1018,13 +1077,14 @@
 __FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
 
-    // update children list in parent process
-	xlist_add_last( XPTR( parent_process_cxy , &parent_process_ptr->children_root ),
-                    XPTR( local_cxy , &process->brothers_list ) );
-	hal_remote_atomic_add( XPTR( parent_process_cxy,
-                                 &parent_process_ptr->children_nr), 1 );
-
-// vmm_display( process , true );
-// vmm_display( parent_process_ptr , true );
-// sched_display( 0 );
+    // get extended pointers on parent children_root, children_lock and children_nr
+    xptr_t children_root_xp = XPTR( parent_process_cxy , &parent_process_ptr->children_root );
+    xptr_t children_lock_xp = XPTR( parent_process_cxy , &parent_process_ptr->children_lock );
+    xptr_t children_nr_xp   = XPTR( parent_process_cxy , &parent_process_ptr->children_nr   );
+
+    // register process in parent children list
+    remote_spinlock_lock( children_lock_xp );
+	xlist_add_last( children_root_xp , XPTR( local_cxy , &process->children_list ) );
+	hal_remote_atomic_add( children_nr_xp , 1 );
+    remote_spinlock_unlock( children_lock_xp );
 
     // return success
@@ -1032,4 +1092,5 @@
     *child_pid    = new_pid;
 
+
 fork_dmsg("\n[DBG] %s : core[%x,%d] exit at cycle %d\n",
 __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
@@ -1044,10 +1105,9 @@
 {
     char           * path;                    // pathname to .elf file
+    pid_t            pid;                     // old_process PID given to new_process
+    pid_t            temp_pid;                // temporary PID given to old_process 
     process_t      * old_process;             // local pointer on old process
     process_t      * new_process;             // local pointer on new process
-    pid_t            old_pid;                 // old process identifier
-    pid_t            new_pid;                 // new (temporary) process identifier
-    thread_t       * old_thread;              // pointer on new thread
-    thread_t       * new_thread;              // pointer on new thread
+    thread_t       * new_thread;              // local pointer on main thread
     pthread_attr_t   attr;                    // main thread attributes
     lid_t            lid;                     // selected core local index
@@ -1056,16 +1116,15 @@
 	// get .elf pathname and PID from exec_info
 	path     = exec_info->path;
-    old_pid  = exec_info->pid;
+    pid      = exec_info->pid;
 
     // this function must be executed by a thread running in owner cluster
-    assert( (CXY_FROM_PID( old_pid ) == local_cxy), __FUNCTION__,
-    "local cluster %x is not owner for process %x\n", local_cxy, old_pid );
-
-exec_dmsg("\n[DBG] %s : core[%x,%d] enters for process %x / path = %s\n",
-__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, old_pid , path );
-
-    // get old process and thread local pointers
-    old_process = (process_t *)cluster_get_local_process_from_pid( old_pid );
-    old_thread  = CURRENT_THREAD;
+    assert( (CXY_FROM_PID( pid ) == local_cxy), __FUNCTION__,
+    "local cluster %x is not owner for process %x\n", local_cxy, pid );
+
+exec_dmsg("\n[DBG] %s : core[%x,%d] enters for process %x / %s / cycle %d\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, pid, path, (uint32_t)hal_get_cycles() );
+
+    // get old_process local pointer
+    old_process = (process_t *)cluster_get_local_process_from_pid( pid );
     
     if( old_process == NULL )
@@ -1075,45 +1134,60 @@
     }
 
-    // allocate memory for new process descriptor
+     // allocate memory for new_process descriptor
     new_process = process_alloc();
 
     if( new_process == NULL )
     {
-        printk("\n[ERROR] in %s : cannot allocate new process descriptor\n", __FUNCTION__ );
+        printk("\n[ERROR] in %s : cannot allocate process descriptor in cluster %x\n",
+        __FUNCTION__ , local_cxy );
         return -1;
     }
 
-    // get a (temporary) PID for new process
-    error = cluster_pid_alloc( new_process , &new_pid );
-
-    if( error )
-    {
-        printk("\n[ERROR] in %s : cannot allocate a temporary PID\n", __FUNCTION__ );
-        process_destroy( new_process );
+    // get a new PID for old_process
+    error = cluster_pid_alloc( old_process , &temp_pid );
+    if( error ) 
+    {
+        printk("\n[ERROR] in %s : cannot get PID in cluster %x\n", 
+        __FUNCTION__ , local_cxy ); 
+        process_free( new_process );
         return -1;
     }
+
+    // request blocking for all threads in old_process (but the calling thread)
+    process_sigaction( old_process , BLOCK_ALL_THREADS );
+
+    // request destruction for all threads in old_process (but the calling thread)
+    process_sigaction( old_process , DELETE_ALL_THREADS );
+
+exec_dmsg("\n[DBG] %s : core[%x,%d] marked old threads for destruction / cycle %d\n",
+__FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid , (uint32_t)hal_get_cycles() );
+
+    // set new PID to old_process 
+    old_process->pid = temp_pid;
 
     // initialize new process descriptor
     process_reference_init( new_process,
-                            new_pid,                            // temporary PID
-                            old_process->ppid,                  // same parent
-                            XPTR( local_cxy , old_process ) );
-
-exec_dmsg("\n[DBG] %s : core[%x,%d] created new process %x / path = %s\n",
-__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, new_pid, path );
-
-    // register "code" and "data" vsegs as well as entry-point
-    // in new process VMM, using information contained in the elf file.
+                            pid,
+                            old_process->parent_xp,             // parent_process_xp
+                            XPTR(local_cxy , old_process) );    // model_process_xp
+
+    // give TXT ownership to new_process
+    process_txt_set_ownership( XPTR( local_cxy , new_process ) );
+
+exec_dmsg("\n[DBG] %s : core[%x,%d] initialised new process %x / cycle %d \n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, new_process, (uint32_t)hal_get_cycles() );
+
+    // register code & data vsegs as well as entry-point in new process VMM,
+    // and register extended pointer on .elf file in process descriptor
 	if( elf_load_process( path , new_process ) )
 	{
-		printk("\n[ERROR] in %s : failed to access .elf file for process %x / path = %s\n",
-		__FUNCTION__, new_pid , path );
-        cluster_pid_release( new_pid );
+		printk("\n[ERROR] in %s : failed to access .elf file for path %s\n",
+		__FUNCTION__ , path );
         process_destroy( new_process );
         return -1;
 	}
 
-exec_dmsg("\n[DBG] %s : core[%x,%d] vsegs registered / path = %s\n",
-__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, path );
+exec_dmsg("\n[DBG] %s : core[%x,%d] vsegs registered in new process %x / cycle %d\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, new_process, (uint32_t)hal_get_cycles() );
 
     // select a core in local cluster to execute the main thread
@@ -1125,6 +1199,6 @@
     attr.lid        = lid;
 
-    // create and initialize thread descriptor
-	error = thread_user_create( new_pid,
+    // create and initialize main thread in local cluster
+	error = thread_user_create( pid,
                                 (void *)new_process->vmm.entry_point,
                                 exec_info->args_pointers,
@@ -1133,47 +1207,52 @@
 	if( error )
 	{
-		printk("\n[ERROR] in %s : cannot create thread for process %x / path = %s\n",
-	    __FUNCTION__, new_pid , path );
-        cluster_pid_release( new_pid );
+		printk("\n[ERROR] in %s : cannot create thread for process %x\n",
+	    __FUNCTION__ , new_process );
         process_destroy( new_process );
         return -1;
 	}
 
-exec_dmsg("\n[DBG] %s : core[%x,%d] created main thread %x\n",
-__FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, new_thread->trdid );
-
-    // update children list rooted in parent process
-	xlist_replace( XPTR( local_cxy , &old_process->brothers_list ) ,
-                   XPTR( local_cxy , &new_process->brothers_list ) );
-
-    // request blocking for all threads in old process (but the calling thread)
-    process_sigaction( old_process , BLOCK_ALL_THREADS );
-
-    // request destruction for all threads in old process (but the calling thread)
-    process_sigaction( old_process , DELETE_ALL_THREADS );
-
-    // update PID for both processes 
-    new_process->pid = old_pid;
-    old_process->pid = 0xFFFFFFFF;
-
-    // release temporary PID
-    cluster_pid_release( new_pid );
-    
+    // check main thread index
+    assert( (new_thread->trdid == 0) , __FUNCTION__ , "main thread must have index 0\n" );
+
+exec_dmsg("\n[DBG] %s : core[%x,%d] created new_process main thread / cycle %d\n",
+__FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
+
+    // get pointers on parent process
+    xptr_t      parent_xp  = new_process->parent_xp;
+    process_t * parent_ptr = GET_PTR( parent_xp );
+    cxy_t       parent_cxy = GET_CXY( parent_xp );
+
+    // get extended pointers on parent children_root, children_lock and children_nr
+    xptr_t root_xp = XPTR( parent_cxy , &parent_ptr->children_root );
+    xptr_t lock_xp = XPTR( parent_cxy , &parent_ptr->children_lock );
+    xptr_t nr_xp   = XPTR( parent_cxy , &parent_ptr->children_nr   );
+
+    // register new_process in parent children list
+    remote_spinlock_lock( lock_xp );
+	xlist_add_last( root_xp , XPTR( local_cxy , &new_process->children_list ) );
+	hal_remote_atomic_add( nr_xp , 1 );
+    remote_spinlock_unlock( lock_xp );
+
+exec_dmsg("\n[DBG] %s : core[%x,%d] updated parent process children list / cycle %d\n",
+__FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
+   
+    // block and mark calling thread for deletion
+    // only when it is an user thread
+    thread_t * this = CURRENT_THREAD;
+    if( this->type == THREAD_USER )
+    {
+        thread_block( this , THREAD_BLOCKED_GLOBAL );
+        hal_atomic_or( &this->flags , THREAD_FLAG_REQ_DELETE );
+    }
+
     // activate new thread
 	thread_unblock( XPTR( local_cxy , new_thread ) , THREAD_BLOCKED_GLOBAL );
 
-exec_dmsg("\n[DBG] %s : core[%x,%d] exit for path = %s\n",
-__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, path  );
-
-    // set BLOCKED_GLOBAL bit
-    thread_block( old_thread , THREAD_BLOCKED_GLOBAL );
-
-    // set REQ_DELETE flag
-    hal_atomic_or( &old_thread->flags , THREAD_FLAG_REQ_DELETE );
-
-    // deschedule
-    sched_yield("suicide after exec"); 
-
-    // never executed but required by compiler
+    hal_fence();
+
+exec_dmsg("\n[DBG] %s : core[%x,%d] exit for path = %s / cycle %d\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, path , (uint32_t)hal_get_cycles() );
+
 	return 0;
 
@@ -1188,8 +1267,10 @@
     "must execute in owner cluster" );
 
+    thread_t * this = CURRENT_THREAD;
+
 kill_dmsg("\n[DBG] %s : core[%x,%d] enter / process %x / sig %d\n",
-__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, pid , sig_id );
-
-    // get pointer on local process descriptor
+__FUNCTION__, local_cxy, this->core->lid, pid , sig_id );
+
+    // get pointer on local target process descriptor
     process_t * process = process_get_local_copy( pid );
 
@@ -1205,7 +1286,11 @@
     switch( sig_id )
     {
-        case SIGSTOP:     // block all threads in all clusters
+        case SIGSTOP:     
         {
+            // block all threads in all clusters
             process_sigaction( process , BLOCK_ALL_THREADS );
+
+            // remove TXT ownership to target process 
+            process_txt_reset_ownership( XPTR( local_cxy , process ) );
         }
         break;
@@ -1217,6 +1302,9 @@
         case SIGKILL:  // block all threads, then delete all threads
         {
-            // block all threads (but the calling thread)
+            // block all threads in all clusters
             process_sigaction( process , BLOCK_ALL_THREADS );
+
+            // remove TXT ownership to target process 
+            process_txt_reset_ownership( XPTR( local_cxy , process ) );
 
             // delete all threads (but the calling thread)
@@ -1224,7 +1312,5 @@
 
             // delete the calling thread if required
-            thread_t * this = CURRENT_THREAD;
-
-            if( this->process == process )
+            if( CURRENT_THREAD->process == process )
             {
                 // set REQ_DELETE flag
@@ -1238,8 +1324,4 @@
     }
 
-//@@@
-sched_display( 0 );
-//@@@
-
 kill_dmsg("\n[DBG] %s : core[%x,%d] exit / process %x / sig %d \n",
 __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, pid , sig_id );
@@ -1280,8 +1362,42 @@
 }  // end process_make_exit()
 
+///////////////////////////////////////////////
+void process_zero_create( process_t * process )
+{
+
+process_dmsg("\n[DBG] %s : core[%x,%d] enter at cycle %d\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, (uint32_t)hal_get_cycles() );
+
+    // initialize PID, REF_XP, PARENT_XP, and STATE
+    process->pid       = 0;
+    process->ref_xp    = XPTR( local_cxy , process );
+    process->parent_xp = XPTR_NULL;
+    process->state     = PROCESS_STATE_RUNNING;
+
+    // reset th_tbl[] array as empty
+    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 children list as empty
+    xlist_root_init( XPTR( local_cxy , &process->children_root ) );
+    remote_spinlock_init( XPTR( local_cxy , &process->children_lock ) );
+    process->children_nr = 0;
+
+	hal_fence();
+
+process_dmsg("\n[DBG] %s : core[%x,%d] exit at cycle %d\n",
+__FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , (uint32_t)hal_get_cycles() );
+
+}  // end process_zero_init()
+
 //////////////////////////
 void process_init_create()
 {
-    process_t      * process;       // local pointer on process_init descriptor
+    process_t      * process;       // local pointer on process descriptor
     pid_t            pid;           // process_init identifier
     thread_t       * thread;        // local pointer on main thread
@@ -1290,5 +1406,5 @@
     error_t          error;
 
-kinit_dmsg("\n[DBG] %s :  core[%x,%d] enters\n", 
+process_dmsg("\n[DBG] %s :  core[%x,%d] enters at cycle %d\n", 
 __FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid );
 
@@ -1307,16 +1423,17 @@
 		printk("\n[PANIC] in %s : cannot allocate PID in cluster %x\n",
 		__FUNCTION__, local_cxy );
-        process_destroy( process );
-    }
-
-    assert( (LPID_FROM_PID(pid) == 1) , __FUNCTION__ , "LPID must be 1 for process_init" );
+        process_free( process );
+    }
+
+    // check allocated PID
+    assert( (pid == 1) , __FUNCTION__ , "process INIT must be first process in cluster 0\n" );
 
     // initialize process descriptor / parent is local process_zero
     process_reference_init( process,
                             pid,
-                            0,
-                            XPTR( local_cxy , &process_zero ) );
-
-kinit_dmsg("\n[DBG] %s : core[%x,%d] / process initialised\n", 
+                            XPTR( local_cxy , &process_zero ),     // parent
+                            XPTR( local_cxy , &process_zero ) );   // model
+
+process_dmsg("\n[DBG] %s : core[%x,%d] / initialisation done\n", 
 __FUNCTION__ , local_cxy, CURRENT_THREAD->core->lid );
 
@@ -1330,6 +1447,16 @@
 	}
 
-kinit_dmsg("\n[DBG] %s : core[%x,%d] vsegs registered / path = %s\n",
+process_dmsg("\n[DBG] %s : core[%x,%d] vsegs registered / path = %s\n",
 __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, CONFIG_PROCESS_INIT_PATH );
+
+    // get extended pointers on process_zero children_root, children_lock
+    xptr_t children_root_xp = XPTR( local_cxy , &process_zero.children_root );
+    xptr_t children_lock_xp = XPTR( local_cxy , &process_zero.children_lock );
+
+    // register process INIT in parent local process_zero 
+    remote_spinlock_lock( children_lock_xp );
+	xlist_add_last( children_root_xp , XPTR( local_cxy , &process->children_list ) );
+	hal_atomic_add( &process_zero.children_nr , 1 );
+    remote_spinlock_unlock( children_lock_xp );
 
     // select a core in local cluster to execute the main thread
@@ -1354,4 +1481,7 @@
 	}
 
+    // check main thread index
+    assert( (thread->trdid == 0) , __FUNCTION__ , "main thread must have index 0\n" );
+
     // activate thread
 	thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_GLOBAL );
@@ -1359,7 +1489,303 @@
     hal_fence();
 
-kinit_dmsg("\n[DBG] %s : core[%x,%d] exit / main thread = %x\n",
+process_dmsg("\n[DBG] %s : core[%x,%d] exit / main thread = %x\n",
 __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, thread );
 
 }  // end process_init_create()
 
+//////////////////////////////////////////
+char * process_state_str( uint32_t state )
+{
+    if     ( state == PROCESS_STATE_RUNNING ) return "RUNNING";
+    else if( state == PROCESS_STATE_KILLED  ) return "KILLED";
+    else if( state == PROCESS_STATE_EXITED  ) return "EXITED";
+    else                                      return "undefined";
+}
+
+/////////////////////////////////////////
+void process_display( xptr_t process_xp )
+{
+    process_t   * process_ptr;
+    cxy_t         process_cxy;
+    xptr_t        parent_xp;       // extended pointer on parent process
+    process_t   * parent_ptr;
+    cxy_t         parent_cxy;
+
+    pid_t         pid;
+    pid_t         ppid;
+    uint32_t      state;
+    xptr_t        ref_xp; 
+    uint32_t      th_nr;
+
+    xptr_t        txt_file_xp;     // extended pointer on TXT_RX pseudo file
+    xptr_t        chdev_xp;        // extended pointer on TXT_RX chdev
+    chdev_t     * chdev_ptr;
+    cxy_t         chdev_cxy;
+    xptr_t        owner_xp;        // extended pointer on TXT owner process 
+
+    xptr_t        elf_file_xp;     // extended pointer on .elf file 
+    cxy_t         elf_file_cxy;
+    vfs_file_t  * elf_file_ptr;
+    vfs_inode_t * elf_inode_ptr;   // local pointer on .elf inode
+
+    char          txt_name[CONFIG_VFS_MAX_NAME_LENGTH];
+    char          elf_name[CONFIG_VFS_MAX_NAME_LENGTH];
+
+    // get cluster and local pointer on process
+    process_ptr = GET_PTR( process_xp );
+    process_cxy = GET_CXY( process_xp );
+
+    // check reference process
+    ref_xp = hal_remote_lwd( XPTR( process_cxy , &process_ptr->ref_xp ) );
+    assert( (process_xp == ref_xp) , __FUNCTION__ , "process is not the reference\n");
+
+    // get PID and state
+    pid   = hal_remote_lw( XPTR( process_cxy , &process_ptr->pid ) );
+    state = hal_remote_lw( XPTR( process_cxy , &process_ptr->state ) );
+
+    // get PPID
+    parent_xp  = hal_remote_lwd( XPTR( process_cxy , &process_ptr->parent_xp ) );
+    parent_cxy = GET_CXY( parent_xp );
+    parent_ptr = GET_PTR( parent_xp );
+    ppid       = hal_remote_lw( XPTR( parent_cxy , &parent_ptr->pid ) );
+
+    // get number of threads
+    th_nr      = hal_remote_lw( XPTR( process_cxy , &process_ptr->th_nr ) );
+
+    // get TXT name and process owner
+    txt_file_xp = hal_remote_lwd( XPTR( process_cxy , &process_ptr->fd_array.array[0] ) );
+
+    assert( (txt_file_xp != XPTR_NULL) , __FUNCTION__ , 
+    "process must be attached to one TXT terminal\n" ); 
+
+    chdev_xp  = chdev_from_file( txt_file_xp );
+    chdev_cxy = GET_CXY( chdev_xp );
+    chdev_ptr = (chdev_t *)GET_PTR( chdev_xp );
+    hal_remote_strcpy( XPTR( local_cxy , txt_name ) ,
+                           XPTR( chdev_cxy , chdev_ptr->name ) );
+    owner_xp = (xptr_t)hal_remote_lwd( XPTR( chdev_cxy , &chdev_ptr->ext.txt.owner_xp ) );
+    
+    // get process .elf name
+    elf_file_xp   = hal_remote_lwd( XPTR( process_cxy , &process_ptr->vfs_bin_xp ) );
+
+    elf_file_cxy  = GET_CXY( elf_file_xp );
+    elf_file_ptr  = (vfs_file_t *)GET_PTR( elf_file_xp );
+    elf_inode_ptr = (vfs_inode_t *)hal_remote_lpt( XPTR( elf_file_cxy , &elf_file_ptr->inode ) );
+    vfs_inode_get_name( XPTR( elf_file_cxy , elf_inode_ptr ) , elf_name );
+
+    // display process info
+    if( owner_xp == process_xp )
+    {
+        printk("PID %X | PPID %X | %s\t| %s (FG) | %X | %d | %s\n", 
+        pid, ppid, process_state_str(state), txt_name, process_ptr, th_nr, elf_name );
+    }
+    else
+    {
+        printk("PID %X | PPID %X | %s\t| %s (BG) | %X | %d | %s\n", 
+        pid, ppid, process_state_str(state), txt_name, process_ptr, th_nr, elf_name );
+    }
+}  // end process_display() 
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+//     Terminals related functions
+////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////
+uint32_t process_txt_alloc()
+{
+    uint32_t  index;       // TXT terminal index
+    xptr_t    chdev_xp;    // extended pointer on TXT_RX chdev
+    chdev_t * chdev_ptr;   // local pointer on TXT_RX chdev
+    cxy_t     chdev_cxy;   // TXT_RX chdev cluster
+    xptr_t    root_xp;     // extended pointer on owner field in chdev
+
+    // scan the user TXT_RX chdevs (TXT0 is reserved for kernel)
+    for( index = 1 ; index < LOCAL_CLUSTER->nb_txt_channels ; index ++ )
+    {
+        // get pointers on TXT_RX[index]
+        chdev_xp  = chdev_dir.txt_rx[index];
+        chdev_cxy = GET_CXY( chdev_xp );
+        chdev_ptr = GET_PTR( chdev_xp );
+
+        // get extended pointer on root of attached process
+        root_xp = XPTR( chdev_cxy , &chdev_ptr->ext.txt.root );
+
+        // return free TXT index if found
+        if( xlist_is_empty( root_xp ) ) return index;  
+    }
+
+    assert( false , __FUNCTION__ , "no free TXT terminal found" );
+
+    return -1;
+
+} // end process_txt_alloc()
+
+/////////////////////////////////////////////
+void process_txt_attach( process_t * process,
+                         uint32_t    txt_id )
+{
+    xptr_t      chdev_xp;     // extended pointer on TXT_RX chdev
+    cxy_t       chdev_cxy;    // TXT_RX chdev cluster
+    chdev_t *   chdev_ptr;    // local pointer on TXT_RX chdev
+    xptr_t      root_xp;      // extended pointer on list root in chdev
+    xptr_t      lock_xp;      // extended pointer on list lock in chdev
+
+process_dmsg("\n[DBG] %s : core[%x,%d] enter for process %x at cycle\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, process->pid, (uint32_t)hal_get_cycles() );
+
+    // check process is reference
+    assert( (process->ref_xp == XPTR( local_cxy , process )) , __FUNCTION__ ,
+    "process is not the reference descriptor" );
+
+    // check terminal index
+    assert( (txt_id < LOCAL_CLUSTER->nb_txt_channels) ,
+    __FUNCTION__ , "illegal TXT terminal index" );
+
+    // get pointers on TXT_RX[txt_id] chdev
+    chdev_xp  = chdev_dir.txt_rx[txt_id];
+    chdev_cxy = GET_CXY( chdev_xp );
+    chdev_ptr = GET_PTR( chdev_xp );
+
+    // get extended pointer on root & lock of attached process list
+    root_xp = XPTR( chdev_cxy , &chdev_ptr->ext.txt.root );
+    lock_xp = XPTR( chdev_cxy , &chdev_ptr->ext.txt.lock );
+
+    // insert process in attached process list
+    remote_spinlock_lock( lock_xp );
+    xlist_add_last( root_xp , XPTR( local_cxy , &process->txt_list ) );
+    remote_spinlock_unlock( lock_xp );
+
+process_dmsg("\n[DBG] %s : core[%x,%d] exit for process %x at cycle\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, process->pid, (uint32_t)hal_get_cycles() );
+
+} // end process_txt_attach()
+
+//////////////////////////////////////////////
+void process_txt_detach( process_t * process )
+{
+    xptr_t      chdev_xp;     // extended pointer on TXT_RX chdev
+    cxy_t       chdev_cxy;    // TXT_RX chdev cluster
+    chdev_t *   chdev_ptr;    // local pointer on TXT_RX chdev
+    xptr_t      lock_xp;      // extended pointer on list lock in chdev
+
+process_dmsg("\n[DBG] %s : core[%x,%d] enter for process %x at cycle\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, process->pid, (uint32_t)hal_get_cycles() );
+
+    // check process is reference
+    assert( (process->ref_xp == XPTR( local_cxy , process )) , __FUNCTION__ ,
+    "process is not the reference descriptor" );
+
+    // get extended pointer on TXT_RX chdev
+    chdev_xp  = chdev_from_file( process->fd_array.array[0] );
+    chdev_cxy = GET_CXY( chdev_xp );
+    chdev_ptr = (chdev_t *)GET_PTR( chdev_xp );
+
+    // get extended pointer on lock of attached process list
+    lock_xp = XPTR( chdev_cxy , &chdev_ptr->ext.txt.lock );
+
+    // unlink process from attached process list
+    remote_spinlock_lock( lock_xp );
+    xlist_unlink( XPTR( local_cxy , &process->txt_list ) );
+    remote_spinlock_unlock( lock_xp );
+   
+process_dmsg("\n[DBG] %s : core[%x,%d] exit for process %x at cycle %d\n",
+__FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, process->pid, (uint32_t)hal_get_cycles() );
+
+} // end process_txt_detach()
+
+///////////////////////////////////////////////////
+void process_txt_set_ownership( xptr_t process_xp )
+{
+    process_t * process_ptr;
+    cxy_t       process_cxy;
+    xptr_t      file_xp;
+    xptr_t      txt_xp;     
+    chdev_t   * txt_ptr;
+    cxy_t       txt_cxy;
+
+    // get cluster and local pointer on process
+    process_cxy = GET_CXY( process_xp );
+    process_ptr = (process_t *)GET_PTR( process_xp );
+
+    // get extended pointer on stdin pseudo file
+    file_xp = hal_remote_lwd( XPTR( process_cxy , &process_ptr->fd_array.array[0] ) );
+
+    // get pointers on TXT chdev
+    txt_xp  = chdev_from_file( file_xp );
+    txt_cxy = GET_CXY( txt_xp );
+    txt_ptr = (chdev_t *)GET_PTR( txt_xp );
+
+    // set owner field in TXT chdev
+    hal_remote_swd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) , process_xp );
+
+}  // end process_txt_set ownership()
+
+/////////////////////////////////////////////////////
+void process_txt_reset_ownership( xptr_t process_xp )
+{
+    process_t * process_ptr;
+    cxy_t       process_cxy;
+    xptr_t      parent_xp;       // extended pointer on parent process
+    process_t * parent_ptr;
+    cxy_t       parent_cxy;
+    xptr_t      file_xp;         // extended pointer on TXT_RX pseudo file
+    xptr_t      txt_xp;          // extended pointer on TXT_RX chdev
+    chdev_t   * txt_ptr;
+    cxy_t       txt_cxy;
+    xptr_t      owner_xp;        // extended pointer on current TXT_RX owner
+    xptr_t      root_xp;         // extended pointer on root of attached process list
+    xptr_t      iter_xp;         // iterator for xlist
+    xptr_t      current_xp;      // extended pointer on current process
+    process_t * current_ptr;
+    cxy_t       current_cxy;
+    pid_t       ppid;
+
+    // get cluster and local pointer on process
+    process_cxy = GET_CXY( process_xp );
+    process_ptr = (process_t *)GET_PTR( process_xp );
+
+    // get extended pointer on stdin pseudo file
+    file_xp = hal_remote_lwd( XPTR( process_cxy , &process_ptr->fd_array.array[0] ) );
+
+    // get pointers on TXT chdev
+    txt_xp  = chdev_from_file( file_xp );
+    txt_cxy = GET_CXY( txt_xp );
+    txt_ptr = (chdev_t *)GET_PTR( txt_xp );
+
+    // get extended pointer on TXT_RX owner
+    owner_xp = hal_remote_lwd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) );
+
+    // transfer ownership to KSH if required
+    if( owner_xp == process_xp )   
+    {
+        // get extended pointer on root of list of attached processes
+        root_xp = hal_remote_lwd( XPTR( txt_cxy , &txt_ptr->ext.txt.root ) );
+
+        // scan attached process list to find KSH process
+        XLIST_FOREACH( root_xp , iter_xp )
+        {
+            current_xp  = XLIST_ELEMENT( iter_xp , process_t , txt_list );
+            current_cxy = GET_CXY( current_xp );
+            current_ptr = GET_PTR( current_xp );
+            parent_xp   = hal_remote_lwd( XPTR( current_cxy , &current_ptr->parent_xp ) );
+
+            parent_cxy  = GET_CXY( parent_xp );
+            parent_ptr  = GET_PTR( parent_xp );
+            ppid        = hal_remote_lw( XPTR( parent_cxy , &parent_ptr->pid ) );
+
+            if( ppid == 1 )  // current is KSH
+            {
+                // set owner field in TXT chdev 
+                hal_remote_swd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) , current_xp );
+                return;
+            }
+        }
+    }
+
+    assert( false , __FUNCTION__ , "KSH process not found" ); 
+
+}  // end process_txt_reset_ownership()
+
+
+     
