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

Last change on this file since 506 was 506, checked in by viala@…, 3 years ago

[syscalls] add interface in implementation.

Add const where possible, fix protoypes to follow interface.

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)
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 othert 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( const 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    assert( (CXY_FROM_PID( pid ) == local_cxy) ,
178    "must be called in the owner cluster\n");
179
180    assert( (LTID_FROM_TRDID( this->trdid ) == 0) ,
181    "must be called by the main thread\n");
182
183    assert( (args == NULL) ,
184    "args not supported yet\n" );
185
186    assert( (envs == NULL) ,
187    "args not supported yet\n" );
188
189    // check pathname length
190    if( hal_strlen_from_uspace( pathname ) >= CONFIG_VFS_MAX_PATH_LENGTH )
191    {
192
193#if DEBUG_SYSCALLS_ERROR
194printk("\n[ERROR] in %s : pathname too long\n", __FUNCTION__ );
195#endif
196        this->errno = ENFILE;
197        return -1;
198    }
199
200    // copy pathname in exec_info structure (kernel space)
201    hal_strcpy_from_uspace( exec_info.path , pathname , CONFIG_VFS_MAX_PATH_LENGTH );
202
203#if DEBUG_SYS_EXEC
204uint64_t      tm_start;
205tm_start = hal_get_cycles();
206if( DEBUG_SYS_EXEC < tm_start )
207printk("\n[DBG] %s : thread %x in process %x enter for path <%s> / cycle = %d\n",
208__FUNCTION__, this->trdid, pid, exec_info.path, (uint32_t)tm_start );
209#endif
210
211    // check and store args in exec_info structure if required
212    if( args != NULL )
213    {
214        if( process_exec_get_strings( &exec_info , true , args ) )
215        {
216
217#if DEBUG_SYSCALLS_ERROR
218printk("\n[ERROR] in %s : thread %x in process %x cannot access args\n",
219__FUNCTION__ , this->trdid, pid );
220#endif
221            this->errno = EINVAL;
222            return -1;
223        }
224    }
225
226    // check and store envs in exec_info structure if required
227    if( envs != NULL )
228    {
229        if( process_exec_get_strings( &exec_info , false , envs ) )
230        {
231
232#if DEBUG_SYCALLS_ERROR
233printk("\n[ERROR] in %s : thread %x in process %x cannot access envs\n",
234__FUNCTION__ , this->trdid, pid );
235#endif
236            this->errno = EINVAL;
237            return -1;
238        }
239    }
240
241    // call relevant kernel function
242    error = process_make_exec( &exec_info );
243
244    if( error )
245    {
246
247#if DEBUG_SYSCALLS_ERROR
248printk("\n[ERROR] in %s : thread %x in process %x cannot create process for <%s>\n",
249__FUNCTION__, this->trdid, pid, exec_info.path );
250#endif
251        this->errno = error;
252        return -1;
253    }
254
255    assert( false , "we should not execute this code" );
256
257    return 0; 
258
259} // end sys_exec()
260
Note: See TracBrowser for help on using the repository browser.