source: soft/giet_vm/giet_boot/boot.c @ 344

Last change on this file since 344 was 342, checked in by alain, 11 years ago

Cosmetic

File size: 84.3 KB
Line 
1//////////////////////////////////////////////////////////////////////////////////////////
2// File     : boot.c
3// Date     : 01/11/2013
4// Author   : alain greiner
5// Copyright (c) UPMC-LIP6
6//////////////////////////////////////////////////////////////////////////////////////////
7// The boot.c file is part of the GIET-VM nano-kernel.
8//
9// This nano-kernel has been written for the MIPS32 processor.
10// The virtual adresses are on 32 bits and use the (unsigned int) type, but the
11// physicals addresses can have up to 40 bits, and use the  (unsigned long long) type.
12// It natively supports clusterised shared mmemory multi-processors architectures,
13// where each processor is identified by a composite index (cluster_xy, local_id),
14// and where there is one physical memory bank per cluster.
15//
16// This code is executed in the boot phase by proc[0] and performs the following tasks:
17// - load into memory the giet_vm binary files, contained in a FAT32 file system,
18// - build the various page tables (one page table per vspace)
19// - initialize the shedulers (one scheduler per processor)
20//
21// 1) The binary files to be loaded are:
22//    - the "map.bin" file contains the hardware architecture description and the
23//      mapping directives. It must be stored in the the seg_boot_mapping segment
24//      (at address SEG_BOOT_MAPPING_BASE defined in hard_config.h file).
25//    - the "sys.elf" file contains the kernel binary code and data.
26//    - the various "application.elf" files.
27//
28// 2) The map.bin file contains the binary representation of the map.xml file defining:
29//    - the hardware architecture: number of clusters, number or processors,
30//      size of the memory segments, and peripherals in each cluster.
31//    - The structure of the various multi-threaded software applications:
32//      number of tasks, communication channels.
33//    - The mapping: grouping of virtual objects (vobj) in the virtual segments (vseg),
34//      placement of virtual segments (vseg) in the physical segments (pseg), placement
35//      of software tasks on the processors,
36//
37// 3) The GIET-VM uses the paged virtual memory to provides two services:
38//    - classical memory protection, when several independant applications compiled
39//      in different virtual spaces are executing on the same hardware platform.
40//    - data placement in NUMA architectures, when we want to control the placement
41//      of the software objects (virtual segments) on the physical memory banks.
42//
43//    The page table are statically build in the boot phase, and they do not
44//    change during execution. The GIET uses only 4 Kbytes pages.
45//    As most applications use only a limited number of segments, the number of PT2s
46//    actually used by a given virtual space is generally smaller than 2048, and is
47//    computed during the boot phase.
48//    The max number of virtual spaces (GIET_NB_VSPACE_MAX) is a configuration parameter.
49//
50//    Each page table (one page table per virtual space) is monolithic, and contains
51//    one PT1 and up to (GIET_NB_PT2_MAX) PT2s. The PT1 is addressed using the ix1 field
52//    (11 bits) of the VPN, and the selected PT2 is addressed using the ix2 field (9 bits).
53//    - PT1[2048] : a first 8K aligned array of unsigned int, indexed by (ix1) field of VPN.
54//    Each entry in the PT1 contains a 32 bits PTD. The MSB bit PTD[31] is
55//    the PTD valid bit, and LSB bits PTD[19:0] are the 20 MSB bits of the physical base
56//    address of the selected PT2.
57//    The PT1 contains 2048 PTD of 4 bytes => 8K bytes.
58//    - PT2[1024][GIET_NB_PT2_MAX] : an array of array of unsigned int.
59//    Each PT2[1024] must be 4K aligned, each entry in a PT2 contains two unsigned int:
60//    the first word contains the protection flags, and the second word contains the PPN.
61//    Each PT2 contains 512 PTE2 of 8bytes => 4K bytes.
62//    The total size of a page table is finally = 8K + (GIET_NB_PT2_MAX)*4K bytes.
63///////////////////////////////////////////////////////////////////////////////////////
64// Implementation Notes:
65//
66// 1) The cluster_id variable is a linear index in the mapping_info array of clusters.
67//    We use the cluster_xy variable for the tological index = x << Y_WIDTH + y
68///////////////////////////////////////////////////////////////////////////////////////
69
70#include <giet_config.h>
71#include <mwmr_channel.h>
72#include <barrier.h>
73#include <memspace.h>
74#include <tty_driver.h>
75#include <xcu_driver.h>
76#include <ioc_driver.h>
77#include <dma_driver.h>
78#include <cma_driver.h>
79#include <nic_driver.h>
80#include <ioc_driver.h>
81#include <iob_driver.h>
82#include <pic_driver.h>
83#include <mwr_driver.h>
84#include <ctx_handler.h>
85#include <irq_handler.h>
86#include <vmem.h>
87#include <utils.h>
88#include <elf-types.h>
89
90// for boot FAT initialisation
91#include <fat32.h>
92
93#include <mips32_registers.h>
94#include <stdarg.h>
95
96#if !defined(X_SIZE)
97# error The X_SIZE value must be defined in the 'hard_config.h' file !
98#endif
99
100#if !defined(Y_SIZE)
101# error The Y_SIZE value must be defined in the 'hard_config.h' file !
102#endif
103
104#if !defined(X_WIDTH)
105# error The X_WIDTH value must be defined in the 'hard_config.h' file !
106#endif
107
108#if !defined(Y_WIDTH)
109# error The Y_WIDTH value must be defined in the 'hard_config.h' file !
110#endif
111
112#if !defined(SEG_BOOT_MAPPING_BASE)
113# error: You must define SEG_BOOT_MAPPING_BASE in the hard_config.h file
114#endif
115
116#if !defined(SEG_BOOT_BUFFER_BASE)
117# error: You must define SEG_BOOT_BUFFER_BASE in the hard_config.h file
118#endif
119
120#if !defined(SEG_BOOT_BUFFER_SIZE)
121# error: You must define SEG_BOOT_BUFFER_SIZE in the hard_config.h file
122#endif
123
124#if !defined(NB_PROCS_MAX)
125# error The NB_PROCS_MAX value must be defined in the 'hard_config.h' file !
126#endif
127
128#if !defined(GIET_NB_VSPACE_MAX)
129# error The GIET_NB_VSPACE_MAX value must be defined in the 'giet_config.h' file !
130#endif
131
132////////////////////////////////////////////////////////////////////////////
133//      Global variables for boot code
134// Both the page tables for the various virtual spaces, and the schedulers
135// for the processors are physically distributed on the clusters.
136// These global variables are just arrays of pointers.
137////////////////////////////////////////////////////////////////////////////
138
139// This global variable is allocated in "fat32.c" file
140extern fat32_fs_t fat;
141
142// Page table addresses arrays
143__attribute__((section (".bootdata"))) 
144volatile paddr_t      _ptabs_paddr[GIET_NB_VSPACE_MAX];
145
146__attribute__((section (".bootdata"))) 
147volatile unsigned int _ptabs_vaddr[GIET_NB_VSPACE_MAX];
148
149// Next free PT2 index array
150__attribute__((section (".bootdata"))) 
151volatile unsigned int _next_free_pt2[GIET_NB_VSPACE_MAX] =
152{ [0 ... GIET_NB_VSPACE_MAX - 1] = 0 };
153
154// Max PT2 index
155__attribute__((section (".bootdata"))) 
156volatile unsigned int _max_pt2[GIET_NB_VSPACE_MAX] =
157{ [0 ... GIET_NB_VSPACE_MAX - 1] = 0 };
158
159// Scheduler pointers array (virtual addresses)
160// indexed by (x,y,lpid) : ((x << Y_WIDTH) + y)*NB_PROCS_MAX + lpid
161__attribute__((section (".bootdata"))) 
162static_scheduler_t* _schedulers[NB_PROCS_MAX<<(X_WIDTH+Y_WIDTH)];
163
164
165/////////////////////////////////////////////////////////////////////
166// This function checks consistence beween the  mapping_info data
167// structure (soft), and the giet_config file (hard).
168/////////////////////////////////////////////////////////////////////
169void boot_mapping_check() 
170{
171    mapping_header_t * header = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
172
173    // checking mapping availability
174    if (header->signature != IN_MAPPING_SIGNATURE) 
175    {
176        _puts("\n[BOOT ERROR] Illegal mapping signature: ");
177        _putx(header->signature);
178        _puts("\n");
179        _exit();
180    }
181
182    // checking number of clusters
183    if ( (header->x_size  != X_SIZE)  || 
184         (header->y_size  != Y_SIZE)  ||
185         (header->x_width != X_WIDTH) ||
186         (header->y_width != Y_WIDTH) )
187    {
188        _puts("\n[BOOT ERROR] Incoherent X_SIZE or Y_SIZE ");
189        _puts("\n             - In hard_config:  X_SIZE = ");
190        _putd( X_SIZE );
191        _puts(" / Y_SIZE = ");
192        _putd( Y_SIZE );
193        _puts(" / X_WIDTH = ");
194        _putd( X_WIDTH );
195        _puts(" / Y_WIDTH = ");
196        _putd( Y_WIDTH );
197        _puts("\n             - In mapping_info: x_size = ");
198        _putd( header->x_size );
199        _puts(" / y_size = ");
200        _putd( header->y_size );
201        _puts(" / x_width = ");
202        _putd( header->x_width );
203        _puts(" / y_width = ");
204        _putd( header->y_width );
205        _puts("\n");
206        _exit();
207    }
208    // checking number of virtual spaces
209    if (header->vspaces > GIET_NB_VSPACE_MAX) 
210    {
211        _puts("\n[BOOT ERROR] : number of vspaces > GIET_NB_VSPACE_MAX\n");
212        _puts("\n");
213        _exit();
214    }
215
216#if BOOT_DEBUG_MAPPING
217_puts("\n - x_size    = ");
218_putd( header->x_size );
219_puts("\n - y_size    = ");
220_putd( header->y_size );
221_puts("\n - procs     = ");
222_putd( header->procs );
223_puts("\n - periphs   = ");
224_putd( header->periphs );
225_puts("\n - vspaces   = ");
226_putd( header->vspaces );
227_puts("\n - tasks     = ");
228_putd( header->tasks );
229_puts("\n");
230_puts("\n - size of header  = ");
231_putd( MAPPING_HEADER_SIZE );
232_puts("\n - size of cluster = ");
233_putd( MAPPING_CLUSTER_SIZE );
234_puts("\n - size of pseg    = ");
235_putd( MAPPING_PSEG_SIZE );
236_puts("\n - size of proc    = ");
237_putd( MAPPING_PROC_SIZE );
238_puts("\n - size of vspace  = ");
239_putd( MAPPING_VSPACE_SIZE );
240_puts("\n - size of vseg    = ");
241_putd( MAPPING_VSEG_SIZE );
242_puts("\n - size of vobj    = ");
243_putd( MAPPING_VOBJ_SIZE );
244_puts("\n - size of task    = ");
245_putd( MAPPING_TASK_SIZE );
246_puts("\n");
247
248unsigned int cluster_id;
249mapping_cluster_t * cluster = _get_cluster_base(header);
250for( cluster_id = 0; cluster_id < X_SIZE*Y_SIZE ; cluster_id++) 
251{
252    _puts("\n - cluster[");
253    _putd( cluster[cluster_id].x );
254    _puts(",");
255    _putd( cluster[cluster_id].y );
256    _puts("]\n   procs   = ");
257    _putd( cluster[cluster_id].procs );
258    _puts("\n   psegs   = ");
259    _putd( cluster[cluster_id].psegs );
260    _puts("\n   periphs = ");
261    _putd( cluster[cluster_id].periphs );
262    _puts("\n");
263}
264#endif
265
266} // end boot_mapping_check()
267
268
269//////////////////////////////////////////////////////////////////////////////
270//     boot_pseg_get()
271// This function returns the pointer on a physical segment
272// identified  by the pseg index.
273//////////////////////////////////////////////////////////////////////////////
274mapping_pseg_t *boot_pseg_get(unsigned int seg_id) 
275{
276    mapping_header_t* header = (mapping_header_t*)SEG_BOOT_MAPPING_BASE;
277    mapping_pseg_t * pseg    = _get_pseg_base(header);
278
279    // checking argument
280    if (seg_id >= header->psegs) 
281    {
282        _puts("\n[BOOT ERROR] : seg_id argument too large\n");
283        _puts("               in function boot_pseg_get()\n");
284        _exit();
285    }
286
287    return &pseg[seg_id];
288} 
289
290//////////////////////////////////////////////////////////////////////////////
291// boot_add_pte()
292// This function registers a new PTE in the page table defined
293// by the vspace_id argument, and updates both PT1 and PT2.
294// A new PT2 is used when required.
295// As the set of PT2s is implemented as a fixed size array (no dynamic
296// allocation), this function checks a possible overflow of the PT2 array.
297//////////////////////////////////////////////////////////////////////////////
298void boot_add_pte(unsigned int vspace_id,
299                  unsigned int vpn, 
300                  unsigned int flags, 
301                  unsigned int ppn,
302                  unsigned int verbose) 
303{
304    unsigned int ix1;
305    unsigned int ix2;
306    paddr_t      pt1_pbase;     // PT1 physical base address
307    paddr_t      pt2_pbase = 0; // PT2 physical base address
308    paddr_t      pte_paddr;     // PTE physucal address
309    unsigned int pt2_id;        // PT2 index
310    unsigned int ptd;           // PTD : entry in PT1
311    unsigned int max_pt2;       // max number of PT2s for a given vspace
312
313    ix1 = vpn >> 9;         // 11 bits
314    ix2 = vpn & 0x1FF;      //  9 bits
315
316    // check that the _max_pt2[vspace_id] has been set
317    max_pt2 = _max_pt2[vspace_id];
318
319    if (max_pt2 == 0) 
320    {
321        _puts("Undefined page table for vspace ");
322        _putd(vspace_id);
323        _puts("\n");
324        _exit();
325    }
326
327
328    // get page table physical base address
329    pt1_pbase = _ptabs_paddr[vspace_id];
330
331    // get ptd in PT1
332    ptd = _physical_read(pt1_pbase + 4 * ix1);
333
334    if ((ptd & PTE_V) == 0)    // invalid PTD: compute PT2 base address,
335                               // and set a new PTD in PT1
336    {
337        pt2_id = _next_free_pt2[vspace_id];
338        if (pt2_id == max_pt2) 
339        {
340            _puts("\n[BOOT ERROR] in boot_add_pte() function\n");
341            _puts("the length of the ptab vobj is too small\n");
342
343            _puts(" max_pt2 = ");
344            _putd( max_pt2 );
345            _puts("\n");
346            _puts(" pt2_id  = ");
347            _putd( pt2_id );
348            _puts("\n");
349           
350            _exit();
351        }
352        else 
353        {
354            pt2_pbase = pt1_pbase + PT1_SIZE + PT2_SIZE * pt2_id;
355            ptd = PTE_V | PTE_T | (unsigned int) (pt2_pbase >> 12);
356            _physical_write( pt1_pbase + 4 * ix1, ptd);
357            _next_free_pt2[vspace_id] = pt2_id + 1;
358        }
359    }
360    else                       // valid PTD: compute PT2 base address
361    {
362        pt2_pbase = ((paddr_t)(ptd & 0x0FFFFFFF)) << 12;
363    }
364
365    // set PTE in PT2 : flags & PPN in two 32 bits words
366    pte_paddr = pt2_pbase + 8 * ix2;
367    _physical_write(pte_paddr    , flags);
368    _physical_write(pte_paddr + 4, ppn);
369
370    if (verbose)
371    {
372        _puts(" / vpn = ");
373        _putx( vpn );
374        _puts(" / ix1 = ");
375        _putx( ix1 );
376        _puts(" / ix2 = ");
377        _putx( ix2 );
378        _puts(" / pt1_pbase = ");
379        _putl( pt1_pbase );
380        _puts(" / ptd = ");
381        _putl( ptd );
382        _puts(" / pt2_pbase = ");
383        _putl( pt2_pbase );
384        _puts(" / pte_paddr = ");
385        _putl( pte_paddr );
386        _puts(" / ppn = ");
387        _putx( ppn );
388        _puts("/\n");
389    }
390
391}   // end boot_add_pte()
392
393
394/////////////////////////////////////////////////////////////////////
395// This function build the page table for a given vspace.
396// The physical base addresses for all vsegs (global and private)
397// must have been previously computed and stored in the mapping.
398// It initializes the MWMR channels.
399/////////////////////////////////////////////////////////////////////
400void boot_vspace_pt_build(unsigned int vspace_id) 
401{
402    unsigned int vseg_id;
403    unsigned int npages;
404    unsigned int ppn;
405    unsigned int vpn;
406    unsigned int flags;
407    unsigned int page_id;
408    unsigned int verbose = 0;   // can be used to activate trace in add_pte()
409
410    mapping_header_t * header = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
411    mapping_vspace_t * vspace = _get_vspace_base(header);
412    mapping_vseg_t   * vseg   = _get_vseg_base(header);
413
414    // private segments
415    for (vseg_id = vspace[vspace_id].vseg_offset;
416         vseg_id < (vspace[vspace_id].vseg_offset + vspace[vspace_id].vsegs);
417         vseg_id++) 
418    {
419        vpn = vseg[vseg_id].vbase >> 12;
420        ppn = (unsigned int) (vseg[vseg_id].pbase >> 12);
421
422        npages = vseg[vseg_id].length >> 12;
423        if ((vseg[vseg_id].length & 0xFFF) != 0) npages++; 
424
425        flags = PTE_V;
426        if (vseg[vseg_id].mode & C_MODE_MASK) flags |= PTE_C;
427        if (vseg[vseg_id].mode & X_MODE_MASK) flags |= PTE_X;
428        if (vseg[vseg_id].mode & W_MODE_MASK) flags |= PTE_W;
429        if (vseg[vseg_id].mode & U_MODE_MASK) flags |= PTE_U;
430       
431        // These three flags (Local, Remote and Dirty) are set to 1 to reduce
432        // latency of TLB miss (L/R) and write (D): Avoid hardware update
433        // mechanism for these flags. This optimization can be performed
434        // because GIET_VM does nothing with these flags.
435
436        flags |= PTE_L;
437        flags |= PTE_R;
438        flags |= PTE_D;
439
440#if BOOT_DEBUG_PT
441        _puts(vseg[vseg_id].name);
442        _puts(" : flags = ");
443        _putx(flags);
444        _puts(" / npages = ");
445        _putd(npages);
446        _puts(" / pbase = ");
447        _putl(vseg[vseg_id].pbase);
448        _puts("\n");
449#endif
450        // loop on 4K pages
451        for (page_id = 0; page_id < npages; page_id++) 
452        {
453            boot_add_pte(vspace_id, vpn, flags, ppn, verbose);
454            vpn++;
455            ppn++;
456        }
457    }
458
459    // global segments
460    for (vseg_id = 0; vseg_id < header->globals; vseg_id++) 
461    {
462        vpn = vseg[vseg_id].vbase >> 12;
463        ppn = (unsigned int)(vseg[vseg_id].pbase >> 12);
464        npages = vseg[vseg_id].length >> 12;
465        if ((vseg[vseg_id].length & 0xFFF) != 0) npages++;
466
467        flags = PTE_V;
468        if (vseg[vseg_id].mode & C_MODE_MASK) flags |= PTE_C;
469        if (vseg[vseg_id].mode & X_MODE_MASK) flags |= PTE_X;
470        if (vseg[vseg_id].mode & W_MODE_MASK) flags |= PTE_W;
471        if (vseg[vseg_id].mode & U_MODE_MASK) flags |= PTE_U;
472
473        // Flags set for optimization (as explained above)
474
475        flags |= PTE_L;
476        flags |= PTE_R;
477        flags |= PTE_D;
478
479#if BOOT_DEBUG_PT
480        _puts(vseg[vseg_id].name);
481        _puts(" : flags = ");
482        _putx(flags);
483        _puts(" / npages = ");
484        _putd(npages);
485        _puts(" / pbase = ");
486        _putl(vseg[vseg_id].pbase);
487        _puts("\n");
488#endif
489        // loop on 4K pages
490        for (page_id = 0; page_id < npages; page_id++) 
491        {
492            boot_add_pte(vspace_id, vpn, flags, ppn, verbose);
493            vpn++;
494            ppn++;
495        }
496    }
497}   // end boot_vspace_pt_build()
498
499
500///////////////////////////////////////////////////////////////////////////
501// Align the value of paddr or vaddr to the required alignement,
502// defined by alignPow2 == L2(alignement).
503///////////////////////////////////////////////////////////////////////////
504paddr_t paddr_align_to(paddr_t paddr, unsigned int alignPow2) 
505{
506    paddr_t mask = (1 << alignPow2) - 1;
507    return ((paddr + mask) & ~mask);
508}
509
510unsigned int vaddr_align_to(unsigned int vaddr, unsigned int alignPow2) 
511{
512    unsigned int mask = (1 << alignPow2) - 1;
513    return ((vaddr + mask) & ~mask);
514}
515
516///////////////////////////////////////////////////////////////////////////
517// Set pbase for a vseg when identity mapping is required.
518// The length of the vseg must be known.
519// The ordered linked list of vsegs mapped on pseg must be updated,
520// and overlap with previously mapped vsegs must be checked.
521///////////////////////////////////////////////////////////////////////////
522void boot_vseg_set_paddr_ident(mapping_vseg_t * vseg) 
523{
524    // checking vseg not already mapped
525    if (vseg->mapped != 0) 
526    {
527        _puts("\n[BOOT ERROR] in boot_vseg_set_paddr_ident() : vseg ");
528        _puts( vseg->name );
529        _puts(" already mapped\n");
530        _exit();
531    }
532
533    // computes selected pseg pointer
534    mapping_pseg_t* pseg = boot_pseg_get( vseg->psegid );
535
536    // computes vseg alignment constraint
537    mapping_header_t* header    = (mapping_header_t*)SEG_BOOT_MAPPING_BASE;
538    mapping_vobj_t*   vobj_base = _get_vobj_base( header );
539    unsigned int      align     = vobj_base[vseg->vobj_offset].align;
540    if ( vobj_base[vseg->vobj_offset].align < 12 ) align = 12;
541
542    // computes required_pbase for identity mapping,
543    paddr_t required_pbase = (paddr_t)vseg->vbase;
544
545    // checks identity constraint against alignment constraint
546    if ( paddr_align_to( required_pbase, align) != required_pbase )
547    {
548        _puts("\n[BOOT ERROR] in boot_vseg_set_paddr_ident() : vseg ");
549        _puts( vseg->name );
550        _puts(" has uncompatible identity and alignment constraints\n");
551        _exit();
552    }
553
554    // We are looking for a contiguous space in target pseg.
555    // If there is vsegs already mapped, we scan the vsegs list to:
556    // - check overlap with already mapped vsegs,
557    // - try mapping in holes between already mapped vsegs,
558    // - update the ordered linked list if success
559    // We don't enter the loop if no vsegs is already mapped.
560    // implementation note: The next_vseg field is unsigned int,
561    // but we use it to store a MIP32 pointer on a vseg...
562
563    mapping_vseg_t*   curr      = 0;
564    mapping_vseg_t*   prev      = 0;
565    unsigned int      min_pbase = pseg->base;
566
567    for ( curr = (mapping_vseg_t*)pseg->next_vseg ; 
568          (curr != 0) && (vseg->mapped == 0) ; 
569          curr = (mapping_vseg_t*)curr->next_vseg )
570    {
571        // looking before current vseg
572        if( (required_pbase >= min_pbase) && 
573            (curr->pbase >= (required_pbase + vseg->length)) ) // space found
574        {
575            vseg->pbase  = required_pbase;
576            vseg->mapped = 1;
577
578            // update linked list
579            vseg->next_vseg = (unsigned int)curr;
580            if( curr == (mapping_vseg_t*)pseg->next_vseg ) 
581                pseg->next_vseg = (unsigned int)vseg;
582            else
583                prev->next_vseg = (unsigned int)vseg;
584        }
585        else                                         // looking in space after curr
586        {
587            prev = curr;
588            min_pbase = curr->pbase + curr->length;
589        }
590    }
591
592    // no success in the loop
593    if( (vseg->mapped == 0) &&
594        (required_pbase >= min_pbase) && 
595        ((required_pbase + vseg->length) <= (pseg->base + pseg->length)) )
596    {
597        vseg->pbase  = required_pbase;
598        vseg->mapped = 1;
599
600        // update linked list
601        vseg->next_vseg = 0;
602        if ((curr == 0) && (prev == 0)) pseg->next_vseg = (unsigned int)vseg;
603        else                            prev->next_vseg = (unsigned int)vseg;
604    }
605
606    if( vseg->mapped == 0 )
607    {
608        _puts("\n[BOOT ERROR] in boot_vseg_set_paddr_ident() : vseg ");
609        _puts( vseg->name );
610        _puts(" cannot be mapped on pseg ");
611        _puts( pseg->name );
612        _puts("\n");
613        _exit();
614    }
615}  // end boot_vseg_set_paddr_ident()
616
617               
618////////////////////////////////////////////////////////////////////////////
619// Set pbase for a vseg when there is no identity mapping constraint.
620// This is the physical memory allocator (written by Q.Meunier).
621// The length of the vseg must be known.
622// All identity mapping vsegs must be already mapped.
623// We use a linked list of already mapped vsegs, ordered by incresing pbase.
624// We try to place the vseg in the "first fit" hole in this list.
625////////////////////////////////////////////////////////////////////////////
626void boot_vseg_set_paddr(mapping_vseg_t * vseg) 
627{
628    // checking vseg not already mapped
629    if ( vseg->mapped != 0 ) 
630    {
631        _puts("\n[BOOT ERROR] in boot_vseg_set_paddr() : vseg ");
632        _puts( vseg->name );
633        _puts(" already mapped\n");
634        _exit();
635    }
636
637    // computes selected pseg pointer
638    mapping_pseg_t*   pseg      = boot_pseg_get( vseg->psegid );
639
640    // computes vseg alignment constraint
641    mapping_header_t* header    = (mapping_header_t*)SEG_BOOT_MAPPING_BASE;
642    mapping_vobj_t*   vobj_base = _get_vobj_base( header );
643    unsigned int      align     = vobj_base[vseg->vobj_offset].align;
644    if ( vobj_base[vseg->vobj_offset].align < 12 ) align = 12;
645
646    // initialise physical base address, with alignment constraint
647    paddr_t possible_pbase = paddr_align_to( pseg->base, align );
648
649    // We are looking for a contiguous space in target pseg
650    // If there is vsegs already mapped, we scan the vsegs list to:
651    // - try mapping in holes between already mapped vsegs,
652    // - update the ordered linked list if success
653    // We don't enter the loop if no vsegs is already mapped.
654    // implementation note: The next_vseg field is unsigned int,
655    // but we use it to store a MIP32 pointer on a vseg...
656
657    mapping_vseg_t*   curr = 0;
658    mapping_vseg_t*   prev = 0;
659
660    for( curr = (mapping_vseg_t*)pseg->next_vseg ; 
661         (curr != 0) && (vseg->mapped == 0) ; 
662         curr = (mapping_vseg_t*)curr->next_vseg )
663    {
664        // looking for space before current vseg
665        if ( (curr->pbase >= possible_pbase + vseg->length) ) // space before curr
666        {
667            vseg->pbase  = possible_pbase;
668            vseg->mapped = 1;
669
670            // update linked list
671            vseg->next_vseg = (unsigned int)curr;
672            if( curr == (mapping_vseg_t*)pseg->next_vseg ) 
673                pseg->next_vseg = (unsigned int)vseg;
674            else
675                prev->next_vseg = (unsigned int)vseg;
676        }
677        else                                            // looking for space after curr
678        {
679            possible_pbase = paddr_align_to( curr->pbase + curr->length, align );
680            prev           = curr;
681        }
682    }
683       
684    // when no space found, try to allocate space after already mapped vsegs
685    if( (vseg->mapped == 0) &&
686        ((possible_pbase + vseg->length) <= (pseg->base + pseg->length)) )
687    {
688        vseg->pbase  = possible_pbase;
689        vseg->mapped = 1;
690
691        // update linked list
692        vseg->next_vseg = 0;
693        if ((curr == 0 ) && (prev == 0)) pseg->next_vseg = (unsigned int)vseg;
694        else                             prev->next_vseg = (unsigned int)vseg;
695    }
696
697    if( vseg->mapped == 0 )
698    {
699        _puts("\n[BOOT ERROR] in boot_vseg_set_paddr() : vseg ");
700        _puts( vseg->name );
701        _puts(" cannot be mapped on pseg ");
702        _puts( pseg->name );
703        _puts(" in cluster[");
704        _putd( pseg->clusterid );
705        _puts("]\n");
706        _exit();
707    }
708}  // end boot_vseg_set_paddr()
709
710///////////////////////////////////////////////////////////////////////////
711// This function computes the physical base address for a vseg
712// as specified in the mapping info data structure.
713// It updates the pbase and the length fields of the vseg.
714// It updates the pbase and vbase fields of all vobjs in the vseg.
715// It updates the _ptabs_paddr[] and _ptabs_vaddr[] arrays.
716// It is a global vseg if vspace_id = (-1).
717///////////////////////////////////////////////////////////////////////////
718void boot_vseg_map(mapping_vseg_t * vseg, unsigned int vspace_id) 
719{
720    unsigned int vobj_id;
721    unsigned int cur_vaddr;
722    paddr_t      cur_paddr;
723    paddr_t      cur_length;
724    unsigned int offset;
725
726    mapping_header_t * header = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
727    mapping_vobj_t   * vobj   = _get_vobj_base(header);
728
729    // loop on the vobjs contained in vseg to compute
730    // the vseg length, required for mapping.
731    cur_length = 0;
732    for ( vobj_id = vseg->vobj_offset; 
733          vobj_id < (vseg->vobj_offset + vseg->vobjs); 
734          vobj_id++ ) 
735    {
736        if (vobj[vobj_id].align)
737        {
738            cur_length = vaddr_align_to(cur_length, vobj[vobj_id].align);
739        }
740        cur_length += vobj[vobj_id].length;
741    }
742    vseg->length = paddr_align_to(cur_length, 12);
743
744    // mapping: computes vseg pbase address
745    if (vseg->ident != 0)                         // identity mapping
746    {
747        boot_vseg_set_paddr_ident( vseg );
748    }
749    else                                          // unconstrained mapping
750    {
751        boot_vseg_set_paddr( vseg );
752    }
753
754    // second loop on vobjs contained in vseg to :
755    // initialize the vaddr and paddr fields of all vobjs,
756    // and initialize the page table pointers arrays
757
758    cur_vaddr = vseg->vbase;
759    cur_paddr = vseg->pbase;
760
761    for (vobj_id = vseg->vobj_offset; 
762         vobj_id < (vseg->vobj_offset + vseg->vobjs); vobj_id++) 
763    {
764        if (vobj[vobj_id].align) 
765        {
766            cur_paddr = paddr_align_to(cur_paddr, vobj[vobj_id].align);
767            cur_vaddr = vaddr_align_to(cur_vaddr, vobj[vobj_id].align);
768        }
769        // set vaddr/paddr for current vobj
770        vobj[vobj_id].vaddr = cur_vaddr;
771        vobj[vobj_id].paddr = cur_paddr;
772       
773        // initialize _ptabs_vaddr[] & boot_ptabs-paddr[] if PTAB
774        if (vobj[vobj_id].type == VOBJ_TYPE_PTAB) 
775        {
776            if (vspace_id == ((unsigned int) -1))    // global vseg
777            {
778                _puts("\n[BOOT ERROR] in boot_vseg_map() function: ");
779                _puts("a PTAB vobj cannot be global");
780                _exit();
781            }
782            // we need at least one PT2
783            if (vobj[vobj_id].length < (PT1_SIZE + PT2_SIZE)) 
784            {
785                _puts("\n[BOOT ERROR] in boot_vseg_map() function, ");
786                _puts("PTAB too small, minumum size is: ");
787                _putx(PT1_SIZE + PT2_SIZE);
788                _exit();
789            }
790            // register both physical and virtual page table address
791            _ptabs_vaddr[vspace_id] = vobj[vobj_id].vaddr;
792            _ptabs_paddr[vspace_id] = vobj[vobj_id].paddr;
793           
794            // reset all valid bits in PT1
795            for ( offset = 0 ; offset < 8192 ; offset = offset + 4)
796            {
797                _physical_write(cur_paddr + offset, 0);
798            }
799
800            // computing the number of second level pages
801            _max_pt2[vspace_id] = (vobj[vobj_id].length - PT1_SIZE) / PT2_SIZE;
802        }
803
804        // set next vaddr/paddr
805        cur_vaddr = cur_vaddr + vobj[vobj_id].length;
806        cur_paddr = cur_paddr + vobj[vobj_id].length;
807    } // end for vobjs
808
809}    // end boot_vseg_map()
810
811/////////////////////////////////////////////////////////////////////
812// This function builds the page tables for all virtual spaces
813// defined in the mapping_info data structure, in three steps:
814// - step 1 : It computes the physical base address for global vsegs
815//            and for all associated vobjs.
816// - step 2 : It computes the physical base address for all private
817//            vsegs and all vobjs in each virtual space.
818// - step 3 : It actually fill the page table for each vspace.
819//
820// Note: It must exist at least one vspace in the mapping_info...
821/////////////////////////////////////////////////////////////////////
822void boot_pt_init() 
823{
824    mapping_header_t * header = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
825    mapping_vspace_t * vspace = _get_vspace_base(header);
826    mapping_vseg_t   * vseg   = _get_vseg_base(header);
827
828    unsigned int vspace_id;
829    unsigned int vseg_id;
830
831    if (header->vspaces == 0 )
832    {
833        _puts("\n[BOOT ERROR] in boot_pt_init() : mapping ");
834        _puts( header->name );
835        _puts(" contains no vspace\n");
836        _exit();
837    }
838
839#if BOOT_DEBUG_PT
840_puts("\n[BOOT DEBUG] ****** mapping global vsegs ******\n");
841#endif
842
843    // step 1 : loop on global vsegs
844
845    // vsegs with identity mapping constraint first
846    for (vseg_id = 0; vseg_id < header->globals; vseg_id++) 
847    {
848        if (vseg[vseg_id].ident == 1) 
849            boot_vseg_map(&vseg[vseg_id], ((unsigned int) (-1)));
850    }
851
852    // unconstrained vsegs second
853    for (vseg_id = 0; vseg_id < header->globals; vseg_id++) 
854    {
855        if (vseg[vseg_id].ident == 0) 
856            boot_vseg_map(&vseg[vseg_id], ((unsigned int) (-1)));
857    }
858
859    // step 2 : loop on virtual vspaces to map private vsegs
860    for (vspace_id = 0; vspace_id < header->vspaces; vspace_id++) 
861    {
862
863#if BOOT_DEBUG_PT
864_puts("\n[BOOT DEBUG] ****** mapping private vsegs in vspace ");
865_puts(vspace[vspace_id].name);
866_puts(" ******\n");
867#endif
868
869        // vsegs with identity mapping constraint first
870        for (vseg_id = vspace[vspace_id].vseg_offset;
871             vseg_id < (vspace[vspace_id].vseg_offset + vspace[vspace_id].vsegs);
872             vseg_id++) 
873        {
874            if (vseg[vseg_id].ident == 1) 
875                boot_vseg_map(&vseg[vseg_id], vspace_id);
876        }
877        // unconstrained vsegs second
878        for (vseg_id = vspace[vspace_id].vseg_offset;
879             vseg_id < (vspace[vspace_id].vseg_offset + vspace[vspace_id].vsegs);
880             vseg_id++) 
881        {
882            if (vseg[vseg_id].ident == 0)
883                boot_vseg_map(&vseg[vseg_id], vspace_id);
884        }
885    }
886
887#if BOOT_DEBUG_PT
888mapping_vseg_t*    curr;
889mapping_pseg_t*    pseg    = _get_pseg_base(header);
890mapping_cluster_t* cluster = _get_cluster_base(header);
891unsigned int       pseg_id;
892for( pseg_id = 0 ; pseg_id < header->psegs ; pseg_id++ )
893{
894    unsigned int cluster_id = pseg[pseg_id].clusterid;
895    _puts("\n[BOOT DEBUG] ****** vsegs mapped on pseg ");
896    _puts( pseg[pseg_id].name );
897    _puts(" in cluster[");
898    _putd( cluster[cluster_id].x );
899    _puts(",");
900    _putd( cluster[cluster_id].y );
901    _puts("] ******\n");
902    for( curr = (mapping_vseg_t*)pseg[pseg_id].next_vseg ;
903         curr != 0 ;
904         curr = (mapping_vseg_t*)curr->next_vseg )
905    {
906        _puts(" - vseg ");
907        _puts( curr->name );
908        _puts(" : len = ");
909        _putx( curr->length );
910        _puts(" / vbase ");
911        _putx( curr->vbase );
912        _puts(" / pbase ");
913        _putl( curr->pbase );
914        _puts("\n");
915    } 
916}
917#endif
918
919    // step 3 : loop on the vspaces to build the page tables
920    for (vspace_id = 0; vspace_id < header->vspaces; vspace_id++) 
921    {
922
923#if BOOT_DEBUG_PT
924_puts("\n[BOOT DEBUG] ****** building page table for vspace ");
925_puts(vspace[vspace_id].name);
926_puts(" ******\n");
927#endif
928
929        boot_vspace_pt_build(vspace_id);
930
931        _puts("\n[BOOT] Page Table for vspace \"");
932        _puts( vspace[vspace_id].name );
933        _puts("\" completed at cycle ");
934        _putd( _get_proctime() );
935        _puts("\n");
936
937#if BOOT_DEBUG_PT
938_puts("  vaddr = ");
939_putx( _ptabs_vaddr[vspace_id] );
940_puts(" / paddr = ");
941_putl( _ptabs_paddr[vspace_id] );
942_puts(" / PT2 number = ");
943_putd( _max_pt2[vspace_id] );
944_puts("\n");
945#endif
946    }
947} // end boot_pt_init()
948
949///////////////////////////////////////////////////////////////////////////////
950// This function initializes all private vobjs defined in the vspaces,
951// such as mwmr channels, barriers and locks, because these vobjs
952// are not known, and not initialized by the compiler.
953// The MMU is supposed to be activated...
954///////////////////////////////////////////////////////////////////////////////
955void boot_vobjs_init() 
956{
957    mapping_header_t* header = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
958    mapping_vspace_t* vspace = _get_vspace_base(header);
959    mapping_vobj_t* vobj     = _get_vobj_base(header);
960
961    unsigned int vspace_id;
962    unsigned int vobj_id;
963
964    // loop on the vspaces
965    for (vspace_id = 0; vspace_id < header->vspaces; vspace_id++) 
966    {
967
968#if BOOT_DEBUG_VOBJS
969_puts("\n[BOOT DEBUG] ****** vobjs initialisation in vspace ");
970_puts(vspace[vspace_id].name);
971_puts(" ******\n");
972#endif
973
974        _set_mmu_ptpr( (unsigned int)(_ptabs_paddr[vspace_id] >> 13) );
975
976        unsigned int ptab_found = 0;
977
978        // loop on the vobjs
979        for (vobj_id = vspace[vspace_id].vobj_offset;
980             vobj_id < (vspace[vspace_id].vobj_offset + vspace[vspace_id].vobjs);
981             vobj_id++) 
982        {
983            switch (vobj[vobj_id].type) 
984            {
985                case VOBJ_TYPE_MWMR:    // storage capacity is (vobj.length/4 - 5) words
986                {
987#if BOOT_DEBUG_VOBJS
988_puts("MWMR    : ");
989_puts(vobj[vobj_id].name);
990_puts(" / vaddr = ");
991_putx(vobj[vobj_id].vaddr);
992_puts(" / paddr = ");
993_putl(vobj[vobj_id].paddr);
994_puts(" / length = ");
995_putx(vobj[vobj_id].length);
996_puts("\n");
997#endif
998                    mwmr_channel_t* mwmr = (mwmr_channel_t *) (vobj[vobj_id].vaddr);
999                    mwmr->ptw = 0;
1000                    mwmr->ptr = 0;
1001                    mwmr->sts = 0;
1002                    mwmr->width = vobj[vobj_id].init;
1003                    mwmr->depth = (vobj[vobj_id].length >> 2) - 6;
1004                    mwmr->lock = 0;
1005#if BOOT_DEBUG_VOBJS
1006_puts("          fifo depth = ");
1007_putd(mwmr->depth);
1008_puts(" / width = ");
1009_putd(mwmr->width);
1010_puts("\n");
1011#endif
1012                    break;
1013                }
1014                case VOBJ_TYPE_ELF:    // initialisation done by the loader
1015                {
1016#if BOOT_DEBUG_VOBJS
1017_puts("ELF     : ");
1018_puts(vobj[vobj_id].name);
1019_puts(" / vaddr = ");
1020_putx(vobj[vobj_id].vaddr);
1021_puts(" / paddr = ");
1022_putl(vobj[vobj_id].paddr);
1023_puts(" / length = ");
1024_putx(vobj[vobj_id].length);
1025_puts("\n");
1026#endif
1027                    break;
1028                }
1029                case VOBJ_TYPE_BLOB:    // initialisation done by the loader
1030                {
1031#if BOOT_DEBUG_VOBJS
1032_puts("BLOB    : ");
1033_puts(vobj[vobj_id].name);
1034_puts(" / vaddr = ");
1035_putx(vobj[vobj_id].vaddr);
1036_puts(" / paddr = ");
1037_putl(vobj[vobj_id].paddr);
1038_puts(" / length = ");
1039_putx(vobj[vobj_id].length);
1040_puts("\n");
1041#endif
1042                    break;
1043                }
1044                case VOBJ_TYPE_BARRIER:    // init is the number of participants
1045                {
1046#if BOOT_DEBUG_VOBJS
1047_puts("BARRIER : ");
1048_puts(vobj[vobj_id].name);
1049_puts(" / vaddr = ");
1050_putx(vobj[vobj_id].vaddr);
1051_puts(" / paddr = ");
1052_putl(vobj[vobj_id].paddr);
1053_puts(" / length = ");
1054_putx(vobj[vobj_id].length);
1055_puts("\n");
1056#endif
1057                    giet_barrier_t* barrier = (giet_barrier_t *) (vobj[vobj_id].vaddr);
1058                    barrier->count = vobj[vobj_id].init;
1059                    barrier->init = vobj[vobj_id].init;
1060#if BOOT_DEBUG_VOBJS
1061_puts("          init_value = ");
1062_putd(barrier->init);
1063_puts("\n");
1064#endif
1065                    break;
1066                }
1067                case VOBJ_TYPE_LOCK:    // init value is "not taken"
1068                {
1069#if BOOT_DEBUG_VOBJS
1070_puts("LOCK    : ");
1071_puts(vobj[vobj_id].name);
1072_puts(" / vaddr = ");
1073_putx(vobj[vobj_id].vaddr);
1074_puts(" / paddr = ");
1075_putl(vobj[vobj_id].paddr);
1076_puts(" / length = ");
1077_putx(vobj[vobj_id].length);
1078_puts("\n");
1079#endif
1080                    unsigned int* lock = (unsigned int *) (vobj[vobj_id].vaddr);
1081                    *lock = 0;
1082                    break;
1083                }
1084                case VOBJ_TYPE_BUFFER:    // nothing to initialise
1085                {
1086#if BOOT_DEBUG_VOBJS
1087_puts("BUFFER  : ");
1088_puts(vobj[vobj_id].name);
1089_puts(" / vaddr = ");
1090_putx(vobj[vobj_id].vaddr);
1091_puts(" / paddr = ");
1092_putl(vobj[vobj_id].paddr);
1093_puts(" / length = ");
1094_putx(vobj[vobj_id].length);
1095_puts("\n");
1096#endif
1097                    break;
1098                }
1099                case VOBJ_TYPE_MEMSPACE:
1100                {
1101#if BOOT_DEBUG_VOBJS
1102_puts("MEMSPACE  : ");
1103_puts(vobj[vobj_id].name);
1104_puts(" / vaddr = ");
1105_putx(vobj[vobj_id].vaddr);
1106_puts(" / paddr = ");
1107_putl(vobj[vobj_id].paddr);
1108_puts(" / length = ");
1109_putx(vobj[vobj_id].length);
1110_puts("\n");
1111#endif
1112                    giet_memspace_t* memspace = (giet_memspace_t *) vobj[vobj_id].vaddr;
1113                    memspace->buffer = (void *) vobj[vobj_id].vaddr + 8;
1114                    memspace->size = vobj[vobj_id].length - 8;
1115#if BOOT_DEBUG_VOBJS
1116_puts("          buffer vbase = ");
1117_putx((unsigned int)memspace->buffer);
1118_puts(" / size = ");
1119_putx(memspace->size);
1120_puts("\n");
1121#endif
1122                    break;
1123                }
1124                case VOBJ_TYPE_PTAB:    // nothing to initialize
1125                {
1126#if BOOT_DEBUG_VOBJS
1127_puts("PTAB    : ");
1128_puts(vobj[vobj_id].name);
1129_puts(" / vaddr = ");
1130_putx(vobj[vobj_id].vaddr);
1131_puts(" / paddr = ");
1132_putl(vobj[vobj_id].paddr);
1133_puts(" / length = ");
1134_putx(vobj[vobj_id].length);
1135_puts("\n");
1136#endif
1137                    ptab_found = 1;
1138                    break;
1139                }
1140                case VOBJ_TYPE_CONST:
1141                {
1142#if BOOT_DEBUG_VOBJS
1143_puts("CONST   : ");
1144_puts(vobj[vobj_id].name);
1145_puts(" / vaddr = ");
1146_putx(vobj[vobj_id].vaddr);
1147_puts(" / paddr = ");
1148_putl(vobj[vobj_id].paddr);
1149_puts(" / length = ");
1150_putx(vobj[vobj_id].length);
1151_puts(" / init = ");
1152_putx(vobj[vobj_id].init);
1153_puts("\n");
1154#endif
1155                    unsigned int* addr = (unsigned int *) vobj[vobj_id].vaddr;
1156                    *addr = vobj[vobj_id].init;
1157
1158#if BOOT_DEBUG_VOBJS
1159_puts("          init = ");
1160_putx(*addr);
1161_puts("\n");
1162#endif
1163                    break;
1164                }
1165                default:
1166                {
1167                    _puts("\n[BOOT ERROR] illegal vobj type: ");
1168                    _putd(vobj[vobj_id].type);
1169                    _puts("\n");
1170                    _exit();
1171                }
1172            }            // end switch type
1173        }            // end loop on vobjs
1174        if (ptab_found == 0) 
1175        {
1176            _puts("\n[BOOT ERROR] Missing PTAB for vspace ");
1177            _putd(vspace_id);
1178            _exit();
1179        }
1180    } // end loop on vspaces
1181
1182} // end boot_vobjs_init()
1183
1184///////////////////////////////////////////////////////////////////////////////
1185// This function returns in the vbase and length buffers the virtual base
1186// address and the length of the  segment allocated to the schedulers array
1187// in the cluster defined by the clusterid argument.
1188///////////////////////////////////////////////////////////////////////////////
1189void boot_get_sched_vaddr( unsigned int  cluster_id,
1190                           unsigned int* vbase, 
1191                           unsigned int* length )
1192{
1193    mapping_header_t* header = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
1194    mapping_vobj_t*   vobj   = _get_vobj_base(header);
1195    mapping_vseg_t*   vseg   = _get_vseg_base(header);
1196    mapping_pseg_t*   pseg   = _get_pseg_base(header);
1197
1198    unsigned int vseg_id;
1199    unsigned int found = 0;
1200
1201    for ( vseg_id = 0 ; (vseg_id < header->vsegs) && (found == 0) ; vseg_id++ )
1202    {
1203        if ( (vobj[vseg[vseg_id].vobj_offset].type == VOBJ_TYPE_SCHED) && 
1204             (pseg[vseg[vseg_id].psegid].clusterid == cluster_id ) )
1205        {
1206            *vbase  = vseg[vseg_id].vbase;
1207            *length = vobj[vseg[vseg_id].vobj_offset].length;
1208            found = 1;
1209        }
1210    }
1211    if ( found == 0 )
1212    {
1213        mapping_cluster_t* cluster = _get_cluster_base(header);
1214        _puts("\n[BOOT ERROR] No vobj of type SCHED in cluster [");
1215        _putd( cluster[cluster_id].x );
1216        _puts(",");
1217        _putd( cluster[cluster_id].y );
1218        _puts("]\n");
1219        _exit();
1220    }
1221} // end boot_get_sched_vaddr()
1222
1223////////////////////////////////////////////////////////////////////////////////////
1224// This function initialises all processors schedulers.
1225// This is done by processor 0, and the MMU must be activated.
1226// - In Step 1, it initialises the _schedulers[gpid] pointers array, and scan
1227//              the processors to initialise the schedulers, including the
1228//              idle_task context (ltid == 14) and HWI / SWI / PTI vectors.
1229// - In Step 2, it scan all tasks in all vspaces to complete the tasks contexts,
1230//              initialisation as specified in the mapping_info data structure.
1231////////////////////////////////////////////////////////////////////////////////////
1232void boot_schedulers_init() 
1233{
1234    mapping_header_t*  header  = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
1235    mapping_cluster_t* cluster = _get_cluster_base(header);
1236    mapping_vspace_t*  vspace  = _get_vspace_base(header);
1237    mapping_task_t*    task    = _get_task_base(header);
1238    mapping_vobj_t*    vobj    = _get_vobj_base(header);
1239    mapping_periph_t*  periph  = _get_periph_base(header);
1240    mapping_irq_t*     irq     = _get_irq_base(header);
1241
1242    unsigned int cluster_id;    // cluster index in mapping_info
1243    unsigned int periph_id;     // peripheral index in mapping_info
1244    unsigned int irq_id;        // irq index in mapping_info
1245    unsigned int vspace_id;     // vspace index in mapping_info
1246    unsigned int task_id;       // task index in mapping_info
1247    unsigned int vobj_id;       // vobj index in mapping_info
1248
1249    unsigned int lpid;          // local processor index (for several loops)
1250
1251    // TTY, NIC, CMA, HBA, user timer, and WTI channel allocators to user tasks:
1252    // - TTY[0] is reserved for the kernel
1253    // - In all clusters the first NB_PROCS_MAX timers
1254    //   are reserved for the kernel (context switch)
1255    unsigned int alloc_tty_channel = 1;              // global
1256    unsigned int alloc_nic_channel = 0;              // global
1257    unsigned int alloc_cma_channel = 0;              // global
1258    unsigned int alloc_hba_channel = 0;              // global
1259    unsigned int alloc_tim_channel[X_SIZE*Y_SIZE];   // one per cluster
1260
1261    // WTI allocators to processors
1262    // In all clusters, first NB_PROCS_MAX WTIs are for WAKUP
1263    unsigned int alloc_wti_channel[X_SIZE*Y_SIZE];   // one per cluster
1264
1265    // pointers on the XCU and PIC peripherals
1266    mapping_periph_t*  xcu = NULL;
1267    mapping_periph_t*  pic = NULL;
1268
1269    // schedulers array base address in a cluster
1270    unsigned int          sched_vbase; 
1271    unsigned int          sched_length; 
1272    static_scheduler_t*   psched; 
1273
1274    /////////////////////////////////////////////////////////////////////////
1275    // Step 1 : loop on the clusters and on the processors
1276    //          to initialize the schedulers[] array of pointers,
1277    //          and the interrupt vectors.
1278    // Implementation note:
1279    // We need to use both proc_id to scan the mapping info structure,
1280    // and lpid to access the schedulers array.
1281    // - the _schedulers[] array of pointers can contain "holes", because
1282    //   it is indexed by the global pid = cluster_xy*NB_PROCS_MAX + lpid
1283    // - the mapping info array of processors is contiguous, it is indexed
1284    //   by proc_id, and use an offset specific in each cluster.
1285
1286    for (cluster_id = 0 ; cluster_id < X_SIZE*Y_SIZE ; cluster_id++) 
1287    {
1288        unsigned int x          = cluster[cluster_id].x;
1289        unsigned int y          = cluster[cluster_id].y;
1290        unsigned int cluster_xy = (x<<Y_WIDTH) + y;
1291
1292#if BOOT_DEBUG_SCHED
1293_puts("\n[BOOT DEBUG] Initialise schedulers in cluster[");
1294_putd( x );
1295_puts(",");
1296_putd( y );
1297_puts("]\n");
1298#endif
1299        alloc_tim_channel[cluster_id] = NB_PROCS_MAX;
1300        alloc_wti_channel[cluster_id] = NB_PROCS_MAX;
1301
1302        // checking processors number
1303        if ( cluster[cluster_id].procs > NB_PROCS_MAX )
1304        {
1305            _puts("\n[BOOT ERROR] Too much processors in cluster[");
1306            _putd( x );
1307            _puts(",");
1308            _putd( y );
1309            _puts("]\n");
1310            _exit();
1311        }
1312 
1313        // no schedulers initialisation if nprocs == 0
1314        if ( cluster[cluster_id].procs > 0 )
1315        {
1316            // get scheduler array virtual base address from mapping
1317            boot_get_sched_vaddr( cluster_id, &sched_vbase, &sched_length );
1318
1319            if ( sched_length < (cluster[cluster_id].procs<<12) ) // 4 Kbytes per scheduler
1320            {
1321                _puts("\n[BOOT ERROR] Schedulers segment too small in cluster[");
1322                _putd( x );
1323                _puts(",");
1324                _putd( y );
1325                _puts("]\n");
1326                _exit();
1327            }
1328
1329            psched = (static_scheduler_t*)sched_vbase;
1330
1331            // scan peripherals to find the ICU/XCU and the PIC component
1332
1333            xcu = NULL; 
1334            for ( periph_id = cluster[cluster_id].periph_offset ;
1335                  periph_id < cluster[cluster_id].periph_offset + cluster[cluster_id].periphs;
1336                  periph_id++ )
1337            {
1338                if( (periph[periph_id].type == PERIPH_TYPE_XCU) || 
1339                    (periph[periph_id].type == PERIPH_TYPE_ICU) )
1340                {
1341                    xcu = &periph[periph_id];
1342
1343                    if ( xcu->arg < cluster[cluster_id].procs )
1344                    {
1345                        _puts("\n[BOOT ERROR] Not enough inputs for XCU[");
1346                        _putd( x );
1347                        _puts(",");
1348                        _putd( y );
1349                        _puts("]\n");
1350                        _exit();
1351                    }
1352                }
1353                if( periph[periph_id].type == PERIPH_TYPE_PIC )   
1354                {
1355                    pic = &periph[periph_id];
1356                }
1357            } 
1358            if ( xcu == NULL )
1359            {         
1360                _puts("\n[BOOT ERROR] No ICU / XCU component in cluster[");
1361                _putd( x );
1362                _puts(",");
1363                _putd( y );
1364                _puts("]\n");
1365                _exit();
1366            }
1367
1368            // loop on processors for sechedulers default values
1369            // initialisation, including WTI and PTI vectors
1370            for ( lpid = 0 ; lpid < cluster[cluster_id].procs ; lpid++ )
1371            {
1372                // set the schedulers pointers array
1373                _schedulers[cluster_xy * NB_PROCS_MAX + lpid] = 
1374                      (static_scheduler_t*)&psched[lpid];
1375
1376#if BOOT_DEBUG_SCHED
1377_puts("\nProc[");
1378_putd( x );
1379_puts(",");
1380_putd( y );
1381_puts(",");
1382_putd( lpid );
1383_puts("] : scheduler virtual base address = ");
1384_putx( (unsigned int)&psched[lpid] );
1385_puts("\n");
1386#endif
1387                // initialise the "tasks" and "current" variables default values
1388                psched[lpid].tasks   = 0;
1389                psched[lpid].current = IDLE_TASK_INDEX;
1390
1391                // default values for HWI / PTI / SWI vectors (valid bit = 0)
1392                unsigned int slot;
1393                for (slot = 0; slot < 32; slot++)
1394                {
1395                    psched[lpid].hwi_vector[slot] = 0;
1396                    psched[lpid].pti_vector[slot] = 0;
1397                    psched[lpid].wti_vector[slot] = 0;
1398                }
1399
1400                // WTI[lpid] <= ISR_WAKUP / PTI[lpid] <= ISR_TICK
1401                psched[lpid].wti_vector[lpid] = ISR_WAKUP | 0x80000000;
1402                psched[lpid].pti_vector[lpid] = ISR_TICK  | 0x80000000;
1403
1404                // initializes the idle_task context in scheduler:
1405                // - the SR slot is 0xFF03 because this task run in kernel mode.
1406                // - it uses the page table of vspace[0]
1407                // - it uses the kernel TTY terminal
1408                // - slots containing addresses (SP, RA, EPC, PTAB, PTPR)
1409                //   must be re-initialised by kernel_parallel_init()
1410
1411                psched[lpid].context[IDLE_TASK_INDEX][CTX_CR_ID]    = 0;
1412                psched[lpid].context[IDLE_TASK_INDEX][CTX_SR_ID]    = 0xFF03;
1413                psched[lpid].context[IDLE_TASK_INDEX][CTX_PTPR_ID]  = _ptabs_paddr[0]>>13;
1414                psched[lpid].context[IDLE_TASK_INDEX][CTX_PTAB_ID]  = _ptabs_vaddr[0];
1415                psched[lpid].context[IDLE_TASK_INDEX][CTX_TTY_ID]   = 0;
1416                psched[lpid].context[IDLE_TASK_INDEX][CTX_LTID_ID]  = IDLE_TASK_INDEX;
1417                psched[lpid].context[IDLE_TASK_INDEX][CTX_VSID_ID]  = 0;
1418                psched[lpid].context[IDLE_TASK_INDEX][CTX_RUN_ID]   = 1;
1419            }  // end for processors
1420
1421            // scan HWIs connected to local XCU
1422            // for round-robin allocation to processors
1423            lpid = 0;
1424            for ( irq_id = xcu->irq_offset ;
1425                  irq_id < xcu->irq_offset + xcu->irqs ;
1426                  irq_id++ )
1427            {
1428                unsigned int type    = irq[irq_id].srctype;
1429                unsigned int srcid   = irq[irq_id].srcid;
1430                unsigned int isr     = irq[irq_id].isr & 0xFFFF;
1431                unsigned int channel = irq[irq_id].channel << 16;
1432
1433                if ( (type != IRQ_TYPE_HWI) || (srcid > 31) )
1434                {
1435                    _puts("\n[BOOT ERROR] Bad IRQ in XCU of cluster[");
1436                    _putd( x );
1437                    _puts(",");
1438                    _putd( y );
1439                    _puts("]\n");
1440                    _exit();
1441                }
1442
1443                psched[lpid].hwi_vector[srcid] = isr | channel | 0x80000000;
1444                lpid = (lpid + 1) % cluster[cluster_id].procs; 
1445
1446            } // end for irqs
1447        } // end if nprocs > 0
1448    } // end for clusters
1449
1450    // If there is an external PIC component, we scan HWIs connected to PIC
1451    // for Round Robin allocation (as WTI) to processors.
1452    // We allocate one WTI per processor, starting from proc[0,0,0],
1453    // and we increment (cluster_id, lpid) as required.
1454    if ( pic != NULL )
1455    {   
1456        unsigned int cluster_id = 0;   // index in clusters array
1457        unsigned int lpid       = 0;   // processor local index
1458
1459        // scan IRQS defined in PIC
1460        for ( irq_id = pic->irq_offset ;
1461              irq_id < pic->irq_offset + pic->irqs ;
1462              irq_id++ )
1463        {
1464            // compute next values for (cluster_id,lpid)
1465            // if no more procesor available in current cluster
1466            unsigned int overflow = 0;
1467            while ( (lpid >= cluster[cluster_id].procs) ||
1468                    (alloc_wti_channel[cluster_id] >= xcu->arg) )
1469            {
1470                overflow++;
1471                cluster_id = (cluster_id + 1) % (X_SIZE*Y_SIZE);
1472                lpid       = 0;
1473
1474                // overflow detection
1475                if ( overflow > (X_SIZE*Y_SIZE*NB_PROCS_MAX*32) )
1476                {
1477                    _puts("\n[BOOT ERROR] Not enough processors for external IRQs\n");
1478                    _exit();
1479                }
1480            }
1481
1482            unsigned int type    = irq[irq_id].srctype;
1483            unsigned int srcid   = irq[irq_id].srcid;
1484            unsigned int isr     = irq[irq_id].isr & 0xFFFF;
1485            unsigned int channel = irq[irq_id].channel << 16;
1486
1487            if ( (type != IRQ_TYPE_HWI) || (srcid > 31) )
1488            {
1489                _puts("\n[BOOT ERROR] Bad IRQ in PIC component\n");
1490                _exit();
1491            }
1492
1493            // get scheduler[cluster_id] address
1494            unsigned int x          = cluster[cluster_id].x;
1495            unsigned int y          = cluster[cluster_id].y;
1496            unsigned int cluster_xy = (x<<Y_WIDTH) + y;
1497            psched                  = _schedulers[cluster_xy * NB_PROCS_MAX];
1498
1499            // update WTI vector for scheduler[cluster_id][lpid]
1500            unsigned int index = alloc_wti_channel[cluster_id];
1501            psched[lpid].wti_vector[index] = isr | channel | 0x80000000;
1502            alloc_wti_channel[cluster_id] = index + 1;
1503            lpid = lpid + 1;
1504
1505            // update IRQ fields in mapping for PIC initialisation
1506            irq[irq_id].dest_id = index;
1507            irq[irq_id].dest_xy = cluster_xy;
1508
1509        }  // end for IRQs
1510    } // end if PIC
1511               
1512#if BOOT_DEBUG_SCHED
1513for ( cluster_id = 0 ; cluster_id < (X_SIZE*Y_SIZE) ; cluster_id++ )
1514{
1515    unsigned int x          = cluster[cluster_id].x;
1516    unsigned int y          = cluster[cluster_id].y;
1517    unsigned int cluster_xy = (x<<Y_WIDTH) + y;
1518    psched                  = _schedulers[cluster_xy * NB_PROCS_MAX];
1519    unsigned int slot;
1520    unsigned int entry;
1521    for ( lpid = 0 ; lpid < cluster[cluster_id].procs ; lpid++ )
1522    {
1523        _puts("\n*** IRQS for proc[");
1524        _putd( x );
1525        _puts(",");
1526        _putd( y );
1527        _puts(",");
1528        _putd( lpid );
1529        _puts("]\n");
1530        for ( slot = 0 ; slot < 32 ; slot++ )
1531        {
1532            entry = psched[lpid].hwi_vector[slot];
1533            if ( entry & 0x80000000 ) 
1534            {
1535                _puts(" - HWI ");
1536                _putd( slot );
1537                _puts(" / isrtype = ");
1538                _putd( entry & 0xFFFF ); 
1539                _puts(" / channel = ");
1540                _putd( (entry >> 16) & 0x7FFF ); 
1541                _puts("\n");
1542            }
1543        }
1544        for ( slot = 0 ; slot < 32 ; slot++ )
1545        {
1546            entry = psched[lpid].wti_vector[slot];
1547            if ( entry & 0x80000000 ) 
1548            {
1549                _puts(" - WTI ");
1550                _putd( slot );
1551                _puts(" / isrtype = ");
1552                _putd( entry & 0xFFFF ); 
1553                _puts(" / channel = ");
1554                _putd( (entry >> 16) & 0x7FFF ); 
1555                _puts("\n");
1556            }
1557        }
1558        for ( slot = 0 ; slot < 32 ; slot++ )
1559        {
1560            entry = psched[lpid].pti_vector[slot];
1561            if ( entry & 0x80000000 ) 
1562            {
1563                _puts(" - PTI ");
1564                _putd( slot );
1565                _puts(" / isrtype = ");
1566                _putd( entry & 0xFFFF ); 
1567                _puts(" / channel = ");
1568                _putd( (entry >> 16) & 0x7FFF ); 
1569                _puts("\n");
1570            }
1571        }
1572    }
1573}
1574#endif
1575
1576    ///////////////////////////////////////////////////////////////////
1577    // Step 2 : loop on the vspaces and the tasks  to complete
1578    //          the schedulers and task contexts initialisation.
1579
1580    for (vspace_id = 0; vspace_id < header->vspaces; vspace_id++) 
1581    {
1582        // We must set the PTPR depending on the vspace, because the start_vector
1583        // and the stack address are defined in virtual space.
1584        _set_mmu_ptpr( (unsigned int)(_ptabs_paddr[vspace_id] >> 13) );
1585
1586        // loop on the tasks in vspace (task_id is the global index)
1587        for (task_id = vspace[vspace_id].task_offset;
1588             task_id < (vspace[vspace_id].task_offset + vspace[vspace_id].tasks);
1589             task_id++) 
1590        {
1591            // compute the cluster coordinates & local processor index
1592            unsigned int x          = cluster[task[task_id].clusterid].x;
1593            unsigned int y          = cluster[task[task_id].clusterid].y;
1594            unsigned int cluster_xy = (x<<Y_WIDTH) + y;
1595            unsigned int lpid       = task[task_id].proclocid;                 
1596
1597#if BOOT_DEBUG_SCHED
1598_puts("\n[BOOT DEBUG] Initialise context for task ");
1599_puts( task[task_id].name );
1600_puts(" in vspace ");
1601_puts( vspace[vspace_id].name );
1602_puts("\n");
1603#endif
1604            // compute gpid (global processor index) and scheduler base address
1605            unsigned int gpid = cluster_xy * NB_PROCS_MAX + lpid;
1606            psched            = _schedulers[gpid];
1607
1608            // ctx_sr : value required before an eret instruction
1609            unsigned int ctx_sr = 0x0000FF13;
1610
1611            // ctx_ptpr : page table physical base address (shifted by 13 bit)
1612            unsigned int ctx_ptpr = (unsigned int)(_ptabs_paddr[vspace_id] >> 13);
1613
1614            // ctx_ptab : page_table virtual base address
1615            unsigned int ctx_ptab = _ptabs_vaddr[vspace_id];
1616
1617            // ctx_tty : TTY terminal global index provided by the global allocator
1618            //           Each user terminal is a private ressource: the number of
1619            //           requested terminal cannot be larger than NB_TTY_CHANNELS.             
1620            unsigned int ctx_tty = 0xFFFFFFFF;
1621            if (task[task_id].use_tty) 
1622            {
1623                if (alloc_tty_channel >= NB_TTY_CHANNELS) 
1624                {
1625                    _puts("\n[BOOT ERROR] TTY channel index too large for task ");
1626                    _puts(task[task_id].name);
1627                    _puts(" in vspace ");
1628                    _puts(vspace[vspace_id].name);
1629                    _puts("\n");
1630                    _exit();
1631                }
1632                ctx_tty = alloc_tty_channel;
1633                alloc_tty_channel++;
1634             }
1635
1636            // ctx_nic : NIC channel global index provided by the global allocator
1637            //           Each channel is a private ressource: the number of
1638            //           requested channels cannot be larger than NB_NIC_CHANNELS.
1639            unsigned int ctx_nic = 0xFFFFFFFF;
1640            if (task[task_id].use_nic) 
1641            {
1642                if (alloc_nic_channel >= NB_NIC_CHANNELS) 
1643                {
1644                    _puts("\n[BOOT ERROR] NIC channel index too large for task ");
1645                    _puts(task[task_id].name);
1646                    _puts(" in vspace ");
1647                    _puts(vspace[vspace_id].name);
1648                    _puts("\n");
1649                    _exit();
1650                }
1651                ctx_nic = alloc_nic_channel;
1652                alloc_nic_channel++;
1653            }
1654
1655            // ctx_cma : CMA channel global index provided by the global allocator
1656            //           Each channel is a private ressource: the number of
1657            //           requested channels cannot be larger than NB_NIC_CHANNELS.
1658            unsigned int ctx_cma = 0xFFFFFFFF;
1659            if (task[task_id].use_cma) 
1660            {
1661                if (alloc_cma_channel >= NB_CMA_CHANNELS) 
1662                {
1663                    _puts("\n[BOOT ERROR] CMA channel index too large for task ");
1664                    _puts(task[task_id].name);
1665                    _puts(" in vspace ");
1666                    _puts(vspace[vspace_id].name);
1667                    _puts("\n");
1668                    _exit();
1669                }
1670                ctx_cma = alloc_cma_channel;
1671                alloc_cma_channel++;
1672            }
1673
1674            // ctx_hba : HBA channel global index provided by the global allocator
1675            //           Each channel is a private ressource: the number of
1676            //           requested channels cannot be larger than NB_NIC_CHANNELS.
1677            unsigned int ctx_hba = 0xFFFFFFFF;
1678            if (task[task_id].use_hba) 
1679            {
1680                if (alloc_hba_channel >= NB_IOC_CHANNELS) 
1681                {
1682                    _puts("\n[BOOT ERROR] IOC channel index too large for task ");
1683                    _puts(task[task_id].name);
1684                    _puts(" in vspace ");
1685                    _puts(vspace[vspace_id].name);
1686                    _puts("\n");
1687                    _exit();
1688                }
1689                ctx_hba = alloc_hba_channel;
1690                alloc_hba_channel++;
1691            }
1692            // ctx_tim : TIMER local channel index provided by the cluster allocator
1693            //           Each timer is a private ressource
1694            unsigned int ctx_tim = 0xFFFFFFFF;
1695            if (task[task_id].use_tim) 
1696            {
1697                unsigned int cluster_id = task[task_id].clusterid;
1698
1699                if ( alloc_tim_channel[cluster_id] >= NB_TIM_CHANNELS ) 
1700                {
1701                    _puts("\n[BOOT ERROR] local TIMER index too large for task ");
1702                    _puts(task[task_id].name);
1703                    _puts(" in vspace ");
1704                    _puts(vspace[vspace_id].name);
1705                    _puts("\n");
1706                    _exit();
1707                }
1708                ctx_tim =  alloc_tim_channel[cluster_id];
1709                alloc_tim_channel[cluster_id]++;
1710            }
1711            // ctx_epc : Get the virtual address of the memory location containing
1712            // the task entry point : the start_vector is stored by GCC in the seg_data
1713            // segment and we must wait the .elf loading to get the entry point value...
1714            vobj_id = vspace[vspace_id].start_vobj_id;     
1715            unsigned int ctx_epc = vobj[vobj_id].vaddr + (task[task_id].startid)*4;
1716
1717            // ctx_sp :  Get the vobj containing the stack
1718            vobj_id = task[task_id].stack_vobj_id;
1719            unsigned int ctx_sp = vobj[vobj_id].vaddr + vobj[vobj_id].length;
1720
1721            // get local task index in scheduler
1722            unsigned int ltid = psched->tasks;
1723
1724            // get vspace thread index
1725            unsigned int thread_id = task[task_id].trdid;
1726
1727            if (ltid >= IDLE_TASK_INDEX) 
1728            {
1729                _puts("\n[BOOT ERROR] in boot_schedulers_init() : ");
1730                _putd( ltid );
1731                _puts(" tasks allocated to processor ");
1732                _putd( gpid );
1733                _puts(" / max is ");
1734                _putd( IDLE_TASK_INDEX );
1735                _puts("\n");
1736                _exit();
1737            }
1738
1739            // update the "tasks" and "current" fields in scheduler:
1740            // the first task to execute is task 0 as soon as there is at least
1741            // one task allocated to processor.
1742            psched->tasks   = ltid + 1;
1743            psched->current = 0;
1744
1745            // initializes the task context in scheduler
1746            psched->context[ltid][CTX_CR_ID]    = 0;
1747            psched->context[ltid][CTX_SR_ID]    = ctx_sr;
1748            psched->context[ltid][CTX_SP_ID]    = ctx_sp;
1749            psched->context[ltid][CTX_EPC_ID]   = ctx_epc;
1750            psched->context[ltid][CTX_PTPR_ID]  = ctx_ptpr;
1751            psched->context[ltid][CTX_TTY_ID]   = ctx_tty;
1752            psched->context[ltid][CTX_CMA_ID]   = ctx_cma;
1753            psched->context[ltid][CTX_HBA_ID]   = ctx_hba;
1754            psched->context[ltid][CTX_NIC_ID]   = ctx_nic;
1755            psched->context[ltid][CTX_TIM_ID]   = ctx_tim;
1756            psched->context[ltid][CTX_PTAB_ID]  = ctx_ptab;
1757            psched->context[ltid][CTX_LTID_ID]  = ltid;
1758            psched->context[ltid][CTX_GTID_ID]  = task_id;
1759            psched->context[ltid][CTX_TRDID_ID] = thread_id;
1760            psched->context[ltid][CTX_VSID_ID]  = vspace_id;
1761            psched->context[ltid][CTX_RUN_ID]   = 1;
1762
1763#if BOOT_DEBUG_SCHED
1764_puts("\nTask ");
1765_putd( task_id );
1766_puts(" allocated to processor[");
1767_putd( x );
1768_puts(",");
1769_putd( y );
1770_puts(",");
1771_putd( lpid );
1772_puts("]\n  - ctx[LTID]   = ");
1773_putd( psched->context[ltid][CTX_LTID_ID] );
1774_puts("\n  - ctx[SR]     = ");
1775_putx( psched->context[ltid][CTX_SR_ID] );
1776_puts("\n  - ctx[SP]     = ");
1777_putx( psched->context[ltid][CTX_SP_ID] );
1778_puts("\n  - ctx[EPC]    = ");
1779_putx( psched->context[ltid][CTX_EPC_ID] );
1780_puts("\n  - ctx[PTPR]   = ");
1781_putx( psched->context[ltid][CTX_PTPR_ID] );
1782_puts("\n  - ctx[TTY]    = ");
1783_putx( psched->context[ltid][CTX_TTY_ID] );
1784_puts("\n  - ctx[NIC]    = ");
1785_putx( psched->context[ltid][CTX_NIC_ID] );
1786_puts("\n  - ctx[CMA]    = ");
1787_putx( psched->context[ltid][CTX_CMA_ID] );
1788_puts("\n  - ctx[IOC]    = ");
1789_putx( psched->context[ltid][CTX_HBA_ID] );
1790_puts("\n  - ctx[TIM]    = ");
1791_putx( psched->context[ltid][CTX_TIM_ID] );
1792_puts("\n  - ctx[PTAB]   = ");
1793_putx( psched->context[ltid][CTX_PTAB_ID] );
1794_puts("\n  - ctx[GTID]   = ");
1795_putx( psched->context[ltid][CTX_GTID_ID] );
1796_puts("\n  - ctx[VSID]   = ");
1797_putx( psched->context[ltid][CTX_VSID_ID] );
1798_puts("\n  - ctx[TRDID]  = ");
1799_putx( psched->context[ltid][CTX_TRDID_ID] );
1800_puts("\n");
1801#endif
1802
1803        } // end loop on tasks
1804    } // end loop on vspaces
1805} // end _schedulers_init()
1806
1807//////////////////////////////////////////////////////////////////////////////////
1808// This function loads the map.bin file from block device.
1809// The fat global varible is defined in fat32.c file.
1810//////////////////////////////////////////////////////////////////////////////////
1811void boot_mapping_init()
1812{
1813    _ioc_init( 0 );
1814
1815    int fd_id = _fat_open( IOC_BOOT_MODE,
1816                           "map.bin",
1817                           0 );         // no creation
1818
1819    if ( fd_id == -1 )
1820    {
1821        _puts("\n[BOOT ERROR] : map.bin file not found \n");
1822        _exit();
1823    }
1824
1825#if BOOT_DEBUG_MAPPING
1826_puts("\n[BOOT] map.bin file successfully open at cycle ");
1827_putd(_get_proctime());
1828_puts("\n");
1829#endif
1830
1831    unsigned int size    = fat.fd[fd_id].file_size;
1832    unsigned int nblocks = size >> 9;
1833    unsigned int offset  = size & 0x1FF;
1834    if ( offset ) nblocks++;
1835
1836    unsigned int ok = _fat_read( IOC_BOOT_MODE,
1837                                 fd_id, 
1838                                 (unsigned int*)SEG_BOOT_MAPPING_BASE, 
1839                                 nblocks,       
1840                                 0 );      // offset
1841    if ( ok == -1 )
1842    {
1843        _puts("\n[BOOT ERROR] : unable to load map.bin file \n");
1844        _exit();
1845    }
1846    _fat_close( fd_id );
1847   
1848    boot_mapping_check();
1849} // end boot_mapping_init()
1850
1851
1852//////////////////////////////////////////////////////////////////////////////////
1853// This function open the .elf file identified by the "pathname" argument.
1854// It loads the complete file in a dedicated buffer, it copies all loadable
1855// segments  at the memory virtual address defined in the .elf file,
1856// and close the file.
1857// Notes:
1858// - The processor PTPR should contain the value corresponding to the
1859//   vspace containing the .elf file.
1860// - As this function requires a temporary memory buffer
1861//   to load the complete .elf file before to copy the various segments
1862//   to te proper location, it uses the seg_boot_buffer defined in map.xml.
1863//////////////////////////////////////////////////////////////////////////////////
1864void load_one_elf_file( unsigned int mode,
1865                        char*        pathname,
1866                        unsigned int vspace_id )    // to use the proper page_table
1867{
1868    unsigned int seg_id;
1869
1870    // get boot buffer address and size
1871    char*             boot_buffer      = (char*)SEG_BOOT_BUFFER_BASE;
1872    unsigned int      boot_buffer_size = SEG_BOOT_BUFFER_SIZE;
1873
1874#if BOOT_DEBUG_ELF
1875_puts("\n[BOOT DEBUG] Start searching file ");
1876_puts( pathname );
1877_puts(" at cycle ");
1878_putd( _get_proctime() );
1879_puts("\n");
1880#endif
1881
1882    // open .elf file
1883    int fd_id = _fat_open( mode,
1884                           pathname,
1885                           0 );      // no creation
1886    if ( fd_id < 0 )
1887    {
1888        _puts("\n[BOOT ERROR] load_one_elf_file() : ");
1889        _puts( pathname );
1890        _puts(" not found\n");
1891        _exit();
1892    }
1893
1894    // check boot_buffer size versus file size
1895    if ( fat.fd[fd_id].file_size > boot_buffer_size )
1896    {
1897        _puts("\n[BOOT ERROR] load_one_elf_file() : ");
1898        _puts( pathname );
1899        _puts(" exceeds the seg_boot_buffer size\n");
1900        _exit();
1901    }
1902
1903    // compute number of sectors
1904    unsigned int nbytes   = fat.fd[fd_id].file_size;
1905    unsigned int nsectors = nbytes>>9;
1906    if( nbytes & 0x1FF) nsectors++;
1907
1908    // load file in boot_buffer
1909    if( _fat_read( mode, 
1910                   fd_id, 
1911                   boot_buffer,
1912                   nsectors,
1913                   0 ) != nsectors )
1914    {
1915        _puts("\n[BOOT ERROR] load_one_elf_file() : unexpected EOF for file ");
1916        _puts( pathname );
1917        _puts("\n");   
1918        _exit();
1919    }
1920
1921    // Check ELF Magic Number in ELF header
1922    Elf32_Ehdr* elf_header_ptr = (Elf32_Ehdr*)boot_buffer;
1923
1924    if ( (elf_header_ptr->e_ident[EI_MAG0] != ELFMAG0) ||
1925         (elf_header_ptr->e_ident[EI_MAG1] != ELFMAG1) ||
1926         (elf_header_ptr->e_ident[EI_MAG2] != ELFMAG2) ||
1927         (elf_header_ptr->e_ident[EI_MAG3] != ELFMAG3) )
1928    {
1929        _puts("\n[BOOT ERROR] load_elf() : file ");
1930        _puts( pathname );
1931        _puts(" does not use ELF format\n");   
1932        _exit();
1933    }
1934
1935    // get program header table pointer
1936    unsigned int pht_index = elf_header_ptr->e_phoff;
1937    if( pht_index == 0 )
1938    {
1939        _puts("\n[BOOT ERROR] load_one_elf_file() : file ");
1940        _puts( pathname );
1941        _puts(" does not contain loadable segment\n");   
1942        _exit();
1943    }
1944    Elf32_Phdr* elf_pht_ptr = (Elf32_Phdr*)(boot_buffer + pht_index);
1945
1946    // get number of segments
1947    unsigned int nsegments   = elf_header_ptr->e_phnum;
1948
1949#if BOOT_DEBUG_ELF
1950_puts("\n[BOOT DEBUG] File ");
1951_puts( pathname );
1952_puts(" loaded at cycle ");
1953_putd( _get_proctime() );
1954_puts(" / bytes = ");
1955_putd( nbytes );
1956_puts(" / sectors = ");
1957_putd( nsectors );
1958_puts("\n");
1959#endif
1960
1961    // Loop on loadable segments in the ELF file
1962    for (seg_id = 0 ; seg_id < nsegments ; seg_id++)
1963    {
1964        if(elf_pht_ptr[seg_id].p_type == PT_LOAD)
1965        {
1966            // Get segment attributes
1967            unsigned int seg_vaddr  = elf_pht_ptr[seg_id].p_vaddr;
1968            unsigned int seg_offset = elf_pht_ptr[seg_id].p_offset;
1969            unsigned int seg_filesz = elf_pht_ptr[seg_id].p_filesz;
1970            unsigned int seg_memsz  = elf_pht_ptr[seg_id].p_memsz;
1971
1972            if( seg_memsz < seg_filesz )
1973            {
1974                _puts("\n[BOOT ERROR] load_one_elf_file() : segment at vaddr = ");
1975                _putx( seg_vaddr );
1976                _puts(" in file ");
1977                _puts( pathname );
1978                _puts(" has a wrong size \n");   
1979                _exit();
1980            }
1981
1982            // fill empty space with 0 as required
1983            if( seg_memsz > seg_filesz )
1984            {
1985                unsigned int i; 
1986                for( i = seg_filesz ; i < seg_memsz ; i++ ) boot_buffer[i+seg_offset] = 0;
1987            } 
1988
1989            unsigned int src_vaddr = (unsigned int)boot_buffer + seg_offset;
1990
1991#if BOOT_DEBUG_ELF
1992_puts(" - segment ");
1993_putd( seg_id );
1994_puts(" / dst_vaddr = ");
1995_putx( seg_vaddr );
1996_puts(" / src_vaddr = ");
1997_putx( src_vaddr );
1998_puts(" / size = ");
1999_putx( seg_filesz );
2000_puts("\n");
2001#endif
2002
2003            // copy the segment from boot buffer to destination buffer
2004            if( NB_DMA_CHANNELS > 0 )
2005            {
2006                _dma_copy( 0,              // DMA cluster index
2007                           0,              // DMA channel index
2008                           vspace_id,     
2009                           seg_vaddr,
2010                           src_vaddr,
2011                           seg_filesz );
2012            }
2013            else
2014            {
2015                _memcpy( (char*)seg_vaddr,
2016                         (char*)src_vaddr,
2017                         seg_filesz );
2018            }
2019        }
2020    } // end for segments
2021
2022    // close .elf file
2023    _fat_close( fd_id );
2024
2025} // end load_one_elf_file()
2026
2027
2028//////////////////////////////////////////////////////////////////////////////////
2029// This function uses the map.bin data structure to load the "kernel.elf" file
2030// as well as the various "application.elf" files.
2031// The "preloader.elf" file is not loaded, because it has been burned in the ROM.
2032// The "boot.elf" file is not loaded, because it has been loaded by the preloader.
2033// This function scans all vobjs defined in the map.bin data structure to collect
2034// all .elf files pathnames, and calls the load_one_elf_file() function to
2035// load all loadable segments at the virtual address found in the .elf file.
2036//////////////////////////////////////////////////////////////////////////////////
2037void boot_elf_load()
2038{
2039    mapping_header_t* header = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
2040    mapping_vspace_t* vspace = _get_vspace_base( header );
2041    mapping_vobj_t*   vobj   = _get_vobj_base( header );
2042    unsigned int      vspace_id;
2043    unsigned int      vobj_id;
2044    unsigned int      found;
2045
2046    // Scan all vobjs corresponding to global vsegs,
2047    // to find the pathname to the kernel.elf file
2048    found = 0;
2049    for( vobj_id = 0 ; vobj_id < header->globals ; vobj_id++ )
2050    {
2051        if(vobj[vobj_id].type == VOBJ_TYPE_ELF) 
2052        {   
2053            found = 1;
2054            break;
2055        }
2056    }
2057
2058    // We need one kernel.elf file
2059    if (found == 0)
2060    {
2061        _puts("[BOOT ERROR] boot_elf_load() : kernel.elf file not found\n");
2062        _exit();
2063    }
2064
2065    load_one_elf_file( IOC_BOOT_MODE,
2066                       vobj[vobj_id].binpath,
2067                       0 );                         // vspace 0
2068
2069    _puts("\n[BOOT] File \"");
2070    _puts( vobj[vobj_id].binpath );
2071    _puts("\" loaded at cycle ");
2072    _putd( _get_proctime() );
2073    _puts("\n");
2074
2075    // loop on the vspaces, scanning all vobjs in a vspace,
2076    // to find the pathname of the .elf file associated to the vspace.
2077    for( vspace_id = 0 ; vspace_id < header->vspaces ; vspace_id++ )
2078    {
2079        // Set PTPR depending on the vspace, as seg_data is defined in virtual space.
2080        _set_mmu_ptpr( (unsigned int)(_ptabs_paddr[vspace_id] >> 13) );
2081
2082        // loop on the vobjs in vspace (vobj_id is the global index)
2083        unsigned int found = 0;
2084        for (vobj_id = vspace[vspace_id].vobj_offset;
2085             vobj_id < (vspace[vspace_id].vobj_offset + vspace[vspace_id].vobjs);
2086             vobj_id++) 
2087        {
2088            if(vobj[vobj_id].type == VOBJ_TYPE_ELF) 
2089            {   
2090                found = 1;
2091                break;
2092            }
2093        }
2094
2095        // We want one .elf file per vspace
2096        if (found == 0)
2097        {
2098            _puts("[BOOT ERROR] boot_elf_load() : .elf file not found for vspace ");
2099            _puts( vspace[vspace_id].name );
2100            _puts("\n");
2101            _exit();
2102        }
2103
2104        load_one_elf_file( IOC_BOOT_MODE,
2105                           vobj[vobj_id].binpath,
2106                           vspace_id );
2107
2108        _puts("\n[BOOT] File \"");
2109        _puts( vobj[vobj_id].binpath );
2110        _puts("\" loaded at cycle ");
2111        _putd( _get_proctime() );
2112        _puts("\n");
2113
2114    }  // end for vspaces
2115
2116    // restaure vspace 0 PTPR
2117    _set_mmu_ptpr( (unsigned int)(_ptabs_paddr[0] >> 13) );
2118
2119} // end boot_elf_load()
2120
2121////////////////////////////////////////////////////////////////////////////////
2122// This function intializes the periherals and coprocessors, as specified
2123// in the mapping_info file.
2124////////////////////////////////////////////////////////////////////////////////
2125void boot_peripherals_init() 
2126{
2127    mapping_header_t * header   = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
2128    mapping_cluster_t * cluster = _get_cluster_base(header);
2129    mapping_periph_t * periph   = _get_periph_base(header);
2130    mapping_vobj_t * vobj       = _get_vobj_base(header);
2131    mapping_coproc_t * coproc   = _get_coproc_base(header);
2132    mapping_cp_port_t * cp_port = _get_cp_port_base(header);
2133    mapping_irq_t * irq         = _get_irq_base(header);
2134
2135    unsigned int cluster_id;
2136    unsigned int periph_id;
2137    unsigned int coproc_id;
2138    unsigned int cp_port_id;
2139    unsigned int channel_id;
2140
2141    // loop on all physical clusters
2142    for (cluster_id = 0; cluster_id < X_SIZE*Y_SIZE; cluster_id++) 
2143    {
2144        // computes cluster coordinates
2145        unsigned int x          = cluster[cluster_id].x;
2146        unsigned int y          = cluster[cluster_id].y;
2147        unsigned int cluster_xy = (x<<Y_WIDTH) + y;
2148
2149#if BOOT_DEBUG_PERI
2150_puts("\n[BOOT DEBUG] Peripherals initialisation in cluster[");
2151_putd( x );
2152_puts(",");
2153_putd( y );
2154_puts("]\n");
2155#endif
2156
2157        // loop on peripherals
2158        for (periph_id = cluster[cluster_id].periph_offset;
2159             periph_id < cluster[cluster_id].periph_offset +
2160             cluster[cluster_id].periphs; periph_id++) 
2161        {
2162            unsigned int type       = periph[periph_id].type;
2163            unsigned int channels   = periph[periph_id].channels;
2164
2165            switch (type) 
2166            {
2167                case PERIPH_TYPE_IOC:    // vci_block_device component
2168                {
2169                    // initialize all channels except channel 0 because it has been
2170                    // initialized by the preloader.
2171                    for (channel_id = 1; channel_id < channels; channel_id++) 
2172                    {
2173                        _ioc_init( channel_id );
2174                    }
2175#if BOOT_DEBUG_PERI
2176_puts("- IOC : channels = ");
2177_putd(channels);
2178_puts("\n");
2179#endif
2180                    break;
2181                }
2182                case PERIPH_TYPE_CMA:    // vci_chbuf_dma component
2183                {
2184                    for (channel_id = 0; channel_id < channels; channel_id++) 
2185                    {
2186                        // TODO
2187                    }
2188#if BOOT_DEBUG_PERI
2189_puts("- CMA : channels = ");
2190_putd(channels);
2191_puts("\n");
2192#endif
2193                    break;
2194                }
2195                case PERIPH_TYPE_NIC:    // vci_multi_nic component
2196                {
2197                    for (channel_id = 0; channel_id < channels; channel_id++) 
2198                    {
2199                        // TODO
2200                    }
2201#if BOOT_DEBUG_PERI
2202_puts("- NIC : channels = ");
2203_putd(channels);
2204_puts("\n");
2205#endif
2206                    break;
2207                }
2208                case PERIPH_TYPE_TTY:    // vci_multi_tty component
2209                {
2210                    // nothing to do
2211#if BOOT_DEBUG_PERI
2212_puts("- TTY : channels = ");
2213_putd(channels);
2214_puts("\n");
2215#endif
2216                    break;
2217                }
2218                case PERIPH_TYPE_IOB:    // vci_io_bridge component
2219                {
2220#if BOOT_DEBUG_PERI
2221_puts("- IOB : channels = ");
2222_putd(channels);
2223_puts("\n");
2224#endif
2225                    if (GIET_USE_IOMMU) 
2226                    {
2227                        // TODO
2228                        // get the iommu page table physical address
2229                        // set IOMMU page table address
2230                        // pseg_base[IOB_IOMMU_PTPR] = ptab_pbase;   
2231                        // activate IOMMU
2232                        // pseg_base[IOB_IOMMU_ACTIVE] = 1;       
2233                    }
2234                    break;
2235                }
2236                case PERIPH_TYPE_PIC:    // vci_iopic component
2237                {
2238                         
2239#if BOOT_DEBUG_PERI
2240_puts("- PIC : channels = ");
2241_putd(channels);
2242_puts("\n");
2243#endif
2244                    // scan all IRQs defined in mapping for PIC component,
2245                    // and initialises addresses for WTI IRQs
2246                    for ( channel_id = periph[periph_id].irq_offset ;
2247                          channel_id < periph[periph_id].irq_offset + periph[periph_id].irqs ;
2248                          channel_id++ )
2249                    {
2250                        unsigned int hwi_id     = irq[channel_id].srcid;   // HWI index in PIC
2251                        unsigned int wti_id     = irq[channel_id].dest_id; // WTI index in XCU
2252                        unsigned int cluster_xy = irq[channel_id].dest_xy; // XCU coordinates
2253                        unsigned int vaddr;
2254
2255                        _xcu_get_wti_address( wti_id, &vaddr );
2256
2257                        _pic_init( hwi_id, vaddr, cluster_xy ); 
2258#if BOOT_DEBUG_PERI
2259_puts("    hwi_index = ");
2260_putd( hwi_id );
2261_puts(" / wti_index = ");
2262_putd( wti_id );
2263_puts(" / vaddr = ");
2264_putx( vaddr );
2265_puts(" in cluster[");
2266_putd( cluster_xy >> Y_WIDTH );
2267_puts(",");
2268_putd( cluster_xy & ((1<<Y_WIDTH)-1) );
2269_puts("]\n");
2270#endif
2271                    }
2272                    break;
2273                }
2274            }  // end switch periph type
2275        }  // end for periphs
2276
2277#if BOOT_DEBUG_PERI
2278_puts("\n[BOOT DEBUG] Coprocessors initialisation in cluster[");
2279_putd( x );
2280_puts(",");
2281_putd( y );
2282_puts("]\n");
2283#endif
2284
2285        // loop on coprocessors
2286        for ( coproc_id = cluster[cluster_id].coproc_offset;
2287              coproc_id < cluster[cluster_id].coproc_offset +
2288              cluster[cluster_id].coprocs; coproc_id++ ) 
2289        {
2290
2291#if BOOT_DEBUG_PERI
2292_puts("- coprocessor name : ");
2293_puts(coproc[coproc_id].name);
2294_puts(" / nb ports = ");
2295_putd((unsigned int) coproc[coproc_id].ports);
2296_puts("\n");
2297#endif
2298            // loop on the coprocessor ports
2299            for ( cp_port_id = coproc[coproc_id].port_offset;
2300                  cp_port_id < coproc[coproc_id].port_offset + coproc[coproc_id].ports;
2301                  cp_port_id++ ) 
2302            {
2303                // Get global index of associted vobj
2304                unsigned int vobj_id   = cp_port[cp_port_id].mwmr_vobj_id; 
2305
2306                // Get MWMR channel base address
2307                paddr_t mwmr_channel_pbase = vobj[vobj_id].paddr;
2308
2309                _mwr_hw_init( cluster_xy,
2310                              cp_port_id, 
2311                              cp_port[cp_port_id].direction, 
2312                              mwmr_channel_pbase );
2313#if BOOT_DEBUG_PERI
2314_puts("     port direction: ");
2315_putd( (unsigned int)cp_port[cp_port_id].direction );
2316_puts(" / mwmr_channel_pbase = ");
2317_putl( mwmr_channel_pbase );
2318_puts(" / name = ");
2319_puts(vobj[vobj_id].name);
2320_puts("\n"); 
2321#endif
2322            } // end for cp_ports
2323        } // end for coprocs
2324    } // end for clusters
2325} // end boot_peripherals_init()
2326
2327/////////////////////////////////////////////////////////////////////////
2328// This function is the entry point of the boot code for all processors.
2329// Most of this code is executed by Processor 0 only.
2330/////////////////////////////////////////////////////////////////////////
2331void boot_init()
2332{
2333    mapping_header_t*  header     = (mapping_header_t *)SEG_BOOT_MAPPING_BASE;
2334    mapping_cluster_t* cluster    = _get_cluster_base(header);
2335    unsigned int       gpid       = _get_procid();
2336 
2337    if ( gpid == 0 )    // only Processor 0 does it
2338    {
2339        _puts("\n[BOOT] boot_init start at cycle ");
2340        _putd(_get_proctime());
2341        _puts("\n");
2342
2343        // Loading the map.bin file into memory and checking it
2344        boot_mapping_init();
2345
2346        _puts("\n[BOOT] Mapping \"");
2347        _puts( header->name );
2348        _puts("\" loaded at cycle ");
2349        _putd(_get_proctime());
2350        _puts("\n");
2351
2352        // Building all page tables
2353        boot_pt_init();
2354
2355        // Activating proc 0 MMU
2356        _set_mmu_ptpr( (unsigned int)(_ptabs_paddr[0]>>13) );
2357        _set_mmu_mode( 0xF );
2358
2359        _puts("\n[BOOT] Processor[0,0,0] : MMU activation at cycle ");
2360        _putd(_get_proctime());
2361        _puts("\n");
2362
2363        // Initialising private vobjs in vspaces
2364        boot_vobjs_init();
2365
2366        _puts("\n[BOOT] Private vobjs initialised at cycle ");
2367        _putd(_get_proctime());
2368        _puts("\n");
2369
2370        // Initializing schedulers
2371        boot_schedulers_init();
2372
2373        _puts("\n[BOOT] Schedulers initialised at cycle ");
2374        _putd(_get_proctime());
2375        _puts("\n");
2376       
2377        // Setting CP0_SCHED register for proc 0
2378        _set_sched( (unsigned int)_schedulers[0] );
2379
2380        // Initializing non replicated peripherals
2381        boot_peripherals_init();
2382
2383        _puts("\n[BOOT] Non replicated peripherals initialised at cycle ");
2384        _putd(_get_proctime());
2385        _puts("\n");
2386
2387        // Loading all .elf files
2388        boot_elf_load();
2389
2390        // P0 starts all other processors
2391        unsigned int clusterid, p;
2392
2393        for ( clusterid = 0 ; clusterid < X_SIZE*Y_SIZE ; clusterid++ ) 
2394        {
2395            unsigned int nprocs     = cluster[clusterid].procs;
2396            unsigned int xdest      = cluster[clusterid].x;
2397            unsigned int ydest      = cluster[clusterid].y;
2398            unsigned int cluster_xy = (xdest<<Y_WIDTH) + ydest;
2399
2400            for ( p = 0 ; p < nprocs; p++ ) 
2401            {
2402                if ( (nprocs > 0) && ((clusterid != 0) || (p != 0)) )
2403                {
2404                    _xcu_send_wti( cluster_xy, p, (unsigned int)boot_init );
2405                }
2406            }
2407        }
2408 
2409    }  // end monoprocessor boot
2410
2411    // reset BEV bit in the status register to use GIET exception
2412    // handler instead of the PRELOADER exception handler
2413    _set_sr( 0 );
2414
2415    // all processor initialise SCHED register
2416    _set_sched( (unsigned int)_schedulers[gpid] );
2417
2418    // all processors (but Proc 0) activate MMU
2419    if ( gpid != 0 )
2420    {
2421        _set_mmu_ptpr( (unsigned int)(_ptabs_paddr[0]>>13) );
2422        _set_mmu_mode( 0xF );
2423    }
2424
2425    // all processors jump to kernel_init (address defined in giet_vsegs.ld)
2426    unsigned int kernel_entry = (unsigned int)&kernel_init_vbase;
2427    asm volatile( "jr   %0" ::"r"(kernel_entry) );
2428
2429} // end boot_init()
2430
2431
2432// Local Variables:
2433// tab-width: 4
2434// c-basic-offset: 4
2435// c-file-offsets:((innamespace . 0)(inline-open . 0))
2436// indent-tabs-mode: nil
2437// End:
2438// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
2439
Note: See TracBrowser for help on using the repository browser.