| 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 | /////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 57 | static 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 | ///////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 165 | int 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 | 
|---|
| 178 | uint64_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 | 
|---|
| 198 | printk("\n[ERROR] in %s : pathname too long\n", __FUNCTION__ ); | 
|---|
| 199 | #endif | 
|---|
| 200 |         this->errno = ENFILE; | 
|---|
| 201 |         return -1; | 
|---|
| 202 |     } | 
|---|
| 203 |  | 
|---|
| 204 |     // copy pathname in exec_info structure (kernel space) | 
|---|
| 205 |     hal_strcpy_from_uspace( exec_info.path , pathname , CONFIG_VFS_MAX_PATH_LENGTH ); | 
|---|
| 206 |  | 
|---|
| 207 | #if DEBUG_SYS_EXEC | 
|---|
| 208 | if( DEBUG_SYS_EXEC < tm_start ) | 
|---|
| 209 | printk("\n[DBG] %s : thread %x in process %x enter for path <%s> / cycle = %d\n", | 
|---|
| 210 | __FUNCTION__, this->trdid, pid, exec_info.path, (uint32_t)tm_start ); | 
|---|
| 211 | #endif | 
|---|
| 212 |  | 
|---|
| 213 |     // check and store args in exec_info structure if required | 
|---|
| 214 |     if( args != NULL ) | 
|---|
| 215 |     { | 
|---|
| 216 |         if( process_exec_get_strings( &exec_info , true , args ) ) | 
|---|
| 217 |         { | 
|---|
| 218 |  | 
|---|
| 219 | #if DEBUG_SYSCALLS_ERROR | 
|---|
| 220 | printk("\n[ERROR] in %s : thread %x in process %x cannot access args\n", | 
|---|
| 221 | __FUNCTION__ , this->trdid, pid ); | 
|---|
| 222 | #endif | 
|---|
| 223 |             this->errno = EINVAL; | 
|---|
| 224 |             return -1; | 
|---|
| 225 |         } | 
|---|
| 226 |     } | 
|---|
| 227 |  | 
|---|
| 228 |     // check and store envs in exec_info structure if required | 
|---|
| 229 |     if( envs != NULL ) | 
|---|
| 230 |     { | 
|---|
| 231 |         if( process_exec_get_strings( &exec_info , false , envs ) ) | 
|---|
| 232 |         { | 
|---|
| 233 |  | 
|---|
| 234 | #if DEBUG_SYSCALLS_ERROR | 
|---|
| 235 | printk("\n[ERROR] in %s : thread %x in process %x cannot access envs\n", | 
|---|
| 236 | __FUNCTION__ , this->trdid, pid ); | 
|---|
| 237 | #endif | 
|---|
| 238 |             this->errno = EINVAL; | 
|---|
| 239 |             return -1; | 
|---|
| 240 |         } | 
|---|
| 241 |     } | 
|---|
| 242 |  | 
|---|
| 243 |     // call relevant kernel function | 
|---|
| 244 |     error = process_make_exec( &exec_info ); | 
|---|
| 245 |  | 
|---|
| 246 |     if( error ) | 
|---|
| 247 |     { | 
|---|
| 248 |  | 
|---|
| 249 | #if DEBUG_SYSCALLS_ERROR | 
|---|
| 250 | printk("\n[ERROR] in %s : thread %x in process %x cannot create process for <%s>\n", | 
|---|
| 251 | __FUNCTION__, this->trdid, pid, exec_info.path ); | 
|---|
| 252 | #endif | 
|---|
| 253 |         this->errno = error; | 
|---|
| 254 |         return -1; | 
|---|
| 255 |     } | 
|---|
| 256 |  | 
|---|
| 257 |     assert( false , "we should not execute this code" ); | 
|---|
| 258 |  | 
|---|
| 259 |     return 0;   | 
|---|
| 260 |  | 
|---|
| 261 | } // end sys_exec() | 
|---|
| 262 |  | 
|---|