/* * 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; } ///////////////////////////////////// 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() */ ///////////////////////////////////////////////////////////////////////////////////////// // This static function returns in the buffer the current value of // the PT1 entry identified by the argument, that must contain a PTD1 // (i.e. a pointer on a PT2). If this PT1 entry is not mapped yet, it allocates a // new PT2 and updates the PT1 entry, using the TSAR_MMU_LOCKED attribute in PT1 // entry, to handle possible concurrent mappings of the missing PTD1: // 1) If the PT1 entry is unmapped, it tries to atomically lock this PTD1. // - if the atomic lock is successful it allocates a new PT1, and updates the PTD1. // - else, it simply waits, in a polling loop, the mapping done by another thread. // In both cases, returns the PTD1 value, when the mapping is completed. // 2) If the PT1 entry is already mapped, it returns the PTD1 value, and does // nothing else. ///////////////////////////////////////////////////////////////////////////////////////// static error_t hal_gpt_allocate_pt2( xptr_t ptd1_xp, uint32_t * ptd1_value ) { cxy_t gpt_cxy; // target GPT cluster = GET_CXY( ptd1_xp ); uint32_t ptd1; // PTD1 value ppn_t pt2_ppn; // PPN of page containing the new PT2 bool_t atomic; page_t * page; xptr_t page_xp; // get GPT cluster identifier gpt_cxy = GET_CXY( ptd1_xp ); // get current ptd1 value ptd1 = hal_remote_l32( ptd1_xp ); if( (ptd1 & TSAR_PTE_MAPPED) == 0) // PTD1 unmapped and unlocked { // atomically lock the PTD1 to prevent concurrent PTD1 mappings atomic = hal_remote_atomic_cas( ptd1_xp, ptd1, ptd1 | TSAR_PTE_LOCKED ); if( atomic ) // PTD1 successfully locked { // allocate one physical page for PT2 if( gpt_cxy == local_cxy ) { kmem_req_t req; req.type = KMEM_PAGE; req.size = 0; // 1 small page req.flags = AF_KERNEL | AF_ZERO; page = (page_t *)kmem_alloc( &req ); } else { rpc_pmem_get_pages_client( gpt_cxy , 0 , &page ); } if( page == NULL ) return -1; // get the PT2 PPN page_xp = XPTR( gpt_cxy , page ); pt2_ppn = ppm_page2ppn( page_xp ); // build PTD1 ptd1 = TSAR_PTE_MAPPED | TSAR_PTE_SMALL | pt2_ppn; // set the PTD1 value in PT1 hal_remote_s32( ptd1_xp , ptd1 ); hal_fence(); #if DEBUG_HAL_GPT_ALLOCATE_PT2 thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_ALLOCATE_PT2 < cycle ) printk("\n[%s] : thread[%x,%x] map PTD1 / cxy %x / ix1 %d / pt1 %x / ptd1 %x\n", __FUNCTION__, this->process->pid, this->trdid, gpt_cxy, ix1, pt1_ptr, ptd1 ); #endif } else // PTD1 modified by another thread { // poll PTD1 until mapped by another thread while( (ptd1 & TSAR_PTE_MAPPED) == 0 ) ptd1 = hal_remote_l32( ptd1_xp ); } } else // PTD1 mapped => just use it { #if DEBUG_HAL_GPT_ALLOCATE_PT2 thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_HAL_GPT_ALLOCATE_PT2 < cycle ) printk("\n[%s] : thread[%x,%x] PTD1 mapped / cxy %x / ix1 %d / pt1 %x / ptd1 %x\n", __FUNCTION__, this->process->pid, this->trdid, gpt_cxy, ix1, pt1_ptr, ptd1 ); #endif } *ptd1_value = ptd1; return 0; } // end hal_gpt_allocate_pt2 //////////////////////////////////////////// error_t hal_gpt_lock_pte( xptr_t gpt_xp, vpn_t vpn, uint32_t * attr, ppn_t * ppn ) { error_t error; uint32_t * pt1_ptr; // local pointer on PT1 base xptr_t pte1_xp; // extended pointer on PT1[x1] entry uint32_t pte1; // value of PT1[x1] entry ppn_t pt2_ppn; // PPN of page containing PT2 uint32_t * pt2_ptr; // local pointer on PT2 base xptr_t pte2_attr_xp; // extended pointer on PT2[ix2].attr uint32_t pte2_attr; // PT2[ix2].attr current value xptr_t pte2_ppn_xp; // extended pointer on PT2[ix2].ppn uint32_t pte2_ppn; // PT2[ix2].ppn current value bool_t atomic; // get cluster and local pointer on GPT cxy_t gpt_cxy = GET_CXY( gpt_xp ); gpt_t * gpt_ptr = GET_PTR( gpt_xp ); // get indexes in PTI & 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 PTE1 == PT1[ix1] pte1_xp = XPTR( gpt_cxy , &pt1_ptr[ix1] ); // get PTE1 value from PT1 // allocate a new PT2 for this PTE1 if required error = hal_gpt_allocate_pt2( pte1_xp , &pte1 ); if( error ) { printk("\n[ERROR] in %s : cannot allocate memory for PT2\n", __FUNCTION__ ); return -1; } if( (pte1 & TSAR_PTE_SMALL) == 0 ) { printk("\n[ERROR] in %s : cannot lock a small page\n", __FUNCTION__ ); return -1; } // get pointer on 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] ); // wait until PTE2 unlocked, get PTE2.attr and set lock do { // busy waiting until TSAR_MMU_LOCK == 0 do { pte2_attr = hal_remote_l32( pte2_attr_xp ); } while( (pte2_attr & TSAR_PTE_LOCKED) != 0 ); // try to atomically set the TSAR_MMU_LOCK attribute atomic = hal_remote_atomic_cas( pte2_attr_xp, pte2_attr, (pte2_attr | TSAR_PTE_LOCKED) ); } while( atomic == 0 ); // get PTE2.ppn pte2_ppn = hal_remote_l32( pte2_ppn_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] locks vpn %x / attr %x / ppn %x / cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, vpn, attr, ppn, gpt_cxy, cycle ); #endif // return PPN and GPT attributes *ppn = hal_remote_l32( pte2_ppn_xp ) & ((1<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 ); // check PTE1 attributes assert( (((pte1 & TSAR_PTE_MAPPED) != 0) && ((pte1 & TSAR_PTE_SMALL) != 0)), "try to unlock a big or unmapped PTE1\n"); // get pointer on 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 pte2_attr_xp = XPTR( gpt_cxy , &pt2_ptr[2 * ix2] ); // get PT2[ix2].attr attr = hal_remote_l32( pte2_attr_xp ); // reset TSAR_MMU_LOCK attribute hal_remote_s32( pte2_attr_xp , attr & ~TSAR_PTE_LOCKED ); #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] unlocks vpn %x / attr %x / ppn %x / cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, vpn, attr, ppn, 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 uint32_t pte2_attr; // current value of PT2[ix2].attr // 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 uint32_t ix1; // index in PT1 uint32_t ix2; // index in PT2 uint32_t tsar_attr; // PTE attributes for TSAR MMU // check attr argument MAPPED and SMALL if( (attr & GPT_MAPPED) == 0 ) return; if( (attr & GPT_SMALL ) == 0 ) return; // 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] ) ); if( (pte1 & TSAR_PTE_MAPPED) == 0 ) return; if( (pte1 & TSAR_PTE_SMALL ) == 0 ) return; // get PT2 base from PTE1 pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); pt2 = GET_PTR( ppm_ppn2base( pt2_ppn ) ); // set PTE2 in this order hal_remote_s32( XPTR( gpt_cxy, &pt2[2 * ix2 + 1] ) , ppn ); hal_fence(); hal_remote_s32( XPTR( gpt_cxy, &pt2[2 * ix2] ) , tsar_attr ); hal_fence(); } // end hal_gpt_update_pte() /* unused until now (march 2019) [AG] ////////////////////////////////////// void hal_gpt_reset_range( gpt * gpt, vpn_t vpn_min, vpn_t vpn_max ) { vpn_t vpn; // current vpn uint32_t * pt1; // PT1 base address uint32_t pte1; // PT1 entry value ppn_t pt2_ppn; // PPN of PT2 uint32_t * pt2; // PT2 base address uint32_t ix1; // index in PT1 uint32_t ix2; // index in PT2 // get PT1 pt1 = gpt->ptr; // initialize current index vpn = vpn_min; // loop on pages while( vpn <= vpn_max ) { // get ix1 index from vpn ix1 = TSAR_MMU_IX1_FROM_VPN( vpn ); // get PTE1 pte1 = pt1[ix1] if( (pte1 & TSAR_PTE_MAPPED) == 0 ) // PT1[ix1] unmapped { // update vpn (next big page) (vpn = ix1 + 1) << 9; } if( (pte1 & TSAR_PTE_SMALL) == 0 ) // it's a PTE1 (big page) { // unmap the big page pt1[ix1] = 0; hal_fence(); // update vpn (next big page) (vpn = ix1 + 1) << 9; } else // it's a PTD1 (small page) { // compute PT2 base address pt2_ppn = TSAR_MMU_PTBA_FROM_PTE1( pte1 ); pt2 = GET_PTR( ppm_ppn2base( pt2_ppn ) ); // get ix2 index from vpn ix2 = TSAR_MMU_IX2_FROM_VPN( vpn ); // unmap the small page pt2[2*ix2] = 0; hal_fence(); // update vpn (next small page) vpn++; } } } // hal_gpt_reset_range() */