source: soft/giet_vm/sys/kernel_init.c @ 165

Last change on this file since 165 was 165, checked in by alain, 12 years ago

Introducing various modifications in kernel initialisation

File size: 21.7 KB
Line 
1///////////////////////////////////////////////////////////////////////////////////
2// File     : kernel_init.c
3// Date     : 26/05/2012
4// Authors  : alain greiner & mohamed karaoui
5// Copyright (c) UPMC-LIP6
6////////////////////////////////////////////////////////////////////////////////////
7// The kernel_init.c files is part of the GIET-VM nano-kernel.
8// It contains the kernel entry point for the second phase of system initialisation:
9// all processors are jumping to _kernel_init, but P[0] is first because other
10// processors are blocked until P[0] complete initilisation of task contexts,
11// vobjs and peripherals.
12// All procs in this phase have their MMU activated, because each processor P[i]
13// must initialise registers SP, SR, PTPR and EPC with informations stored
14// in _scheduler[i].
15////////////////////////////////////////////////////////////////////////////////////
16
17#include <common.h>
18#include <ctx_handler.h>
19#include <sys_handler.h>
20#include <mapping_info.h>
21#include <giet_config.h>
22#include <mips32_registers.h>
23#include <irq_handler.h>
24#include <hwr_mapping.h>
25#include <mwmr_channel.h>
26#include <barrier.h>
27#include <drivers.h>
28
29#define in_kinit __attribute__((section (".kinit")))
30 
31///////////////////////////////////////////////////////////////////////////////////
32// declarations required to avoid forward references
33///////////////////////////////////////////////////////////////////////////////////
34
35void    _kernel_vobjs_init(unsigned int*);
36void    _kernel_tasks_init(unsigned int*);
37void    _kernel_peripherals_init(void);
38void    _kernel_interrupt_vector_init(void);
39void    _kernel_start_all_procs(void);
40
41//////////////////////////////////////////////////////////////////////////////////
42// This function is the entry point for the second step of the boot sequence.
43//////////////////////////////////////////////////////////////////////////////////
44in_kinit void _kernel_init()
45{
46    // array of pointers on the page tables (used for task context initialisation)
47    unsigned int        kernel_ptabs[GIET_NB_VSPACE_MAX]; 
48
49    // values to be written in registers
50    unsigned int        sp_value;
51    unsigned int        sr_value;
52    unsigned int        ptpr_value;
53    unsigned int        epc_value;
54
55    unsigned int        pid =   _procid();
56
57    // only processor 0 executes system initialisation
58    if ( pid == 0 )
59    {
60        _kernel_vobjs_init(kernel_ptabs);
61        _kernel_tasks_init(kernel_ptabs);
62        _kernel_interrupt_vector_init();
63        _kernel_peripherals_init();
64        _kernel_start_all_procs();
65    }
66
67    // each processor initialises it's SP, SR, PTPR, and EPC registers
68    // from values defined in _scheduler[pid], starts it's private
69    // context switch timer (if there is more than one task allocated)
70    // and jumps to user code.
71    // It does nothing, and keep idle if no task allocated.
72
73    static_scheduler_t*  sched = &_scheduler[pid];
74
75    if ( sched->tasks )         // at leat one task allocated
76    {
77        // initialise registers
78        sp_value   = sched->context[0][CTX_SP_ID];
79        sr_value   = sched->context[0][CTX_SR_ID];
80        ptpr_value = sched->context[0][CTX_PTPR_ID];
81        epc_value  = sched->context[0][CTX_EPC_ID];
82
83        // start TICK timer
84        if ( sched->tasks > 1 )
85        {
86            unsigned int cluster_id = pid / NB_PROCS;
87            unsigned int proc_id    = pid % NB_PROCS;
88           _timer_write( cluster_id, proc_id, TIMER_PERIOD, GIET_TICK_VALUE );
89           _timer_write( cluster_id, proc_id, TIMER_MODE  , 0x3 );
90        }
91    }
92    else                                        // no task allocated
93    {
94        _get_lock( &_tty_put_lock );
95        _puts("\n No task allocated to processor ");
96        _putw( pid );
97        _puts(" => keep idle\n");
98        _release_lock ( &_tty_put_lock );
99
100        // enable interrupts in kernel mode
101        asm volatile ( "li         $26, 0xFF01  \n"
102                       "mtc0   $26, $12     \n" 
103                       ::: "$26" );
104
105                // infinite loop in kernel mode
106        while (1) asm volatile("nop");
107    }
108
109    asm volatile (
110       "move    $29,    %0              \n"             /* SP <= ctx[CTX_SP_ID] */
111       "mtc0    %1,             $12             \n"             /* SR <= ctx[CTX_SR_ID] */
112       "mtc2    %2,             $0              \n"             /* PTPR <= ctx[CTX_PTPR_ID] */
113       "mtc0    %3,             $14             \n"             /* EPC <= ctx[CTX_EPC_ID] */
114       "eret                                    \n"             /* jump to user code */
115       "nop                                             \n"
116       :
117       : "r"(sp_value), "r"(sr_value), "r"(ptpr_value), "r"(epc_value) );
118
119} // end _kernel_init()
120
121//////////////////////////////////////////////////////////////////////////////////
122// This function wakeup all processors.
123// It should be executed by P[0] when the kernel initialisation is done.
124//////////////////////////////////////////////////////////////////////////////////
125in_kinit void _kernel_start_all_procs()
126{
127    mapping_header_t*   header = (mapping_header_t*)&seg_mapping_base; 
128
129    _puts("\n[INIT] Starting parallel execution at cycle : ");
130    _putw( _proctime() );
131    _puts("\n");
132
133    header->signature = OUT_MAPPING_SIGNATURE;
134}
135
136//////////////////////////////////////////////////////////////////////////////////
137// _eret()
138// The address of this function is used to initialise the return address (RA)
139// in all task contexts (when the task has never been executed.
140//////////////////////////////////////////////////////////////////////////////////
141in_kinit void _eret()
142{
143    asm volatile("eret \n"
144                 "nop");
145}
146
147///////////////////////////////////////////////////////////////////////////////
148// This function maps a given task, defined in a given vspace
149// on the processor allocated in the mapping_info structure,
150// and initialises the task context.
151// There is one scheduler per processor, and processor can be shared
152// by several applications running in different vspaces.
153// There is one private context array handled by each scheduler.
154//
155// The following values must be initialised in all task contexts:
156// - sp     stack pointer = stack_base + stack_length
157// - ra     return address = &_eret
158// - epc    start address = start_vector[task->startid]
159// - sr     status register = OxFF13
160// - tty    TTY terminal index (global index)
161// - fb     FB_DMA channel index (global index)
162// - ptpr   page table base address / 8K
163// - mode   mmu_mode = 0xF (TLBs and caches activated)
164////////////////////////////////////////////////////////////////////////////////
165in_kinit void _task_map( unsigned int   task_id,    // global index
166                                         unsigned int   vspace_id,  // global index
167                         unsigned int   tty_id,         // TTY index
168                         unsigned int   fb_id,          // FB index
169                         unsigned int   pt_base )   // page table base adddress
170{
171    mapping_header_t*   header = (mapping_header_t*)&seg_mapping_base; 
172
173    mapping_task_t*     task   = _get_task_base(header);
174    mapping_vspace_t*   vspace = _get_vspace_base(header);
175    mapping_vobj_t*     vobj   = _get_vobj_base( header );
176
177    // values to be initialised in task context
178    unsigned int                ra = (unsigned int)&_eret;
179    unsigned int                sr   = 0x0000FF13; 
180    unsigned int                tty  = tty_id; 
181    unsigned int                fb   = fb_id;   
182    unsigned int                ptpr = pt_base >> 13;
183    unsigned int                mode = 0xF;
184    unsigned int                sp;
185    unsigned int                epc;     
186
187    // compute epc value
188    // Get the (virtual) base address of the start_vector that
189    // contains the start addresses for all tasks defined in a vspace.
190    mapping_vobj_t* vobj_data = &vobj[vspace[vspace_id].vobj_offset + 
191                                      vspace[vspace_id].start_offset]; 
192    unsigned int* start_vector = (unsigned int*)vobj_data->vaddr;
193    epc  = start_vector[task[task_id].startid];
194
195    // compute sp value
196    // Get the vobj containing the stack
197    unsigned int vobj_id = task[task_id].vobjlocid + vspace[vspace_id].vobj_offset;
198    sp = vobj[vobj_id].vaddr + vobj[vobj_id].length;
199
200    // compute global processor index
201    unsigned int proc_id = task[task_id].clusterid * NB_PROCS + task[task_id].proclocid;
202
203    // compute and check local task index
204    unsigned int ltid = _scheduler[proc_id].tasks;
205    if ( ltid >= GIET_NB_TASKS_MAX )
206    {
207        _puts("\n[INIT ERROR] : too much tasks allocated to processor ");
208        _putw( proc_id );
209        _puts("\n");
210        _exit();
211    }
212   
213    // update number of tasks allocated to scheduler
214    _scheduler[proc_id].tasks = ltid + 1;
215
216    // initializes the task context
217    _scheduler[proc_id].context[ltid][CTX_SR_ID]    = sr;
218    _scheduler[proc_id].context[ltid][CTX_SP_ID]    = sp;
219    _scheduler[proc_id].context[ltid][CTX_RA_ID]    = ra;
220    _scheduler[proc_id].context[ltid][CTX_EPC_ID]   = epc;
221    _scheduler[proc_id].context[ltid][CTX_TTY_ID]   = tty;
222        _scheduler[proc_id].context[ltid][CTX_FBDMA_ID] = fb;
223    _scheduler[proc_id].context[ltid][CTX_PTPR_ID]  = ptpr;
224    _scheduler[proc_id].context[ltid][CTX_MODE_ID]  = mode;
225    _scheduler[proc_id].context[ltid][CTX_TASK_ID]  = task_id;
226   
227#if INIT_DEBUG
228_puts("Task ");
229_puts( task[task_id].name );
230_puts(" allocated to processor ");
231_putw( proc_id );
232_puts(" / ltid = ");
233_putw( ltid );
234_puts("\n");
235
236_puts("  - SR          = ");
237_putw( sr );
238_puts("  saved at ");
239_putw( (unsigned int)&_scheduler[proc_id].context[ltid][CTX_SR_ID] );
240_puts("\n");
241
242_puts("  - RA          = ");
243_putw( ra );
244_puts("  saved at ");
245_putw( (unsigned int)&_scheduler[proc_id].context[ltid][CTX_RA_ID] );
246_puts("\n");
247
248_puts("  - SP          = ");
249_putw( sp );
250_puts("  saved at ");
251_putw( (unsigned int)&_scheduler[proc_id].context[ltid][CTX_SP_ID] );
252_puts("\n");
253
254_puts("  - EPC         = ");
255_putw( epc );
256_puts("  saved at ");
257_putw( (unsigned int)&_scheduler[proc_id].context[ltid][CTX_EPC_ID] );
258_puts("\n");
259
260_puts("  - TTY         = ");
261_putw( tty );
262_puts("  saved at ");
263_putw( (unsigned int)&_scheduler[proc_id].context[ltid][CTX_TTY_ID] );
264_puts("\n");
265
266_puts("  - FB          = ");
267_putw( fb );
268_puts("  saved at ");
269_putw( (unsigned int)&_scheduler[proc_id].context[ltid][CTX_FBDMA_ID] );
270_puts("\n");
271
272_puts("  - PTPR        = ");
273_putw( ptpr<<13 );
274_puts("  saved at ");
275_putw( (unsigned int)&_scheduler[proc_id].context[ltid][CTX_PTPR_ID] );
276_puts("\n");
277#endif
278
279} // end _task_map()
280
281///////////////////////////////////////////////////////////////////////////////
282// This function initializes all private vobjs defined in the vspaces,
283// such as mwmr channels, barriers and locks, depending on the vobj type.
284// (Most of the vobjs are not known, and not initialised by the compiler).
285// This function initialises the kernel_ptabs[] array indexed by the vspace_id,
286// and containint the base addresses of all page tables.
287// This kernel_ptabs[] array is used to initialise the task contexts.
288///////////////////////////////////////////////////////////////////////////////
289in_kinit void _kernel_vobjs_init( unsigned int* kernel_ptabs )
290{
291    mapping_header_t*   header  = (mapping_header_t*)&seg_mapping_base; 
292    mapping_vspace_t*   vspace  = _get_vspace_base( header );     
293    mapping_vobj_t*     vobj    = _get_vobj_base( header );
294
295    unsigned int        vspace_id; 
296    unsigned int        vobj_id;
297
298    // loop on the vspaces
299    for ( vspace_id = 0 ; vspace_id < header->vspaces ; vspace_id++ )
300    {
301        char ptab_found = 0;
302
303#if INIT_DEBUG
304_puts("[INIT] --- vobjs initialisation in vspace "); 
305_puts(vspace[vspace_id].name);
306_puts("\n");
307#endif
308        // loop on the vobjs
309            for(vobj_id= vspace[vspace_id].vobj_offset; 
310                        vobj_id < (vspace[vspace_id].vobj_offset+ vspace[vspace_id].vobjs);
311                        vobj_id++)
312            {
313            switch( vobj[vobj_id].type )
314            {
315                case VOBJ_TYPE_PTAB:    // initialise page table pointers array
316                {
317                    ptab_found = 1;
318                    kernel_ptabs[vspace_id] = vobj[vobj_id].paddr;
319
320#if INIT_DEBUG
321_puts("[INIT]   PTAB address = "); 
322_putw(kernel_ptabs[vspace_id]); 
323_puts("\n");
324#endif
325                    break;
326                }
327                case VOBJ_TYPE_MWMR:    // storage capacity is (vobj.length/4 - 5) words
328                        {
329                    mwmr_channel_t* mwmr = (mwmr_channel_t*)(vobj[vobj_id].vaddr);
330                    mwmr->ptw   = 0;
331                    mwmr->ptr   = 0;
332                    mwmr->sts   = 0;
333                    mwmr->depth = (vobj[vobj_id].length>>2) - 5;
334                    mwmr->lock  = 0;
335#if INIT_DEBUG
336_puts("[INIT]   MWMR channel ");
337_puts( vobj->name);
338_puts(" / depth = ");
339_putw( mwmr->depth );
340_puts("\n");
341#endif
342                    break;
343                }
344                case VOBJ_TYPE_ELF:             // initialisation done by the loader
345                {
346
347#if INIT_DEBUG
348_puts("[INIT]   ELF section "); 
349_puts( vobj->name);
350_puts(" / length = ");
351_putw( vobj->length ); 
352_puts("\n");
353#endif
354                     break;
355                }
356                case VOBJ_TYPE_BARRIER: // init is the number of participants
357                {
358                    giet_barrier_t* barrier = (giet_barrier_t*)(vobj[vobj_id].vaddr);
359                    barrier->count = 0;
360                    barrier->init  = vobj[vobj_id].init;
361#if INIT_DEBUG
362_puts("   BARRIER "); 
363_puts( vobj->name);
364_puts(" / init_value = ");
365_putw( barrier->init );
366_puts("\n");
367#endif
368                    break;
369                }
370                case VOBJ_TYPE_LOCK:    // init is "not taken"
371                {
372                    unsigned int* lock = (unsigned int*)(vobj[vobj_id].vaddr);
373                    *lock = 0;
374#if INIT_DEBUG
375_puts("   LOCK "); 
376_puts( vobj->name);
377_puts("\n");
378#endif
379                    break;
380                }
381                case VOBJ_TYPE_BUFFER:  // nothing to do
382                {
383
384#if INIT_DEBUG
385_puts("   BUFFER "); 
386_puts( vobj->name);
387_puts(" / length = ");
388_putw( vobj->length ); 
389_puts("\n");
390#endif
391                    break;
392                }
393                default:
394                {
395                    _puts("\n[INIT ERROR] illegal vobj of name ");
396                    _puts(vobj->name);
397                    _puts(" / in vspace = ");
398                    _puts(vobj->name);
399                    _puts("\n ");
400                    _exit();
401                }
402            } // end switch type
403        } // end loop on vobjs
404        if( !ptab_found )
405        {
406            _puts("\n[INIT ERROR] Missing PTAB for vspace ");
407            _putw( vspace_id );
408            _exit();
409        }
410    } // end loop on vspaces
411
412    _puts("\n[INIT] Vobjs initialisation completed at cycle : ");
413    _putw( _proctime() );
414    _puts("\n");
415
416} // end _vobjs_init()
417
418///////////////////////////////////////////////////////////////////////////////
419// This function initialises all task contexts and processors schedulers.
420// It sets the default values for all schedulers (tasks <= 0, current <= 0).
421// Finally, it scan all tasks in all vspaces to initialise the schedulers,
422// and the tasks contexts, as defined in the mapping_info data structure.
423// A global TTY index and a global FB channel are allocated if required.
424// TTY[0] is reserved for the kernel.
425///////////////////////////////////////////////////////////////////////////////
426in_kinit void _kernel_tasks_init(unsigned int* ptabs)
427{
428    mapping_header_t*   header  = (mapping_header_t*)&seg_mapping_base; 
429    mapping_cluster_t*  cluster = _get_cluster_base( header );
430    mapping_vspace_t*   vspace  = _get_vspace_base( header );     
431    mapping_task_t*     task    = _get_task_base( header );
432
433    unsigned int        base_tty_id = 1;     // TTY index allocator
434    unsigned int        base_fb_id  = 0;     // FB channel index allocator
435
436    unsigned int        cluster_id; 
437    unsigned int        proc_id; 
438    unsigned int        vspace_id; 
439    unsigned int        task_id;
440
441    // initialise the schedulers (not done by the compiler)
442    for ( cluster_id = 0 ; cluster_id < header->clusters ; cluster_id++ )
443    {
444        for ( proc_id = 0 ; proc_id < cluster[cluster_id].procs ; proc_id++ )
445        {
446            if ( proc_id >= NB_PROCS )
447            {
448                _puts("\n[INIT ERROR] The number of processors in cluster ");
449                _putw( cluster_id );
450                _puts(" is larger than NB_PROCS \n");
451                _exit();
452            }
453            _scheduler[cluster_id*NB_PROCS+proc_id].tasks   = 0;
454            _scheduler[cluster_id*NB_PROCS+proc_id].current = 0;
455        }
456    }
457
458    // loop on the virtual spaces
459    for ( vspace_id = 0 ; vspace_id < header->vspaces ; vspace_id++ )
460    {
461
462#if INIT_DEBUG
463_puts("\n[INIT] mapping tasks in vspace ");
464_puts(vspace[vspace_id].name);
465_puts("\n");
466#endif
467        // loop on the tasks
468        for ( task_id = vspace[vspace_id].task_offset ; 
469              task_id < (vspace[vspace_id].task_offset + vspace[vspace_id].tasks) ; 
470              task_id++ )
471        {
472            unsigned int        tty_id = 0xFFFFFFFF;
473            unsigned int        fb_id  = 0xFFFFFFFF;
474            if ( task[task_id].use_tty ) 
475            {
476                tty_id = base_tty_id;
477                base_tty_id++;
478            }
479            if ( task[task_id].use_fb  ) 
480            {
481                fb_id = base_fb_id;
482                base_fb_id++;
483            }
484            _task_map( task_id,                         // global task index
485                       vspace_id,                       // vspace index
486                       tty_id,                          // global tty index
487                       fb_id,                           // global fbdma index
488                       ptabs[vspace_id] );      // page table pointer
489        } // end loop on tasks
490    } // end oop on vspaces
491
492    _puts("\n[INIT] Task Contexts initialisation completed at cycle ");
493    _putw( _proctime() );
494    _puts("\n");
495
496#if INIT_DEBUG
497for ( cluster_id = 0 ; cluster_id < header->clusters ; cluster_id++ )
498{
499    _puts("\nCluster ");
500    _putw( cluster_id );
501    _puts("\n");
502    for ( proc_id = 0 ; proc_id < cluster[cluster_id].procs ; proc_id++ )
503    {
504        unsigned int ltid;              // local task index
505        unsigned int gtid;              // global task index
506        unsigned int pid = cluster_id * NB_PROCS + proc_id;
507       
508        _puts(" - processor ");
509        _putw( pid );
510        _puts("\n");
511        for ( ltid = 0 ; ltid < _scheduler[pid].tasks ; ltid++ )
512        {
513            gtid = _scheduler[pid].context[ltid][CTX_TASK_ID];
514            _puts("    task : ");
515            _puts( task[gtid].name );
516            _puts("\n");
517        }
518    }
519}
520#endif
521
522} // end _kernel_task_init()
523
524////////////////////////////////////////////////////////////////////////////////
525// This function intializes the external periherals such as the TTY controller,
526// the IOC (external disk controller), the NIC (external network controller),
527// the FBDMA (frame buffer controller), etc.
528////////////////////////////////////////////////////////////////////////////////
529in_kinit void _kernel_peripherals_init()
530{
531    _puts("\n[INIT] Peripherals initialisation completed at cycle ");
532    _putw( _proctime() );
533    _puts("\n");
534
535} // end _kernel_peripherals_init()
536
537////////////////////////////////////////////////////////////////////////////////
538// This function intialises the centralised interrupt vector,
539// and the ICUs mask registers for all processors in all clusters.
540// It strongly depends on the actual peripheral hardware wiring.
541// In this peculiar version, all clusters are identical,
542// the number of processors per cluster cannot be larger than 8.
543// Processor 0 handle all interrupts corresponding to TTYs, DMAs and IOC
544// (ICU inputs from from IRQ[8] to IRQ[31]). Only the 8 TIMER interrupts
545// (ICU iputs IRQ[0] to IRQ[7]), that are used for context switching
546// are distributed to the 8 processors.
547////////////////////////////////////////////////////////////////////////////////
548in_kinit void _kernel_interrupt_vector_init()
549{
550    mapping_header_t*   header  = (mapping_header_t*)&seg_mapping_base; 
551    mapping_cluster_t*  cluster = _get_cluster_base( header );
552
553    unsigned int cluster_id;
554    unsigned int proc_id;
555
556    // ICU mask values (up to 8 processors per cluster)
557    unsigned int icu_mask[8] = { 0xFFFFFF01,
558                                 0x00000002,
559                                 0x00000004,
560                                 0x00000008,
561                                 0x00000010,
562                                 0x00000020,
563                                 0x00000040,
564                                 0x00000080 };
565
566    // initialise ICUs for each processor in each cluster
567    for ( cluster_id = 0 ; cluster_id < header->clusters ; cluster_id++ )
568    {
569        for ( proc_id = 0 ; proc_id < cluster[cluster_id].procs ; proc_id++ )
570        {
571            _icu_write( cluster_id, proc_id, ICU_MASK_SET, icu_mask[proc_id] ); 
572        }
573    }
574
575    // initialize Interrupt vector
576
577    _interrupt_vector[0]   = &_isr_switch;
578    _interrupt_vector[1]   = &_isr_switch;
579    _interrupt_vector[2]   = &_isr_switch;
580    _interrupt_vector[3]   = &_isr_switch;
581    _interrupt_vector[4]   = &_isr_switch;
582    _interrupt_vector[5]   = &_isr_switch;
583    _interrupt_vector[6]   = &_isr_switch;
584    _interrupt_vector[7]   = &_isr_switch;
585
586    _interrupt_vector[8]   = &_isr_dma_0;
587    _interrupt_vector[9]   = &_isr_dma_1;
588    _interrupt_vector[10]  = &_isr_dma_2;
589    _interrupt_vector[11]  = &_isr_dma_3;
590    _interrupt_vector[12]  = &_isr_dma_4;
591    _interrupt_vector[13]  = &_isr_dma_5;
592    _interrupt_vector[14]  = &_isr_dma_6;
593    _interrupt_vector[15]  = &_isr_dma_7;
594
595    _interrupt_vector[16]  = &_isr_tty_get_0;
596    _interrupt_vector[17]  = &_isr_tty_get_1;
597    _interrupt_vector[18]  = &_isr_tty_get_2;
598    _interrupt_vector[19]  = &_isr_tty_get_3;
599    _interrupt_vector[20]  = &_isr_tty_get_4;
600    _interrupt_vector[21]  = &_isr_tty_get_5;
601    _interrupt_vector[22]  = &_isr_tty_get_6;
602    _interrupt_vector[23]  = &_isr_tty_get_7;
603    _interrupt_vector[24]  = &_isr_tty_get_8;
604    _interrupt_vector[25]  = &_isr_tty_get_9;
605    _interrupt_vector[26]  = &_isr_tty_get_10;
606    _interrupt_vector[27]  = &_isr_tty_get_11;
607    _interrupt_vector[28]  = &_isr_tty_get_12;
608    _interrupt_vector[29]  = &_isr_tty_get_13;
609    _interrupt_vector[30]  = &_isr_tty_get_14;
610
611    _interrupt_vector[31]  = &_isr_ioc;
612
613    _puts("\n[INIT] Interrupt vector initialisation completed at cycle ");
614    _putw( _proctime() );
615    _puts("\n");
616
617} // end _kernel_interrup_vector_init()
Note: See TracBrowser for help on using the repository browser.