/* * sys_exec.c - Kernel function implementing the "exec" system call. * * Authors Alain Greiner (2016,2017,2017,2019,2020) * * 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 #include #include /////////////////////////////// int sys_exec( char * pathname, // .elf file pathname in user space char ** user_args, // pointer on process arguments in user space char ** user_envs ) // pointer on env variables in user space { error_t error; vseg_t * vseg; // get calling thread, process, & pid thread_t * this = CURRENT_THREAD; process_t * process = this->process; pid_t pid = process->pid; trdid_t trdid = this->trdid; assert( __FUNCTION__, (CXY_FROM_PID( pid ) == local_cxy) , "must be called in the owner cluster\n"); assert( __FUNCTION__, (LTID_FROM_TRDID( trdid ) == 0) , "must be called by the main thread\n"); assert( __FUNCTION__, (user_envs == NULL) , "environment variables not supported yet\n" ); #if DEBUG_SYS_EXEC || DEBUG_SYSCALLS_ERROR uint64_t tm_start = hal_get_cycles(); #endif // check "pathname" mapped in user space if( vmm_get_vseg( process , (intptr_t)pathname , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s : thread[%x,%] / pathname pointer %x unmapped\n", __FUNCTION__, pid, trdid, pathname ); #endif this->errno = EINVAL; return -1; } // check "pathname" length if( hal_strlen_from_uspace( pathname ) >= CONFIG_VFS_MAX_PATH_LENGTH ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s : thread[%x,%x] / pathname too long\n", __FUNCTION__, pid, trdid ); #endif this->errno = ENFILE; return -1; } // check "args" mapped in user space if non NULL if( (user_args != NULL) && (vmm_get_vseg( process , (intptr_t)user_args , &vseg )) ) { #if DEBUG_SYSCALLS_ERROR printk("\n[ERROR] in %s for thread[%x,%] : user_args pointer %x unmapped\n", __FUNCTION__, pid, trdid, user_args ); #endif this->errno = EINVAL; return -1; } // check "envs" mapped in user space if not NULL if( (user_envs != NULL) && (vmm_get_vseg( process , (intptr_t)user_envs , &vseg )) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s : thread[%x,%] / user_envs pointer %x unmapped\n", __FUNCTION__, pid, trdid, user_envs ); #endif this->errno = EINVAL; return -1; } #if DEBUG_SYS_EXEC if( DEBUG_SYS_EXEC < (uint32_t)tm_start ) printk("\n[%s] thread[%x,%x] enter / path <%s> / args %x / envs %x / cycle %d\n", __FUNCTION__, pid, trdid, &process->exec_info.path[0], user_args, user_envs, cycle ); #endif // 1. copy "pathname" in kernel exec_info structure hal_strcpy_from_uspace( XPTR( local_cxy , &process->exec_info.path[0] ), pathname, CONFIG_VFS_MAX_PATH_LENGTH ); // 2. copy "arguments" pointers & strings in process exec_info if required if( user_args != NULL ) { if( process_exec_get_strings( true , user_args , &process->exec_info ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s : thread[%x,%] get arguments for <%s>\n", __FUNCTION__, pid, trdid, pathname ); #endif this->errno = EINVAL; return -1; } #if DEBUG_SYS_EXEC if( DEBUG_SYS_EXEC < (uint32_t)tm_start ) printk("\n[%s] thread[%x,%x] got arguments / arg[0] = <%s>\n", __FUNCTION__, pid, trdid, process->exec_info.args_pointers[0] ); #endif } // 3. copy "environment" pointers & strings in process exec_info if required if( user_envs != NULL ) { if( process_exec_get_strings( false , user_envs , &process->exec_info ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s : thread[%x,%] get env variables for <%s>\n", __FUNCTION__, pid, trdid, pathname ); #endif this->errno = EINVAL; return -1; } #if DEBUG_SYS_EXEC if( DEBUG_SYS_EXEC < (uint32_t)tm_start ) printk("\n[%s] thread[%x,%x] got envs / env[0] = <%s>\n", __FUNCTION__, pid, trdid, process->exec_info.envs_pointers[0] ); #endif } // call relevant kernel function (no return if success) error = process_make_exec(); if( error ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s : thread[%x,%x] cannot create process <%s>\n", __FUNCTION__, pid, trdid, process->exec_info.path ); #endif this->errno = error; return -1; } return 0; } // end sys_exec()