/* * hal_gpt.c - implementation of the Generic Page Table API for TSAR-MIPS32 * * Author Alain Greiner (2016,2017,2018) * * 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 define the masks for the TSAR MMU PTE attributes (from TSAR MMU specification) //////////////////////////////////////////////////////////////////////////////////////// #define TSAR_PTE_MAPPED 0x80000000 #define TSAR_PTE_SMALL 0x40000000 #define TSAR_PTE_LOCAL 0x20000000 #define TSAR_PTE_REMOTE 0x10000000 #define TSAR_PTE_CACHABLE 0x08000000 #define TSAR_PTE_WRITABLE 0x04000000 #define TSAR_PTE_EXECUTABLE 0x02000000 #define TSAR_PTE_USER 0x01000000 #define TSAR_PTE_GLOBAL 0x00800000 #define TSAR_PTE_DIRTY 0x00400000 #define TSAR_PTE_COW 0x00000001 // only for small pages #define TSAR_PTE_SWAP 0x00000004 // only for small pages #define TSAR_PTE_LOCKED 0x00000008 // only for small pages //////////////////////////////////////////////////////////////////////////////////////// // TSAR MMU related macros (from the TSAR MMU specification) // - IX1 on 11 bits // - IX2 on 9 bits // - PPN on 28 bits //////////////////////////////////////////////////////////////////////////////////////// #define TSAR_MMU_IX1_WIDTH 11 #define TSAR_MMU_IX2_WIDTH 9 #define TSAR_MMU_PPN_WIDTH 28 #define TSAR_MMU_PTE1_ATTR_MASK 0xFFC00000 #define TSAR_MMU_PTE1_PPN_MASK 0x0007FFFF #define TSAR_MMU_IX1_FROM_VPN( vpn ) ((vpn >> 9) & 0x7FF) #define TSAR_MMU_IX2_FROM_VPN( vpn ) (vpn & 0x1FF) #define TSAR_MMU_PTBA_FROM_PTE1( pte1 ) (pte1 & 0x0FFFFFFF) #define TSAR_MMU_PPN_FROM_PTE1( pte1 ) ((pte1 & 0x0007FFFF)<<9) #define TSAR_MMU_ATTR_FROM_PTE1( pte1 ) (pte1 & 0xFFC00000) #define TSAR_MMU_PPN_FROM_PTE2( pte2 ) (pte2 & 0x0FFFFFFF) #define TSAR_MMU_ATTR_FROM_PTE2( pte2 ) (pte2 & 0xFFC000FF) /////////////////////////////////////////////////////////////////////////////////////// // This static function translates the GPT attributes to the TSAR attributes /////////////////////////////////////////////////////////////////////////////////////// static inline uint32_t gpt2tsar( uint32_t gpt_attr ) { uint32_t tsar_attr = 0; if( gpt_attr & GPT_MAPPED ) tsar_attr |= TSAR_PTE_MAPPED; if( gpt_attr & GPT_SMALL ) tsar_attr |= TSAR_PTE_SMALL; if( gpt_attr & GPT_WRITABLE ) tsar_attr |= TSAR_PTE_WRITABLE; if( gpt_attr & GPT_EXECUTABLE ) tsar_attr |= TSAR_PTE_EXECUTABLE; if( gpt_attr & GPT_CACHABLE ) tsar_attr |= TSAR_PTE_CACHABLE; if( gpt_attr & GPT_USER ) tsar_attr |= TSAR_PTE_USER; if( gpt_attr & GPT_DIRTY ) tsar_attr |= TSAR_PTE_DIRTY; if( gpt_attr & GPT_ACCESSED ) tsar_attr |= TSAR_PTE_LOCAL; if( gpt_attr & GPT_GLOBAL ) tsar_attr |= TSAR_PTE_GLOBAL; if( gpt_attr & GPT_COW ) tsar_attr |= TSAR_PTE_COW; if( gpt_attr & GPT_SWAP ) tsar_attr |= TSAR_PTE_SWAP; if( gpt_attr & GPT_LOCKED ) tsar_attr |= TSAR_PTE_LOCKED; return tsar_attr; } /////////////////////////////////////////////////////////////////////////////////////// // This static function translates the TSAR attributes to the GPT attributes /////////////////////////////////////////////////////////////////////////////////////// static inline uint32_t tsar2gpt( uint32_t tsar_attr ) { uint32_t gpt_attr = 0; if( tsar_attr & TSAR_PTE_MAPPED ) gpt_attr |= GPT_MAPPED; if( tsar_attr & TSAR_PTE_MAPPED ) gpt_attr |= GPT_READABLE; if( tsar_attr & TSAR_PTE_SMALL ) gpt_attr |= GPT_SMALL; if( tsar_attr & TSAR_PTE_WRITABLE ) gpt_attr |= GPT_WRITABLE; if( tsar_attr & TSAR_PTE_EXECUTABLE ) gpt_attr |= GPT_EXECUTABLE; if( tsar_attr & TSAR_PTE_CACHABLE ) gpt_attr |= GPT_CACHABLE; if( tsar_attr & TSAR_PTE_USER ) gpt_attr |= GPT_USER; if( tsar_attr & TSAR_PTE_DIRTY ) gpt_attr |= GPT_DIRTY; if( tsar_attr & TSAR_PTE_LOCAL ) gpt_attr |= GPT_ACCESSED; if( tsar_attr & TSAR_PTE_REMOTE ) gpt_attr |= GPT_ACCESSED; if( tsar_attr & TSAR_PTE_GLOBAL ) gpt_attr |= GPT_GLOBAL; if( tsar_attr & TSAR_PTE_COW ) gpt_attr |= GPT_COW; if( tsar_attr & TSAR_PTE_SWAP ) gpt_attr |= GPT_SWAP; if( tsar_attr & TSAR_PTE_LOCKED ) gpt_attr |= GPT_LOCKED; return gpt_attr; } /////////////////////////////////////////////////////////////////////////////////////// // The blocking hal_gpt_lock_pte() function implements a busy-waiting policy to get // exclusive access to a specific GPT entry. // - when non zero, the following variable defines the max number of iterations // in the busy waiting loop. // - when zero, the watchdog mechanism is deactivated. /////////////////////////////////////////////////////////////////////////////////////// #define GPT_LOCK_WATCHDOG 100000 ///////////////////////////////////// error_t hal_gpt_create( gpt_t * gpt ) { page_t * page; xptr_t page_xp; thread_t * this = CURRENT_THREAD; #if DEBUG_HAL_GPT_CREATE uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_CREATE < cycle ) printk("\n[%s] : thread[%x,%x] enter / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // check page size assert( (CONFIG_PPM_PAGE_SIZE == 4096) , "for TSAR, the page size must be 4 Kbytes\n" ); // allocates 2 physical pages for PT1 kmem_req_t req; req.type = KMEM_PAGE; req.size = 1; // 2 small pages req.flags = AF_KERNEL | AF_ZERO; page = (page_t *)kmem_alloc( &req ); if( page == NULL ) { printk("\n[PANIC] in %s : no memory for PT1 / process %x / cluster %x\n", __FUNCTION__, this->process->pid, local_cxy ); return ENOMEM; } // initialize generic page table descriptor page_xp = XPTR( local_cxy , page ); gpt->ptr = GET_PTR( ppm_page2base( page_xp ) ); gpt->ppn = ppm_page2ppn( page_xp ); #if DEBUG_HAL_GPT_CREATE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_CREATE < cycle ) printk("\n[%s] : thread[%x,%x] exit / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif return 0; } // end hal_gpt_create() /////////////////////////////////// void hal_gpt_destroy( gpt_t * gpt ) { uint32_t ix1; uint32_t ix2; uint32_t * pt1; uint32_t pte1; ppn_t pt2_ppn; uint32_t * pt2; uint32_t attr; kmem_req_t req; #if DEBUG_HAL_GPT_DESTROY uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_HAL_GPT_DESTROY < cycle ) printk("\n[%s] : thread[%x,%x] enter / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // get pointer on PT1 pt1 = (uint32_t *)gpt->ptr; // scan the PT1 for( ix1 = 0 ; ix1 < 2048 ; ix1++ ) { pte1 = pt1[ix1]; if( (pte1 & TSAR_PTE_MAPPED) != 0 ) // PTE1 mapped { if( (pte1 & TSAR_PTE_SMALL) == 0 ) // BIG page { printk("\n[WARNING] in %s : mapped big page / ix1 %x\n", __FUNCTION__ , ix1 ); } else // PT2 exist { // get local pointer on PT2 pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); xptr_t base_xp = ppm_ppn2base( pt2_ppn ); pt2 = GET_PTR( base_xp ); // scan the PT2 for( ix2 = 0 ; ix2 < 512 ; ix2++ ) { attr = TSAR_MMU_ATTR_FROM_PTE2( pt2[2 * ix2] ); if( (attr & TSAR_PTE_MAPPED) != 0 ) // PTE2 mapped { printk("\n[WARNING] in %s : mapped small page / ix1 %x / ix2 %x\n", __FUNCTION__ , ix1, ix2 ); } } // release the page allocated for the PT2 req.type = KMEM_PAGE; req.ptr = GET_PTR( ppm_base2page( XPTR(local_cxy , pt2 ) ) ); kmem_free( &req ); } } } // release the PT1 req.type = KMEM_PAGE; req.ptr = GET_PTR( ppm_base2page( XPTR(local_cxy , pt1 ) ) ); kmem_free( &req ); #if DEBUG_HAL_GPT_DESTROY cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_DESTROY < cycle ) printk("\n[%s] : thread[%x,%x] exit / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif } // end hal_gpt_destroy() /* ///////////////////////////////////////////////////////////////////////////////////// // This static function can be used for debug. ///////////////////////////////////////////////////////////////////////////////////// static void hal_gpt_display( process_t * process ) { gpt_t * gpt; uint32_t ix1; uint32_t ix2; uint32_t * pt1; uint32_t pte1; ppn_t pt2_ppn; uint32_t * pt2; uint32_t pte2_attr; ppn_t pte2_ppn; vpn_t vpn; // check argument assert( (process != NULL) , "NULL process pointer\n"); // get pointer on gpt gpt = &(process->vmm.gpt); // get pointer on PT1 pt1 = (uint32_t *)gpt->ptr; printk("\n***** Tsar Page Table for process %x : &gpt = %x / &pt1 = %x\n\n", process->pid , gpt , pt1 ); // scan the PT1 for( ix1 = 0 ; ix1 < 2048 ; ix1++ ) { pte1 = pt1[ix1]; if( (pte1 & TSAR_PTE_MAPPED) != 0 ) { if( (pte1 & TSAR_PTE_SMALL) == 0 ) // BIG page { vpn = ix1 << 9; printk(" - BIG : vpn = %x / pt1[%d] = %X\n", vpn , ix1 , pte1 ); } else // SMALL pages { pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); xptr_t base_xp = ppm_ppn2base ( pt2_ppn ); pt2 = GET_PTR( base_xp ); // scan the PT2 for( ix2 = 0 ; ix2 < 512 ; ix2++ ) { pte2_attr = TSAR_MMU_ATTR_FROM_PTE2( pt2[2 * ix2] ); pte2_ppn = TSAR_MMU_PPN_FROM_PTE2( pt2[2 * ix2 + 1] ); if( (pte2_attr & TSAR_PTE_MAPPED) != 0 ) { vpn = (ix1 << 9) | ix2; printk(" - SMALL : vpn %X / ppn %X / attr %X\n", vpn , pte2_ppn , tsar2gpt(pte2_attr) ); } } } } } } // end hal_gpt_display() */ //////////////////////////////////////////// error_t hal_gpt_lock_pte( xptr_t gpt_xp, vpn_t vpn, uint32_t * attr, ppn_t * ppn ) { uint32_t * pt1_ptr; // local pointer on PT1 base xptr_t ptd1_xp; // extended pointer on PT1[x1] entry uint32_t ptd1; // value of PT1[x1] entry xptr_t page_xp; ppn_t pt2_ppn; // PPN of page containing PT2 uint32_t * pt2_ptr; // local pointer on PT2 base xptr_t pte2_xp; // extended pointer on PT2[ix2].attr uint32_t pte2_attr; // PT2[ix2].attr current value uint32_t pte2_ppn; // PT2[ix2].ppn current value bool_t atomic; #if GPT_LOCK_WATCHDOG uint32_t count = 0; #endif // get cluster and local pointer on GPT cxy_t gpt_cxy = GET_CXY( gpt_xp ); gpt_t * gpt_ptr = GET_PTR( gpt_xp ); #if DEBUG_HAL_GPT_LOCK_PTE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_LOCK_PTE < cycle ) printk("\n[%s] : thread[%x,%x] enters / vpn %x in cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, vpn, gpt_cxy, cycle ); #endif // get indexes in PTI & PT2 from vpn uint32_t ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); // index in PT1 uint32_t ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); // index in PT2 // get local pointer on PT1 pt1_ptr = hal_remote_lpt( XPTR( gpt_cxy , &gpt_ptr->ptr ) ); // build extended pointer on PTD1 == PT1[ix1] ptd1_xp = XPTR( gpt_cxy , &pt1_ptr[ix1] ); // get current PT1 entry value ptd1 = hal_remote_l32( ptd1_xp ); // If PTD1 is unmapped and unlocked, try to atomically lock this PT1 entry. // This PTD1 lock prevent multiple concurrent PT2 allocations // - only the thread that successfully locked the PTD1 allocates a new PT2 // and updates the PTD1 // - all other threads simply wait until the missing PTD1 is mapped. if( ptd1 == 0 ) { // try to atomically lock the PTD1 to prevent concurrent PT2 allocations atomic = hal_remote_atomic_cas( ptd1_xp, ptd1, ptd1 | TSAR_PTE_LOCKED ); if( atomic ) { // allocate one 4 Kbytes physical page for PT2 page_xp = ppm_remote_alloc_pages( gpt_cxy , 0 ); if( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot allocate memory for PT2\n", __FUNCTION__ ); return -1; } // get the PT2 PPN pt2_ppn = ppm_page2ppn( page_xp ); // build PTD1 ptd1 = TSAR_PTE_MAPPED | TSAR_PTE_SMALL | pt2_ppn; // set the PTD1 value in PT1 // this unlocks the PTD1 hal_remote_s32( ptd1_xp , ptd1 ); hal_fence(); #if (DEBUG_HAL_GPT_LOCK_PTE & 1) if( DEBUG_HAL_GPT_LOCK_PTE < cycle ) printk("\n[%s] : thread[%x,%x] allocates a new PT2 for vpn %x in cluster %x\n", __FUNCTION__, this->process->pid, this->trdid, vpn, gpt_cxy ); #endif } // end if atomic } // end if (ptd1 == 0) // wait until PTD1 is mapped by another thread while( (ptd1 & TSAR_PTE_MAPPED) == 0 ) { ptd1 = hal_remote_l32( ptd1_xp ); #if GPT_LOCK_WATCHDOG if( count > GPT_LOCK_WATCHDOG ) { thread_t * thread = CURRENT_THREAD; printk("\n[PANIC] in %s : thread[%x,%x] waiting PTD1 / vpn %x / cxy %x / %d iterations\n", __FUNCTION__, thread->process->pid, thread->trdid, vpn, gpt_cxy, count ); hal_core_sleep(); } count++; #endif } // check ptd1 because only small page can be locked assert( (ptd1 & TSAR_PTE_SMALL), "cannot lock a big page\n"); #if (DEBUG_HAL_GPT_LOCK_PTE & 1) if( DEBUG_HAL_GPT_LOCK_PTE < cycle ) printk("\n[%s] : thread[%x,%x] get ptd1 %x for vpn %x in cluster %x\n", __FUNCTION__, this->process->pid, this->trdid, ptd1, vpn, gpt_cxy ); #endif // get pointer on PT2 base from PTD1 pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( ptd1 ); pt2_ptr = GET_PTR( ppm_ppn2base( pt2_ppn ) ); // build extended pointers on PT2[ix2].attr pte2_xp = XPTR( gpt_cxy , &pt2_ptr[2 * ix2] ); // wait until PTE2 atomically set using a remote CAS do { #if GPT_LOCK_WATCHDOG count = 0; #endif // wait until PTE lock released by the current owner do { pte2_attr = hal_remote_l32( pte2_xp ); #if GPT_LOCK_WATCHDOG if( count > GPT_LOCK_WATCHDOG ) { thread_t * thread = CURRENT_THREAD; printk("\n[PANIC] in %s : thread[%x,%x] waiting PTE2 / vpn %x / cxy %x / %d iterations\n", __FUNCTION__, thread->process->pid, thread->trdid, vpn, gpt_cxy, count ); hal_core_sleep(); } count++; #endif } while( (pte2_attr & TSAR_PTE_LOCKED) != 0 ); // try to atomically set the TSAR_PTE_LOCKED attribute atomic = hal_remote_atomic_cas( pte2_xp, pte2_attr, (pte2_attr | TSAR_PTE_LOCKED) ); } while( atomic == 0 ); // get PTE2.ppn pte2_ppn = hal_remote_l32( pte2_xp + 4 ); #if DEBUG_HAL_GPT_LOCK_PTE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_LOCK_PTE < cycle ) printk("\n[%s] : thread[%x,%x] exit / vpn %x in cluster %x / attr %x / ppn %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, vpn, gpt_cxy, pte2_attr, pte2_ppn, cycle ); #endif // return PPN and GPT attributes *ppn = pte2_ppn & ((1<process->pid, this->trdid, vpn, gpt_cxy, cycle ); #endif // compute indexes in P1 and PT2 uint32_t ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); // index in PT1 uint32_t ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); // index in PT2 // get local pointer on PT1 pt1_ptr = hal_remote_lpt( XPTR( gpt_cxy , &gpt_ptr->ptr ) ); // build extended pointer on PTD1 == PT1[ix1] ptd1_xp = XPTR( gpt_cxy , &pt1_ptr[ix1] ); // get current ptd1 value ptd1 = hal_remote_l32( ptd1_xp ); // check PTD1 attributes assert( ((ptd1 & TSAR_PTE_MAPPED) != 0), "unmapped PTE1\n"); assert( ((ptd1 & TSAR_PTE_SMALL ) != 0), "big page PTE1\n"); // get pointer on PT2 base from PTD1 pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( ptd1 ); pt2_ptr = GET_PTR( ppm_ppn2base( pt2_ppn ) ); // build extended pointers on PT2[ix2].attr pte2_xp = XPTR( gpt_cxy , &pt2_ptr[2 * ix2] ); // get PT2[ix2].attr pte2_attr = hal_remote_l32( pte2_xp ); // check PTE2 attributes assert( ((pte2_attr & TSAR_PTE_MAPPED) != 0), "unmapped PTE2\n"); assert( ((pte2_attr & TSAR_PTE_LOCKED) != 0), "unlocked PTE2\n"); // reset TSAR_PTE_LOCKED attribute hal_remote_s32( pte2_xp , pte2_attr & ~TSAR_PTE_LOCKED ); #if DEBUG_HAL_GPT_LOCK_PTE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_LOCK_PTE < cycle ) printk("\n[%s] : thread[%x,%x] unlocks vpn %x in cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, vpn, gpt_cxy, cycle ); #endif } // end hal_gpt_unlock_pte() /////////////////////////////////////// void hal_gpt_set_pte( xptr_t gpt_xp, vpn_t vpn, uint32_t attr, ppn_t ppn ) { cxy_t gpt_cxy; // target GPT cluster gpt_t * gpt_ptr; // target GPT local pointer uint32_t * pt1_ptr; // local pointer on PT1 base xptr_t pte1_xp; // extended pointer on PT1 entry uint32_t pte1; // PT1 entry value if PTE1 ppn_t pt2_ppn; // PPN of PT2 uint32_t * pt2_ptr; // local pointer on PT2 base xptr_t pte2_attr_xp; // extended pointer on PT2[ix2].attr xptr_t pte2_ppn_xp; // extended pointer on PT2[ix2].ppn uint32_t pte2_attr; // current value of PT2[ix2].attr uint32_t ix1; // index in PT1 uint32_t ix2; // index in PT2 uint32_t tsar_attr; // PTE attributes for TSAR MMU uint32_t small; // requested PTE is for a small page // get cluster and local pointer on GPT gpt_cxy = GET_CXY( gpt_xp ); gpt_ptr = GET_PTR( gpt_xp ); // compute indexes in PT1 and PT2 ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); pt1_ptr = hal_remote_lpt( XPTR( gpt_cxy , &gpt_ptr->ptr ) ); small = attr & GPT_SMALL; // compute tsar attributes from generic attributes tsar_attr = gpt2tsar( attr ); // build extended pointer on PTE1 = PT1[ix1] pte1_xp = XPTR( gpt_cxy , &pt1_ptr[ix1] ); // get current pte1 value pte1 = hal_remote_l32( pte1_xp ); if( small == 0 ) ///////////////// map a big page in PT1 { // check PT1 entry not mapped assert( (pte1 == 0) , "try to set a big page in an already mapped PTE1\n" ); // check VPN aligned assert( (ix2 == 0) , "illegal vpn for a big page\n" ); // check PPN aligned assert( ((ppn & 0x1FF) == 0) , "illegal ppn for a big page\n" ); // set the PTE1 value in PT1 pte1 = (tsar_attr & TSAR_MMU_PTE1_ATTR_MASK) | ((ppn >> 9) & TSAR_MMU_PTE1_PPN_MASK); hal_remote_s32( pte1_xp , pte1 ); hal_fence(); #if DEBUG_HAL_GPT_SET_PTE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_SET_PTE < cycle ) printk("\n[%s] : thread[%x,%x] map PTE1 / cxy %x / ix1 %x / pt1 %x / pte1 %x\n", __FUNCTION__, this->process->pid, this->trdid, gpt_cxy, ix1, pt1_ptr, pte1 ); #endif } else ///////////////// map a small page in PT2 { // PTE1 must be mapped because PTE2 must be locked assert( (pte1 & TSAR_PTE_MAPPED), "PTE1 must be mapped\n" ); // get PT2 base from PTE1 pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); pt2_ptr = GET_PTR( ppm_ppn2base( pt2_ppn ) ); // build extended pointers on PT2[ix2].attr and PT2[ix2].ppn pte2_attr_xp = XPTR( gpt_cxy , &pt2_ptr[2 * ix2] ); pte2_ppn_xp = XPTR( gpt_cxy , &pt2_ptr[2 * ix2 + 1] ); // get current value of PTE2.attr pte2_attr = hal_remote_l32( pte2_attr_xp ); // PTE2 must be locked assert( (pte2_attr & TSAR_PTE_LOCKED), "PTE2 must be locked\n" ); // set PTE2 in PT2 (in this order) hal_remote_s32( pte2_ppn_xp , ppn ); hal_fence(); hal_remote_s32( pte2_attr_xp , tsar_attr ); hal_fence(); #if DEBUG_HAL_GPT_SET_PTE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_SET_PTE < cycle ) printk("\n[%s] : thread[%x,%x] map PTE2 / cxy %x / ix2 %x / pt2 %x / attr %x / ppn %x\n", __FUNCTION__, this->process->pid, this->trdid, gpt_cxy, ix2, pt2_ptr, tsar_attr, ppn ); #endif } } // end of hal_gpt_set_pte() /////////////////////////////////////// void hal_gpt_reset_pte( xptr_t gpt_xp, vpn_t vpn ) { cxy_t gpt_cxy; // target GPT cluster gpt_t * gpt_ptr; // target GPT local pointer uint32_t ix1; // index in PT1 uint32_t ix2; // index in PT2 uint32_t * pt1_ptr; // PT1 base address xptr_t pte1_xp; // extended pointer on PT1[ix1] uint32_t pte1; // PT1 entry value ppn_t pt2_ppn; // PPN of PT2 uint32_t * pt2_ptr; // PT2 base address xptr_t pte2_attr_xp; // extended pointer on PT2[ix2].attr xptr_t pte2_ppn_xp; // extended pointer on PT2[ix2].ppn // get cluster and local pointer on GPT gpt_cxy = GET_CXY( gpt_xp ); gpt_ptr = GET_PTR( gpt_xp ); // get ix1 & ix2 indexes ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); // get local pointer on PT1 base pt1_ptr = hal_remote_lpt( XPTR( gpt_cxy , &gpt_ptr->ptr ) ); // build extended pointer on PTE1 = PT1[ix1] pte1_xp = XPTR( gpt_cxy , &pt1_ptr[ix1] ); // get current PTE1 value pte1 = hal_remote_l32( pte1_xp ); if( (pte1 & TSAR_PTE_MAPPED) == 0 ) // PTE1 unmapped => do nothing { return; } if( (pte1 & TSAR_PTE_SMALL) == 0 ) // it's a PTE1 => unmap it from PT1 { hal_remote_s32( pte1_xp , 0 ); hal_fence(); #if DEBUG_HAL_GPT_RESET_PTE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_RESET_PTE < cycle ) printk("\n[%s] : thread[%x,%x] unmap PTE1 / cxy %x / vpn %x / ix1 %x\n", __FUNCTION__, this->process->pid, this->trdid, gpt_cxy, vpn, ix1 ); #endif return; } else // it's a PTE2 => unmap it from PT2 { // compute PT2 base address pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); pt2_ptr = GET_PTR( ppm_ppn2base( pt2_ppn ) ); // build extended pointer on PT2[ix2].attr and PT2[ix2].ppn pte2_attr_xp = XPTR( gpt_cxy , &pt2_ptr[2 * ix2] ); pte2_ppn_xp = XPTR( gpt_cxy , &pt2_ptr[2 * ix2 + 1] ); // unmap the PTE2 hal_remote_s32( pte2_attr_xp , 0 ); hal_fence(); hal_remote_s32( pte2_ppn_xp , 0 ); hal_fence(); #if DEBUG_HAL_GPT_RESET_PTE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_RESET_PTE < cycle ) printk("\n[%s] : thread[%x,%x] unmap PTE2 / cxy %x / vpn %x / ix2 %x\n", __FUNCTION__, this->process->pid, this->trdid, gpt_cxy, vpn, ix2 ); #endif return; } } // end hal_gpt_reset_pte() //////////////////////////////////////// void hal_gpt_get_pte( xptr_t gpt_xp, vpn_t vpn, uint32_t * attr, ppn_t * ppn ) { uint32_t * pt1; // local pointer on PT1 base uint32_t pte1; // PTE1 value uint32_t * pt2; // local pointer on PT2 base ppn_t pt2_ppn; // PPN of page containing the PT2 xptr_t pte2_attr_xp; // extended pointer on PT2[ix2].attr xptr_t pte2_ppn_xp; // extended pointer on PT2[ix2].ppn uint32_t pte2_attr; // current value of PT2[ix2].attr ppn_t pte2_ppn; // current value of PT2[ix2].ppn // get cluster and local pointer on GPT cxy_t gpt_cxy = GET_CXY( gpt_xp ); gpt_t * gpt_ptr = GET_PTR( gpt_xp ); // compute indexes in PT1 and PT2 uint32_t ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); uint32_t ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); // get PT1 base pt1 = hal_remote_lpt( XPTR( gpt_cxy , &gpt_ptr->ptr ) ); // get pte1 pte1 = hal_remote_l32( XPTR( gpt_cxy , &pt1[ix1] ) ); // check PTE1 mapped if( (pte1 & TSAR_PTE_MAPPED) == 0 ) // PTE1 unmapped { *attr = 0; *ppn = 0; return; } // access GPT if( (pte1 & TSAR_PTE_SMALL) == 0 ) // it's a PTE1 { // get PPN & ATTR from PT1 *attr = tsar2gpt( TSAR_MMU_ATTR_FROM_PTE1( pte1 ) ); *ppn = TSAR_MMU_PPN_FROM_PTE1( pte1 ) | (vpn & ((1<process->pid, this->trdid, src_cxy, local_cxy, cycle ); #endif // get remote src_gpt cluster and local pointer src_cxy = GET_CXY( src_gpt_xp ); src_gpt = GET_PTR( src_gpt_xp ); // get remote src_pt1 and local dst_pt1 src_pt1 = (uint32_t *)hal_remote_lpt( XPTR( src_cxy , &src_gpt->ptr ) ); dst_pt1 = (uint32_t *)dst_gpt->ptr; // check src_pt1 and dst_pt1 existence assert( (src_pt1 != NULL) , "src_pt1 does not exist\n"); assert( (dst_pt1 != NULL) , "dst_pt1 does not exist\n"); // compute SRC indexes src_ix1 = TSAR_MMU_IX1_FROM_VPN( src_vpn ); src_ix2 = TSAR_MMU_IX2_FROM_VPN( src_vpn ); // compute DST indexes dst_ix1 = TSAR_MMU_IX1_FROM_VPN( dst_vpn ); dst_ix2 = TSAR_MMU_IX2_FROM_VPN( dst_vpn ); // get src_pte1 src_pte1 = hal_remote_l32( XPTR( src_cxy , &src_pt1[src_ix1] ) ); // do nothing if src_pte1 not MAPPED or not SMALL if( (src_pte1 & TSAR_PTE_MAPPED) && (src_pte1 & TSAR_PTE_SMALL) ) { // get dst_pt1 entry dst_pte1 = dst_pt1[dst_ix1]; // map dst_pte1 if required if( (dst_pte1 & TSAR_PTE_MAPPED) == 0 ) { // allocate one physical page for a new PT2 req.type = KMEM_PAGE; req.size = 0; // 1 small page req.flags = AF_KERNEL | AF_ZERO; page = (page_t *)kmem_alloc( &req ); if( page == NULL ) { printk("\n[ERROR] in %s : cannot allocate PT2\n", __FUNCTION__ ); return -1; } // build extended pointer on page descriptor page_xp = XPTR( local_cxy , page ); // get PPN for this new PT2 dst_pt2_ppn = (ppn_t)ppm_page2ppn( page_xp ); // build the new dst_pte1 dst_pte1 = TSAR_PTE_MAPPED | TSAR_PTE_SMALL | dst_pt2_ppn; // register it in DST_GPT dst_pt1[dst_ix1] = dst_pte1; } // get pointer on src_pt2 src_pt2_ppn = (ppn_t)TSAR_MMU_PTBA_FROM_PTE1( src_pte1 ); src_pt2 = GET_PTR( ppm_ppn2base( src_pt2_ppn ) ); // get pointer on dst_pt2 dst_pt2_ppn = (ppn_t)TSAR_MMU_PTBA_FROM_PTE1( dst_pte1 ); dst_pt2 = GET_PTR( ppm_ppn2base( dst_pt2_ppn ) ); // get attr and ppn from SRC_PT2 src_pte2_attr = hal_remote_l32( XPTR( src_cxy , &src_pt2[2 * src_ix2] ) ); src_pte2_ppn = hal_remote_l32( XPTR( src_cxy , &src_pt2[2 * src_ix2 + 1] ) ); // do nothing if src_pte2 not MAPPED if( (src_pte2_attr & TSAR_PTE_MAPPED) != 0 ) { // set PPN in DST PTE2 dst_pt2[2 * dst_ix2 + 1] = src_pte2_ppn; // set attributes in DST PTE2 if( cow && (src_pte2_attr & TSAR_PTE_WRITABLE) ) { dst_pt2[2 * dst_ix2] = (src_pte2_attr | TSAR_PTE_COW) & (~TSAR_PTE_WRITABLE); } else { dst_pt2[2 * dst_ix2] = src_pte2_attr; } // return "successfully copied" *mapped = true; *ppn = src_pte2_ppn; #if DEBUG_HAL_GPT_COPY cycle = (uint32_t)hal_get_cycles; if( DEBUG_HAL_GPT_COPY < cycle ) printk("\n[%s] : thread[%x,%x] exit / copy done for src_vpn %x / dst_vpn %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, src_vpn, dst_vpn, cycle ); #endif hal_fence(); return 0; } // end if PTE2 mapped } // end if PTE1 mapped // return "nothing done" *mapped = false; *ppn = 0; #if DEBUG_HAL_GPT_COPY cycle = (uint32_t)hal_get_cycles; if( DEBUG_HAL_GPT_COPY < cycle ) printk("\n[%s] : thread[%x,%x] exit / nothing done / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif hal_fence(); return 0; } // end hal_gpt_pte_copy() ///////////////////////////////////////// void hal_gpt_set_cow( xptr_t gpt_xp, vpn_t vpn_base, vpn_t vpn_size ) { cxy_t gpt_cxy; gpt_t * gpt_ptr; vpn_t vpn; uint32_t ix1; uint32_t ix2; uint32_t * pt1; uint32_t pte1; uint32_t * pt2; ppn_t pt2_ppn; uint32_t attr; // get GPT cluster and local pointer gpt_cxy = GET_CXY( gpt_xp ); gpt_ptr = GET_PTR( gpt_xp ); // get local PT1 pointer pt1 = (uint32_t *)hal_remote_lpt( XPTR( gpt_cxy , &gpt_ptr->ptr ) ); // loop on pages for( vpn = vpn_base ; vpn < (vpn_base + vpn_size) ; vpn++ ) { ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); // get PTE1 value pte1 = hal_remote_l32( XPTR( gpt_cxy , &pt1[ix1] ) ); // only MAPPED & SMALL PTEs are modified if( (pte1 & TSAR_PTE_MAPPED) && (pte1 & TSAR_PTE_SMALL) ) { // compute PT2 base address pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); pt2 = GET_PTR( ppm_ppn2base( pt2_ppn ) ); assert( (GET_CXY( ppm_ppn2base( pt2_ppn ) ) == gpt_cxy ), "PT2 and PT1 must be in the same cluster\n"); // get current PTE2 attributes attr = hal_remote_l32( XPTR( gpt_cxy , &pt2[2*ix2] ) ); // only MAPPED PTEs are modified if( attr & TSAR_PTE_MAPPED ) { attr = (attr | TSAR_PTE_COW) & (~TSAR_PTE_WRITABLE); hal_remote_s32( XPTR( gpt_cxy , &pt2[2*ix2] ) , attr ); } } } // end loop on pages } // end hal_gpt_set_cow() ////////////////////////////////////////// void hal_gpt_update_pte( xptr_t gpt_xp, vpn_t vpn, uint32_t attr, // generic GPT attributes ppn_t ppn ) { uint32_t * pt1; // PT1 base addres uint32_t pte1; // PT1 entry value ppn_t pt2_ppn; // PPN of PT2 uint32_t * pt2; // PT2 base address xptr_t pte2_xp; // exended pointer on PTE2 uint32_t ix1; // index in PT1 uint32_t ix2; // index in PT2 uint32_t tsar_attr; // PTE attributes for TSAR MMU // check MAPPED, SMALL, and not LOCKED in attr argument assert( ((attr & GPT_MAPPED) != 0), "attribute MAPPED must be set in new attributes\n" ); assert( ((attr & GPT_SMALL ) != 0), "attribute SMALL must be set in new attributes\n" ); assert( ((attr & GPT_LOCKED) == 0), "attribute LOCKED must not be set in new attributes\n" ); // get cluster and local pointer on remote GPT cxy_t gpt_cxy = GET_CXY( gpt_xp ); gpt_t * gpt_ptr = GET_PTR( gpt_xp ); // compute indexes in PT1 and PT2 ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); // get PT1 base pt1 = (uint32_t *)hal_remote_lpt( XPTR( gpt_cxy , &gpt_ptr->ptr ) ); // compute tsar_attr from generic attributes tsar_attr = gpt2tsar( attr ); // get PTE1 value pte1 = hal_remote_l32( XPTR( gpt_cxy , &pt1[ix1] ) ); // check MAPPED and SMALL in target PTE1 assert( ((pte1 & GPT_MAPPED) != 0), "attribute MAPPED must be set in target PTE1\n" ); assert( ((pte1 & GPT_SMALL ) != 0), "attribute SMALL must be set in target PTE1\n" ); // get PT2 base from PTE1 pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); pt2 = GET_PTR( ppm_ppn2base( pt2_ppn ) ); // get extended pointer on PTE2 pte2_xp = XPTR( gpt_cxy , &pt2[2*ix2] ); // check MAPPED in target PTE2 assert( ((hal_remote_l32(pte2_xp) & GPT_MAPPED) != 0), "attribute MAPPED must be set in target PTE2\n" ); // set PTE2 in this order hal_remote_s32( pte2_xp , ppn ); hal_fence(); hal_remote_s32( pte2_xp + 4 , tsar_attr ); hal_fence(); } // end hal_gpt_update_pte()