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