source: trunk/kernel/mm/vmm.c @ 406

Last change on this file since 406 was 406, checked in by alain, 4 years ago

This version executed successfully the user "init" process on a mono-processor TSAR architecture.

File size: 38.6 KB
Line 
1/*
2 * vmm.c - virtual memory manager related operations interface.
3 *
4 * Authors   Ghassan Almaless (2008,2009,2010,2011, 2012)
5 *           Mohamed Lamine Karaoui (2015)
6 *           Alain Greiner (2016)
7 *
8 * Copyright (c) UPMC Sorbonne Universites
9 *
10 * This file is part of ALMOS-MKH.
11 *
12 * ALMOS-MKH is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 2.0 of the License.
15 *
16 * ALMOS-MKH is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26#include <kernel_config.h>
27#include <hal_types.h>
28#include <hal_special.h>
29#include <hal_gpt.h>
30#include <printk.h>
31#include <memcpy.h>
32#include <rwlock.h>
33#include <list.h>
34#include <bits.h>
35#include <process.h>
36#include <thread.h>
37#include <vseg.h>
38#include <cluster.h>
39#include <scheduler.h>
40#include <vfs.h>
41#include <mapper.h>
42#include <page.h>
43#include <kmem.h>
44#include <vmm.h>
45
46//////////////////////////////////////////////////////////////////////////////////
47//   Extern global variables
48//////////////////////////////////////////////////////////////////////////////////
49
50extern  process_t  process_zero;   // defined in cluster.c file
51
52
53////////////////////////////////////
54void vmm_init( process_t * process )
55{
56    error_t   error;
57    vseg_t  * vseg_kentry;
58    vseg_t  * vseg_args;
59    vseg_t  * vseg_envs;
60    vseg_t  * vseg_heap;
61    intptr_t  base;
62    intptr_t  size;
63
64    vmm_dmsg("\n[DMSG] %s : enter for process %x\n", __FUNCTION__ , process->pid );
65
66    // get pointer on VMM
67    vmm_t   * vmm = &process->vmm;
68
69    assert( ((CONFIG_VMM_KENTRY_SIZE + CONFIG_VMM_ARGS_SIZE + CONFIG_VMM_ENVS_SIZE) 
70            <= CONFIG_VMM_ELF_BASE) , __FUNCTION__ , "UTILS zone too small\n" );
71
72    assert( (CONFIG_THREAD_MAX_PER_CLUSTER <= 32) , __FUNCTION__ ,
73            "no more than 32 threads per cluster for a single process\n");
74
75    assert( ((CONFIG_VMM_STACK_SIZE * CONFIG_THREAD_MAX_PER_CLUSTER) <=
76             (CONFIG_VMM_VSPACE_SIZE - CONFIG_VMM_STACK_BASE)) , __FUNCTION__ ,
77             "STACK zone too small\n");
78
79    // initialize the rwlock protecting the vsegs list
80        rwlock_init( &vmm->vsegs_lock );
81
82    // initialize local list of vsegs and radix-tree
83    vmm->vsegs_nr = 0;
84        list_root_init( &vmm->vsegs_root );
85
86    // register kentry vseg in VMM
87    base = CONFIG_VMM_KENTRY_BASE << CONFIG_PPM_PAGE_SHIFT;
88    size = CONFIG_VMM_KENTRY_SIZE << CONFIG_PPM_PAGE_SHIFT;
89
90    vseg_kentry = vmm_create_vseg( process , base , size , VSEG_TYPE_CODE );
91
92    assert( (vseg_kentry != NULL) , __FUNCTION__ , "cannot register kentry vseg\n" );
93
94    vmm->kent_vpn_base = base;
95
96    // register args vseg in VMM
97    base = (CONFIG_VMM_KENTRY_BASE + 
98            CONFIG_VMM_KENTRY_SIZE ) << CONFIG_PPM_PAGE_SHIFT;
99    size = CONFIG_VMM_ARGS_SIZE << CONFIG_PPM_PAGE_SHIFT;
100
101    vseg_args = vmm_create_vseg( process , base , size , VSEG_TYPE_DATA );
102
103    assert( (vseg_args != NULL) , __FUNCTION__ , "cannot register args vseg\n" );
104
105    vmm->args_vpn_base = base;
106
107    // register the envs vseg in VMM
108    base = (CONFIG_VMM_KENTRY_BASE + 
109            CONFIG_VMM_KENTRY_SIZE +
110            CONFIG_VMM_ARGS_SIZE   ) << CONFIG_PPM_PAGE_SHIFT;
111    size = CONFIG_VMM_ENVS_SIZE << CONFIG_PPM_PAGE_SHIFT;
112
113    vseg_envs = vmm_create_vseg( process , base , size , VSEG_TYPE_DATA );
114
115    assert( (vseg_envs != NULL) , __FUNCTION__ , "cannot register envs vseg\n" );
116
117    vmm->envs_vpn_base = base;
118
119    // register the heap vseg in VMM
120    base = CONFIG_VMM_HEAP_BASE << CONFIG_PPM_PAGE_SHIFT;
121    size = (CONFIG_VMM_MMAP_BASE-CONFIG_VMM_HEAP_BASE) << CONFIG_PPM_PAGE_SHIFT;
122
123    vseg_heap = vmm_create_vseg( process , base , size , VSEG_TYPE_HEAP );
124
125    assert( (vseg_heap != NULL) , __FUNCTION__ , "cannot register heap vseg\n" );
126
127    vmm->heap_vpn_base = base;
128
129    // initialize generic page table
130    error = hal_gpt_create( &vmm->gpt );
131
132    assert( (error == 0) , __FUNCTION__ , "cannot initialize page table\n");
133
134    // initialize STACK allocator
135    vmm->stack_mgr.bitmap   = 0;
136    vmm->stack_mgr.vpn_base = CONFIG_VMM_STACK_BASE;
137
138    // initialize MMAP allocator
139    vmm->mmap_mgr.vpn_base        = CONFIG_VMM_MMAP_BASE;
140    vmm->mmap_mgr.vpn_size        = CONFIG_VMM_STACK_BASE - CONFIG_VMM_MMAP_BASE;
141    vmm->mmap_mgr.first_free_vpn  = CONFIG_VMM_MMAP_BASE;
142    uint32_t i;
143    for( i = 0 ; i < 32 ; i++ ) list_root_init( &vmm->mmap_mgr.zombi_list[i] );
144
145    // initialize instrumentation counters
146        vmm->pgfault_nr          = 0;
147        vmm->u_err_nr            = 0;
148        vmm->m_err_nr            = 0;
149
150    hal_fence();
151
152    vmm_dmsg("\n[DMSG] %s : exit for process %x / entry_point = %x\n",
153    __FUNCTION__ , process->pid , process->vmm.entry_point );
154
155}  // end vmm_init()
156
157//////////////////////////////////////////
158error_t vmm_copy( process_t * dst_process,
159                  process_t * src_process )
160{
161    error_t error;
162
163    vmm_t * src_vmm = &src_process->vmm;
164    vmm_t * dst_vmm = &dst_process->vmm;
165
166    // take the src_vmm vsegs_lock
167    rwlock_wr_lock( &src_vmm->vsegs_lock );
168
169    // initialize dst_vmm vsegs_lock
170    rwlock_init( &dst_vmm->vsegs_lock );
171
172    // initialize the dst_vmm vsegs list and the radix tree
173    dst_vmm->vsegs_nr = 0;
174    list_root_init( &dst_vmm->vsegs_root );
175
176    // loop on src_vmm list of vsegs to create
177    // and register vsegs copies in dst_vmm
178    list_entry_t * iter;
179    vseg_t       * src_vseg;
180    vseg_t       * dst_vseg;
181    LIST_FOREACH( &src_vmm->vsegs_root , iter )
182    {
183        // get pointer on current src_vseg
184        src_vseg = LIST_ELEMENT( iter , vseg_t , list );
185
186        // allocate memory for a new dst_vseg
187        dst_vseg = vseg_alloc();
188
189        if( dst_vseg == NULL )
190        {
191            // release all allocated vsegs
192            LIST_FOREACH( &dst_vmm->vsegs_root , iter )
193            {
194                dst_vseg = LIST_ELEMENT( iter , vseg_t , list );
195                vseg_free( dst_vseg );
196            }
197            return ENOMEM;
198        }
199
200        // copy src_vseg to dst_vseg
201        vseg_init_from_ref( dst_vseg , XPTR( local_cxy , src_vseg ) );
202
203        // register dst_vseg in dst_vmm
204        vseg_attach( dst_vmm , dst_vseg );
205    }
206
207    // release the src_vmm vsegs_lock
208    rwlock_wr_unlock( &src_vmm->vsegs_lock );
209
210    // initialize generic page table
211    error = hal_gpt_create( &dst_vmm->gpt );
212
213    if( error )
214    {
215        printk("\n[ERROR] in %s : cannot initialize page table\n", __FUNCTION__ );
216        return ENOMEM;
217    }
218
219    // initialize STACK allocator
220    dst_vmm->stack_mgr.bitmap   = 0;
221    dst_vmm->stack_mgr.vpn_base = CONFIG_VMM_STACK_BASE;
222
223    // initialize MMAP allocator
224    dst_vmm->mmap_mgr.vpn_base        = CONFIG_VMM_MMAP_BASE;
225    dst_vmm->mmap_mgr.vpn_size        = CONFIG_VMM_STACK_BASE - CONFIG_VMM_MMAP_BASE;
226    dst_vmm->mmap_mgr.first_free_vpn  = CONFIG_VMM_MMAP_BASE;
227    uint32_t i;
228    for( i = 0 ; i < 32 ; i++ ) list_root_init( &dst_vmm->mmap_mgr.zombi_list[i] );
229
230    // initialize instrumentation counters
231        dst_vmm->pgfault_nr    = 0;
232        dst_vmm->u_err_nr      = 0;
233        dst_vmm->m_err_nr      = 0;
234
235    // copy base addresses
236    dst_vmm->kent_vpn_base = src_vmm->kent_vpn_base;
237    dst_vmm->args_vpn_base = src_vmm->args_vpn_base;
238    dst_vmm->envs_vpn_base = src_vmm->envs_vpn_base;
239    dst_vmm->heap_vpn_base = src_vmm->heap_vpn_base;
240    dst_vmm->code_vpn_base = src_vmm->code_vpn_base;
241    dst_vmm->data_vpn_base = src_vmm->data_vpn_base;
242
243    dst_vmm->entry_point   = src_vmm->entry_point;
244
245    // HEAP TODO : new heap for child ???
246    dst_vmm->heap_vseg     = src_vmm->heap_vseg;
247
248    // initialize generic page table
249    error = hal_gpt_create( &dst_vmm->gpt );
250
251    if( error )
252    {
253        printk("\n[ERROR] in %s : cannot initialize page table\n", __FUNCTION__ );
254        return ENOMEM;
255    }
256
257    // copy GPT content from src_vmm to dst_vmm, activating "Copy-On-Write"
258    // TODO register Copy-On_Write in page descriptors
259    bool_t cow = true;
260    hal_gpt_copy( &dst_vmm->gpt , &src_vmm->gpt , cow );
261
262    hal_fence();
263
264    return 0;
265
266}  // vmm_copy()
267
268///////////////////////////////////////
269void vmm_destroy( process_t * process )
270{
271        vseg_t * vseg;
272
273    // get pointer on VMM
274    vmm_t  * vmm = &process->vmm;
275
276    // get lock protecting vseg list
277        rwlock_wr_lock( &vmm->vsegs_lock );
278
279    // remove all vsegs registered in vmm
280        while( !list_is_empty( &vmm->vsegs_root ) )
281        {
282                vseg = LIST_FIRST( &vmm->vsegs_root ,  vseg_t , list );
283                vseg_detach( vmm , vseg );
284        vseg_free( vseg );
285        }
286
287    // release lock
288        rwlock_wr_unlock(&vmm->vsegs_lock);
289
290    // remove all vsegs from zombi_lists in MMAP allocator
291    uint32_t i;
292    for( i = 0 ; i<32 ; i++ )
293    {
294            while( !list_is_empty( &vmm->mmap_mgr.zombi_list[i] ) )
295            {
296                    vseg = LIST_FIRST( &vmm->mmap_mgr.zombi_list[i] , vseg_t , list );
297                    vseg_detach( vmm , vseg );
298            vseg_free( vseg );
299            }
300    }
301
302    // release memory allocated to the local page table
303    hal_gpt_destroy( &vmm->gpt );
304
305}  // end vmm_destroy()
306
307/////////////////////////////////////////////////
308vseg_t * vmm_check_conflict( process_t * process,
309                             vpn_t       vpn_base,
310                             vpn_t       vpn_size )
311{
312    vmm_t        * vmm = &process->vmm;
313        vseg_t       * vseg;
314    list_entry_t * iter;
315
316    // scan the list of registered vsegs
317        LIST_FOREACH( &vmm->vsegs_root , iter )
318        {
319                vseg = LIST_ELEMENT( iter , vseg_t , list );
320
321                if( ((vpn_base + vpn_size) > vseg->vpn_base) &&
322             (vpn_base < (vseg->vpn_base + vseg->vpn_size)) ) return vseg;
323        }
324    return NULL;
325
326}  // end vmm_check_conflict()
327
328////////////////////////////////////////////////////////////////////////////////////////////
329// This static function is called by the vmm_create_vseg() function, and implements
330// the VMM stack_vseg specific allocator.
331////////////////////////////////////////////////////////////////////////////////////////////
332// @ vmm      : pointer on VMM.
333// @ vpn_base : (return value) first allocated page
334// @ vpn_size : (return value) number of allocated pages
335////////////////////////////////////////////////////////////////////////////////////////////
336static error_t vmm_stack_alloc( vmm_t * vmm,
337                                vpn_t * vpn_base,
338                                vpn_t * vpn_size )
339{
340    // get stack allocator pointer
341    stack_mgr_t * mgr = &vmm->stack_mgr;
342
343    // get lock on stack allocator
344    spinlock_lock( &mgr->lock );
345
346    // get first free slot index in bitmap
347    int32_t index = bitmap_ffc( &mgr->bitmap , 4 );
348    if( (index < 0) || (index > 31) )
349    {
350        spinlock_unlock( &mgr->lock );
351        return ENOMEM;
352    }
353
354    // update bitmap
355    bitmap_set( &mgr->bitmap , index );
356
357    // release lock on stack allocator
358    spinlock_unlock( &mgr->lock );
359
360    // returns vpn_base, vpn_size (one page non allocated)
361    *vpn_base = mgr->vpn_base + index * CONFIG_VMM_STACK_SIZE + 1;
362    *vpn_size = CONFIG_VMM_STACK_SIZE - 1;
363    return 0;
364
365} // end vmm_stack_alloc()
366
367////////////////////////////////////////////////////////////////////////////////////////////
368// This static function is called by the vmm_create_vseg() function, and implements
369// the VMM MMAP specific allocator.
370////////////////////////////////////////////////////////////////////////////////////////////
371// @ vmm      : [in] pointer on VMM.
372// @ npages   : [in] requested number of pages.
373// @ vpn_base : [out] first allocated page.
374// @ vpn_size : [out] actual number of allocated pages.
375////////////////////////////////////////////////////////////////////////////////////////////
376static error_t vmm_mmap_alloc( vmm_t * vmm,
377                               vpn_t   npages,
378                               vpn_t * vpn_base,
379                               vpn_t * vpn_size )
380{
381    uint32_t   index;
382    vseg_t   * vseg;
383    vpn_t      base;
384    vpn_t      size;
385    vpn_t      free;
386
387    // mmap vseg size must be power of 2
388    // compute actual size and index in zombi_list array
389    size  = POW2_ROUNDUP( npages );
390    index = bits_log2( size );
391
392    // get mmap allocator pointer
393    mmap_mgr_t * mgr = &vmm->mmap_mgr;
394
395    // get lock on mmap allocator
396    spinlock_lock( &mgr->lock );
397
398    // get vseg from zombi_list or from mmap zone
399    if( list_is_empty( &mgr->zombi_list[index] ) )     // from mmap zone
400    {
401        // check overflow
402        free = mgr->first_free_vpn;
403        if( (free + size) > mgr->vpn_size ) return ENOMEM;
404
405        // update STACK allocator
406        mgr->first_free_vpn += size;
407
408        // compute base
409        base = free;
410    }
411    else                                             // from zombi_list
412    {
413        // get pointer on zombi vseg from zombi_list
414        vseg = LIST_FIRST( &mgr->zombi_list[index] , vseg_t , list );
415
416        // remove vseg from free-list
417        list_unlink( &vseg->list );
418
419        // compute base
420        base = vseg->vpn_base;
421    }
422
423    // release lock on mmap allocator
424    spinlock_unlock( &mgr->lock );
425
426    // returns vpn_base, vpn_size
427    *vpn_base = base;
428    *vpn_size = size;
429    return 0;
430
431}  // end vmm_mmap_alloc()
432
433//////////////////////////////////////////////
434vseg_t * vmm_create_vseg( process_t * process,
435                          intptr_t    base,
436                              intptr_t    size,
437                              uint32_t    type )
438{
439    vseg_t     * vseg;          // created vseg pointer
440    vpn_t        vpn_base;      // first page index
441    vpn_t        vpn_size;      // number of pages
442        error_t      error;
443
444    // get pointer on VMM
445        vmm_t * vmm = &process->vmm;
446
447        vmm_dmsg("\n[DMSG] %s : enter for process %x / base = %x / size = %x / type = %s\n",
448                     __FUNCTION__ , process->pid , base , size , vseg_type_str(type) );
449
450    // compute base, size, vpn_base, vpn_size, depending on vseg type
451    // we use the VMM specific allocators for STACK and MMAP vsegs
452    if( type == VSEG_TYPE_STACK )
453    {
454        // get vpn_base and vpn_size from STACK allocator
455        error = vmm_stack_alloc( vmm , &vpn_base , &vpn_size );
456        if( error )
457        {
458            printk("\n[ERROR] in %s : no vspace for stack vseg / process %x in cluster %x\n",
459                   __FUNCTION__ , process->pid , local_cxy );
460            return NULL;
461        }
462
463        // compute vseg base and size from vpn_base and vpn_size
464        base = vpn_base << CONFIG_PPM_PAGE_SHIFT;
465        size = vpn_size << CONFIG_PPM_PAGE_SHIFT;
466    }
467    else if( (type == VSEG_TYPE_ANON) ||
468             (type == VSEG_TYPE_FILE) ||
469             (type == VSEG_TYPE_REMOTE) )
470    {
471        // get vpn_base and vpn_size from MMAP allocator
472        vpn_t npages = size >> CONFIG_PPM_PAGE_SHIFT;
473        error = vmm_mmap_alloc( vmm , npages , &vpn_base , &vpn_size );
474        if( error )
475        {
476            printk("\n[ERROR] in %s : no vspace for mmap vseg / process %x in cluster %x\n",
477                   __FUNCTION__ , process->pid , local_cxy );
478            return NULL;
479        }
480
481        // compute vseg base and size from vpn_base and vpn_size
482        base = vpn_base << CONFIG_PPM_PAGE_SHIFT;
483        size = vpn_size << CONFIG_PPM_PAGE_SHIFT;
484    }
485    else
486    {
487        uint32_t vpn_min = base >> CONFIG_PPM_PAGE_SHIFT;
488        uint32_t vpn_max = (base + size - 1) >> CONFIG_PPM_PAGE_SHIFT;
489
490        vpn_base = vpn_min;
491            vpn_size = vpn_max - vpn_min + 1;
492    }
493
494    // check collisions
495    vseg = vmm_check_conflict( process , vpn_base , vpn_size );
496    if( vseg != NULL )
497    {
498        printk("\n[ERROR] in %s for process %x : new vseg [vpn_base = %x / vpn_size = %x]\n"
499               "  overlap existing vseg [vpn_base = %x / vpn_size = %x]\n",
500               __FUNCTION__ , process->pid, vpn_base, vpn_size,
501               vseg->vpn_base, vseg->vpn_size );
502        return NULL;
503    }
504
505    // allocate physical memory for vseg descriptor
506        vseg = vseg_alloc();
507        if( vseg == NULL )
508        {
509            printk("\n[ERROR] in %s for process %x : cannot allocate memory for vseg\n",
510             __FUNCTION__ , process->pid );
511        return NULL;
512        }
513
514    // initialize vseg descriptor
515        vseg_init( vseg , base, size , vpn_base , vpn_size , type , local_cxy );
516
517    // update "heap_vseg" in VMM
518        if( type == VSEG_TYPE_HEAP ) process->vmm.heap_vseg = vseg;
519
520    // attach vseg to vmm
521        rwlock_wr_lock( &vmm->vsegs_lock );
522        vseg_attach( vmm , vseg );
523        rwlock_wr_unlock( &vmm->vsegs_lock );
524
525        vmm_dmsg("\n[DMSG] %s : exit for process %x / vseg [%x, %x] registered\n",
526                     __FUNCTION__ , process->pid , vseg->min , vseg->max );
527
528        return vseg;
529
530}  // vmm_create_vseg()
531
532/////////////////////////////////////
533void vmm_remove_vseg( vseg_t * vseg )
534{
535    // get pointers on calling process and VMM
536    thread_t   * this    = CURRENT_THREAD;
537    process_t  * process = this->process;
538    vmm_t      * vmm     = &this->process->vmm;
539    uint32_t     type    = vseg->type;
540
541    // detach vseg from VMM
542        rwlock_wr_lock( &vmm->vsegs_lock );
543    vseg_detach( &process->vmm , vseg );
544        rwlock_wr_unlock( &vmm->vsegs_lock );
545
546    // release the stack slot to VMM stack allocator if STACK type
547    if( type == VSEG_TYPE_STACK )
548    {
549        // get pointer on stack allocator
550        stack_mgr_t * mgr = &vmm->stack_mgr;
551
552        // compute slot index
553        uint32_t index = ((vseg->vpn_base - mgr->vpn_base - 1) / CONFIG_VMM_STACK_SIZE);
554
555        // update stacks_bitmap
556        spinlock_lock( &mgr->lock );
557        bitmap_clear( &mgr->bitmap , index );
558        spinlock_unlock( &mgr->lock );
559    }
560
561    // release the vseg to VMM mmap allocator if MMAP type
562    if( (type == VSEG_TYPE_ANON) || (type == VSEG_TYPE_FILE) || (type == VSEG_TYPE_REMOTE) )
563    {
564        // get pointer on mmap allocator
565        mmap_mgr_t * mgr = &vmm->mmap_mgr;
566
567        // compute zombi_list index
568        uint32_t index = bits_log2( vseg->vpn_size );
569
570        // update zombi_list
571        spinlock_lock( &mgr->lock );
572        list_add_first( &mgr->zombi_list[index] , &vseg->list );
573        spinlock_unlock( &mgr->lock );
574    }
575
576    // release physical memory allocated for vseg descriptor if no MMAP type
577    if( (type != VSEG_TYPE_ANON) && (type != VSEG_TYPE_FILE) && (type != VSEG_TYPE_REMOTE) )
578    {
579        vseg_free( vseg );
580    }
581}
582
583//////////////////////////////////////////////
584error_t vmm_map_kernel_vseg( vseg_t    * vseg,
585                             uint32_t    attr )
586{
587    vpn_t       vpn;        // VPN of PTE to be set
588    vpn_t       vpn_min;    // VPN of first PTE to be set
589    vpn_t       vpn_max;    // VPN of last PTE to be set (excluded)
590        ppn_t       ppn;        // PPN of allocated physical page
591        uint32_t    order;      // ln( number of small pages for one single PTE )
592        page_t    * page;
593    error_t     error;
594
595    // check vseg type : must be a kernel vseg
596    uint32_t type = vseg->type;
597    assert( ((type==VSEG_TYPE_KCODE) || (type==VSEG_TYPE_KDATA) || (type==VSEG_TYPE_KDEV)),
598            __FUNCTION__ , "not a kernel vseg\n" );
599
600    // get pointer on page table
601    gpt_t * gpt = &process_zero.vmm.gpt;
602
603    // define number of small pages per PTE
604        if( attr & GPT_SMALL ) order = 0;   // 1 small page
605        else                   order = 9;   // 512 small pages
606
607    // loop on pages in vseg
608    vpn_min = vseg->vpn_base;
609    vpn_max = vpn_min + vseg->vpn_size;
610        for( vpn = vpn_min ; vpn < vpn_max ; vpn++ )
611        {
612        // allocate a physical page from local PPM
613            kmem_req_t req;
614            req.type  = KMEM_PAGE;
615            req.size  = order;
616            req.flags = AF_KERNEL | AF_ZERO;
617            page      = (page_t *)kmem_alloc( &req );
618                if( page == NULL )
619        {
620            printk("\n[ERROR] in %s : cannot allocate physical memory\n", __FUNCTION__ );
621            return ENOMEM;
622        }
623
624        // set page table entry
625        ppn = ppm_page2ppn( XPTR( local_cxy , page ) );
626        error = hal_gpt_set_pte( gpt , vpn , ppn , attr );
627                if( error )
628        {
629            printk("\n[ERROR] in %s : cannot register PPE\n", __FUNCTION__ );
630            return ENOMEM;
631        }
632        }
633
634        return 0;
635}
636
637/////////////////////////////////////////
638void vmm_unmap_vseg( process_t * process,
639                     vseg_t    * vseg )
640{
641    vpn_t       vpn;        // VPN of current PTE
642    vpn_t       vpn_min;    // VPN of first PTE
643    vpn_t       vpn_max;    // VPN of last PTE (excluded)
644
645    // get pointer on process page table
646    gpt_t     * gpt = &process->vmm.gpt;
647
648    // loop on pages in vseg
649    vpn_min = vseg->vpn_base;
650    vpn_max = vpn_min + vseg->vpn_size;
651        for( vpn = vpn_min ; vpn < vpn_max ; vpn++ )
652    {
653        hal_gpt_reset_pte( gpt , vpn );
654    }
655}
656
657///////////////////////////////////////////////////////////////////////////////////////
658// This low-level static function is called by the vmm_get_vseg() and vmm_resize_vseg()
659// functions.  It scan the list of registered vsegs to find the unique vseg containing
660// a given virtual address.
661///////////////////////////////////////////////////////////////////////////////////////
662// @ vmm     : pointer on the process VMM.
663// @ vaddr   : virtual address.
664// @ return vseg pointer if success / return NULL if not found.
665///////////////////////////////////////////////////////////////////////////////////////
666static vseg_t * vseg_from_vaddr( vmm_t    * vmm,
667                                 intptr_t   vaddr )
668{
669    list_entry_t * iter;
670    vseg_t       * vseg = NULL;
671
672    // get lock protecting the vseg list
673    rwlock_rd_lock( &vmm->vsegs_lock );
674
675    // scan the list of vsegs
676    LIST_FOREACH( &vmm->vsegs_root , iter )
677    {
678        vseg = LIST_ELEMENT( iter , vseg_t , list );
679        if( (vaddr >= vseg->min) && (vaddr < vseg->max) ) break;
680    }
681
682    // release the lock
683    rwlock_rd_unlock( &vmm->vsegs_lock );
684
685    return vseg;
686}
687
688/////////////////////////////////////////////
689error_t vmm_resize_vseg( process_t * process,
690                         intptr_t    base,
691                         intptr_t    size )
692{
693    error_t   error;
694    vseg_t  * new;
695    vpn_t     vpn_min;
696    vpn_t     vpn_max;
697
698    // get pointer on process VMM
699    vmm_t * vmm = &process->vmm;
700
701    intptr_t addr_min = base;
702        intptr_t addr_max = base + size;
703
704    // get pointer on vseg
705        vseg_t * vseg = vseg_from_vaddr( vmm , base );
706
707        if( vseg == NULL)  return EINVAL;
708
709    // get VMM lock protecting vsegs list
710        rwlock_wr_lock( &vmm->vsegs_lock );
711
712        if( (vseg->min > addr_min) || (vseg->max < addr_max) )   // region not included in vseg
713    {
714        error = EINVAL;
715    }
716        else if( (vseg->min == addr_min) && (vseg->max == addr_max) ) // vseg must be removed
717    {
718        vmm_remove_vseg( vseg );
719        error = 0;
720    }
721        else if( vseg->min == addr_min )                         // vseg must be resized
722    {
723        // update vseg base address
724        vseg->min = addr_max;
725
726        // update vpn_base and vpn_size
727        vpn_min        = vseg->min >> CONFIG_PPM_PAGE_SHIFT;
728        vpn_max        = (vseg->max - 1) >> CONFIG_PPM_PAGE_SHIFT;
729        vseg->vpn_base = vpn_min;
730        vseg->vpn_size = vpn_max - vpn_min + 1;
731        error = 0;
732    }
733        else if( vseg->max == addr_max )                          // vseg must be resized
734    {
735        // update vseg max address
736        vseg->max = addr_min;
737
738        // update vpn_base and vpn_size
739        vpn_min        = vseg->min >> CONFIG_PPM_PAGE_SHIFT;
740        vpn_max        = (vseg->max - 1) >> CONFIG_PPM_PAGE_SHIFT;
741        vseg->vpn_base = vpn_min;
742        vseg->vpn_size = vpn_max - vpn_min + 1;
743        error = 0;
744    }
745    else                                                      // vseg cut in three regions
746    {
747        // resize existing vseg
748        vseg->max = addr_min;
749
750        // update vpn_base and vpn_size
751        vpn_min        = vseg->min >> CONFIG_PPM_PAGE_SHIFT;
752        vpn_max        = (vseg->max - 1) >> CONFIG_PPM_PAGE_SHIFT;
753        vseg->vpn_base = vpn_min;
754        vseg->vpn_size = vpn_max - vpn_min + 1;
755
756        // create new vseg
757        new = vmm_create_vseg( process , addr_min , (vseg->max - addr_max) , vseg->type ); 
758        if( new == NULL ) error = EINVAL;
759        else              error = 0;
760    }
761
762    // release VMM lock
763        rwlock_wr_unlock( &vmm->vsegs_lock );
764
765        return error;
766
767}  // vmm_resize_vseg()
768
769///////////////////////////////////////////
770error_t  vmm_get_vseg( process_t * process,
771                       intptr_t    vaddr,
772                       vseg_t   ** found_vseg )
773{
774    vmm_t  * vmm = &process->vmm;
775
776    // get vseg from vaddr
777    vseg_t * vseg = vseg_from_vaddr( vmm , vaddr );
778
779    if( vseg == NULL )   // vseg not found in local cluster => try to get it from ref
780        {
781        // get extended pointer on reference process
782        xptr_t ref_xp = process->ref_xp;
783
784        // get cluster and local pointer on reference process
785        cxy_t       ref_cxy = GET_CXY( ref_xp );
786        process_t * ref_ptr = (process_t *)GET_PTR( ref_xp );
787
788        if( local_cxy == ref_cxy )  return -1;   // local cluster is the reference
789
790        // get extended pointer on reference vseg
791        xptr_t   vseg_xp;
792        error_t  error;
793
794        rpc_vmm_get_vseg_client( ref_cxy , ref_ptr , vaddr , &vseg_xp , &error );
795           
796        if( error )   return -1;       // vseg not found => illegal user vaddr
797       
798        // allocate a vseg in local cluster
799        vseg = vseg_alloc();
800
801        if( vseg == NULL ) return -1;
802
803        // initialise local vseg from reference
804        vseg_init_from_ref( vseg , vseg_xp );
805
806        // register local vseg in local VMM
807        vseg_attach( &process->vmm , vseg );
808    }   
809   
810    // success
811    *found_vseg = vseg;
812    return 0;
813
814}  // end vmm_get_vseg()
815
816////////////////////////////////////////
817error_t vmm_get_one_ppn( vseg_t * vseg,
818                         vpn_t    vpn,
819                         ppn_t  * ppn )
820{
821    error_t    error;
822    cxy_t      page_cxy;          // physical page cluster
823    page_t   * page_ptr;          // local pointer on physical page descriptor
824    uint32_t   index;             // missing page index in vseg mapper
825    uint32_t   type;              // vseg type;
826
827    type      = vseg->type;
828    index     = vpn - vseg->vpn_base;
829
830    vmm_dmsg("\n[DMSG] %s : core[%x,%d] enter for vpn = %x / type = %s / index = %d\n",
831    __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, vpn, vseg_type_str(type), index );
832
833    // FILE type : get the physical page from the file mapper
834    if( type == VSEG_TYPE_FILE )
835    {
836        // get extended pointer on mapper
837        xptr_t     mapper_xp = vseg->mapper_xp;
838
839        assert( (mapper_xp != XPTR_NULL), __FUNCTION__,
840        "mapper not defined for a FILE vseg\n" );
841       
842        // get mapper cluster and local pointer
843        cxy_t      mapper_cxy = GET_CXY( mapper_xp );
844        mapper_t * mapper_ptr = (mapper_t *)GET_PTR( mapper_xp );
845
846        // get page descriptor from mapper
847        if( mapper_cxy == local_cxy )             // mapper is local
848        {
849            page_ptr = mapper_get_page( mapper_ptr , index );
850        }
851        else                                      // mapper is remote
852        {
853            rpc_mapper_get_page_client( mapper_cxy , mapper_ptr , index , &page_ptr );
854        }
855
856        if ( page_ptr == NULL ) return EINVAL;
857
858        page_cxy = mapper_cxy;
859    }
860
861    // Other types : allocate a physical page from target cluster,
862    else
863    {
864        uint32_t flags = vseg->flags;
865
866        // get target cluster for physical page
867        if( flags & VSEG_DISTRIB ) // depends on VPN LSB
868        {
869            uint32_t x_size = LOCAL_CLUSTER->x_size;
870            uint32_t y_size = LOCAL_CLUSTER->y_size;
871            page_cxy = vpn & ((x_size * y_size) - 1);
872        }
873        else                       // defined in vseg descriptor
874        {
875            page_cxy = vseg->cxy;
876        }
877
878        // allocate a physical page in target cluster
879        kmem_req_t   req;
880        if( page_cxy == local_cxy )  // target cluster is the local cluster
881        {
882            req.type  = KMEM_PAGE;
883            req.size  = 0;
884            req.flags = AF_NONE;
885            page_ptr  = (page_t *)kmem_alloc( &req );
886        }
887        else                           // target cluster is not the local cluster
888        {
889            rpc_pmem_get_pages_client( page_cxy , 0 , &page_ptr );
890        }
891
892        if( page_ptr == NULL ) return ENOMEM;
893
894        // initialise missing page from .elf file mapper for DATA and CODE types
895        // => the mapper_xp field is an extended pointer on the .elf file mapper
896        if( (type == VSEG_TYPE_CODE) || (type == VSEG_TYPE_DATA) )
897        {
898            // get extended pointer on mapper
899            xptr_t     mapper_xp = vseg->mapper_xp;
900
901            assert( (mapper_xp != XPTR_NULL), __FUNCTION__,
902            "mapper not defined for a CODE or DATA vseg\n" );
903       
904            // get mapper cluster and local pointer
905            cxy_t      mapper_cxy = GET_CXY( mapper_xp );
906            mapper_t * mapper_ptr = (mapper_t *)GET_PTR( mapper_xp );
907
908            // compute missing page offset in vseg
909            uint32_t offset = index << CONFIG_PPM_PAGE_SHIFT;
910
911            // compute missing page offset in .elf file
912            uint32_t elf_offset = vseg->file_offset + offset;
913
914            vmm_dmsg("\n[DMSG] %s : core[%x,%d] for vpn = %x / elf_offset = %x\n",
915            __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, vpn, elf_offset );
916
917            // compute extended pointer on page base
918            xptr_t base_xp  = ppm_page2base( XPTR( page_cxy , page_ptr ) );
919
920            // file_size (in .elf mapper) can be smaller than vseg_size (BSS)
921            uint32_t file_size = vseg->file_size;
922
923            if( file_size < offset )                 // missing page fully in  BSS
924            {
925                vmm_dmsg("\n[DMSG] %s : core[%x,%d] for vpn = %x / fully in BSS\n",
926                __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, vpn );
927
928                if( page_cxy == local_cxy )
929                {
930                    memset( GET_PTR( base_xp ) , 0 , CONFIG_PPM_PAGE_SIZE );
931                }
932                else
933                {
934                   hal_remote_memset( base_xp , 0 , CONFIG_PPM_PAGE_SIZE );       
935                }
936            }
937            else if( file_size >= (offset + CONFIG_PPM_PAGE_SIZE) )  // fully in  mapper
938            {
939                vmm_dmsg("\n[DMSG] %s : core[%x,%d] for vpn = %x / fully in mapper\n",
940                __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, vpn );
941
942                if( mapper_cxy == local_cxy ) 
943                {
944                    error = mapper_move_kernel( mapper_ptr,
945                                                true,             // to_buffer
946                                                elf_offset,
947                                                base_xp,
948                                                CONFIG_PPM_PAGE_SIZE ); 
949                }
950                else 
951                {
952                    rpc_mapper_move_buffer_client( mapper_cxy,
953                                                   mapper_ptr,
954                                                   true,         // to buffer
955                                                   false,        // kernel buffer
956                                                   elf_offset,
957                                                   base_xp,
958                                                   CONFIG_PPM_PAGE_SIZE,
959                                                   &error );
960                }
961                if( error ) return EINVAL;
962            }
963            else  // both in mapper and in BSS :
964                  // - (file_size - offset)             bytes from mapper
965                  // - (page_size + offset - file_size) bytes from BSS
966            {
967                vmm_dmsg("\n[DMSG] %s : core[%x,%d] for vpn = %x / both mapper & BSS\n"
968                         "      %d bytes from mapper / %d bytes from BSS\n",
969                __FUNCTION__, local_cxy, CURRENT_THREAD->core->lid, vpn,
970                file_size - offset , offset + CONFIG_PPM_PAGE_SIZE - file_size  );
971
972                // initialize mapper part
973                if( mapper_cxy == local_cxy )
974                {
975                    error = mapper_move_kernel( mapper_ptr,
976                                                true,         // to buffer
977                                                elf_offset,
978                                                base_xp,
979                                                file_size - offset ); 
980                }
981                else                               
982                {
983                    rpc_mapper_move_buffer_client( mapper_cxy,
984                                                   mapper_ptr,
985                                                   true,         // to buffer
986                                                   false,        // kernel buffer
987                                                   elf_offset,
988                                                   base_xp,
989                                                   file_size - offset, 
990                                                   &error );
991                }
992                if( error ) return EINVAL;
993
994                // initialize BSS part
995                if( page_cxy == local_cxy )
996                {
997                    memset( GET_PTR( base_xp ) + file_size - offset , 0 , 
998                            offset + CONFIG_PPM_PAGE_SIZE - file_size );
999                }
1000                else
1001                {
1002                   hal_remote_memset( base_xp + file_size - offset , 0 , 
1003                                      offset + CONFIG_PPM_PAGE_SIZE - file_size );
1004                }
1005            }   
1006        }  // end initialisation for CODE or DATA types   
1007    } 
1008
1009    // return ppn
1010    *ppn = ppm_page2ppn( XPTR( page_cxy , page_ptr ) );
1011
1012    vmm_dmsg("\n[DMSG] %s : core[%x,%d] exit for vpn = %x / ppn = %x\n",
1013    __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , vpn , *ppn );
1014
1015    return 0;
1016
1017}  // end vmm_get_one_ppn()
1018
1019/////////////////////////////////////////
1020error_t vmm_get_pte( process_t * process,
1021                     vpn_t       vpn,
1022                     uint32_t  * ret_attr,
1023                     ppn_t     * ret_ppn )
1024{
1025    vseg_t  * vseg;   // pointer on vseg containing VPN
1026    ppn_t     ppn;    // physical page number
1027    uint32_t  attr;   // attributes from GPT entry
1028    error_t   error;
1029
1030    // this function must be called by a thread running in the reference cluster
1031    assert( (GET_CXY( process->ref_xp ) == local_cxy ) , __FUNCTION__ ,
1032    "not called in the reference cluster\n" );
1033
1034    vmm_dmsg("\n[DMSG] %s : core[%x,%d] enter for vpn = %x in process %x\n",
1035    __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , vpn , process->pid );
1036
1037    // get VMM pointer
1038    vmm_t * vmm = &process->vmm;
1039
1040    // access GPT to get PTE attributes and PPN
1041    hal_gpt_get_pte( &vmm->gpt , vpn , &attr , &ppn );
1042
1043    // if PTE is unmapped
1044    // 1) get VSEG containing the missing VPN
1045    // 2) get & initialize physical page (depending on vseg type),
1046    // 3) register the PTE in reference GPT
1047    if( (attr & GPT_MAPPED) == 0 )
1048    {
1049        vmm_dmsg("\n[DMSG] %s : core[%x,%d] page %x unmapped => try to map it\n",
1050        __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , vpn );
1051
1052        // 1. get vseg pointer
1053        error = vmm_get_vseg( process , vpn<<CONFIG_PPM_PAGE_SHIFT , &vseg );
1054
1055        if( error )
1056        {
1057            printk("\n[ERROR] in %s : out of segment / process = %x / vpn = %x\n",
1058                   __FUNCTION__ , process->pid , vpn );
1059            return error;
1060        }
1061
1062        vmm_dmsg("\n[DMSG] %s : core[%x,%d] found vseg %s / vpn_base = %x / vpn_size = %x\n",
1063        __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid ,
1064        vseg_type_str(vseg->type) , vseg->vpn_base , vseg->vpn_size );
1065
1066        // 2. get physical page number, depending on vseg type
1067        error = vmm_get_one_ppn( vseg , vpn , &ppn );
1068
1069        if( error )
1070        {
1071            printk("\n[ERROR] in %s : cannot allocate memory / process = %x / vpn = %x\n",
1072                   __FUNCTION__ , process->pid , vpn );
1073            return error;
1074        }
1075
1076        // 3. define attributes from vseg flags and register in GPT
1077        attr = GPT_MAPPED | GPT_SMALL;
1078        if( vseg->flags & VSEG_USER  ) attr |= GPT_USER;
1079        if( vseg->flags & VSEG_WRITE ) attr |= GPT_WRITABLE;
1080        if( vseg->flags & VSEG_EXEC  ) attr |= GPT_EXECUTABLE;
1081        if( vseg->flags & VSEG_CACHE ) attr |= GPT_CACHABLE;
1082
1083        error = hal_gpt_set_pte( &vmm->gpt , vpn , ppn , attr );
1084
1085        if( error )
1086        {
1087            printk("\n[ERROR] in %s : cannot register PTE / process = %x / vpn = %x\n",
1088                   __FUNCTION__ , process->pid , vpn );
1089            return error;
1090        }
1091    }  // end new PTE
1092
1093    vmm_dmsg("\n[DMSG] %s : core[%x,%d] exit for vpn = %x / ppn = %x\n",
1094    __FUNCTION__ , local_cxy , CURRENT_THREAD->core->lid , vpn , ppn );
1095
1096    *ret_ppn  = ppn;
1097    *ret_attr = attr;
1098    return 0;
1099
1100}  // end vmm_get_pte()
1101
1102///////////////////////////////////////////////////
1103error_t vmm_handle_page_fault( process_t * process,
1104                               vpn_t       vpn )
1105{
1106    uint32_t         attr;          // missing page attributes
1107    ppn_t            ppn;           // missing page PPN
1108    error_t          error;         // return value
1109
1110    // get reference process cluster and local pointer
1111    cxy_t       ref_cxy = GET_CXY( process->ref_xp );
1112    process_t * ref_ptr = (process_t *)GET_PTR( process->ref_xp );
1113
1114    // get missing PTE attributes and PPN from reference cluster
1115    if( local_cxy != ref_cxy )   // local cluster is not the reference cluster
1116    {
1117        rpc_vmm_get_pte_client( ref_cxy , ref_ptr , vpn , &attr , &ppn , &error );
1118    }
1119    else                              // local cluster is the reference cluster
1120    {
1121        error = vmm_get_pte( process , vpn , &attr , &ppn );
1122    }
1123
1124    return error;
1125
1126}  // end vmm_handle_page_fault()
1127
1128
1129///////////////////////////////////////////
1130error_t vmm_v2p_translate( bool_t    ident,
1131                           void    * ptr,
1132                           paddr_t * paddr )
1133{
1134    process_t * process = CURRENT_THREAD->process;
1135
1136    if( ident )  // identity mapping
1137    {
1138        *paddr = (paddr_t)PADDR( local_cxy , (lpa_t)ptr );
1139        return 0;
1140    }
1141
1142    // access page table
1143    error_t  error;
1144    vpn_t    vpn;
1145    uint32_t attr;
1146    ppn_t    ppn;
1147    uint32_t offset;
1148
1149    vpn    = (vpn_t)( (intptr_t)ptr >> CONFIG_PPM_PAGE_SHIFT );
1150    offset = (uint32_t)( ((intptr_t)ptr) & CONFIG_PPM_PAGE_MASK );
1151
1152    if( local_cxy == GET_CXY( process->ref_xp) ) // calling process is reference process
1153    {
1154        error = vmm_get_pte( process, vpn , &attr , &ppn );
1155    }
1156    else                                         // calling process is not reference process
1157    {
1158        cxy_t       ref_cxy = GET_CXY( process->ref_xp );
1159        process_t * ref_ptr = (process_t *)GET_PTR( process->ref_xp );
1160        rpc_vmm_get_pte_client( ref_cxy , ref_ptr , vpn , &attr , &ppn , &error );
1161    }
1162
1163    // set paddr
1164    *paddr = (((paddr_t)ppn) << CONFIG_PPM_PAGE_SHIFT) | offset;
1165
1166    return error;
1167
1168}  // end vmm_v2p_translate()
1169
1170
Note: See TracBrowser for help on using the repository browser.