source: trunk/kernel/syscalls/sys_exec.c @ 687

Last change on this file since 687 was 683, checked in by alain, 4 years ago

All modifications required to support the <tcp_chat> application
including error recovery in case of packet loss.A

File size: 12.1 KB
Line 
1/*
2 * sys_exec.c - Kernel function implementing the "exec" system call.
3 *
4 * Authors   Alain Greiner (2016,2017,2017,2019,2020)
5 *
6 * Copyright (c) UPMC Sorbonne Universites
7 *
8 * This file is part of ALMOS-MKH.
9 *
10 * ALMOS-MKH is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2.0 of the License.
13 *
14 * ALMOS-MKH is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include <kernel_config.h>
25#include <hal_kernel_types.h>
26#include <hal_uspace.h>
27#include <errno.h>
28#include <printk.h>
29#include <core.h>
30#include <vfs.h>
31#include <cluster.h>
32#include <process.h>
33#include <thread.h>
34#include <vmm.h>
35#include <ppm.h>
36#include <rpc.h>
37
38#include <syscalls.h>
39
40////////////////////////////////////////////////i////////////////////////////////////////
41// This static function is called twice by the sys_exec() function :
42// - to register the main() arguments (args) in the process <exec_info> structure.
43// - to register the environment variables (envs) in the <exec_info> structure.
44// In both cases the input is an array of NULL terminated string pointers in user space,
45// identified by the <u_pointers> argument. The strings can be dispatched anywhere in
46// the calling user process space. The max number of envs, and the max number of args 
47// are defined by the CONFIG_PROCESS_ARGS_NR and CONFIG_PROCESS_ENVS_MAX_NR parameters.
48////////////////////////////////////////////////i////////////////////////////////////////
49// Implementation Note:
50// Both the array of pointers and the strings themselve are stored in kernel space in one
51// single, dynamically allocated, kernel buffer containing an integer number of pages,
52// defined by the CONFIG_VMM_ENVS_SIZE and CONFIG_VMM_STACK_SIZE parameters.
53// These two kernel buffers contains :
54// - in the first bytes a fixed size kernel array of kernel pointers on the strings.
55// - in the following bytes the strings themselves.
56// The exec_info_t structure is defined in the <process.h> file.
57////////////////////////////////////////////////i////////////////////////////////////////
58// @ is_args     : [in]    true if called for (args) / false if called for (envs).
59// @ u_pointers  : [in]    array of pointers on the strings (in user space).
60// @ exec_info   : [inout] pointer on the exec_info structure.
61// @ return 0 if success / non-zero if too many strings or no memory.
62////////////////////////////////////////////////i////////////////////////////////////////
63static error_t exec_get_strings( bool_t         is_args,
64                                 char        ** u_pointers,
65                                 exec_info_t  * exec_info )
66{
67    uint32_t     index;           // slot index in pointers array
68    uint32_t     length;          // string length (in bytes)
69    uint32_t     pointers_bytes;  // number of bytes to store pointers
70    uint32_t     max_index;       // max size of pointers array
71    char      ** k_pointers;      // base of kernel array of pointers
72    char       * k_buf_ptr;       // pointer on first empty slot in strings buffer
73    uint32_t     k_buf_space;     // number of bytes available in string buffer
74    char       * k_buf;           // kernel buffer for both pointers & strings
75
76#if DEBUG_SYS_EXEC
77thread_t * this  = CURRENT_THREAD;
78uint32_t   cycle = (uint32_t)hal_get_cycles();
79#endif
80
81    // Allocate one block of physical memory for both the pointers and the strings
82
83    if( is_args )
84    {
85        k_buf = kmem_alloc( bits_log2(CONFIG_VMM_ARGS_SIZE << CONFIG_PPM_PAGE_ORDER), AF_ZERO );
86
87        pointers_bytes = (CONFIG_PROCESS_ARGS_MAX_NR + 1) * sizeof(char *);
88        k_pointers     = (char **)k_buf;
89        k_buf_ptr      = k_buf + pointers_bytes;
90        k_buf_space    = (CONFIG_VMM_ARGS_SIZE << CONFIG_PPM_PAGE_ORDER) - pointers_bytes;
91        max_index      = CONFIG_PROCESS_ARGS_MAX_NR + 1;
92
93#if DEBUG_SYS_EXEC
94if( DEBUG_SYS_EXEC < cycle )
95printk("\n[%s] thread[%x,%x] for args / u_buf %x / k_buf %x\n",
96__FUNCTION__, this->process->pid, this->trdid, u_pointers, k_buf );
97#endif
98
99    }
100    else  // envs
101    {
102        k_buf = kmem_alloc( bits_log2(CONFIG_VMM_ENVS_SIZE << CONFIG_PPM_PAGE_ORDER), AF_ZERO );
103
104        pointers_bytes = (CONFIG_PROCESS_ENVS_MAX_NR + 1) * sizeof(char *);
105        k_pointers     = (char **)k_buf;
106        k_buf_ptr      = k_buf + pointers_bytes;
107        k_buf_space    = (CONFIG_VMM_ENVS_SIZE << CONFIG_PPM_PAGE_ORDER) - pointers_bytes;
108        max_index      = CONFIG_PROCESS_ENVS_MAX_NR + 1;
109
110#if DEBUG_SYS_EXEC
111if( DEBUG_SYS_EXEC < cycle )
112printk("\n[%s] thread[%x,%x] for envs / u_buf %x / k_buf %x\n",
113__FUNCTION__, this->process->pid, this->trdid, u_pointers, k_buf );
114#endif
115
116    }
117
118    // copy the user array of pointers to kernel buffer
119    hal_copy_from_uspace( XPTR( local_cxy , k_pointers ),
120                          u_pointers,
121                          pointers_bytes );
122
123    // WARNING : the pointers copied in the k_pointers[] array are user pointers,
124    // after the loop below, the k_pointers[] array contains kernel pointers.
125
126#if DEBUG_SYS_EXEC
127if( DEBUG_SYS_EXEC < cycle )
128printk("\n[%s] thread[%x,%x] moved u_ptr array of pointers to k_ptr array\n",
129__FUNCTION__, this->process->pid, this->trdid );
130#endif
131
132    // scan kernel array of pointers to copy strings to kernel buffer
133    for( index = 0 ; index < max_index ; index++ )
134    {
135        // exit loop if (k_pointers[index] == NUll)
136        if( k_pointers[index] == NULL ) break;
137
138        // compute string length (without the NUL character)
139        length = hal_strlen_from_uspace( k_pointers[index] );
140
141        // return error if overflow in kernel buffer
142        if( length > k_buf_space ) return -1;
143
144        // copy the string itself to kernel buffer
145        hal_copy_from_uspace( XPTR( local_cxy , k_buf_ptr ),
146                              k_pointers[index],
147                              length + 1 );
148
149#if DEBUG_SYS_EXEC
150if( DEBUG_SYS_EXEC < cycle )
151printk("\n[%s] thread[%x,%x] copied string[%d] <%s> to kernel buffer / length %d\n",
152__FUNCTION__, this->process->pid, this->trdid, index, k_buf_ptr, length );
153#endif
154
155        // replace the user pointer by a kernel pointer in the k_pointer[] array
156        k_pointers[index] = k_buf_ptr;
157
158        // increment loop variables
159        k_buf_ptr   += (length + 1);
160        k_buf_space -= (length + 1);
161
162#if DEBUG_SYS_EXEC
163if( DEBUG_SYS_EXEC < cycle )
164{
165    if( k_pointers[0] != NULL )
166    printk("\n[%s] thread[%x,%x] : &arg0 = %x / arg0 = <%s>\n",
167    __FUNCTION__, this->process->pid, this->trdid, k_pointers[0], k_pointers[0] );
168    else 
169    printk("\n[%s] thread[%x,%x] : unexpected NULL value for &arg0\n",
170    __FUNCTION__, this->process->pid, this->trdid );
171}
172#endif
173
174    }  // end loop on index
175
176    // update into exec_info structure
177    if( is_args )
178    {
179        exec_info->args_pointers  =  k_pointers;
180        exec_info->args_nr        =  index;
181    }
182    else
183    {
184        exec_info->envs_pointers  =  k_pointers;
185        exec_info->envs_buf_free  =  k_buf_ptr;
186        exec_info->envs_nr        =  index;
187    }
188
189#if DEBUG_SYS_EXEC
190if( DEBUG_SYS_EXEC < cycle )
191printk("\n[%s] thread[%x,%x] copied %d strings to kernel buffer\n",
192__FUNCTION__, this->process->pid, this->trdid, index );
193#endif
194
195    return 0;
196
197} // end exec_get_strings()
198
199
200///////////////////////////////
201int sys_exec( char  * pathname,    // .elf file pathname in user space
202              char ** user_args,   // pointer on array of process arguments in user space
203              char ** user_envs )  // pointer on array of env variables in user space
204{
205    error_t       error;
206    vseg_t      * vseg;
207
208    // get calling thread, process, & pid
209    thread_t    * this    = CURRENT_THREAD;
210    process_t   * process = this->process;
211    pid_t         pid     = process->pid;
212    trdid_t       trdid   = this->trdid;
213
214assert( __FUNCTION__, (CXY_FROM_PID( pid ) == local_cxy) ,
215"must be called in the owner cluster\n");
216
217assert( __FUNCTION__, (LTID_FROM_TRDID( trdid ) == 0) ,
218"must be called by the main thread\n");
219
220assert( __FUNCTION__, (user_envs == NULL) ,
221"environment variables not supported yet\n" );
222
223#if DEBUG_SYS_EXEC || DEBUG_SYSCALLS_ERROR
224uint64_t     tm_start = hal_get_cycles();
225#endif
226
227    // check "pathname" mapped in user space
228    if( vmm_get_vseg( process , (intptr_t)pathname , &vseg ) )
229        {
230
231#if DEBUG_SYSCALLS_ERROR
232if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
233printk("\n[ERROR] in %s : thread[%x,%] / pathname pointer %x unmapped\n",
234__FUNCTION__, pid, trdid, pathname );
235#endif
236        this->errno = EINVAL;
237                return -1;
238        }
239
240    // check "pathname" length
241    if( hal_strlen_from_uspace( pathname ) >= CONFIG_VFS_MAX_PATH_LENGTH )
242    {
243
244#if DEBUG_SYSCALLS_ERROR
245if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
246printk("\n[ERROR] in %s : thread[%x,%x] / pathname too long\n",
247__FUNCTION__, pid, trdid );
248#endif
249        this->errno = ENFILE;
250        return -1;
251    }
252
253    // check "args" mapped in user space if non NULL
254    if( (user_args != NULL) && (vmm_get_vseg( process , (intptr_t)user_args , &vseg )) )
255        {
256
257#if DEBUG_SYSCALLS_ERROR
258if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
259printk("\n[ERROR] in %s for thread[%x,%] : user_args pointer %x unmapped\n",
260__FUNCTION__, pid, trdid, user_args );
261#endif
262        this->errno = EINVAL;
263                return -1;
264        }
265
266    // check "envs" mapped in user space if not NULL
267    if( (user_envs != NULL) && (vmm_get_vseg( process , (intptr_t)user_envs , &vseg )) )
268        {
269
270#if DEBUG_SYSCALLS_ERROR
271if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
272printk("\n[ERROR] in %s : thread[%x,%] / user_envs pointer %x unmapped\n",
273__FUNCTION__, pid, trdid, user_envs );
274#endif
275        this->errno = EINVAL;
276                return -1;
277        }
278
279    // 1. copy "pathname" in kernel exec_info structure
280    hal_strcpy_from_uspace( XPTR( local_cxy , &process->exec_info.path[0] ),
281                            pathname,
282                            CONFIG_VFS_MAX_PATH_LENGTH );
283
284#if DEBUG_SYS_EXEC
285if( DEBUG_SYS_EXEC < (uint32_t)tm_start )
286printk("\n[%s] thread[%x,%x] enter / path <%s> / args %x / envs %x / cycle %d\n",
287__FUNCTION__, pid, trdid, &process->exec_info.path[0],
288user_args, user_envs, (uint32_t)tm_start );
289#endif
290
291    // 2. copy "arguments" pointers & strings in process exec_info if required
292    if( user_args != NULL )
293    {
294        if( exec_get_strings( true , user_args , &process->exec_info ) )
295        {
296
297#if DEBUG_SYSCALLS_ERROR
298if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
299printk("\n[ERROR] in %s : thread[%x,%] cannot get arguments for <%s>\n",
300__FUNCTION__, pid, trdid, pathname );
301#endif
302            this->errno = EINVAL;
303            return -1;
304        }
305
306#if DEBUG_SYS_EXEC
307if( DEBUG_SYS_EXEC < (uint32_t)tm_start )
308printk("\n[%s] thread[%x,%x] set arguments in exec_info / arg[0] = <%s>\n",
309__FUNCTION__, pid, trdid, process->exec_info.args_pointers[0] );
310#endif
311
312    }
313
314    // 3. copy "environment" pointers & strings in process exec_info if required
315    if( user_envs != NULL )
316    {
317        if( exec_get_strings( false , user_envs , &process->exec_info ) )
318        {
319
320#if DEBUG_SYSCALLS_ERROR
321if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
322printk("\n[ERROR] in %s : thread[%x,%] cannot get env variables for <%s>\n",
323__FUNCTION__, pid, trdid, pathname );
324#endif
325            this->errno = EINVAL;
326            return -1;
327        }
328
329#if DEBUG_SYS_EXEC
330if( DEBUG_SYS_EXEC < (uint32_t)tm_start )
331printk("\n[%s] thread[%x,%x] set envs in exec_info / env[0] = <%s>\n",
332__FUNCTION__, pid, trdid, process->exec_info.envs_pointers[0] );
333#endif
334
335    }
336
337    // call relevant kernel function (no return if success)
338    error = process_make_exec();
339
340    if( error )
341    {
342
343#if DEBUG_SYSCALLS_ERROR
344if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
345printk("\n[ERROR] in %s : thread[%x,%x] cannot create process <%s>\n",
346__FUNCTION__, pid, trdid, process->exec_info.path );
347#endif
348        this->errno = error;
349        return -1;
350    }
351
352    return 0; 
353
354} // end sys_exec()
355
Note: See TracBrowser for help on using the repository browser.