/* * kern/do_exec.c - excecutes a new user process, load init. * * Copyright (c) 2008,2009,2010,2011,2012 Ghassan Almaless * Copyright (c) 2011,2012,2013,2014,2015 UPMC Sorbonne Universites * * This file is part of ALMOS-kernel. * * ALMOS-kernel 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-kernel 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-kernel; 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 #include #include #include #include #include #include #include #define USR_LIMIT (CONFIG_USR_LIMIT) #define DEV_STDIN CONFIG_DEV_STDIN #define DEV_STDOUT CONFIG_DEV_STDOUT #define DEV_STDERR CONFIG_DEV_STDERR #define TASK_DEFAULT_HEAP_SIZE CONFIG_TASK_HEAP_MIN_SIZE #define INIT_PATH "/bin/init" /* FIXME : ecopy et estrlen attendent une adresse de vecteur dans l'espace utilisateur. Or pour * l'instant l'environnement des processus est "forgé" dans do_exec(), c'est donc des buffers * noyaux. Solution temporaire : on utilise kexec_copy et kexec_strlen. */ ////////////////////////////////////// error_t args_len( char ** vect, uint32_t pages_max, uint32_t * pages_nr, uint32_t * entries_nr, exec_copy_t ecopy, exec_strlen_t estrlen) { uint32_t cntr; uint32_t pgnr; error_t error; uint32_t count; uint32_t len; char * ptr; cntr = 0; count = 0; pgnr = 0; while(1) { if((error = ecopy(&ptr, &vect[cntr], sizeof(ptr)))) { printk(INFO, "INFO: %s:%s: EFAULT Has been Catched on vect, cntr %d, strlen &vect[%x] = %x\n", \ __FUNCTION__, __LINE__, cntr, &vect[cntr], vect[cntr]); return err; } if(ptr == NULL) break; if((err=estrlen(ptr, &len))) { printk(INFO, "INFO: %s:%s: EFAULT Has been Catched, cntr %d, strlen &vect[%x] = %x\n", \ __FUNCTION__, __LINE__, cntr, &vect[cntr], vect[cntr]); return err; } cntr ++; len ++; count += (ARROUND_UP(len, 8)); pgnr = ARROUND_UP(count, PMM_PAGE_SIZE); if((pgnr >> PMM_PAGE_SHIFT) >= pages_max) return E2BIG; } count = (cntr + 1) * sizeof(char*); *pages_nr = (ARROUND_UP(count, PMM_PAGE_SIZE)) / PMM_PAGE_SIZE; *entries_nr = cntr; return 0; } //for now the pointers can be either be from uspace //or kernel space. Solutions : //first keep this binaroty and add a flag to //choose how to copy the arguments //second make them all user space ptr //third make them kernel space ptr //the first solution (although ugly), is the most //perfroming(compared to u->k) and simple //(compared to k->u) ////////////////////////////////////////////// ////////////////////////////////////////////// error_t compute_args( process_t * process, char ** vect, page_t ** pgtbl, uint32_t pages_max, uint32_t start, uint32_t * current, uint32_t * pgnr, uint32_t * pgindex, exec_copy_t ecopy, exec_strlen_t estrlen) { kmem_req_t req; uint32_t count; uint32_t cntr; uint32_t pages_nr; char ** args; char * ptr; uint32_t i; uint32_t len; uint32_t index; uint32_t paddr; error_t error; req.type = KMEM_PAGE; req.size = 0; req.flags = AF_USER | AF_ZERO | AF_REMOTE; req.ptr = process->cluster; pages_nr = 0; count = 0; cntr = 0; index = *pgindex; error = args_len(vect, pages_max, &pages_nr, &cntr, ecopy, estrlen); if( error ) return error; *pgnr += pages_nr; for(i = index; i < (pages_nr + index); i++) { pgtbl[i] = kmem_alloc(&req); if(pgtbl[i] == NULL) return ENOMEM; } *current = start - (pages_nr * PMM_PAGE_SIZE); paddr = (uint32_t) ppm_page2addr(pgtbl[index]); count = (cntr + 1) * sizeof(char*); args = (char **) paddr; pages_nr = 0; /* Copy the table of pointer */ while(count > PMM_PAGE_SIZE) { error = ecopy((void*)paddr, vect + (pages_nr << PMM_PAGE_SHIFT), PMM_PAGE_SIZE); if( error ) return error; index ++; paddr = (uint32_t) ppm_page2addr(pgtbl[index]); count -= PMM_PAGE_SIZE; pages_nr ++; } if(count > 0) { error = ecopy((void*)paddr, vect + (pages_nr << PMM_PAGE_SHIFT), count); if( error ) return error; } paddr += count; /* Copy the the table of pointer content */ for(i=0; i < cntr; i++) { if((error = ecopy(&ptr, &vect[i], sizeof(ptr)))) { printk(INFO, "INFO: %s: EFAULT Has been Catched on vect\n", __FUNCTION__); return err; } error = estrlen(ptr, &len); if(err) return err; len++; /* Update pointer */ args[i] = (char*)(*current + (pages_nr << PMM_PAGE_SHIFT) + count); /* The content could span multiple pages */ while(len) { if((PMM_PAGE_SIZE - count) > (ARROUND_UP(len, 8))) { error = ecopy((void*)paddr, ptr, len); if(err) return err; paddr += (ARROUND_UP(len, 8)); count += (ARROUND_UP(len, 8)); len = 0; } else { error = ecopy((void*)paddr, ptr, PMM_PAGE_SIZE - count); if(err) return err; ptr += (PMM_PAGE_SIZE - count); len -= (PMM_PAGE_SIZE - count); index ++; paddr = (uint32_t) ppm_page2addr(pgtbl[index]); pages_nr ++; count = 0; } } } *pgindex = index + 1; return 0; } /////////////////////////////////////// error_t map_args( process_s * process, page_t ** pgtbl, uint32_t start_index, uint32_t end_index, uint32_t start_addr ) { struct pmm_s *pmm; pmm_page_info_t info; uint32_t current_vma; error_t err; uint32_t i; pmm = &process->vmm.pmm; info.attr = PMM_PRESENT | PMM_READ | PMM_WRITE | PMM_CACHED | PMM_USER; info.cluster = process->cluster; current_vma = start_addr; for(i = start_index; i < end_index; i++) { info.ppn = ppm_page2ppn(pgtbl[i]); if((error = pmm_set_page(pmm, current_vma, &info))) return err; pgtbl[i] = NULL; current_vma += PMM_PAGE_SIZE; } return 0; } ////////////////////////////////////// error_t do_exec( process_t * process, char * pathname, page_t * pgtbl[], uint32_t env_end, uint32_t env_index, uint32_t argv_end, // user_stack_top uint32_t argv_index, thread_t ** new ) { error_t error; pthread_attr_t attr; uint32_t usr_stack_top; int32_t order; thread_t * main_thread; attr.arg2 = (void*)env_end; /* environ */ attr.arg1 = (void*)argv_end; /* argv */ usr_stack_top = argv_end; /* FIXME: * zero should not be hard-coded. Use something like MAIN_KERNEL which represents the * kernel with the init and sh processes (assuming that both of them are on the same kernel). */ if( ((process->pid != PID_MIN_GLOBAL+1) && (current_cid == 0)) || ( (process->pid != PID_MIN_GLOBAL) && (current_cid != 0) ) ) { vmm_destroy(&process->vmm); pmm_release(&process->vmm.pmm); error = vmm_init(&process->vmm); if(err) goto DO_EXEC_ERR; } attr.stack_addr = (void*)(usr_stack_top - CONFIG_PTHREAD_STACK_SIZE); attr.stack_size = CONFIG_PTHREAD_STACK_SIZE; // create stack vseg error = (error_t) vmm_mmap( process, attr.stack_addr, // base USR_LIMIT - (uint32_t)attr.stack_addr, // size VSEG_TYPE_STACK, // type VSEG_RD | VSEG_WR, // flags // PRIVATE | ANON | STACK | FIXED, NULL, // file 0 ); // offset if( error == VM_FAILURE ) { error = CURRENT_THREAD->info.errno; printk(INFO, "%s: failed to mmap main's stack for process %x / error = %d\n", __FUNCTION__, process->pid , error ); return error; } // TODO ??? AG error = map_args(process, pgtbl, 0, env_index, (uint32_t)attr.arg2); if( error ) { return error; } // TODO ??? AG error = map_args(process, pgtbl, env_index, argv_index, (uint32_t)attr.arg1); if(err) { return error; } // register "entry_point" in VMM, register "code" and "data" vsegs in VMM, // using informations contained in the elf file identified by the pathname. error = elf_load_process( path_name , process ); if( error ) { printk(INFO, "%s: failed to access elf for process %x\n", __FUNCTION__, process->pid ); return error; } // create "heap" vseg error = (error_t) vmm_mmap( process, (void *)process->vmm.heap_start, // base PROCESS_DEFAULT_HEAP_SIZE, // size VSEG_TYPE_HEAP, // type VSEG_RD | VSEG_WR, // flags // PRIVATE | ANON | HEAP | FIXED, NULL, // file 0 ); // offset if( error == VM_FAILURE ) { error = CURRENT_THREAD->info.errno; printk(INFO, "%s: failed to mmap heap for process %x\n", __FUNCTION__, process->pid ); return error; } process->vmm.heap_current += TASK_DEFAULT_HEAP_SIZE; // initialize pthread attributes attr.pid = exec_info->pid; attr.entry_func = (void*)process->vmm.entry_point; attr.entry_args = bloup; // TODO [AG] attr.flags = PT_FLAG_DETACH; attr.stack_size = CONFIG_PTHREAD_STACK_SIZE; attr.sched_policy = SCHED_RR; attr.cxy = LOCAL_CLUSTER; attr.lid = bloup; // TODO [AG] // attr.flags = (CURRENT_THREAD->info.attr.flags | PT_ATTR_DETACH); // attr.sched_policy = SCHED_RR; // attr.cid = process->cluster->id; // attr.cpu_lid = process->cpu->lid; // attr.cpu_gid = process->cpu->gid; // attr.entry_func = (void*)process->vmm.entry_point; // attr.exit_func = NULL; // attr.stack_size = attr.stack_size - SIG_DEFAULT_STACK_SIZE; // attr.sigstack_addr = (void*)((uint32_t)attr.stack_addr + attr.stack_size); // attr.sigstack_size = SIG_DEFAULT_STACK_SIZE; // attr.sigreturn_func = 0; // create and initialise thread descriptor, // register the thread in local process descriptor error = thread_user_create( &attr , &main_thread ); if( error ) { printk(INFO, "%s: failed to create main thread for process %x\n" __FUNCTION__, process->pid ); return error; } return 0; } // end do_exec() ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// error_t prepare_args( process_t * process, char ** argv, char ** envp, page_t * pgtbl[], uint32_t * argv_index, uint32_t * env_index, uint32_t * argv_end, uint32_t * env_end, exec_copy_t ecopy, exec_strlen_t estrlen ) { uint32_t usr_stack_top = USR_LIMIT; uint32_t pages_nr = 0; uint32_t pages_max = CONFIG_TASK_ARGS_PAGES_MAX_NR; uint32_t index = 0; error_t error = 0; memset( &pgtbl[0] , 0 , pages_max*sizeof(page_t *) ); // env_index & env_end error = compute_args( process, envp, &pgtbl[0], pages_max, USR_LIMIT, &usr_stack_top, &pages_nr, &index, ecopy, estrlen ); *env_index = index; *env_end = usr_stack_top; /* environ */ if( error ) return error; // argv_index & argv_end error = compute_args( process, argv, &pgtbl[0], pages_max - pages_nr, usr_stack_top, &usr_stack_top, &pages_nr, &index, ecopy, estrlen ); *argv_index = index; *argv_end = usr_stack_top; /* argv */ return error; } ////////////////////////////////// void free_args( page_t * pgtbl[] ) { uint32_t index; for(index = 0; index < CONFIG_TASK_ARGS_PAGES_MAX_NR; index ++) { if(pgtbl[index] != NULL) ppm_free_pages(pgtbl[index]); } } ///////////////////////////////////////////// error_t full_do_exec( process_t * process, char * path_name, char ** argv, char ** envp, uint32_t * isFatal, thread_t ** new, exec_copy_t ecopy, exec_strlen_t estrlen ) { error_t err; uint32_t argv_index; uint32_t env_index; uint32_t argv_end; uint32_t env_end; page_t * pgtbl[CONFIG_TASK_ARGS_PAGES_MAX_NR]; *isFatal = 0; error = prepare_args( process, argv, envp, pgtbl, &argv_index, &env_index, &argv_end, &env_end, ecopy, estrlen ); if(err) goto EXEC_EXIT; error = do_exec( process, path_name, &pgtbl[0], env_end, env_index, argv_end, argv_index, new ); if(err) { printk(INFO, "%s: do_exec failed", __FUNCTION__); *isFatal = 1; } EXEC_EXIT: if(err) { free_args(pgtbl); } return err; } /////////////////////////////////// error_t kexec_copy( void * dst, void * src, uint32_t count ) { memcpy( dst , src , count ); return 0; } ///////////////////////////////////// error_t kexec_strlen( char * dst, uint32_t * count) { *count = strlen( dst ); return 0; } //////////////////////////////////////////////// error_t process_load_init( process_t * process ) { process_t * init; dqdt_attr_t attr; thread_t * main_thread; struct vfs_file_s stdin; struct vfs_file_s stdout; struct vfs_file_s stderr; struct ku_obj ku_path; error_t err; error_t err1; error_t err2; uint32_t isFatal; char *environ[] = {"ALMOS_VERSION="CONFIG_ALMOS_VERSION, NULL}; char *argv[] = {INIT_PATH, "init", NULL}; printk(INFO, "INFO: Loading Init Process [ %s ]\n", INIT_PATH); error = dqdt_process_placement(dqdt_root, &attr); /* Force init to be on current cluster */ attr.cid_exec = current_cid; assert(error == 0); if((error = process_create(&init, &attr, CPU_USR_MODE))) return err; error = vmm_init(&init->vmm); if(err) goto INIT_ERR; error = pmm_init(&init->vmm.pmm, current_cluster); if(err) goto INIT_ERR; error = pmm_dup(&init->vmm.pmm, &process->vmm.pmm); if(err) goto INIT_ERR; vfs_file_up(&process->vfs_root); init->vfs_root = process->vfs_root; vfs_file_up(&process->vfs_root); init->vfs_cwd = process->vfs_root; init->vmm.limit_addr = CONFIG_USR_LIMIT; init->uid = 0; init->parent = process->pid; atomic_init(&init->childs_nr, 0); atomic_init(&process->childs_nr, 1); list_add_last(&process->children, &init->list); KK_BUFF(ku_path, ((void*) DEV_STDIN)); error = vfs_open(&init->vfs_cwd, &ku_path, VFS_O_RDONLY, 0, &stdin); KK_BUFF(ku_path, ((void*) DEV_STDOUT)); err1 = vfs_open(&init->vfs_cwd, &ku_path, VFS_O_WRONLY, 0, &stdout); KK_BUFF(ku_path, ((void*) DEV_STDERR)); err2 = vfs_open(&init->vfs_cwd, &ku_path, VFS_O_WRONLY, 0, &stderr); if(error || err1 || err2) { if(!err) vfs_close(&stdin, NULL); if(!err1) vfs_close(&stdout, NULL); if(!err2) vfs_close(&stderr, NULL); printk(ERROR,"ERROR: do_exec: cannot open fds [%d, %d, %d]\n", err, err1, err2); error = ENOMEM; goto INIT_ERR; } process_fd_set(init, 0, &stdin); process_fd_set(init, 1, &stdout); process_fd_set(init, 2, &stderr); CURRENT_THREAD->info.attr.flags = PT_ATTR_AUTO_NXTT | PT_ATTR_MEM_PRIO | PT_ATTR_AUTO_MGRT; error = full_do_exec(init, INIT_PATH, &argv[0], &environ[0], &isFatal, &main_thread, kexec_copy, kexec_strlen); if(error == 0) { assert(main_thread != NULL && (main_thread->signature == THREAD_ID)); init->state = TASK_READY; error = sched_register(main_thread); assert(error == 0); /* FIXME: ask DQDT for another core */ #if CONFIG_ENABLE_TASK_TRACE main_thread->info.isTraced = true; #endif sched_add_created(main_thread); printk(INFO, "INFO: Init Process Loaded [ %s ]\n", INIT_PATH); return 0; } INIT_ERR: process_destroy(init); return err; }