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