/* * sys_exec.c - Kernel function implementing the "exec" syscall * * Authors Mohamed Lamine Karaoui (2015) * Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////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 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; // 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, 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 { 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 sys_exec()