Index: trunk/kernel/kern/process.c
===================================================================
--- trunk/kernel/kern/process.c	(revision 669)
+++ trunk/kernel/kern/process.c	(revision 683)
@@ -2,7 +2,7 @@
  * process.c - process related functions definition.
  *
- * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
+ * Authors  Ghassan Almaless       (2008,2009,2010,2011,2012)
  *          Mohamed Lamine Karaoui (2015)
- *          Alain Greiner (2016,2017,2018,2019,2020)
+ *          Alain Greiner          (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -70,29 +70,4 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
-/////////////////////////////////
-process_t * process_alloc( void )
-{
-
-assert( __FUNCTION__, (sizeof(process_t) < CONFIG_PPM_PAGE_SIZE),
-"process descriptor exceeds 1 page" );
-
-	kmem_req_t req;
-
-    req.type  = KMEM_PPM;
-	req.order = 0;
-	req.flags = AF_KERNEL | AF_ZERO;
-    return kmem_alloc( &req );
-}
-
-////////////////////////////////////////
-void process_free( process_t * process )
-{
-    kmem_req_t  req;
-
-	req.type = KMEM_PPM;
-	req.ptr  = process;
-	kmem_free( &req );
-}
-
 ////////////////////////////////////////////////////
 error_t process_reference_init( process_t * process,
@@ -116,5 +91,10 @@
     vmm_t     * vmm;
 
-    // build extended pointer on this reference process
+#if DEBUG_PROCESS_REFERENCE_INIT || DEBUG_PROCESS_ERROR
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+#endif
+
+    // build extended pointer on reference process
     process_xp = XPTR( local_cxy , process );
 
@@ -130,6 +110,4 @@
 
 #if DEBUG_PROCESS_REFERENCE_INIT
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_REFERENCE_INIT < cycle )
 printk("\n[%s] thread[%x,%x] enter to initialize process %x / cycle %d\n",
@@ -156,5 +134,9 @@
     if( error ) 
     {
-        printk("\n[ERROR] in %s : cannot create empty GPT\n", __FUNCTION__ );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot create empty GPT / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
         return -1;
     }
@@ -173,4 +155,9 @@
     if( error ) 
     {
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot register kernel vsegs in VMM / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
         printk("\n[ERROR] in %s : cannot register kernel vsegs in VMM\n", __FUNCTION__ );
         return -1;
@@ -233,5 +220,9 @@
         if( error )
         {
-            printk("\n[ERROR] in %s : cannot open stdout pseudo-file\n", __FUNCTION__ );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot open stdin pseudo file / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
             return -1;
         }
@@ -256,5 +247,9 @@
         if( error )
         {
-            printk("\n[ERROR] in %s : cannot open stdout pseudo-file\n", __FUNCTION__ );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot open stdout pseudo file / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
             return -1;
         }
@@ -279,5 +274,9 @@
         if( error )
         {
-            printk("\n[ERROR] in %s : cannot open stderr pseudo-file\n", __FUNCTION__ );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot open stderr pseudo file / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
             return -1;
         }
@@ -302,5 +301,16 @@
 
         // recreate all open files from parent process fd_array to child process fd_array
-        process_fd_replicate( process_xp , parent_xp );
+        error = process_fd_replicate( process_xp , parent_xp );
+
+        if( error )
+        {
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot replicate fd_array / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+            return -1;
+        }
+
     }
 
@@ -379,4 +389,9 @@
     vmm_t   * vmm;
 
+#if DEBUG_PROCESS_COPY_INIT || DEBUG_PROCESS_ERROR
+thread_t * this = CURRENT_THREAD;  
+uint32_t cycle = (uint32_t)hal_get_cycles();
+#endif
+
     // get reference process cluster and local pointer
     cxy_t       ref_cxy = GET_CXY( reference_process_xp );
@@ -394,6 +409,4 @@
 
 #if DEBUG_PROCESS_COPY_INIT
-thread_t * this = CURRENT_THREAD;  
-uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_COPY_INIT < cycle )
 printk("\n[%s] thread[%x,%x] enter for process %x / cycle %d\n",
@@ -410,7 +423,12 @@
     // create an empty GPT as required by the architecture
     error = hal_gpt_create( &vmm->gpt );
+
     if( error ) 
     {
-        printk("\n[ERROR] in %s : cannot create empty GPT\n", __FUNCTION__ );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot create empty GPT / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
         return -1;
     }
@@ -421,7 +439,12 @@
     // register kernel vsegs in VMM as required by the architecture
     error = hal_vmm_kernel_update( local_process );
+
     if( error ) 
     {
-        printk("\n[ERROR] in %s : cannot register kernel vsegs in VMM\n", __FUNCTION__ );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot register kernel vsegs in VMM / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
         return -1;
     }
@@ -431,7 +454,12 @@
     // initialize locks protecting GPT and VSL
     error = vmm_user_init( local_process );
+
     if( error ) 
     {
-        printk("\n[ERROR] in %s : cannot register user vsegs in VMM\n", __FUNCTION__ );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot register user vsegs in VMM / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
         return -1;
     }
@@ -598,5 +626,5 @@
 
     // release memory allocated to process descriptor
-    process_free( process );
+	kmem_free( process , bits_log2(sizeof(process_t)) );
 
 #if DEBUG_PROCESS_DESTROY
@@ -974,12 +1002,15 @@
 {
     error_t        error;
-    process_t    * process_ptr;   // local pointer on process
+    process_t    * process;       // local pointer on process
     xptr_t         process_xp;    // extended pointer on process
 
+#if DEBUG_PROCESS_GET_LOCAL_COPY || DEBUG_PROCESS_ERROR
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+#endif
+
     cluster_t * cluster = LOCAL_CLUSTER;
 
 #if DEBUG_PROCESS_GET_LOCAL_COPY
-thread_t * this = CURRENT_THREAD;
-uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_GET_LOCAL_COPY < cycle )
 printk("\n[%s] thread[%x,%x] enter for process %x in cluster %x / cycle %d\n",
@@ -996,6 +1027,6 @@
     {
         process_xp  = XLIST_ELEMENT( iter , process_t , local_list );
-        process_ptr = GET_PTR( process_xp );
-        if( process_ptr->pid == pid )
+        process     = GET_PTR( process_xp );
+        if( process->pid == pid )
         {
             found = true;
@@ -1017,12 +1048,20 @@
 
         // allocate memory for local process descriptor
-        process_ptr = process_alloc();
-
-        if( process_ptr == NULL )  return NULL;
+        process = kmem_alloc( bits_log2(sizeof(process_t)) , AF_ZERO );
+
+        if( process == NULL )  return NULL;
 
         // initialize local process descriptor copy
-        error = process_copy_init( process_ptr , ref_xp );
-
-        if( error ) return NULL;
+        error = process_copy_init( process , ref_xp );
+
+        if( error )
+        {
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot initialize local process copy / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+            return NULL;
+        }
     }
 
@@ -1031,8 +1070,8 @@
 if( DEBUG_PROCESS_GET_LOCAL_COPY < cycle )
 printk("\n[%s] thread[%x,%x] exit in cluster %x / process %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, local_cxy, process_ptr, cycle );
-#endif
-
-    return process_ptr;
+__FUNCTION__, this->process->pid, this->trdid, local_cxy, process, cycle );
+#endif
+
+    return process;
 
 }  // end process_get_local_copy()
@@ -1111,4 +1150,9 @@
     xptr_t    max_xp;          // extended pointer on max field in fd_array
 
+#if DEBUG_PROCESS_FD_REGISTER
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+#endif
+
     // get target process cluster and local pointer
     process_t * process_ptr = GET_PTR( process_xp );
@@ -1120,10 +1164,8 @@
 
 #if DEBUG_PROCESS_FD_REGISTER
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-pid_t      pid   = hal_remote_l32( XPTR( process_cxy , &process_ptr->pid) );
+pid_t  tgt_pid   = hal_remote_l32( XPTR( process_cxy , &process_ptr->pid) );
 if( DEBUG_PROCESS_FD_REGISTER < cycle )
 printk("\n[%s] thread[%x,%x] enter for process %x / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, pid, cycle );
+__FUNCTION__, this->process->pid, this->trdid, tgt_pid, cycle );
 #endif
 
@@ -1168,5 +1210,5 @@
 if( DEBUG_PROCESS_FD_REGISTER < cycle )
 printk("\n[%s] thread[%x,%x] exit for process %x / fdid %d / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, pid, id, cycle );
+__FUNCTION__, this->process->pid, this->trdid, tgt_pid, id, cycle );
 #endif
 
@@ -1384,7 +1426,7 @@
 }  // end process_fd_get_xptr_from_local()
 
-/////////////////////////////////////////
-void process_fd_replicate( xptr_t dst_xp,
-                           xptr_t src_xp )
+////////////////////////////////////////////
+error_t process_fd_replicate( xptr_t dst_xp,
+                              xptr_t src_xp )
 {
     uint32_t fdid;      // current file descriptor index
@@ -1435,6 +1477,12 @@
             if( error )
             {
-                printk("\n[ERROR] in %s : cannot create new file\n", __FUNCTION__ );
-                return;
+
+#if DEBUG_PROCESS_ERROR
+thread_t * this  = CURRENT_THREAD;
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+printk("\n[ERROR] in %s : thread[%x,%x] cannot create file descriptor / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+                return -1;
             }
 
@@ -1446,4 +1494,6 @@
     // release lock on source process fd_array
 	remote_queuelock_release( src_lock_xp );
+
+    return 0;
 
 }  // end process_fd_replicate()
@@ -1494,5 +1544,16 @@
     uint32_t max = hal_remote_l32( XPTR( owner_cxy , &owner_ptr->fd_array.max ));
 
-    printk("\n***** fd_array for pid %x in cluster %x / max %d *****\n",
+    // get pointers on TXT0 chdev
+    xptr_t    txt0_xp  = chdev_dir.txt_tx[0];
+    cxy_t     txt0_cxy = GET_CXY( txt0_xp );
+    chdev_t * txt0_ptr = GET_PTR( txt0_xp );
+
+    // get extended pointer on remote TXT0 lock
+    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
+
+    // get TXT0 lock 
+    remote_busylock_acquire( lock_xp );
+
+    nolock_printk("\n***** fd_array for pid %x in cluster %x / max %d *****\n",
     pid, process_cxy, max );
 
@@ -1520,5 +1581,5 @@
 
                 // display relevant file descriptor info
-                printk(" - %d : type %s / ptr %x (%s)\n",
+                nolock_printk(" - %d : type %s / ptr %x (%s)\n",
                 fdid, process_fd_type_str(file_type), file_ptr, name );
             }
@@ -1526,5 +1587,5 @@
             {
                 // display relevant file decriptor info
-                printk(" - %d : type %s / ptr %x\n",
+                nolock_printk(" - %d : type %s / ptr %x\n",
                 fdid , process_fd_type_str(file_type), file_ptr );
             }
@@ -1532,8 +1593,11 @@
         else
         {
-            printk(" - %d : empty slot\n",
-            fdid );
+            nolock_printk(" - %d : empty slot\n", fdid );
         }
     }
+
+    // get TXT0 lock 
+    remote_busylock_acquire( lock_xp );
+
 }   // end process_fd_display()
 
@@ -1548,5 +1612,8 @@
 {
     ltid_t         ltid;
+    ltid_t         ltid_min;
+
     bool_t         found = false;
+    lpid_t         lpid  = LPID_FROM_PID( process->pid );
  
 // check arguments
@@ -1554,10 +1621,12 @@
 assert( __FUNCTION__, (thread != NULL) , "thread argument is NULL" );
 
-    // get the lock protecting th_tbl for all threads
-    // but the idle thread executing kernel_init (cannot yield)
+    // get the lock protecting th_tbl for all threads but the idle thread 
     if( thread->type != THREAD_IDLE ) rwlock_wr_acquire( &process->th_lock );
 
+    // compute ltid_min : 0 for an user thread / 1 for a kernel thread
+    ltid_min = (lpid == 0) ? 1 : 0;
+ 
     // scan th_tbl
-    for( ltid = 0 ; ltid < CONFIG_THREADS_MAX_PER_CLUSTER ; ltid++ )
+    for( ltid = ltid_min ; ltid < CONFIG_THREADS_MAX_PER_CLUSTER ; ltid++ )
     {
         if( process->th_tbl[ltid] == NULL )
@@ -1581,5 +1650,5 @@
     if( thread->type != THREAD_IDLE ) rwlock_wr_release( &process->th_lock );
 
-    return (found) ? 0 : 0xFFFFFFFF;
+    return (found) ? 0 : -1;
 
 }  // end process_register_thread()
@@ -1647,6 +1716,6 @@
 "parent process must be the reference process" );
 
-#if DEBUG_PROCESS_MAKE_FORK
-uint32_t   cycle;
+#if DEBUG_PROCESS_MAKE_FORK || DEBUG_PROCESS_ERROR
+uint32_t   cycle  = (uint32_t)hal_get_cycles();
 thread_t * this  = CURRENT_THREAD;
 trdid_t    trdid = this->trdid;
@@ -1655,5 +1724,4 @@
 
 #if( DEBUG_PROCESS_MAKE_FORK & 1 )
-cycle   = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_FORK < cycle )
 printk("\n[%s] thread[%x,%x] enter / cluster %x / cycle %d\n",
@@ -1662,10 +1730,13 @@
 
     // allocate a process descriptor
-    process = process_alloc();
+    process = kmem_alloc( bits_log2(sizeof(process_t)) , AF_ZERO );
 
     if( process == NULL )
     {
-        printk("\n[ERROR] in %s : cannot get process in cluster %x\n", 
-        __FUNCTION__, local_cxy ); 
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate process descriptor / cxy %x / cycle %d\n", 
+__FUNCTION__, pid, trdid, local_cxy, cycle ); 
+#endif
         return -1;
     }
@@ -1675,15 +1746,17 @@
     if( error ) 
     {
-        printk("\n[ERROR] in %s : cannot get PID in cluster %x\n", 
-        __FUNCTION__, local_cxy ); 
-        process_free( process );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot get PID / cxy %x / cycle %d\n", 
+__FUNCTION__, pid, trdid, local_cxy, cycle ); 
+#endif
+	    kmem_free( process , bits_log2(sizeof(process_t)) );
         return -1;
     }
 
 #if( DEBUG_PROCESS_MAKE_FORK & 1 )
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_FORK < cycle )
-printk("\n[%s] thread[%x,%x] allocated child_process %x / cycle %d\n",
-__FUNCTION__, pid, trdid, new_pid, cycle );
+printk("\n[%s] thread[%x,%x] allocated child_process %x\n",
+__FUNCTION__, pid, trdid, new_pid );
 #endif
 
@@ -1694,15 +1767,18 @@
     if( error ) 
     {
-        printk("\n[ERROR] in %s : cannot initialize child process in cluster %x\n", 
-        __FUNCTION__, local_cxy ); 
-        process_free( process );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot initialize child process / cxy %x / cycle %d\n", 
+__FUNCTION__, pid, trdid, local_cxy, cycle ); 
+#endif
+        cluster_pid_release( new_pid );
+	    kmem_free( process , bits_log2(sizeof(process_t)) );
         return -1;
     }
 
 #if( DEBUG_PROCESS_MAKE_FORK & 1 )
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_FORK < cycle )
-printk("\n[%s] thread[%x,%x] initialized child_process %x / cycle %d\n",
-__FUNCTION__, pid, trdid, new_pid, cycle );
+printk("\n[%s] thread[%x,%x] initialized child_process %x\n",
+__FUNCTION__, pid, trdid, new_pid );
 #endif
 
@@ -1712,17 +1788,19 @@
     if( error )
     {
-        printk("\n[ERROR] in %s : cannot copy VMM in cluster %x\n", 
-        __FUNCTION__, local_cxy ); 
-        process_free( process );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot copy VMM to child process / cxy %x / cycle %d\n", 
+__FUNCTION__, pid, trdid, local_cxy, cycle ); 
+#endif
         cluster_pid_release( new_pid );
+	    kmem_free( process , bits_log2(sizeof(process_t)) );
         return -1;
     }
 
 #if( DEBUG_PROCESS_MAKE_FORK & 1 )
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_FORK < cycle )
 {
-    printk("\n[%s] thread[%x,%x] copied VMM from parent to child / cycle %d\n",
-    __FUNCTION__, pid, trdid, cycle );
+    printk("\n[%s] thread[%x,%x] copied VMM from parent to child\n",
+    __FUNCTION__, pid, trdid );
     hal_vmm_display( XPTR( local_cxy , process ) , true );
 }
@@ -1736,8 +1814,7 @@
 
 #if( DEBUG_PROCESS_MAKE_FORK & 1 )
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_FORK < cycle )
-printk("\n[%s] thread[%x,%x] / child_process %x takes TXT ownership / cycle %d\n",
-__FUNCTION__ , pid, trdid, new_pid, cycle );
+printk("\n[%s] thread[%x,%x] / child_process %x takes TXT ownership\n",
+__FUNCTION__ , pid, trdid, new_pid );
 #endif
 
@@ -1753,8 +1830,11 @@
     if( error )
     {
-        printk("\n[ERROR] in %s : cannot create thread in cluster %x\n",
-        __FUNCTION__, local_cxy ); 
-        process_free( process );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot create main thread / cxy %x / cycle %d\n", 
+__FUNCTION__, pid, trdid, local_cxy, cycle ); 
+#endif
         cluster_pid_release( new_pid );
+	    kmem_free( process , bits_log2(sizeof(process_t)) );
         return -1;
     }
@@ -1765,8 +1845,7 @@
 
 #if( DEBUG_PROCESS_MAKE_FORK & 1 )
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_FORK < cycle )
-printk("\n[%s] thread[%x,%x] created main thread %x / cycle %d\n", 
-__FUNCTION__, pid, trdid, thread, cycle );
+printk("\n[%s] thread[%x,%x] created main thread %x\n", 
+__FUNCTION__, pid, trdid, thread );
 #endif
 
@@ -1787,8 +1866,7 @@
  
 #if( DEBUG_PROCESS_MAKE_FORK & 1 )
-cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_FORK < cycle )
-printk("\n[%s] thread[%x,%x] set COW in DATA / ANON / REMOTE for parent and child / cycle %d\n",
-__FUNCTION__, pid, trdid, cycle );
+printk("\n[%s] thread[%x,%x] set COW in DATA / ANON / REMOTE for parent and child\n",
+__FUNCTION__, pid, trdid );
 #endif
 
@@ -1819,164 +1897,79 @@
 }   // end process_make_fork()
 
-////////////////////////////////////////////////i//////////////////////////////////////
-// This static function is called by the thread_user_exec() function :
-// - to register the main() arguments (args) in the <exec_info> structure.
-// - to register the environment variables (envs) in the <exec_info> structure.
-// In both cases the input is an array of NULL terminated string pointers in user
-// space, and the strings can be dispatched anywhere in the user process space.
-// This array of pointers is defined by the <u_pointers> argument. The empty slots 
-// contain the NULL value, and the N non-empty slots are indexed from 0 to (N-1).
-// - The max number of envs, and the max number of args are defined by the
-//   CONFIG_PROCESS_ARGS_NR and CONFIG_PROCESS_ENVS_MAX_NR parameters. 
-// - The numbers of pages to store the (args) and (envs) strings are defined by the
-//   CONFIG_VMM_ENVS_SIZE and CONFIG_VMM_STACK_SIZE parameters.
-///////////////////////////////////////////////////////////////////////////////////////
-// Implementation note:
-// It allocates a kernel buffer to store a kernel copy of both the array of pointers,
-// and the strings. It set the pointers and copies the strings in this kernel buffer.
-// Finally, it registers the buffer & the actual number of strings in the process
-// exec_info structure  (defined in the <process.h> file).
-///////////////////////////////////////////////////////////////////////////////////////
-// @ is_args     : [in]    true if called for (args) / false if called for (envs).
-// @ u_pointers  : [in]    array of pointers on the strings (in user space).
-// @ exec_info   : [out]   pointer on the exec_info structure.
-// @ return 0 if success / non-zero if too many strings or no memory.
-///////////////////////////////////////////////////////////////////////////////////////
-error_t process_exec_get_strings( bool_t         is_args,
-                                  char        ** u_pointers,
-                                  exec_info_t  * exec_info )
-{
-    uint32_t     index;           // slot index in pointers array
-    uint32_t     length;          // string length (in bytes)
-    uint32_t     pointers_bytes;  // number of bytes to store pointers
-    uint32_t     max_index;       // max size of pointers array
-    char      ** k_pointers;      // base of kernel array of pointers
-    char       * k_buf_ptr;       // pointer on first empty slot in strings buffer
-    uint32_t     k_buf_space;     // number of bytes available in string buffer
-    kmem_req_t   req;             // kernel memory allocator request
-    char       * k_buf;           // kernel buffer for both pointers & strings 
-
-#if DEBUG_PROCESS_EXEC_GET_STRINGS 
-thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-#endif
-
-    // Allocate one block of physical memory for both the pointers and the strings
-    // as defined by the CONFIG_VMM_ARGS_SIZE and CONFIG_VMM_ENVS_SIZE parameters
-    // - the array of pointers is stored in the first bytes of the kernel buffer
-    // - the strings themselve are stored in the next bytes of this buffer
-    // Set the k_pointers, k_buf_ptr, k_buf_space, and max_index
-
-    if( is_args )
-    {
-        req.type   = KMEM_PPM;
-        req.order  = bits_log2( CONFIG_VMM_ARGS_SIZE );
-        req.flags  = AF_KERNEL | AF_ZERO;
-        k_buf      = kmem_alloc( &req );
-
-        pointers_bytes = CONFIG_PROCESS_ARGS_MAX_NR * sizeof(char *);
-        k_pointers     = (char **)k_buf;
-        k_buf_ptr      = k_buf + pointers_bytes;
-        k_buf_space    = (CONFIG_VMM_ARGS_SIZE * CONFIG_PPM_PAGE_SIZE) - pointers_bytes;
-        max_index      = CONFIG_PROCESS_ARGS_MAX_NR;
-
-#if DEBUG_PROCESS_EXEC_GET_STRINGS 
-if( DEBUG_PROCESS_EXEC_GET_STRINGS < cycle )
-printk("\n[%s] thread[%x,%x] for args / u_buf %x / k_buf %x\n",
-__FUNCTION__, this->process->pid, this->trdid, u_pointers, k_buf );
-#endif
-
-    }
-    else
-    {
-        req.type   = KMEM_PPM;
-        req.order  = bits_log2( CONFIG_VMM_ENVS_SIZE );
-        req.flags  = AF_KERNEL | AF_ZERO;
-        k_buf      = kmem_alloc( &req );
-
-        pointers_bytes = CONFIG_PROCESS_ENVS_MAX_NR * sizeof(char *);
-        k_pointers     = (char **)k_buf;
-        k_buf_ptr      = k_buf + pointers_bytes;
-        k_buf_space    = (CONFIG_VMM_ENVS_SIZE * CONFIG_PPM_PAGE_SIZE) - pointers_bytes;
-        max_index      = CONFIG_PROCESS_ENVS_MAX_NR;
-
-#if DEBUG_PROCESS_EXEC_GET_STRINGS 
-if( DEBUG_PROCESS_EXEC_GET_STRINGS < cycle )
-printk("\n[%s] thread[%x,%x] for envs / u_buf %x / k_buf %x\n",
-__FUNCTION__, this->process->pid, this->trdid, u_pointers, k_buf );
-#endif
-
-    }
-
-    // copy the user array of pointers to kernel buffer
-    hal_copy_from_uspace( XPTR( local_cxy , k_pointers ),
-                          u_pointers,
-                          pointers_bytes );
-
-    // WARNING : the pointers copied in the k_pointers[] array are user pointers,
-    // after the loop below, the k_pointers[] array contains kernel pointers.
-
-#if DEBUG_PROCESS_EXEC_GET_STRINGS 
-if( DEBUG_PROCESS_EXEC_GET_STRINGS < cycle )
-printk("\n[%s] thread[%x,%x] copied u_ptr array to k_ptr array\n"
-"    p0 = %x / p1 = %x / p2 = %x / p3 = %x\n",
-__FUNCTION__, this->process->pid, this->trdid,
-k_pointers[0], k_pointers[1], k_pointers[2], k_pointers[3] );
-#endif
-
-    // scan kernel array of pointers to copy strings to kernel buffer
-    for( index = 0 ; index < max_index ; index++ )
-    {
-        // exit loop if (k_pointers[] == NUll)
-        if( k_pointers[index] == NULL ) break;
-
-        // compute string length 
-        length = hal_strlen_from_uspace( k_pointers[index] ) + 1;
-
-        // return error if overflow in kernel buffer
-        if( length > k_buf_space ) return -1;
-
-        // copy the string to kernel buffer
-        hal_copy_from_uspace( XPTR( local_cxy , k_buf_ptr ),
-                              k_pointers[index],
-                              length );
-
-#if DEBUG_PROCESS_EXEC_GET_STRINGS 
-if( DEBUG_PROCESS_EXEC_GET_STRINGS < cycle )
-printk("\n[%s] thread[%x,%x] copied string[%d] <%s> to kernel buffer / length %d\n",
-__FUNCTION__, this->process->pid, this->trdid, index, k_buf_ptr, length );
-#endif
-
-        // replace the user pointer by a kernel pointer in the k_pointer[] array
-        k_pointers[index] = k_buf_ptr;
-
-        // increment loop variables
-        k_buf_ptr   += length;
-        k_buf_space -= length;
-
-    }  // end loop on index
-
-    // update into exec_info structure
-    if( is_args )
-    {
-        exec_info->args_pointers  =  k_pointers;
-        exec_info->args_nr        =  index;
-    }
-    else
-    {
-        exec_info->envs_pointers  =  k_pointers;
-        exec_info->envs_buf_free  =  k_buf_ptr;
-        exec_info->envs_nr        =  index;
-    }
-
-#if DEBUG_PROCESS_EXEC_GET_STRINGS 
-if( DEBUG_PROCESS_EXEC_GET_STRINGS < cycle )
-printk("\n[%s] thread[%x,%x] copied %d strings to kernel buffer\n",
-__FUNCTION__, this->process->pid, this->trdid, index );
-#endif
-
-    return 0;
-
-} // end process_exec_get_strings()
+#if DEBUG_PROCESS_MAKE_EXEC
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This static debug function displays the current state of the exec_info structure
+// embedded in the calling process descriptor.
+//
+// WARNING : It can be used after execution of the sys_exec function, but it cannot
+//           be used after execution of the process_make_exec() function, because the
+//           kernel pointers have been replaced by user pointers. 
+/////////////////////////////////////////////////////////////////////////////////////////
+static void process_exec_info_display( bool_t args_ok,
+                                       bool_t envs_ok )
+{
+    uint32_t   i;
+    char     * str;    // local pointer on a string
+
+    process_t * process = CURRENT_THREAD->process;
+
+    // get relevant info from calling process descriptor
+    pid_t       pid      = process->pid;
+
+    uint32_t    args_nr  = process->exec_info.args_nr;
+    char     ** args     = process->exec_info.args_pointers;
+
+    uint32_t    envs_nr  = process->exec_info.envs_nr;
+    char     ** envs     = process->exec_info.envs_pointers;
+
+    char      * path     = process->exec_info.path;
+
+    // get pointers on TXT0 chdev
+    xptr_t    txt0_xp  = chdev_dir.txt_tx[0];
+    cxy_t     txt0_cxy = GET_CXY( txt0_xp );
+    chdev_t * txt0_ptr = GET_PTR( txt0_xp );
+
+    // get extended pointer on remote TXT0 lock
+    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
+
+    // get TXT0 lock 
+    remote_busylock_acquire( lock_xp );
+
+    nolock_printk("\n***** exec_info for process %x in cluster %x / %s\n",
+    pid , local_cxy , path ); 
+
+    // display arguments if required
+    if( args_ok )
+    {
+        for( i = 0 ; i < args_nr ; i++ )
+        {
+            str = args[i];
+            if( str != NULL)         // display pointer and string
+            nolock_printk(" - &arg[%d] = %x / arg[%d] = <%s>\n", i, str, i, str );
+            else                     // display WARNING
+            nolock_printk(" - unexpected NULL pointer for &arg[%d]\n", i );
+        }
+    }
+
+    // display env variables if required
+    if( envs_ok )
+    {
+        for( i = 0 ; i < envs_nr ; i++ )
+        {
+            str = envs[i];
+            if( str != NULL)     // display pointer and string
+            nolock_printk(" - &env[%d] = %x / env[%d] = <%s>\n", i, str, i, str );
+            else                     // display WARNING 
+            nolock_printk(" - unexpected NULL pointer for &env[%d]\n", i );
+        }
+    }
+
+    // release TXT0 lock 
+    remote_busylock_release( lock_xp );
+
+}  // end process_exec_info_display()
+
+#endif // DEBUG_PROCESS_MAKE_EXEC
 
 /////////////////////////////////
@@ -2003,4 +1996,8 @@
     uint32_t         envs_size;               // envs vseg size (bytes)
 
+#if DEBUG_PROCESS_MAKE_EXEC || DEBUG_PROCESS_ERROR
+uint32_t cycle = (uint32_t)hal_get_cycles();
+#endif
+
     // get calling thread, process, pid, trdid, and ref_xp 
     this    = CURRENT_THREAD;
@@ -2014,5 +2011,4 @@
 
 #if DEBUG_PROCESS_MAKE_EXEC
-uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_PROCESS_MAKE_EXEC < cycle )
 printk("\n[%s] thread[%x,%x] enters for <%s> / cycle %d\n",
@@ -2032,6 +2028,9 @@
 	if( error )
 	{
-		printk("\n[ERROR] in %s : thread[%x,%x] failed to open file <%s>\n",
-        __FUNCTION__, pid, trdid, elf_path );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] failed to open file <%s> / cycle %d\n", 
+__FUNCTION__, pid, trdid, elf_path, cycle ); 
+#endif
 		return -1;
 	}
@@ -2064,6 +2063,7 @@
 #endif
 
-    // 4. register the "args" vseg in VSL and map it in GPT, if required
-    // this vseg contains both the array of pointers and the strings
+    // 4. register the "args" vseg in VSL and map it in GPT, if args_nr != 0.
+    //    As this vseg contains an array of pointers, the kernel pointers 
+    //    are replaced by user pointers in new process space.
     args_nr = process->exec_info.args_nr;
 
@@ -2071,6 +2071,6 @@
     {
         // get args vseg base and size in user space
-        args_base = CONFIG_VMM_UTILS_BASE << CONFIG_PPM_PAGE_SHIFT;
-        args_size = CONFIG_VMM_ARGS_SIZE << CONFIG_PPM_PAGE_SHIFT;
+        args_base = CONFIG_VMM_UTILS_BASE << CONFIG_PPM_PAGE_ORDER;
+        args_size = CONFIG_VMM_ARGS_SIZE << CONFIG_PPM_PAGE_ORDER;
 
         // create and register args vseg in VMM
@@ -2085,6 +2085,9 @@
         if( vseg == NULL )
         {
-	         printk("\n[ERROR] in %s : thread[%x,%x] cannot get args vseg for <%s>\n",
-             __FUNCTION__, pid, trdid, elf_path );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot create args vseg for <%s> / cycle %d\n", 
+__FUNCTION__, pid, trdid, elf_path, cycle ); 
+#endif
 		     return -1;
         }
@@ -2098,26 +2101,30 @@
 }
 #endif
-        // map all pages for this "args" vseg
+        // map all pages for the "args" vseg
         uint32_t fake_attr;   // required for hal_gpt_lock_pte()
         ppn_t    fake_ppn;    // required for hal_gpt_lock_pte()
 
-        xptr_t   gpt  = XPTR( local_cxy , &process->vmm.gpt );
-        uint32_t attr = GPT_MAPPED | GPT_SMALL | GPT_READABLE | GPT_USER | GPT_CACHABLE;
-        vpn_t    vpn  = CONFIG_VMM_UTILS_BASE;
-        ppn_t    ppn  = ((ppn_t)process->exec_info.args_pointers >> CONFIG_PPM_PAGE_SHIFT);
+        xptr_t   base_xp = XPTR( local_cxy , process->exec_info.args_pointers );
+        xptr_t   gpt_xp  = XPTR( local_cxy , &process->vmm.gpt );
+        uint32_t attr    = GPT_MAPPED | GPT_SMALL | GPT_READABLE | GPT_USER | GPT_CACHABLE;
+        vpn_t    vpn     = CONFIG_VMM_UTILS_BASE;
+        ppn_t    ppn     = ppm_base2ppn( base_xp );
 
         for( n = 0 ; n < CONFIG_VMM_ARGS_SIZE ; n++ )  
         {
             // lock the PTE
-            if (hal_gpt_lock_pte( gpt , vpn , &fake_attr , &fake_ppn ) )
+            if (hal_gpt_lock_pte( gpt_xp , vpn + n , &fake_attr , &fake_ppn ) )
             {
-                printk("\n[ERROR] in %s : thread[%x,%x] cannot map args vpn %x for <%s>\n",
-                __FUNCTION__, pid, trdid, vpn, elf_path );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] cannot map vpn[%x] of args vseg for <%s> / cycle %d\n", 
+__FUNCTION__, pid, trdid,  vpn + n , elf_path , cycle ); 
+#endif
 		        return -1;
             }
 
             // map and unlock the PTE
-            hal_gpt_set_pte( gpt , vpn + n , attr , ppn + n );
-        }
+            hal_gpt_set_pte( gpt_xp , vpn + n , attr , ppn + n );
+       }
 
 #if( DEBUG_PROCESS_MAKE_EXEC & 1 )
@@ -2127,18 +2134,32 @@
     __FUNCTION__, pid, trdid );
     hal_vmm_display( ref_xp , true );
+    process_exec_info_display( true , false );   // args & not envs
 }
 #endif
 
-        // set user space pointers in array of pointers
-        char  ** ptr    = process->exec_info.args_pointers;
-
+        // build pointer on args buffer in kernel space
+        char  ** k_args = process->exec_info.args_pointers;
+
+        // build pointer on args buffer in user space
+        char  ** u_args = (char **)args_base;
+
+        // set user space pointers in kernel args buffer
         for( n = 0 ; n < args_nr ; n++ )
         {
-            ptr[n] = ptr[n] + args_base - (intptr_t)ptr;
+            k_args[n] = (char *)((intptr_t)k_args[n] + (intptr_t)u_args - (intptr_t)k_args);
         } 
-    }
-
-    // 5. register the "envs" vseg in VSL and map it in GPT, if required
-    // this vseg contains both the array of pointers and the strings
+
+#if( DEBUG_PROCESS_MAKE_EXEC & 1 )
+if( DEBUG_PROCESS_MAKE_EXEC < cycle )
+printk("\n[%s] thread[%x,%x] args user pointers set in exec_info\n",
+__FUNCTION__, pid, trdid );
+#endif
+
+    }
+
+    // 5. register the "envs" vseg in VSL and map it in GPT, if envs_nr != 0.
+    //    As this vseg contains an array of pointers, the kernel pointers 
+    //    are replaced by user pointers in new process space.
+
     envs_nr = process->exec_info.envs_nr;
 
@@ -2146,8 +2167,15 @@
     {
         // get envs vseg base and size in user space from config
-        envs_base = (CONFIG_VMM_UTILS_BASE + CONFIG_VMM_ARGS_SIZE) << CONFIG_PPM_PAGE_SHIFT;
-        envs_size = CONFIG_VMM_ENVS_SIZE << CONFIG_PPM_PAGE_SHIFT;
-
-        // TODO (inspired from args)
+        envs_base = (CONFIG_VMM_UTILS_BASE + CONFIG_VMM_ARGS_SIZE) << CONFIG_PPM_PAGE_ORDER;
+        envs_size = CONFIG_VMM_ENVS_SIZE << CONFIG_PPM_PAGE_ORDER;
+
+        // TODO (should be similar to the code for args above)
+
+#if( DEBUG_PROCESS_MAKE_EXEC & 1 )
+if( DEBUG_PROCESS_MAKE_EXEC < cycle )
+printk("\n[%s] thread[%x,%x] envs user pointers set in exec_info\n",
+__FUNCTION__, pid, trdid );
+#endif
+
     }
 
@@ -2156,8 +2184,12 @@
     // register extended pointer on .elf file in process descriptor
 	error = elf_load_process( file_xp , process );
+
     if( error )
 	{
-		printk("\n[ERROR] in %s : thread[%x,%x] failed to access <%s>\n",
-        __FUNCTION__, pid, trdid, elf_path );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] failed to access file <%s> / cycle %d\n", 
+__FUNCTION__, pid, trdid , elf_path , cycle ); 
+#endif
         return -1;
 	}
@@ -2183,6 +2215,9 @@
     if( vseg == NULL )
     {
-	    printk("\n[ERROR] in %s : thread[%x,%x] cannot set u_stack vseg for <%s>\n",
-        __FUNCTION__, pid, trdid, elf_path );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] failed to set u_stack vseg for <%s> / cycle %d\n", 
+__FUNCTION__, pid, trdid , elf_path , cycle ); 
+#endif
 		return -1;
     }
@@ -2205,9 +2240,13 @@
     if( error )
     {
-        printk("\n[ERROR] in %s : thread[%x,%x] cannot update thread for <%s>\n",
-        __FUNCTION__ , pid, trdid, elf_path );
+
+#if DEBUG_PROCESS_ERROR
+printk("\n[ERROR] in %s : thread[%x,%x] failed to set main thread for <%s> / cycle %d\n", 
+__FUNCTION__, pid, trdid , elf_path , cycle ); 
+#endif
         return -1;
     }
 
+    // should not be reached, avoid a warning
 	return 0;
 
@@ -2294,4 +2333,5 @@
 if( DEBUG_PROCESS_ZERO_CREATE < cycle )
 printk("\n[%s] initialized hal specific VMM in cluster%x\n", __FUNCTION__, local_cxy );
+hal_vmm_display( XPTR( local_cxy , process ) , true ); 
 #endif
 
@@ -2356,5 +2396,5 @@
 
     // allocates memory for process descriptor from local cluster
-	process = process_alloc(); 
+    process = kmem_alloc( bits_log2(sizeof(process_t)) , AF_ZERO );
     if( process == NULL )
     {
@@ -2506,23 +2546,27 @@
 }  // end process_init_create()
 
-/////////////////////////////////////////
-void process_display( xptr_t process_xp )
-{
-    process_t   * process_ptr;
-    cxy_t         process_cxy;
+///////////////////////////////////////////////////
+uint32_t process_build_string( xptr_t   process_xp,
+                               char   * buffer,
+                               uint32_t size )
+{
+    int32_t       length;          // actual length of the string
+
+    process_t   * process_ptr;     // process descriptor local pointer
+    cxy_t         process_cxy;     // process descriptor cluster identifier
 
     xptr_t        parent_xp;       // extended pointer on parent process
-    process_t   * parent_ptr;
-    cxy_t         parent_cxy;
+    process_t   * parent_ptr;      // parent process local pointer
+    cxy_t         parent_cxy;      // parent process cluster identifier
 
     xptr_t        owner_xp;        // extended pointer on owner process
-    process_t   * owner_ptr;
-    cxy_t         owner_cxy;
-
-    pid_t         pid;
-    pid_t         ppid;
-    lpid_t        lpid;
-    uint32_t      state;
-    uint32_t      th_nr;
+    process_t   * owner_ptr;       // owner process local pointer
+    cxy_t         owner_cxy;       // owner process cluster identifier
+
+    pid_t         pid;             // process identifier
+    pid_t         ppid;            // parent process identifier
+    lpid_t        lpid;            // local process identifier
+    uint32_t      state;           // terminaison state
+    uint32_t      th_nr;           // number of threads
 
     xptr_t        txt_file_xp;     // extended pointer on TXT_RX file descriptor
@@ -2540,4 +2584,6 @@
     char          elf_name[CONFIG_VFS_MAX_NAME_LENGTH];
 
+assert( __FUNCTION__ , (size >= 80 ) , "buffer size too small" );
+
     // get cluster and local pointer on process
     process_ptr = GET_PTR( process_xp );
@@ -2566,10 +2612,9 @@
     if( lpid )                                   // user process
     {
-
         // get extended pointer on file descriptor associated to TXT_RX 
         txt_file_xp = hal_remote_l64( XPTR( owner_cxy , &owner_ptr->fd_array.array[0] ) );
 
-        assert( __FUNCTION__, (txt_file_xp != XPTR_NULL) ,
-        "process must be attached to one TXT terminal" ); 
+assert( __FUNCTION__, (txt_file_xp != XPTR_NULL) ,
+"process must be attached to one TXT terminal" ); 
 
         // get TXT_RX chdev pointers
@@ -2582,7 +2627,7 @@
                            XPTR( txt_chdev_cxy , txt_chdev_ptr->name ) );
     
+        // get TXT_owner process
         txt_owner_xp = (xptr_t)hal_remote_l64( XPTR( txt_chdev_cxy, 
                                                      &txt_chdev_ptr->ext.txt.owner_xp ) );
-
         // get process .elf name
         elf_file_xp   = hal_remote_l64( XPTR( process_cxy , &process_ptr->vfs_bin_xp ) );
@@ -2594,5 +2639,5 @@
     else                                         // kernel process_zero
     {
-        // TXT name and .elf name are not registered in kernel process_zero
+        // TXT name and .elf name are not registered in kernel process
         strcpy( txt_name , "txt0_rx" );
         txt_owner_xp = process_xp; 
@@ -2603,12 +2648,38 @@
     if( txt_owner_xp == process_xp )
     {
-        nolock_printk("PID %X | %s (FG) | %X | PPID %X | TS %X | %d | %s\n", 
+        length = snprintk( buffer, size,
+        "PID %X | %s (FG) | %X | PPID %X | TS %X | %d | %s\n", 
         pid, txt_name, process_ptr, ppid, state, th_nr, elf_name );
     }
     else
     {
-        nolock_printk("PID %X | %s (BG) | %X | PPID %X | TS %X | %d | %s\n", 
+        length = snprintk( buffer, size,
+        "PID %X | %s (BG) | %X | PPID %X | TS %X | %d | %s\n", 
         pid, txt_name, process_ptr, ppid, state, th_nr, elf_name );
     }
+
+    // check length 
+    if( (length < 0) )
+    {
+        length = snprintk( buffer , size , 
+        "buffer too small for process %x in cluster %x", pid , process_cxy );
+    }
+
+    return length;  
+
+}  // end process_build_string()
+
+/////////////////////////////////////////
+void process_display( xptr_t process_xp )
+{
+    char  buffer[CONFIG_PROCESS_DISPLAY_BUF_SIZE];
+
+    // build the string to be displayed
+    process_build_string( process_xp,
+                          buffer,
+                          CONFIG_PROCESS_DISPLAY_BUF_SIZE ); 
+    // display the string
+    nolock_puts( buffer );
+
 }  // end process_display() 
 
