Index: trunk/kernel/syscalls/sys_exec.c
===================================================================
--- trunk/kernel/syscalls/sys_exec.c	(revision 690)
+++ trunk/kernel/syscalls/sys_exec.c	(revision 1)
@@ -1,6 +1,7 @@
 /*
- * sys_exec.c - Kernel function implementing the "exec" system call.
- *
- * Authors   Alain Greiner (2016,2017,2017,2019,2020)
+ * sys_exec.c - Kernel function implementing the "exec" syscall
+ * 
+ * Authors    Mohamed Lamine Karaoui (2015)
+ *            Alain Greiner          (2016)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -22,7 +23,6 @@
  */
 
-#include <kernel_config.h>
-#include <hal_kernel_types.h>
-#include <hal_uspace.h>
+#include <almos_config.h>
+#include <hal_types.h>
 #include <errno.h>
 #include <printk.h>
@@ -36,320 +36,249 @@
 #include <rpc.h>
 
-#include <syscalls.h>
-
-////////////////////////////////////////////////i////////////////////////////////////////
+
+////////////////////////////////////////////////i////////////////////////////////////// 
+// This static function is called by the sys_exec() function to register the .elf
+// pathname in the exec_info  structure, from a string stored in user space.
+////////////////////////////////////////////////i////////////////////////////////////// 
+// @ exec_info : pointer on the exec_info structure.
+// @ pathname  : string containing the path to the .elf file (user space).
+// return 0 if success / non-zero if one string too long, or too many strings. 
+///////////////////////////////////////////////////////////////////////////////////////
+static error_t process_exec_get_path( exec_info_t  * exec_info,
+                                      char         * pathname )
+{
+    uint32_t length;
+
+    // get string length
+	hal_strlen_from_uspace( pathname , &length );
+	if( length > 255 ) 
+    {
+        printk(ERROR, "%s: elf file pathname larger than 255 bytes\n", __FUNCTION__ );
+        return EINVAL;
+    }
+
+    // copy string to exec_info
+    hal_copy_from_uspace( &exec_info->path[0] , pathname , length );
+
+    return 0;
+}
+
+////////////////////////////////////////////////i////////////////////////////////////// 
 // This static function is called twice by the sys_exec() function :
-// - to register the main() arguments (args) in the process <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,
-// identified by the <u_pointers> argument. The strings can be dispatched anywhere in 
-// the calling user process space. 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. 
-////////////////////////////////////////////////i////////////////////////////////////////
-// Implementation Note:
-// Both the array of pointers and the strings themselve are stored in kernel space in one
-// single, dynamically allocated, kernel buffer containing an integer number of pages,
-// defined by the CONFIG_VMM_ENVS_SIZE and CONFIG_VMM_STACK_SIZE parameters.
-// These two kernel buffers contains :
-// - in the first bytes a fixed size kernel array of kernel pointers on the strings.
-// - in the following bytes the strings themselves.
-// The exec_info_t structure is defined in the <process.h> file.
-////////////////////////////////////////////////i////////////////////////////////////////
-// @ 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   : [inout] pointer on the exec_info structure.
-// @ return 0 if success / non-zero if too many strings or no memory.
-////////////////////////////////////////////////i////////////////////////////////////////
-static error_t exec_get_strings( bool_t         is_args,
-                                 char        ** u_pointers,
-                                 exec_info_t  * exec_info )
+// - 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 string pointers in user space,
+// and a set of strings in user space. 
+// We allocate one physical page to store a kernel copy of the array of pointers,
+// we allocate one or several physical pages to store the strings themselve,
+// and register these buffers and the number of strings in the exec_info structure.
+// The max number of strings is 1024 (for both args and envs). The numbers of pages 
+// to store the (args) and (envs) strings are configuration parameters.
+////////////////////////////////////////////////i////////////////////////////////////// 
+// @ exec_info : pointer on the exec_info structure.
+// @ is_args   : true if called for (args) / false if called for (envs).
+// @ pointers  : array of pointers on the strings (in user space).
+// @ return 0 if success / non-zero if too many strings or no more memory. 
+///////////////////////////////////////////////////////////////////////////////////////
+static error_t process_exec_get_strings( exec_info_t  * exec_info,
+                                         bool_t         is_args,
+			                             char        ** u_pointers )
 {
-    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
-    char       * k_buf;           // kernel buffer for both pointers & strings 
-
-#if DEBUG_SYS_EXEC 
-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
-
-    if( is_args )
-    {
-        k_buf = kmem_alloc( bits_log2(CONFIG_VMM_ARGS_SIZE << CONFIG_PPM_PAGE_ORDER), AF_ZERO );
-
-        pointers_bytes = (CONFIG_PROCESS_ARGS_MAX_NR + 1) * sizeof(char *);
-        k_pointers     = (char **)k_buf;
-        k_buf_ptr      = k_buf + pointers_bytes;
-        k_buf_space    = (CONFIG_VMM_ARGS_SIZE << CONFIG_PPM_PAGE_ORDER) - pointers_bytes;
-        max_index      = CONFIG_PROCESS_ARGS_MAX_NR + 1;
-
-#if DEBUG_SYS_EXEC 
-if( DEBUG_SYS_EXEC < 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  // envs
-    {
-        k_buf = kmem_alloc( bits_log2(CONFIG_VMM_ENVS_SIZE << CONFIG_PPM_PAGE_ORDER), AF_ZERO );
-
-        pointers_bytes = (CONFIG_PROCESS_ENVS_MAX_NR + 1) * sizeof(char *);
-        k_pointers     = (char **)k_buf;
-        k_buf_ptr      = k_buf + pointers_bytes;
-        k_buf_space    = (CONFIG_VMM_ENVS_SIZE << CONFIG_PPM_PAGE_ORDER) - pointers_bytes;
-        max_index      = CONFIG_PROCESS_ENVS_MAX_NR + 1;
-
-#if DEBUG_SYS_EXEC 
-if( DEBUG_SYS_EXEC < 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 ),
+    uint32_t     index;       // string index
+    uint32_t     found_null;  // NULL pointer found in array of pointers
+    uint32_t     length;      // string length
+    uint32_t     strings;     // actual number of strings
+    kmem_req_t   req;         // kmem request
+    page_t     * page;        // page descriptor
+    uint32_t     order;       // ln2( number of pages to store strings )
+    char      ** k_pointers;  // base of kernel buffer containing array of pointers
+    char       * buf_ptr;     // pointer on first empty slot in kernel strings buffer
+    char       * buf_base;    // base address of the kernel strings buffer
+
+    // compute ln2( number of pages for kernel strings buffer )
+    if( is_args ) order = CONFIG_PROCESS_ARGS_ORDER;
+    else          order = CONFIG_PROCESS_ENVS_ORDER;
+
+	req.type   = KMEM_PAGE;
+	req.flags  = AF_KERNEL | AF_ZERO;
+
+    // allocate one physical page for kernel array of pointers
+    req.type   = 0;
+    page       = kmem_alloc( &req );
+    if( page == NULL )
+    {
+        printk("ERROR in %s : cannot allocate memory for pointers\n", __FUNCTION__ );
+        return ENOMEM;
+    }
+    k_pointers = ppm_page2base( page );
+    
+    // allocate several physical pages to store the strings themselve
+    req.type   = order;
+    page       = kmem_alloc( &req );
+    if( page == NULL )
+    {
+        printk("ERROR in %s : cannot allocate memory for strings\n", __FUNCTION__ );
+        return ENOMEM;
+    }
+    buf_base    = ppm_page2base( page );
+    
+    // copy the array of pointers to kernel buffer 
+    hal_copy_from_uspace( 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_SYS_EXEC 
-if( DEBUG_SYS_EXEC < cycle )
-printk("\n[%s] thread[%x,%x] moved u_ptr array of pointers to k_ptr array\n",
-__FUNCTION__, this->process->pid, this->trdid );
-#endif
-
-    // scan kernel array of pointers to copy strings to kernel buffer
-    for( index = 0 ; index < max_index ; index++ )
-    {
-        // exit loop if (k_pointers[index] == NUll)
-        if( k_pointers[index] == NULL ) break;
-
-        // compute string length (without the NUL character)
-        length = hal_strlen_from_uspace( k_pointers[index] );
-
-        // return error if overflow in kernel buffer
-        if( length > k_buf_space ) return -1;
-
-        // copy the string itself to kernel buffer
-        hal_copy_from_uspace( XPTR( local_cxy , k_buf_ptr ),
-                              k_pointers[index],
-                              length + 1 );
-
-#if DEBUG_SYS_EXEC 
-if( DEBUG_SYS_EXEC < 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 + 1);
-        k_buf_space -= (length + 1);
-
-#if DEBUG_SYS_EXEC 
-if( DEBUG_SYS_EXEC < cycle )
+                          CONFIG_PPM_PAGE_SIZE );
+
+    // scan local copy of array of pointers to copy the strings
+    found_null = 0;
+    buf_ptr    = buf_base;
+    for( index = 0 ; index < 1024 ; index++ )
+    {
+        if( k_pointers[index] == NULL ) 
+        {
+            found_null = 1;
+            break;
+        }
+
+        // compute string length
+	    hal_strlen_from_uspace( k_pointers[index] , &length );
+ 
+        // copy the user string to kernel buffer
+        hal_copy_from_uspace( k_strings,
+                              k_pointers[index];
+                              length );
+
+        // update k_pointer[index] entry
+        k_pointers[index] = buf_ptr;
+
+        // increment pointer on kernel strings buffer
+        buf_ptr += (length + 1);
+    }
+
+    // update into exec_info structure 
+    if( found_null && is_args )
+    {
+        exec_info->args_pointers  =  k_pointers;
+        exec_info->args_buf_base  =  buf_base;
+        exec_info->args_nr        =  index;
+    }
+    else if( found_null && !is_args )
+    {
+        exec_info->envs_pointers  =  k_pointers;
+        exec_info->envs_buf_base  =  buf_base;
+        exec_info->envs_buf_free  =  buf_ptr;
+        exec_info->envs_nr        =  index;
+    }
+    else 
+    {
+        printk("ERROR in %s : number of strings larger than 1024\n", __FUNCTION__ );
+        return EINVAL;
+    }
+
+    return 0;
+} // end process_exec_get_strings()
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This function is executed in a "client" cluster by a process whose PID can belong
+// to another "server" cluster (defined by the MSB bits of the calling process PID).
+// A new process descriptor, and the associated main thread descriptor must be created
+// in the "server" cluster, using directly the process_make_exec() function if local,
+// or the rpc_process_exec_client() function if server is remote.
+/////////////////////////////////////////////////////////////////////////////////////////
+// Implementation note:
+// This function build an exec_info_t structure containing all informations
+// required to create the new process descriptor and the associated thread.
+// It calls the static process_exec_get_path() and process_exec_get_strings() functions
+// to copy the .elf pathname, the main() arguments and the environment variables from
+// user buffers to the exec_info_t, and calls the static process_make_exec() function.
+/////////////////////////////////////////////////////////////////////////////////////////
+int sys_exec( char  * filename,     // .elf file pathname
+              char ** argv,         // process arguments
+              char ** envp )        // environment variables
 {
-    if( k_pointers[0] != NULL )
-    printk("\n[%s] thread[%x,%x] : &arg0 = %x / arg0 = <%s>\n",
-    __FUNCTION__, this->process->pid, this->trdid, k_pointers[0], k_pointers[0] );
-    else 
-    printk("\n[%s] thread[%x,%x] : unexpected NULL value for &arg0\n",
-    __FUNCTION__, this->process->pid, this->trdid );
-}
-#endif
-
-    }  // 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_SYS_EXEC 
-if( DEBUG_SYS_EXEC < cycle )
-printk("\n[%s] thread[%x,%x] copied %d strings to kernel buffer\n",
-__FUNCTION__, this->process->pid, this->trdid, index );
-#endif
+    exec_info_t  exec_info;         // structure to pass to process_make_exec()
+    thread_t   * thread;            // pointer on thread created in server cluster
+	error_t      error   = 0;
+	process_t  * process = CURRENT_PROCESS;
+
+    // check arguments
+	if((filename == NULL) || (argv == NULL) || (envp == NULL)) 
+    {
+        printk("\n[ERROR] in %s : missing arguments / file = %x / argv = %x / envp = %x\n",
+               __FUNCTION__ , filename , argv , envp );
+        return EINVAL;
+    }
+
+    // compute client_cxy (local cluster) and server_cxy (target cluster)
+    cxy_t     cxy_server = CXY_FROM_PID( process->pid );
+    cxy_t     cxy_client = local_cxy;
+    bool_t    is_local   = (cxy_server == cxy_client);
+
+    exec_dmsg("\n[INFO] %s starts for process %x on core %d in cluster %x"
+                 " / target_cluster = %x / cycle %d\n",
+                 __FUNCTION__, process->pid , CURRENT_CORE->lid, 
+                 cxy_client, cxy_server, hal_time_stamp());
+
+    // Change process's state to prevent any concurent access TODO ??? [AG]
+
+    // initialize exec_info structure 
+    exec_info->pid      = process->pid;
+    exec_info->ppid     = process->ppid;
+    exec_info->fd_array = &process->fd_array;
+    exec_info->vfs_root = &process->vfs_root;
+    exec_info->vfs_cwd  = &process->vfs_cwd;
+    exec_info->vfs_bin  = &process->vfs_bin;
+
+	// check pathname and store it in exec_info structure
+	error = process_exec_get_path( &exec_info , filename );
+	if ( error ) return EINVAL;
+
+	// check and store argv in exec_info structure
+	error = process_exec_get_strings( &exec_info , true , argv );
+	if( error )  return EINVAL;
+	
+	// check and store envp in exec_info structure
+	error = process_exec_get_strings( &exec_info , false , envp );
+	if( error )  return EINVAL;
+	
+    if( is_local )  ////////////// local exec ////////////////////////// 
+    {
+        exec_dmsg("\n[INFO] %s starts local exec for process %x at cycle %d\n",
+                  __FUNCTION__, process->pid, hal_time_stamp());
+
+        // call directly the local process_make_exec() function
+        error = process_make_exec( &exec_info , &thread );
+        if error
+        {
+            printk("\n[ERROR] in %s : failed in local exec for process %x\n",
+                   __FUNCTION__ , process->pid );
+            return EINVAL;
+        }
+
+        exec_dmsg("\n[INFO] %s completes local exec for process %x at cycle %d\n",
+                  __FUNCTION__, process->pid , hal_time_stamp() );
+    }
+    else             ///////////// remote exec /////////////////////////
+    {
+        exec_dmsg("\n[INFO] %s starts remote exec for process %x at cycle %d\n",
+                  __FUNCTION__, process->pid, hal_time_stamp() );
+
+        // call the rpc_process_exec_client() function    
+        rpc_process_exec_client( cxy_server , &exec_info , &thread , &error );
+
+        if( error )
+        {
+            printk("\n[ERROR] in %s : failed in remote exec for process %x\n",
+                   __FUNCTION__ , process->pid );
+            return EINVAL;
+        }
+
+        exec_dmsg("\n[INFO] %s completes remote exec for process %x at cycle %d\n",
+                  __FUNCTION__, process->pid , hal_time_stamp() );
+    }
+
+    // If no error, delete the current thread an process descriptors.
+    thread_kill( CURRENT_THREAD );
+    process_kill( CURRENT_PROCESS );
 
     return 0;
-
-} // end exec_get_strings()
-
-
-///////////////////////////////
-int sys_exec( char  * pathname,    // .elf file pathname in user space
-              char ** user_args,   // pointer on array of process arguments in user space
-              char ** user_envs )  // pointer on array of env variables in user space
-{
-    error_t       error;
-    vseg_t      * vseg;
-
-    // get calling thread, process, & pid
-    thread_t    * this    = CURRENT_THREAD;
-    process_t   * process = this->process;
-    pid_t         pid     = process->pid;
-    trdid_t       trdid   = this->trdid;
-
-assert( __FUNCTION__, (CXY_FROM_PID( pid ) == local_cxy) ,
-"must be called in the owner cluster\n");
-
-assert( __FUNCTION__, (LTID_FROM_TRDID( trdid ) == 0) ,
-"must be called by the main thread\n");
-
-assert( __FUNCTION__, (user_envs == NULL) ,
-"environment variables not supported yet\n" );
-
-#if DEBUG_SYS_EXEC || DEBUG_SYSCALLS_ERROR 
-uint64_t     tm_start = hal_get_cycles();
-#endif
-
-    // check "pathname" mapped in user space
-    if( vmm_get_vseg( process , (intptr_t)pathname , &vseg ) )
-	{
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%] / pathname pointer %x unmapped\n",
-__FUNCTION__, pid, trdid, pathname );
-#endif
-        this->errno = EINVAL;
-		return -1;
-	}
-
-    // check "pathname" length
-    if( hal_strlen_from_uspace( pathname ) >= CONFIG_VFS_MAX_PATH_LENGTH )
-    {
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%x] / pathname too long\n",
-__FUNCTION__, pid, trdid );
-#endif
-        this->errno = ENFILE;
-        return -1;
-    }
-
-    // check "args" mapped in user space if non NULL
-    if( (user_args != NULL) && (vmm_get_vseg( process , (intptr_t)user_args , &vseg )) )
-	{
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s for thread[%x,%] : user_args pointer %x unmapped\n",
-__FUNCTION__, pid, trdid, user_args );
-#endif
-        this->errno = EINVAL;
-		return -1;
-	}
-
-    // check "envs" mapped in user space if not NULL
-    if( (user_envs != NULL) && (vmm_get_vseg( process , (intptr_t)user_envs , &vseg )) )
-	{
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%] / user_envs pointer %x unmapped\n",
-__FUNCTION__, pid, trdid, user_envs );
-#endif
-        this->errno = EINVAL;
-		return -1;
-	}
-
-    // 1. copy "pathname" in kernel exec_info structure 
-    hal_strcpy_from_uspace( XPTR( local_cxy , &process->exec_info.path[0] ),
-                            pathname,
-                            CONFIG_VFS_MAX_PATH_LENGTH );
-
-#if DEBUG_SYS_EXEC
-if( DEBUG_SYS_EXEC < (uint32_t)tm_start )
-printk("\n[%s] thread[%x,%x] enter / path <%s> / args %x / envs %x / cycle %d\n",
-__FUNCTION__, pid, trdid, &process->exec_info.path[0],
-user_args, user_envs, (uint32_t)tm_start );
-#endif
-
-    // 2. copy "arguments" pointers & strings in process exec_info if required
-    if( user_args != NULL )
-    {
-        if( exec_get_strings( true , user_args , &process->exec_info ) )
-        {
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%] cannot get arguments for <%s>\n",
-__FUNCTION__, pid, trdid, pathname );
-#endif
-            this->errno = EINVAL;
-            return -1;
-        }
-
-#if DEBUG_SYS_EXEC
-if( DEBUG_SYS_EXEC < (uint32_t)tm_start )
-printk("\n[%s] thread[%x,%x] set arguments in exec_info / arg[0] = <%s>\n",
-__FUNCTION__, pid, trdid, process->exec_info.args_pointers[0] );
-#endif
-
-    }
-
-    // 3. copy "environment" pointers & strings in process exec_info if required
-    if( user_envs != NULL )
-    {
-        if( exec_get_strings( false , user_envs , &process->exec_info ) )
-        {
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%] cannot get env variables for <%s>\n",
-__FUNCTION__, pid, trdid, pathname );
-#endif
-            this->errno = EINVAL;
-            return -1;
-        }
-
-#if DEBUG_SYS_EXEC 
-if( DEBUG_SYS_EXEC < (uint32_t)tm_start )
-printk("\n[%s] thread[%x,%x] set envs in exec_info / env[0] = <%s>\n",
-__FUNCTION__, pid, trdid, process->exec_info.envs_pointers[0] );
-#endif
-
-    }
-
-    // call relevant kernel function (no return if success)
-    error = process_make_exec();
-
-    if( error )
-    {
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%x] cannot create process <%s>\n",
-__FUNCTION__, pid, trdid, process->exec_info.path );
-#endif
-        this->errno = error;
-        return -1;
-    }
-
-    return 0;  
-
 } // end sys_exec()
 
