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

Last change on this file since 625 was 625, checked in by alain, 5 years ago

Fix a bug in the vmm_remove_vseg() function: the physical pages
associated to an user DATA vseg were released to the kernel when
the target process descriptor was in the reference cluster.
This physical pages release should be done only when the page
forks counter value is zero.
All other modifications are cosmetic.

File size: 9.0 KB
Line 
1/*
2 * sys_exec.c - Kernel function implementing the "exec" system call.
3 *
4 * Authors   Alain Greiner (2016,2017,2017,2019)
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 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 string pointers in user space,
45// and a set of strings in user space.
46// We allocate one physical page to store a kernel copy of the array of pointers,
47// we allocate one or several physical pages to store the strings themselve,
48// and register these buffers and the number of strings in the exec_info structure.
49// The max number of strings is 1024 (for both args and envs). The numbers of pages
50// to store the (args) and (envs) strings are configuration parameters.
51///////////////////////////////////////////////////////////////////////////////////////
52// @ exec_info   : pointer on the exec_info structure.
53// @ is_args     : true if called for (args) / false if called for (envs).
54// @ u_pointers  : array of pointers on the strings (in user space).
55// @ return 0 if success / non-zero if too many strings or no more memory.
56///////////////////////////////////////////////////////////////////////////////////////
57static error_t process_exec_get_strings( exec_info_t  * exec_info,
58                                         bool_t         is_args,
59                                         char        ** u_pointers )
60{
61    uint32_t     index;       // string index
62    uint32_t     found_null;  // NULL pointer found in array of pointers
63    uint32_t     length;      // string length
64    kmem_req_t   req;         // kmem request
65    page_t     * page;        // page descriptor
66    xptr_t       base_xp;     // extended pointer on page base
67    uint32_t     order;       // ln2( number of pages to store strings )
68    char      ** k_pointers;  // base of kernel array of pointers
69    char       * k_buf_ptr;   // pointer on first empty slot in kernel strings buffer
70    char       * k_buf_base;  // base address of the kernel strings buffer
71
72    // compute ln2( number of pages for kernel strings buffer )
73    if( is_args ) order = bits_log2( CONFIG_VMM_ARGS_SIZE );
74    else          order = bits_log2( CONFIG_VMM_ENVS_SIZE );
75
76    req.type   = KMEM_PAGE;
77    req.flags  = AF_KERNEL | AF_ZERO;
78
79    // allocate one physical page for kernel array of pointers
80    req.type   = 0;
81    page       = kmem_alloc( &req );
82
83    if( page == NULL ) return ENOMEM;
84
85    base_xp = ppm_page2base( XPTR( local_cxy , page ) );
86    k_pointers = (char **)GET_PTR( base_xp );
87
88    // allocate several physical pages to store the strings themselve
89    req.type   = order;
90    page       = kmem_alloc( &req );
91
92    if( page == NULL ) return ENOMEM;
93
94    base_xp = ppm_page2base( XPTR( local_cxy , page ) );
95    k_buf_base = (char *)GET_PTR( base_xp );
96
97    // copy the array of pointers to kernel buffer
98    hal_copy_from_uspace( k_pointers,
99                          u_pointers,
100                          CONFIG_PPM_PAGE_SIZE );
101
102    // scan kernel array of pointers to copy the strings
103    found_null = 0;
104    k_buf_ptr  = k_buf_base;
105    for( index = 0 ; index < 1024 ; index++ )
106    {
107        if( k_pointers[index] == NULL )
108        {
109            found_null = 1;
110            break;
111        }
112
113        // compute string length
114        length = hal_strlen_from_uspace( k_pointers[index] );
115
116        // copy the user string to kernel buffer
117        hal_copy_from_uspace( k_buf_ptr,
118                              k_pointers[index],
119                              length );
120
121        // update k_pointer[index] entry
122        k_pointers[index] = k_buf_ptr;
123
124        // increment pointer on kernel strings buffer
125        k_buf_ptr += (length + 1);
126    }
127
128    // update into exec_info structure
129    if( found_null && is_args )
130    {
131        exec_info->args_pointers  =  k_pointers;
132        exec_info->args_buf_base  =  k_buf_base;
133        exec_info->args_nr        =  index;
134    }
135    else if( found_null && !is_args )
136    {
137        exec_info->envs_pointers  =  k_pointers;
138        exec_info->envs_buf_base  =  k_buf_base;
139        exec_info->envs_buf_free  =  k_buf_ptr;
140        exec_info->envs_nr        =  index;
141    }
142    else
143    {
144        return EINVAL;
145    }
146
147    return 0;
148} // end process_exec_get_strings()
149
150/////////////////////////////////////////////////////////////////////////////////////////
151// Implementation note:
152// This function must be called by the main thread (thread 0 in owner cluster).
153// It build an exec_info_t structure containing all informations
154// required to initialize the new process descriptor and the associated thread.
155// It includes the new process main() arguments, the environment variables,
156// and the pathname to the new process .elf file.
157// It calls the process_exec_get_strings() functions to copy the main() arguments and
158// the environment variables from user buffers to the exec_info_t structure, allocate
159// and call the process_make_exec() function.
160// As it must destroy all process copies, and all other threads in all clusters,
161// the process_make_exec() function must be executed in the owner cluster.
162//
163// TODO : the args & envs arguments are not supported yet : both must be NULL  [AG]
164/////////////////////////////////////////////////////////////////////////////////////////
165int sys_exec( char  * pathname,       // .elf file pathname
166              char ** args,           // process arguments
167              char ** envs )          // environment variables
168{
169    exec_info_t   exec_info;          // structure to pass to process_make_exec()
170    error_t       error;
171
172    // get calling thread, process, & pid
173    thread_t    * this    = CURRENT_THREAD;
174    process_t   * process = this->process;
175    pid_t         pid     = process->pid;
176
177#if DEBUG_SYS_EXEC
178uint64_t     tm_start = hal_get_cycles();
179#endif
180
181    assert( (CXY_FROM_PID( pid ) == local_cxy) ,
182    "must be called in the owner cluster\n");
183
184    assert( (LTID_FROM_TRDID( this->trdid ) == 0) ,
185    "must be called by the main thread\n");
186
187    assert( (args == NULL) ,
188    "args not supported yet\n" );
189
190    assert( (envs == NULL) ,
191    "args not supported yet\n" );
192
193    // check pathname length
194    if( hal_strlen_from_uspace( pathname ) >= CONFIG_VFS_MAX_PATH_LENGTH )
195    {
196
197#if DEBUG_SYSCALLS_ERROR
198printk("\n[ERROR] in %s : thread[%x,%x] pathname too long\n",
199__FUNCTION__, pid, this->trdid );
200#endif
201        this->errno = ENFILE;
202        return -1;
203    }
204
205    // copy pathname in exec_info structure (kernel space)
206    hal_strcpy_from_uspace( exec_info.path , pathname , CONFIG_VFS_MAX_PATH_LENGTH );
207
208#if DEBUG_SYS_EXEC
209if( DEBUG_SYS_EXEC < tm_start )
210printk("\n[%s] thread[%x,%x] enter for path <%s> / cycle = %d\n",
211__FUNCTION__, pid, this->trdid, exec_info.path, (uint32_t)tm_start );
212#endif
213
214    // check and store args in exec_info structure if required
215    if( args != NULL )
216    {
217        if( process_exec_get_strings( &exec_info , true , args ) )
218        {
219
220#if DEBUG_SYSCALLS_ERROR
221printk("\n[ERROR] in %s : thread[%x,%x] cannot access args for <%s>\n",
222__FUNCTION__, pid, this->trdid, exec_info.path );
223#endif
224            this->errno = EINVAL;
225            return -1;
226        }
227    }
228
229    // check and store envs in exec_info structure if required
230    if( envs != NULL )
231    {
232        if( process_exec_get_strings( &exec_info , false , envs ) )
233        {
234
235#if DEBUG_SYSCALLS_ERROR
236printk("\n[ERROR] in %s : thread[%x,%x] cannot access envs for <%s>\n",
237__FUNCTION__ , pid, this->trdid, exec_info.path );
238#endif
239            this->errno = EINVAL;
240            return -1;
241        }
242    }
243
244    // call relevant kernel function
245    error = process_make_exec( &exec_info );
246
247    if( error )
248    {
249
250#if DEBUG_SYSCALLS_ERROR
251printk("\n[ERROR] in %s : thread[%x,%x] cannot create process for <%s>\n",
252__FUNCTION__, pid, this->trdid, exec_info.path );
253#endif
254        this->errno = error;
255        return -1;
256    }
257
258    assert( false , "we should never execute this code" );
259
260    return 0; 
261
262} // end sys_exec()
263
Note: See TracBrowser for help on using the repository browser.