/* * sys_get.c - Kernel function implementing all non_standard "get_xxx()" syscalls. * * Author Alain Greiner (2016,2017,2018,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 ///////////////////////////////////////////////////////////////////////////////// // This function returns a printable string for the sys_get command type. ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_SYS_GET static char* get_cmd_type_str( uint32_t type ) { if ( type == GET_PROCESSES ) return "PROCESSES"; else if( type == GET_CONFIG ) return "CONFIG"; else if( type == GET_CORE_ID ) return "CORE_ID"; else if( type == GET_NB_CORES ) return "NB_CORES"; else if( type == GET_BEST_CORE ) return "BEST_CORE"; else if( type == GET_CYCLE ) return "CYCLE"; else if( type == GET_THREAD_INFO ) return "THREAD_INFO"; else return "undefined"; } #endif ////////////////////////// int sys_get ( reg_t arg0, reg_t arg1, reg_t arg2, reg_t arg3 ) { vseg_t * vseg; int ret; thread_t * this = CURRENT_THREAD; process_t * process = this->process; uint32_t type = arg0; #if DEBUG_SYS_GET || DEBUG_SYSCALLS_ERROR || CONFIG_INSTRUMENTATION_SYSCALLS uint64_t tm_start = hal_get_cycles(); #endif #if DEBUG_SYS_GET if( DEBUG_SYS_GET < (uint32_t)tm_start ) printk("\n[%s] thread[%x,%x] enter / %s / a1 %x / a2 %x / a3 %x / cycle %d\n", __FUNCTION__, process->pid, this->trdid, get_cmd_type_str( type ), arg1, arg2, arg3, (uint32_t)tm_start ); #endif switch( type ) { /////////////// case GET_CYCLE: { char * u_buf = (char *)(intptr_t)arg1; // check buffer in user space if( vmm_get_vseg( process , (intptr_t)arg1 , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for CYCLE : thread[%x,%x] / user buffer unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , (intptr_t)arg1 ); #endif this->errno = EINVAL; ret = -1; break; } // call relevant core function uint64_t k_cycle = hal_get_cycles(); // copy to user space hal_copy_to_uspace( u_buf, XPTR( local_cxy , &k_cycle ), sizeof(uint64_t) ); ret = 0; break; } /////////////////// case GET_PROCESSES: { uint32_t cxy = ((uint32_t)arg1 >> 16); uint32_t owned = ((uint32_t)arg1 & 0xFFFF); char * u_buf = (char *)(intptr_t)arg2; uint32_t size = (uint32_t)arg3; char k_buf[CONFIG_PROCESS_DISPLAY_BUF_SIZE]; // kernel buffer (one line) uint32_t length; // number of bytes copied in k_buf (without NUL) uint32_t offset = 0; // number of bytes already copied to u_buf xptr_t iter_xp; // check buffer in user space if( vmm_get_vseg( process , (intptr_t)u_buf , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for PROCESSES : thread[%x,%x] / user buffer unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , (intptr_t)arg2 ); #endif this->errno = EINVAL; ret = -1; break; } #if DEBUG_SYS_GET if( DEBUG_SYS_GET < (uint32_t)tm_start ) printk("\n[%s] for PROCESSES : thread[%x,%x] cxy %x / owned %d\n", __FUNCTION__, process->pid, this->trdid, cxy, owned ); #endif // build extended pointers on root and lock of process list in cluster cxy xptr_t root_xp = XPTR( cxy , &LOCAL_CLUSTER->pmgr.local_root ); xptr_t lock_xp = XPTR( cxy , &LOCAL_CLUSTER->pmgr.local_lock ); // take lock on process list remote_queuelock_acquire( lock_xp ); // loop on all processes in cluster cxy XLIST_FOREACH( root_xp , iter_xp ) { // get process pointers xptr_t process_xp = XLIST_ELEMENT( iter_xp , process_t , local_list ); process_t * process_ptr = GET_PTR( process_xp ); cxy_t process_cxy = GET_CXY( process_xp ); // get process PID pid_t pid = hal_remote_l32( XPTR( process_cxy , &process_ptr->pid ) ); #if DEBUG_SYS_GET if( DEBUG_SYS_GET < (uint32_t)tm_start ) printk("\n[%s] for PROCESSES : thread[%x,%x] in FOREACH / pid %x\n", __FUNCTION__, process->pid, this->trdid, pid ); #endif // when owned is set, display only owned user processes if( (owned == 0) || ((CXY_FROM_PID( pid ) == cxy) && (LPID_FROM_PID( pid ) != 0)) ) { // build string for one line (one process descriptor) length = process_build_string( process_xp, k_buf, CONFIG_PROCESS_DISPLAY_BUF_SIZE ); // copy this string to u_buf (with the '\n' , but without the NUL) hal_copy_to_uspace( u_buf + offset, XPTR( local_cxy , k_buf ), length ); // update offset offset += length; } // check user buffer overflow if( (offset + CONFIG_PROCESS_DISPLAY_BUF_SIZE) >= size ) { // build a warning message length = snprintk( k_buf , CONFIG_PROCESS_DISPLAY_BUF_SIZE , " ... user buffer too for all process in cluster %x", cxy ); // copy this string to u_buf hal_copy_to_uspace( u_buf + offset, XPTR( local_cxy , k_buf ), length ); // exit FOREACH loop break; } else // add the NUL character in u_buf { char byte = 0; // copy this NUL character to u_buf hal_copy_to_uspace( u_buf + offset, XPTR( local_cxy , &byte ), 1 ); } } // end FOREACH // release lock on process list remote_queuelock_release( lock_xp ); ret = 0; break; } //////////////// case GET_CONFIG: { hard_config_t * u_config = (hard_config_t *)arg1; hard_config_t k_config; // local kernel structure // check u_config mapped in user space if( vmm_get_vseg( process , (intptr_t)u_config , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for CONFIG : thread[%x,%x] / user pointer %x unmapped\n", __FUNCTION__ , process->pid , this->trdid , (intptr_t)u_config ); #endif this->errno = EINVAL; ret = -1; break; } // copy config parameters from cluster descriptor to kernel structure k_config.x_size = LOCAL_CLUSTER->x_size; k_config.y_size = LOCAL_CLUSTER->y_size; k_config.ncores = LOCAL_CLUSTER->cores_nr; k_config.txt_channels = LOCAL_CLUSTER->nb_txt_channels; k_config.nic_channels = LOCAL_CLUSTER->nb_nic_channels; k_config.ioc_channels = LOCAL_CLUSTER->nb_ioc_channels; k_config.fbf_channels = LOCAL_CLUSTER->nb_fbf_channels; // copy k_config structure to user space hal_copy_to_uspace( u_config , XPTR(local_cxy, &k_config ), sizeof(hard_config_t) ); ret = 0; break; } ///////////////// case GET_CORE_ID: { uint32_t * u_cxy = (uint32_t *)arg1; uint32_t * u_lid = (uint32_t *)arg2; // check cxy buffer in user space if( vmm_get_vseg( process , (intptr_t)u_cxy , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for CORE_ID : thread[%x,%x] / cxy buffer unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , (intptr_t)u_cxy ); #endif this->errno = EINVAL; ret = -1; break; } // check lid buffer in user space if( vmm_get_vseg( process , (intptr_t)u_lid , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for CORE_ID : thread[%x,%x] / lid buffer unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , (intptr_t)u_lid ); #endif this->errno = EINVAL; ret = -1; break; } // get lid from core decriptor lid_t lid = this->core->lid; // copy cxy to user space hal_copy_to_uspace( u_cxy, XPTR( local_cxy , &local_cxy ), sizeof(uint32_t) ); hal_copy_to_uspace( u_lid, XPTR( local_cxy , &lid ), sizeof(uint32_t) ); ret = 0; break; } ////////////////// case GET_NB_CORES: { uint32_t cores; cxy_t cxy = arg1; uint32_t * u_cores = (uint32_t *)arg2; // check ncores buffer in user space if( vmm_get_vseg( process , (intptr_t)u_cores , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for NB_CORES : thread[%x,%x] / user buffer unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , (intptr_t)u_cores ); #endif this->errno = EFAULT; ret = -1; break; } // get number of cores in cluster if( cluster_is_active( cxy ) ) { cores = hal_remote_l32( XPTR( cxy , &LOCAL_CLUSTER->cores_nr ) ); } else // target cluster undefined { cores = 0; } // copy to user space hal_copy_to_uspace( u_cores, XPTR( local_cxy , &cores ), sizeof(uint32_t) ); ret = 0; break; } /////////////////// case GET_BEST_CORE: { uint32_t base_cxy = ((uint32_t)arg1 >> 16); uint32_t level = ((uint32_t)arg1 & 0xFFFF); uint32_t * cxy = (uint32_t *)(intptr_t)arg2; uint32_t * lid = (uint32_t *)(intptr_t)arg3; uint32_t k_cxy; uint32_t k_lid; // check buffer in user space if( vmm_get_vseg( process , (intptr_t)cxy , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for BEST_CORE : thread[%x,%x] / user buffer cxy unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , cxy ); #endif this->errno = EINVAL; ret = -1; break; } // check buffer in user space if( vmm_get_vseg( process , (intptr_t)lid , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for BEST_CORE : thread[%x,%x] / user buffer lid unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , lid ); #endif this->errno = EINVAL; ret = -1; break; } // check level argument if( level > 5 ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for BEST_CORE : thread[%x,%x] / level argument %d too large\n", __FUNCTION__ , process->pid , this->trdid , level ); #endif this->errno = EINVAL; ret = -1; break; } // get extended pointer on the macro-cluster DQDT root node xptr_t root_xp = dqdt_get_root( base_cxy , level ); if( root_xp == XPTR_NULL ) // macro-cluster undefined { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for BEST_CORE ; thread[%x,%x] DQDT undefined\n", __FUNCTION__, process->pid, this->trdid ); #endif this->errno = EINVAL; ret = -1; break; } // get best core in best cluster k_cxy = (uint32_t)dqdt_get_cluster_for_thread( root_xp ); k_lid = (uint32_t)cluster_select_local_core( k_cxy ); // copy to user space hal_copy_to_uspace( cxy, XPTR( local_cxy , &k_cxy ), sizeof(uint32_t) ); hal_copy_to_uspace( lid, XPTR( local_cxy , &k_lid ), sizeof(uint32_t) ); ret = 0; break; } ///////////////////// case GET_THREAD_INFO: { thread_info_t * u_info = (thread_info_t *)arg1; // check buffer in user space if( vmm_get_vseg( process , (intptr_t)u_info , &vseg ) ) { #if DEBUG_SYSCALLS_ERROR if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start ) printk("\n[ERROR] in %s for THREAD_INFO : thread[%x,%x] / user buffer unmapped %x\n", __FUNCTION__ , process->pid , this->trdid , (intptr_t)u_info ); #endif this->errno = EINVAL; ret = -1; break; } // copy this thread_info_t structure to user space hal_copy_to_uspace( u_info, XPTR( local_cxy , &this->info), sizeof(thread_info_t) ); ret = 0; break; } default: { ret = -1; } } // end switch cmd hal_fence(); #if (DEBUG_SYS_GET || CONFIG_INSTRUMENTATION_SYSCALLS) uint64_t tm_end = hal_get_cycles(); #endif #if DEBUG_SYS_GET if( DEBUG_SYS_GET < tm_end ) printk("\n[%s] thread[%x,%x] exit / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, (uint32_t)tm_end ); #endif #if CONFIG_INSTRUMENTATION_SYSCALLS hal_atomic_add( &syscalls_cumul_cost[SYS_GET] , tm_end - tm_start ); hal_atomic_add( &syscalls_occurences[SYS_GET] , 1 ); #endif return ret; } // end sys_get()