/* * vmm.c - virtual memory manager related operations interface. * * Authors Ghassan Almaless (2008,2009,2010,2011, 2012) * Mohamed Lamine Karaoui (2015) * Alain Greiner (2016) * * 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 #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////// // Extern global variables ////////////////////////////////////////////////////////////////////////////////// extern process_t process_zero; // defined in cluster.c file //////////////////////////////////// void vmm_init( process_t * process ) { error_t error; vseg_t * vseg_kentry; vseg_t * vseg_args; vseg_t * vseg_envs; vseg_t * vseg_heap; intptr_t base; intptr_t size; vmm_dmsg("\n[INFO] %s : enter for process %x\n", __FUNCTION__ , process->pid ); // get pointer on VMM vmm_t * vmm = &process->vmm; assert( ((CONFIG_VMM_KENTRY_SIZE + CONFIG_VMM_ARGS_SIZE + CONFIG_VMM_ENVS_SIZE) <= CONFIG_VMM_ELF_BASE) , __FUNCTION__ , "UTILS zone too small\n" ); assert( (CONFIG_THREAD_MAX_PER_CLUSTER <= 32) , __FUNCTION__ , "no more than 32 threads per cluster for a single process\n"); assert( ((CONFIG_VMM_STACK_SIZE * CONFIG_THREAD_MAX_PER_CLUSTER) <= (CONFIG_VMM_VSPACE_SIZE - CONFIG_VMM_STACK_BASE)) , __FUNCTION__ , "STACK zone too small\n"); // initialize the rwlock protecting the vsegs list rwlock_init( &vmm->vsegs_lock ); // initialize local list of vsegs and radix-tree vmm->vsegs_nr = 0; list_root_init( &vmm->vsegs_root ); error = grdxt_init( &vmm->grdxt, CONFIG_VMM_GRDXT_W1, CONFIG_VMM_GRDXT_W2, CONFIG_VMM_GRDXT_W3 ); assert( (error == 0) , __FUNCTION__ , "cannot initialize radix tree\n" ); // register kentry vseg in VMM base = 1 << CONFIG_PPM_PAGE_SHIFT; size = CONFIG_VMM_KENTRY_SIZE << CONFIG_PPM_PAGE_SHIFT; vseg_kentry = vmm_create_vseg( process , base , size , VSEG_TYPE_CODE ); assert( (vseg_kentry != NULL) , __FUNCTION__ , "cannot register kentry vseg\n" ); vmm->kent_vpn_base = 1; // register the args vseg in VMM base = (CONFIG_VMM_KENTRY_SIZE + 1 )<args_vpn_base = CONFIG_VMM_KENTRY_SIZE + 1; // register the envs vseg in VMM base = (CONFIG_VMM_KENTRY_SIZE + CONFIG_VMM_ARGS_SIZE + 1 )<envs_vpn_base = CONFIG_VMM_KENTRY_SIZE + CONFIG_VMM_ARGS_SIZE + 1; // register the heap vseg in VMM base = CONFIG_VMM_HEAP_BASE << CONFIG_PPM_PAGE_SHIFT; size = (CONFIG_VMM_MMAP_BASE-CONFIG_VMM_HEAP_BASE) << CONFIG_PPM_PAGE_SHIFT; vseg_heap = vmm_create_vseg( process , base , size , VSEG_TYPE_HEAP ); assert( (vseg_heap != NULL) , __FUNCTION__ , "cannot register heap vseg\n" ); vmm->heap_vpn_base = CONFIG_VMM_HEAP_BASE; // initialize generic page table error = hal_gpt_create( &vmm->gpt ); assert( (error == 0) , __FUNCTION__ , "cannot initialize page table\n"); // initialize STACK allocator vmm->stack_mgr.bitmap = 0; vmm->stack_mgr.vpn_base = CONFIG_VMM_STACK_BASE; // initialize MMAP allocator vmm->mmap_mgr.vpn_base = CONFIG_VMM_MMAP_BASE; vmm->mmap_mgr.vpn_size = CONFIG_VMM_STACK_BASE - CONFIG_VMM_MMAP_BASE; vmm->mmap_mgr.first_free_vpn = CONFIG_VMM_MMAP_BASE; uint32_t i; for( i = 0 ; i < 32 ; i++ ) list_root_init( &vmm->mmap_mgr.zombi_list[i] ); // initialize instrumentation counters vmm->pgfault_nr = 0; vmm->u_err_nr = 0; vmm->m_err_nr = 0; hal_fence(); vmm_dmsg("\n[INFO] %s : exit for process %x\n", __FUNCTION__ , process->pid ); } // end vmm_init() ////////////////////////////////////////// error_t vmm_copy( process_t * dst_process, process_t * src_process ) { error_t error; vmm_t * src_vmm = &src_process->vmm; vmm_t * dst_vmm = &dst_process->vmm; // take the src_vmm vsegs_lock rwlock_wr_lock( &src_vmm->vsegs_lock ); // initialize dst_vmm vsegs_lock rwlock_init( &dst_vmm->vsegs_lock ); // initialize the dst_vmm vsegs list and the radix tree dst_vmm->vsegs_nr = 0; list_root_init( &dst_vmm->vsegs_root ); error = grdxt_init( &dst_vmm->grdxt, CONFIG_VMM_GRDXT_W1, CONFIG_VMM_GRDXT_W2, CONFIG_VMM_GRDXT_W3 ); if( error ) { printk("\n[ERROR] in %s : cannot initialize radix tree for process %x\n", __FUNCTION__ , dst_process->pid ); return ENOMEM; } // loop on src_vmm list of vsegs to create // and register vsegs copies in dst_vmm list_entry_t * iter; vseg_t * src_vseg; vseg_t * dst_vseg; LIST_FOREACH( &src_vmm->vsegs_root , iter ) { // get pointer on current src_vseg src_vseg = LIST_ELEMENT( iter , vseg_t , list ); // allocate memory for a new dst_vseg dst_vseg = vseg_alloc(); if( dst_vseg == NULL ) { // release all allocated vsegs LIST_FOREACH( &dst_vmm->vsegs_root , iter ) { dst_vseg = LIST_ELEMENT( iter , vseg_t , list ); vseg_free( dst_vseg ); } return ENOMEM; } // copy src_vseg to dst_vseg vseg_init_from_ref( dst_vseg , XPTR( local_cxy , src_vseg ) ); // register dst_vseg in dst_vmm vseg_attach( dst_vmm , dst_vseg ); } // release the src_vmm vsegs_lock rwlock_wr_unlock( &src_vmm->vsegs_lock ); // initialize generic page table error = hal_gpt_create( &dst_vmm->gpt ); if( error ) { printk("\n[ERROR] in %s : cannot initialize page table\n", __FUNCTION__ ); return ENOMEM; } // initialize STACK allocator dst_vmm->stack_mgr.bitmap = 0; dst_vmm->stack_mgr.vpn_base = CONFIG_VMM_STACK_BASE; // initialize MMAP allocator dst_vmm->mmap_mgr.vpn_base = CONFIG_VMM_MMAP_BASE; dst_vmm->mmap_mgr.vpn_size = CONFIG_VMM_STACK_BASE - CONFIG_VMM_MMAP_BASE; dst_vmm->mmap_mgr.first_free_vpn = CONFIG_VMM_MMAP_BASE; uint32_t i; for( i = 0 ; i < 32 ; i++ ) list_root_init( &dst_vmm->mmap_mgr.zombi_list[i] ); // initialize instrumentation counters dst_vmm->pgfault_nr = 0; dst_vmm->u_err_nr = 0; dst_vmm->m_err_nr = 0; // copy base addresses dst_vmm->kent_vpn_base = src_vmm->kent_vpn_base; dst_vmm->args_vpn_base = src_vmm->args_vpn_base; dst_vmm->envs_vpn_base = src_vmm->envs_vpn_base; dst_vmm->heap_vpn_base = src_vmm->heap_vpn_base; dst_vmm->code_vpn_base = src_vmm->code_vpn_base; dst_vmm->data_vpn_base = src_vmm->data_vpn_base; dst_vmm->entry_point = src_vmm->entry_point; // HEAP TODO : new heap for child ??? dst_vmm->heap_vseg = src_vmm->heap_vseg; // initialize generic page table error = hal_gpt_create( &dst_vmm->gpt ); if( error ) { printk("\n[ERROR] in %s : cannot initialize page table\n", __FUNCTION__ ); return ENOMEM; } // copy GPT content from src_vmm to dst_vmm, activating "Copy-On-Write" // TODO register Copy-On_Write in page descriptors bool_t cow = true; hal_gpt_copy( &dst_vmm->gpt , &src_vmm->gpt , cow ); hal_fence(); return 0; } // vmm_copy() /////////////////////////////////////// void vmm_destroy( process_t * process ) { vseg_t * vseg; // get pointer on VMM vmm_t * vmm = &process->vmm; // get lock protecting vseg list rwlock_wr_lock( &vmm->vsegs_lock ); // remove all vsegs registered in vmm while( !list_is_empty( &vmm->vsegs_root ) ) { vseg = LIST_FIRST( &vmm->vsegs_root , vseg_t , list ); vseg_detach( vmm , vseg ); vseg_free( vseg ); } // delete vsegs radix_tree grdxt_destroy( &vmm->grdxt ); // release lock rwlock_wr_unlock(&vmm->vsegs_lock); // remove all vsegs from zombi_lists in MMAP allocator uint32_t i; for( i = 0 ; i<32 ; i++ ) { while( !list_is_empty( &vmm->mmap_mgr.zombi_list[i] ) ) { vseg = LIST_FIRST( &vmm->mmap_mgr.zombi_list[i] , vseg_t , list ); vseg_detach( vmm , vseg ); vseg_free( vseg ); } } // release memory allocated to the local page table hal_gpt_destroy( &vmm->gpt ); } // end vmm_destroy() ///////////////////////////////////////////////// vseg_t * vmm_check_conflict( process_t * process, vpn_t vpn_base, vpn_t vpn_size ) { vmm_t * vmm = &process->vmm; vseg_t * vseg; list_entry_t * iter; // scan the list of registered vsegs LIST_FOREACH( &vmm->vsegs_root , iter ) { vseg = LIST_ELEMENT( iter , vseg_t , list ); if( ((vpn_base + vpn_size) > vseg->vpn_base) && (vpn_base < (vseg->vpn_base + vseg->vpn_size)) ) return vseg; } return NULL; } // end vmm_check_conflict() //////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the vmm_create_vseg() function, and implements // the VMM stack_vseg specific allocator. //////////////////////////////////////////////////////////////////////////////////////////// // @ vmm : pointer on VMM. // @ vpn_base : (return value) first allocated page // @ vpn_size : (return value) number of allocated pages //////////////////////////////////////////////////////////////////////////////////////////// static error_t vmm_stack_alloc( vmm_t * vmm, vpn_t * vpn_base, vpn_t * vpn_size ) { // get stack allocator pointer stack_mgr_t * mgr = &vmm->stack_mgr; // get lock on stack allocator spinlock_lock( &mgr->lock ); // get first free slot index in bitmap int32_t index = bitmap_ffc( &mgr->bitmap , 4 ); if( (index < 0) || (index > 31) ) { spinlock_unlock( &mgr->lock ); return ENOMEM; } // update bitmap bitmap_set( &mgr->bitmap , index ); // release lock on stack allocator spinlock_unlock( &mgr->lock ); // returns vpn_base, vpn_size (one page non allocated) *vpn_base = mgr->vpn_base + index * CONFIG_VMM_STACK_SIZE + 1; *vpn_size = CONFIG_VMM_STACK_SIZE - 1; return 0; } // end vmm_stack_alloc() //////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the vmm_create_vseg() function, and implements // the VMM MMAP specific allocator. //////////////////////////////////////////////////////////////////////////////////////////// // @ vmm : [in] pointer on VMM. // @ npages : [in] requested number of pages. // @ vpn_base : [out] first allocated page. // @ vpn_size : [out] actual number of allocated pages. //////////////////////////////////////////////////////////////////////////////////////////// static error_t vmm_mmap_alloc( vmm_t * vmm, vpn_t npages, vpn_t * vpn_base, vpn_t * vpn_size ) { uint32_t index; vseg_t * vseg; vpn_t base; vpn_t size; vpn_t free; // mmap vseg size must be power of 2 // compute actual size and index in zombi_list array size = POW2_ROUNDUP( npages ); index = bits_log2( size ); // get mmap allocator pointer mmap_mgr_t * mgr = &vmm->mmap_mgr; // get lock on mmap allocator spinlock_lock( &mgr->lock ); // get vseg from zombi_list or from mmap zone if( list_is_empty( &mgr->zombi_list[index] ) ) // from mmap zone { // check overflow free = mgr->first_free_vpn; if( (free + size) > mgr->vpn_size ) return ENOMEM; // update STACK allocator mgr->first_free_vpn += size; // compute base base = free; } else // from zombi_list { // get pointer on zombi vseg from zombi_list vseg = LIST_FIRST( &mgr->zombi_list[index] , vseg_t , list ); // remove vseg from free-list list_unlink( &vseg->list ); // compute base base = vseg->vpn_base; } // release lock on mmap allocator spinlock_unlock( &mgr->lock ); // returns vpn_base, vpn_size *vpn_base = base; *vpn_size = size; return 0; } // end vmm_mmap_alloc() ////////////////////////////////////////////// vseg_t * vmm_create_vseg( process_t * process, intptr_t base, intptr_t size, uint32_t type ) { vseg_t * vseg; // created vseg pointer vpn_t vpn_base; // first page index vpn_t vpn_size; // number of pages error_t error; // get pointer on VMM vmm_t * vmm = &process->vmm; vmm_dmsg("\n[INFO] %s : enter for process %x / base = %x / size = %x / type = %s\n", __FUNCTION__ , process->pid , base , size , vseg_type_str(type) ); // compute base, size, vpn_base, vpn_size, depending on vseg type // we use the VMM specific allocators for STACK and MMAP vsegs if( type == VSEG_TYPE_STACK ) { // get vpn_base and vpn_size from STACK allocator error = vmm_stack_alloc( vmm , &vpn_base , &vpn_size ); if( error ) { printk("\n[ERROR] in %s : no vspace for stack vseg / process %x in cluster %x\n", __FUNCTION__ , process->pid , local_cxy ); return NULL; } // compute vseg base and size from vpn_base and vpn_size base = vpn_base << CONFIG_PPM_PAGE_SHIFT; size = vpn_size << CONFIG_PPM_PAGE_SHIFT; } else if( (type == VSEG_TYPE_ANON) || (type == VSEG_TYPE_FILE) || (type == VSEG_TYPE_REMOTE) ) { // get vpn_base and vpn_size from MMAP allocator vpn_t npages = size >> CONFIG_PPM_PAGE_SHIFT; error = vmm_mmap_alloc( vmm , npages , &vpn_base , &vpn_size ); if( error ) { printk("\n[ERROR] in %s : no vspace for mmap vseg / process %x in cluster %x\n", __FUNCTION__ , process->pid , local_cxy ); return NULL; } // compute vseg base and size from vpn_base and vpn_size base = vpn_base << CONFIG_PPM_PAGE_SHIFT; size = vpn_size << CONFIG_PPM_PAGE_SHIFT; } else { uint32_t vpn_min = base >> CONFIG_PPM_PAGE_SHIFT; uint32_t vpn_max = (base + size - 1) >> CONFIG_PPM_PAGE_SHIFT; vpn_base = vpn_min; vpn_size = vpn_max - vpn_min + 1; } // check collisions vseg = vmm_check_conflict( process , vpn_base , vpn_size ); if( vseg != NULL ) { printk("\n[ERROR] in %s for process %x : new vseg [vpn_base = %x / vpn_size = %x]\n" " overlap existing vseg [vpn_base = %x / vpn_size = %x]\n", __FUNCTION__ , process->pid, vpn_base, vpn_size, vseg->vpn_base, vseg->vpn_size ); return NULL; } // allocate physical memory for vseg descriptor vseg = vseg_alloc(); if( vseg == NULL ) { printk("\n[ERROR] in %s for process %x : cannot allocate memory for vseg\n", __FUNCTION__ , process->pid ); return NULL; } // initialize vseg descriptor vseg_init( vseg , base, size , vpn_base , vpn_size , type , local_cxy ); // update "heap_vseg" in VMM process->vmm.heap_vseg = vseg; // attach vseg to vmm rwlock_wr_lock( &vmm->vsegs_lock ); vseg_attach( vmm , vseg ); rwlock_wr_unlock( &vmm->vsegs_lock ); vmm_dmsg("\n[INFO] %s : exit for process %x / vseg [%x, %x] has been mapped\n", __FUNCTION__ , process->pid , vseg->min , vseg->max ); return vseg; } ///////////////////////////////////// void vmm_remove_vseg( vseg_t * vseg ) { // get pointers on calling process and VMM thread_t * this = CURRENT_THREAD; process_t * process = this->process; vmm_t * vmm = &this->process->vmm; uint32_t type = vseg->type; // detach vseg from VMM rwlock_wr_lock( &vmm->vsegs_lock ); vseg_detach( &process->vmm , vseg ); rwlock_wr_unlock( &vmm->vsegs_lock ); // release the stack slot to VMM stack allocator if STACK type if( type == VSEG_TYPE_STACK ) { // get pointer on stack allocator stack_mgr_t * mgr = &vmm->stack_mgr; // compute slot index uint32_t index = ((vseg->vpn_base - mgr->vpn_base - 1) / CONFIG_VMM_STACK_SIZE); // update stacks_bitmap spinlock_lock( &mgr->lock ); bitmap_clear( &mgr->bitmap , index ); spinlock_unlock( &mgr->lock ); } // release the vseg to VMM mmap allocator if MMAP type if( (type == VSEG_TYPE_ANON) || (type == VSEG_TYPE_FILE) || (type == VSEG_TYPE_REMOTE) ) { // get pointer on mmap allocator mmap_mgr_t * mgr = &vmm->mmap_mgr; // compute zombi_list index uint32_t index = bits_log2( vseg->vpn_size ); // update zombi_list spinlock_lock( &mgr->lock ); list_add_first( &mgr->zombi_list[index] , &vseg->list ); spinlock_unlock( &mgr->lock ); } // release physical memory allocated for vseg descriptor if no MMAP type if( (type != VSEG_TYPE_ANON) && (type != VSEG_TYPE_FILE) && (type != VSEG_TYPE_REMOTE) ) { vseg_free( vseg ); } } ////////////////////////////////////////////// error_t vmm_map_kernel_vseg( vseg_t * vseg, uint32_t attr ) { vpn_t vpn; // VPN of PTE to be set vpn_t vpn_min; // VPN of first PTE to be set vpn_t vpn_max; // VPN of last PTE to be set (excluded) ppn_t ppn; // PPN of allocated physical page uint32_t order; // ln( number of small pages for one single PTE ) page_t * page; error_t error; // check vseg type : must be a kernel vseg uint32_t type = vseg->type; assert( ((type==VSEG_TYPE_KCODE) || (type==VSEG_TYPE_KDATA) || (type==VSEG_TYPE_KDEV)), __FUNCTION__ , "not a kernel vseg\n" ); // get pointer on page table gpt_t * gpt = &process_zero.vmm.gpt; // define number of small pages per PTE if( attr & GPT_SMALL ) order = 0; // 1 small page else order = 9; // 512 small pages // loop on pages in vseg vpn_min = vseg->vpn_base; vpn_max = vpn_min + vseg->vpn_size; for( vpn = vpn_min ; vpn < vpn_max ; vpn++ ) { // allocate a physical page from local PPM kmem_req_t req; req.type = KMEM_PAGE; req.size = order; req.flags = AF_KERNEL | AF_ZERO; page = (page_t *)kmem_alloc( &req ); if( page == NULL ) { printk("\n[ERROR] in %s : cannot allocate physical memory\n", __FUNCTION__ ); return ENOMEM; } // set page table entry ppn = ppm_page2ppn( XPTR( local_cxy , page ) ); error = hal_gpt_set_pte( gpt , vpn , ppn , attr ); if( error ) { printk("\n[ERROR] in %s : cannot register PPE\n", __FUNCTION__ ); return ENOMEM; } } return 0; } ///////////////////////////////////////// void vmm_unmap_vseg( process_t * process, vseg_t * vseg ) { vpn_t vpn; // VPN of current PTE vpn_t vpn_min; // VPN of first PTE vpn_t vpn_max; // VPN of last PTE (excluded) // get pointer on process page table gpt_t * gpt = &process->vmm.gpt; // loop on pages in vseg vpn_min = vseg->vpn_base; vpn_max = vpn_min + vseg->vpn_size; for( vpn = vpn_min ; vpn < vpn_max ; vpn++ ) { hal_gpt_reset_pte( gpt , vpn ); } } ///////////////////////////////////////////// error_t vmm_resize_vseg( process_t * process, intptr_t base, intptr_t size ) { error_t error; // get pointer on process VMM vmm_t * vmm = &process->vmm; intptr_t addr_min = base; intptr_t addr_max = base + size; uint32_t shift = CONFIG_PPM_PAGE_SHIFT; // get pointer on vseg vseg_t * vseg = grdxt_lookup( &vmm->grdxt , (uint32_t)(base >> shift) ); if( vseg == NULL) return EINVAL; // get VMM lock protecting vsegs list rwlock_wr_lock( &vmm->vsegs_lock ); if( (vseg->min > addr_min) || (vseg->max < addr_max) ) // region not included in vseg { error = EINVAL; } else if( (vseg->min == addr_min) && (vseg->max == addr_max) ) // vseg must be removed { vmm_remove_vseg( vseg ); error = 0; } else if( vseg->min == addr_min ) // vseg must be resized { panic("resize not implemented yet"); error = 0; } else if( vseg->max == addr_max ) // vseg must be resized { panic("resize not implemented yet"); error = 0; } else // vseg cut in three regions => vseg must be resized & new vseg created { panic("resize not implemented yet"); error = 0; } // release VMM lock rwlock_wr_unlock( &vmm->vsegs_lock ); return error; } /////////////////////////////////////////// error_t vmm_get_vseg( process_t * process, intptr_t vaddr, vseg_t ** found_vseg ) { vmm_t * vmm; vseg_t * vseg; // get pointer on process VMM vmm = &process->vmm; // get lock protecting the vseg list rwlock_rd_lock( &vmm->vsegs_lock ); // get pointer on vseg from local radix tree vseg = grdxt_lookup( &vmm->grdxt, (uint32_t)(vaddr >> CONFIG_PPM_PAGE_SHIFT) ); // release the lock rwlock_rd_unlock( &vmm->vsegs_lock ); if( vseg == NULL ) // vseg not found in local cluster => try to get it from ref { // get extended pointer on reference process xptr_t ref_xp = process->ref_xp; // get cluster and local pointer on reference process cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = (process_t *)GET_PTR( ref_xp ); if( local_cxy == ref_cxy ) return -1; // local cluster is the reference // get extended pointer on reference vseg xptr_t vseg_xp; error_t error; rpc_vmm_get_vseg_client( ref_cxy , ref_ptr , vaddr , &vseg_xp , &error ); if( error ) return -1; // vseg not found => illegal user vaddr // allocate a vseg in local cluster vseg = vseg_alloc(); if( vseg == NULL ) panic("no memory for vseg copy in cluster %x", local_cxy ); // initialise local vseg from reference vseg_init_from_ref( vseg , vseg_xp ); // register local vseg in local VMM error = vseg_attach( &process->vmm , vseg ); if( error ) panic("no memory for vseg registration in cluster %x", local_cxy ); } // success *found_vseg = vseg; return 0; } // end vmm_get_vseg() //////////////////////////////////////// error_t vmm_get_one_ppn( vseg_t * vseg, vpn_t vpn, ppn_t * ppn ) { error_t error; cxy_t page_cxy; // physical page cluster page_t * page_ptr; // local pointer on physical page descriptor uint32_t type = vseg->type; xptr_t mapper_xp = vseg->mapper_xp; uint32_t flags = vseg->flags; // get mapper cluster and local pointer cxy_t mapper_cxy = GET_CXY( mapper_xp ); mapper_t * mapper_ptr = (mapper_t *)GET_PTR( mapper_xp ); // FILE type : simply get the physical page from the file mapper if( type == VSEG_TYPE_FILE ) { // compute index in file mapper uint32_t index = vpn - vseg->vpn_base; // get page descriptor from mapper if( mapper_cxy == local_cxy ) // mapper is local { page_ptr = mapper_get_page( mapper_ptr , index ); } else // mapper is remote { rpc_mapper_get_page_client( mapper_cxy , mapper_ptr , index , &page_ptr ); } if ( page_ptr == NULL ) return EINVAL; page_cxy = mapper_cxy; } // all other types : allocate a physical page from target cluster, else { // get target cluster for physical page if( flags & VSEG_DISTRIB ) // depends on VPN LSB { uint32_t x_width = LOCAL_CLUSTER->x_width; uint32_t y_width = LOCAL_CLUSTER->y_width; page_cxy = vpn & ((1<<(x_width + y_width)) - 1); } else // defined in vseg descriptor { page_cxy = vseg->cxy; } // allocate a physical page in target cluster kmem_req_t req; if( page_cxy == local_cxy ) // target cluster is the local cluster { req.type = KMEM_PAGE; req.size = 0; req.flags = AF_NONE; page_ptr = (page_t *)kmem_alloc( &req ); } else // target cluster is not the local cluster { rpc_pmem_get_pages_client( page_cxy , 0 , &page_ptr ); } if( page_ptr == NULL ) return ENOMEM; // initialise page from .elf file mapper for DATA and CODE types if( (type == VSEG_TYPE_CODE) || (type == VSEG_TYPE_DATA) ) { // compute missing page index in vseg vpn_t page_index = vpn - vseg->vpn_base; // compute missing page offset in .elf file intptr_t page_offset = vseg->file_offset + (page_index << CONFIG_PPM_PAGE_SHIFT); // compute extended pointer on page first byte xptr_t base_xp = ppm_page2base( XPTR( page_cxy , page_ptr ) ); // file_size can be smaller than vseg_size for BSS intptr_t file_size = vseg->file_size; if( file_size < page_offset ) // fully in BSS { if( page_cxy == local_cxy ) { memset( GET_PTR( base_xp ) , 0 , CONFIG_PPM_PAGE_SIZE ); } else { hal_remote_memset( base_xp , 0 , CONFIG_PPM_PAGE_SIZE ); } } else if( file_size >= (page_offset + CONFIG_PPM_PAGE_SIZE) ) // fully in mapper { if( mapper_cxy == local_cxy ) { error = mapper_move_kernel( mapper_ptr, true, // to_buffer page_offset, base_xp, CONFIG_PPM_PAGE_SIZE ); } else { rpc_mapper_move_buffer_client( mapper_cxy, mapper_ptr, true, // to buffer false, // kernel buffer page_offset, (uint64_t)base_xp, CONFIG_PPM_PAGE_SIZE, &error ); } if( error ) return EINVAL; } else // in mapper : from page_offset -> (file_size - page_offset) // in BSS : from file_size -> (page_offset + page_size) { // initialize mapper part if( mapper_cxy == local_cxy ) { error = mapper_move_kernel( mapper_ptr, true, // to buffer page_offset, base_xp, file_size - page_offset ); } else { rpc_mapper_move_buffer_client( mapper_cxy, mapper_ptr, true, // to buffer false, // kernel buffer page_offset, (uint64_t)base_xp, file_size - page_offset, &error ); } if( error ) return EINVAL; // initialize BSS part if( page_cxy == local_cxy ) { memset( GET_PTR( base_xp ) + file_size - page_offset , 0 , page_offset + CONFIG_PPM_PAGE_SIZE - file_size ); } else { hal_remote_memset( base_xp + file_size - page_offset , 0 , page_offset + CONFIG_PPM_PAGE_SIZE - file_size ); } } } // end initialisation for CODE or DATA types } // return ppn *ppn = ppm_page2ppn( XPTR( page_cxy , page_ptr ) ); return 0; } // end vmm_get_one_ppn() ///////////////////////////////////////// error_t vmm_get_pte( process_t * process, vpn_t vpn, uint32_t * ret_attr, ppn_t * ret_ppn ) { vseg_t * vseg; // pointer on vseg containing VPN ppn_t ppn; // physical page number uint32_t attr; // attributes from GPT entry error_t error; // this function must be called by a thread running in the reference cluster assert( (GET_CXY( process->ref_xp ) == local_cxy ) , __FUNCTION__ , " not called in the reference cluster\n" ); // get VMM pointer vmm_t * vmm = &process->vmm; // access GPT to get PTE attributes and PPN hal_gpt_get_pte( &vmm->gpt , vpn , &attr , &ppn ); // if PTE is unmapped // 1) get VSEG containing the missing VPN // 2) get & initialize physical page (depending on vseg type), // 3) register the PTE in reference GPT if( (attr & GPT_MAPPED) == 0 ) { // 1. get vseg pointer error = vmm_get_vseg( process , vpn<pid , vpn ); return EINVAL; } // 2. get physical page number, depending on vseg type error = vmm_get_one_ppn( vseg , vpn , &ppn ); if( error ) { printk("\n[ERROR] in %s : cannot allocate memory / process = %x / vpn = %x\n", __FUNCTION__ , process->pid , vpn ); return error; } // 3. define attributes from vseg flags and register in GPT attr = GPT_MAPPED | GPT_SMALL; if( vseg->flags & VSEG_USER ) attr |= GPT_USER; if( vseg->flags & VSEG_WRITE ) attr |= GPT_WRITABLE; if( vseg->flags & VSEG_EXEC ) attr |= GPT_EXECUTABLE; if( vseg->flags & VSEG_CACHE ) attr |= GPT_CACHABLE; error = hal_gpt_set_pte( &vmm->gpt , vpn , ppn , attr ); if( error ) { printk("\n[ERROR] in %s : cannot register PTE / process = %x / vpn = %x\n", __FUNCTION__ , process->pid , vpn ); return ENOMEM; } } // end new PTE *ret_ppn = ppn; *ret_attr = attr; return 0; } // end vmm_get_pte() /////////////////////////////////////////////////// error_t vmm_handle_page_fault( process_t * process, vseg_t * vseg, vpn_t vpn ) { uint32_t attr; // missing page attributes ppn_t ppn; // missing page PPN error_t error; // return value // get local VMM pointer vmm_t * vmm = &process->vmm; // get reference process cluster and local pointer cxy_t ref_cxy = GET_CXY( process->ref_xp ); process_t * ref_ptr = (process_t *)GET_PTR( process->ref_xp ); // get missing PTE attributes and PPN from reference cluster if( local_cxy != ref_cxy ) // local cluster is not the reference cluster { rpc_vmm_get_pte_client( ref_cxy , ref_ptr , vpn , &attr , &ppn , &error ); } else // local cluster is the reference cluster { error = vmm_get_pte( process , vpn , &attr , &ppn ); } return error; } // end vmm_handle_page_fault() /////////////////////////////////////////// error_t vmm_v2p_translate( bool_t ident, void * ptr, paddr_t * paddr ) { process_t * process = CURRENT_THREAD->process; if( ident ) // identity mapping { *paddr = (paddr_t)PADDR( local_cxy , (lpa_t)ptr ); return 0; } // access page table error_t error; vpn_t vpn; uint32_t attr; ppn_t ppn; uint32_t offset; vpn = (vpn_t)( (intptr_t)ptr >> CONFIG_PPM_PAGE_SHIFT ); offset = (uint32_t)( ((intptr_t)ptr) & CONFIG_PPM_PAGE_MASK ); if( local_cxy == GET_CXY( process->ref_xp) ) // calling process is reference process { error = vmm_get_pte( process, vpn , &attr , &ppn ); } else // calling process is not reference process { cxy_t ref_cxy = GET_CXY( process->ref_xp ); process_t * ref_ptr = (process_t *)GET_PTR( process->ref_xp ); rpc_vmm_get_pte_client( ref_cxy , ref_ptr , vpn , &attr , &ppn , &error ); } // set paddr *paddr = (((paddr_t)ppn) << CONFIG_PPM_PAGE_SHIFT) | offset; return error; } // end vmm_v2p_translate()