source: trunk/hal/tsar_mips32/core/hal_exception.c @ 632

Last change on this file since 632 was 632, checked in by alain, 16 months ago

This version replace the RPC by direct remote memory access
for physical pages allacation/release.
It is commited before being tested.

File size: 20.0 KB
Line 
1/*
2 * hal_exception.c - implementation of exception handler for TSAR-MIPS32.
3 *
4 * Author   Alain Greiner (2016, 2017)
5 *
6 * Copyright (c) UPMC Sorbonne Universites
7 *
8 * This file is part of ALMOS-MKH.
9 *
10 * ALMOS-MKH is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2.0 of the License.
13 *
14 * ALMOS-MKH is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include <hal_kernel_types.h>
25#include <hal_irqmask.h>
26#include <hal_special.h>
27#include <hal_exception.h>
28#include <thread.h>
29#include <printk.h>
30#include <chdev.h>
31#include <vmm.h>
32#include <errno.h>
33#include <scheduler.h>
34#include <core.h>
35#include <syscalls.h>
36#include <shared_syscalls.h>
37#include <remote_busylock.h>
38#include <hal_kentry.h>
39#include <hal_exception.h>
40
41//////////////////////////////////////////////////////////////////////////////////////////
42//  Extern global variables
43//////////////////////////////////////////////////////////////////////////////////////////
44
45extern   chdev_directory_t    chdev_dir;  // allocated in the kernel_init.c file.
46
47//////////////////////////////////////////////////////////////////////////////////////////
48// This enum defines the mask values for an MMU exception code reported by the mips32.
49//////////////////////////////////////////////////////////////////////////////////////////
50
51typedef enum
52{
53    MMU_WRITE_PT1_UNMAPPED        = 0x0001,
54    MMU_WRITE_PT2_UNMAPPED        = 0x0002,
55    MMU_WRITE_PRIVILEGE_VIOLATION = 0x0004,
56    MMU_WRITE_ACCESS_VIOLATION    = 0x0008,
57    MMU_WRITE_UNDEFINED_XTN       = 0x0020,
58    MMU_WRITE_PT1_ILLEGAL_ACCESS  = 0x0040,
59    MMU_WRITE_PT2_ILLEGAL_ACCESS  = 0x0080,
60    MMU_WRITE_DATA_ILLEGAL_ACCESS = 0x0100,
61
62    MMU_READ_PT1_UNMAPPED         = 0x1001,
63    MMU_READ_PT2_UNMAPPED         = 0x1002,
64    MMU_READ_PRIVILEGE_VIOLATION  = 0x1004,
65    MMU_READ_EXEC_VIOLATION       = 0x1010,
66    MMU_READ_UNDEFINED_XTN        = 0x1020,
67    MMU_READ_PT1_ILLEGAL_ACCESS   = 0x1040,
68    MMU_READ_PT2_ILLEGAL_ACCESS   = 0x1080,
69    MMU_READ_DATA_ILLEGAL_ACCESS  = 0x1100,
70}
71mmu_exception_subtype_t;
72
73//////////////////////////////////////////////////////////////////////////////////////////
74// This enum defines the relevant values for XCODE field in mips32 CP0_CR register.
75//////////////////////////////////////////////////////////////////////////////////////////
76
77typedef enum
78{
79    XCODE_ADEL = 0x4,        // Illegal address for data load
80    XCODE_ADES = 0x5,        // Illegal address for data store
81    XCODE_IBE  = 0x6,        // Instruction MMU exception       (can be NON-FATAL)
82    XCODE_DBE  = 0x7,        // Data MMU exception              (can be NON-FATAL)
83    XCODE_RI   = 0xA,        // Reserved instruction exception
84    XCODE_CPU  = 0xB,        // Coprocessor unusable exception  (can be NON-FATAl)
85    XCODE_OVR  = 0xC,        // Arithmetic Overflow exception
86}
87xcode_values_t;
88
89/////////////////////////////////////////////
90char * hal_mmu_exception_str( uint32_t code )
91{
92  switch (code) {
93    case (MMU_WRITE_PT1_UNMAPPED):        return "WRITE_PT1_UNMAPPED";
94    case (MMU_WRITE_PT2_UNMAPPED):        return "WRITE_PT2_UNMAPPED";
95    case (MMU_WRITE_PRIVILEGE_VIOLATION): return "WRITE_PRIVILEGE_VIOLATION";
96    case (MMU_WRITE_ACCESS_VIOLATION):    return "WRITE_ACCESS_VIOLATION";
97    case (MMU_WRITE_UNDEFINED_XTN):       return "WRITE_UNDEFINED_XTN";
98    case (MMU_WRITE_PT1_ILLEGAL_ACCESS):  return "WRITE_PT1_ILLEGAL_ACCESS";
99    case (MMU_WRITE_PT2_ILLEGAL_ACCESS):  return "WRITE_PT2_ILLEGAL_ACCESS";
100    case (MMU_WRITE_DATA_ILLEGAL_ACCESS): return "WRITE_DATA_ILLEGAL_ACCESS";
101    case (MMU_READ_PT1_UNMAPPED):         return "READ_PT1_UNMAPPED";
102    case (MMU_READ_PT2_UNMAPPED):         return "READ_PT2_UNMAPPED";
103    case (MMU_READ_PRIVILEGE_VIOLATION):  return "READ_PRIVILEGE_VIOLATION";
104    case (MMU_READ_EXEC_VIOLATION):       return "READ_EXEC_VIOLATION";
105    case (MMU_READ_UNDEFINED_XTN):        return "READ_UNDEFINED_XTN";
106    case (MMU_READ_PT1_ILLEGAL_ACCESS):   return "READ_PT1_ILLEGAL_ACCESS";
107    case (MMU_READ_PT2_ILLEGAL_ACCESS):   return "READ_PT2_ILLEGAL_ACCESS";
108    case (MMU_READ_DATA_ILLEGAL_ACCESS):  return "READ_DATA_ILLEGAL_ACCESS";
109    default:                              return "undefined";
110  }
111}
112
113//////////////////////////////////////////////////////////////////////////////////////////
114// This function is called when a FPU Coprocessor Unavailable exception has been
115// detected for the calling thread.
116// It enables the FPU, It saves the current FPU context in the current owner thread
117// descriptor if required, and restore the FPU context from the calling thread descriptor.
118//////////////////////////////////////////////////////////////////////////////////////////
119// @ this     : pointer on faulty thread descriptor.
120// @ return always EXCP_NON_FATAL
121//////////////////////////////////////////////////////////////////////////////////////////
122error_t hal_fpu_exception( thread_t * this )
123{
124        core_t   * core = this->core; 
125
126    // enable FPU (in core SR) 
127        hal_fpu_enable();
128
129    // save FPU register values in current owner thread if required
130        if( core->fpu_owner != NULL )
131    {
132        if( core->fpu_owner != this )
133            {
134            // save the FPU registers to current owner thread context
135                    hal_fpu_context_save( XPTR( local_cxy , core->fpu_owner ) );
136
137            // restore FPU registers from requesting thread context
138                hal_fpu_context_restore( this );
139
140            // attach the FPU to the requesting thread
141                core->fpu_owner = this;
142        }
143        }
144    else
145    {
146        // restore FPU registers from requesting thread context
147            hal_fpu_context_restore( this );
148
149        // attach the FPU to the requesting thread
150            core->fpu_owner = this;
151    }
152
153        return EXCP_NON_FATAL;
154
155}  // end hal_fpu_exception()
156
157//////////////////////////////////////////////////////////////////////////////////////////
158// This function is called when an MMU exception has been detected (IBE / DBE).
159// It get the relevant exception arguments from the MMU.
160// It signal a fatal error in case of illegal access. In case of page unmapped,
161// it get the client process to access the relevant VMM: for a RPC thread, the client
162// process is NOT the calling thread process.
163// Then, it checks that the faulty address belongs to a registered vseg, update the local
164// vseg list from the reference cluster if required, and signal a fatal user error
165// in case of illegal virtual address. Finally, it updates the local page table from the
166// reference cluster.
167// WARNING : In order to prevent deadlocks, this function enable IRQs before calling the
168// vmm_handle_page_fault() and the vmm_handle_cow() functions, because concurrent calls
169// to these functions can create cross dependencies...
170//////////////////////////////////////////////////////////////////////////////////////////
171// @ this     : pointer on faulty thread descriptor.
172// @ excPC    :
173// @ is_ins   : IBE if true / DBE if false.
174// @ return EXCP_NON_FATAL / EXCP_USER_ERROR / EXCP_KERNEL_PANIC
175//////////////////////////////////////////////////////////////////////////////////////////
176error_t hal_mmu_exception( thread_t * this,
177                           uint32_t   excPC,
178                           bool_t     is_ins ) 
179{
180        process_t      * process;
181    error_t          error;
182
183    uint32_t         mmu_ins_excp_code;
184    uint32_t         mmu_ins_bad_vaddr;
185    uint32_t         mmu_dat_excp_code;
186    uint32_t         mmu_dat_bad_vaddr;
187
188    uint32_t         bad_vaddr;
189    uint32_t         excp_code;
190
191    // check thread type
192   if( CURRENT_THREAD->type != THREAD_USER )
193    {
194        printk("\n[PANIC] in %s : illegal thread type %s\n",
195        __FUNCTION__, thread_type_str(CURRENT_THREAD->type) );
196
197        return EXCP_KERNEL_PANIC;
198    }
199
200    // get faulty thread process 
201    process = this->process;
202
203    // get relevant values from MMU
204        hal_get_mmu_excp( &mmu_ins_excp_code,
205                          &mmu_ins_bad_vaddr,
206                          &mmu_dat_excp_code, 
207                          &mmu_dat_bad_vaddr );
208
209    // get exception code and faulty vaddr, depending on IBE/DBE
210    if( is_ins )
211    {
212        excp_code = mmu_ins_excp_code;
213        bad_vaddr = mmu_ins_bad_vaddr;
214    }
215    else 
216    {
217        excp_code = mmu_dat_excp_code;
218        bad_vaddr = mmu_dat_bad_vaddr;
219    }
220
221#if DEBUG_HAL_EXCEPTIONS
222uint32_t cycle = (uint32_t)hal_get_cycles();
223if( DEBUG_HAL_EXCEPTIONS < cycle )
224printk("\n[%s] thread[%x,%x] on core [%x,%x] enter\n  is_ins %d / %s / vaddr %x / cycle %d\n",
225__FUNCTION__, process->pid, this->trdid, local_cxy, this->core->lid,
226is_ins, hal_mmu_exception_str(excp_code), bad_vaddr, cycle);
227#endif
228
229   // analyse exception code
230    switch( excp_code )
231    {
232        case MMU_WRITE_PT1_UNMAPPED:      // can be non fatal
233        case MMU_WRITE_PT2_UNMAPPED:      // can be non fatal
234        case MMU_READ_PT1_UNMAPPED:       // can be non fatal
235        case MMU_READ_PT2_UNMAPPED:       // can be non fatal
236        {
237            // try to map the unmapped PTE
238            error = vmm_handle_page_fault( process, 
239                                           bad_vaddr >> CONFIG_PPM_PAGE_SHIFT );
240
241            if( error == EXCP_NON_FATAL )            // page-fault successfully handled
242            {
243
244#if DEBUG_HAL_EXCEPTIONS
245cycle = (uint32_t)hal_get_cycles();
246if( DEBUG_HAL_EXCEPTIONS < cycle )
247printk("\n[%s] thread[%x,%x] on core [%x,%x] exit\n  page-fault handled for vaddr = %x\n",
248__FUNCTION__, process->pid, this->trdid, local_cxy, this->core->lid, bad_vaddr );
249#endif
250 
251                return EXCP_NON_FATAL;
252            }
253            else if( error == EXCP_USER_ERROR )      // illegal vaddr
254            {
255                printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
256                "  %s : epc %x / badvaddr %x / is_ins %d\n",
257                __FUNCTION__, this->process->pid, this->trdid, local_cxy,
258                this->core->lid, (uint32_t)hal_get_cycles(),
259                hal_mmu_exception_str(excp_code), excPC, bad_vaddr, is_ins );
260
261                        return EXCP_USER_ERROR;
262            } 
263            else  // error == EXCP_KERNEL_PANIC 
264            {
265                printk("\n[PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
266                "  %s : epc %x / badvaddr %x / is_ins %d\n",
267                __FUNCTION__, this->process->pid, this->trdid, local_cxy,
268                this->core->lid, (uint32_t)hal_get_cycles(),
269                hal_mmu_exception_str(excp_code), excPC, bad_vaddr, is_ins );
270
271                        return EXCP_KERNEL_PANIC;
272            } 
273        }
274        case MMU_WRITE_PRIVILEGE_VIOLATION:  // illegal user error
275        case MMU_READ_PRIVILEGE_VIOLATION:   // illegal
276        {
277            printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
278            "  %s : epc %x / badvaddr %x / is_ins %d\n",
279            __FUNCTION__, this->process->pid, this->trdid, local_cxy,
280            this->core->lid, (uint32_t)hal_get_cycles(),
281            hal_mmu_exception_str(excp_code), excPC, bad_vaddr, is_ins );
282
283            return EXCP_USER_ERROR;
284        }
285        case MMU_WRITE_ACCESS_VIOLATION:    // can be non fatal if COW
286        {
287            // try to handle a possible COW
288            error = vmm_handle_cow( process,
289                                    bad_vaddr >> CONFIG_PPM_PAGE_SHIFT );
290
291            if( error == EXCP_NON_FATAL )        // COW successfully handled
292            {
293
294#if DEBUG_HAL_EXCEPTIONS
295cycle = (uint32_t)hal_get_cycles();
296if( DEBUG_HAL_EXCEPTIONS < cycle )
297printk("\n[%s] thread[%x,%x] exit / copy-on-write handled for vaddr = %x\n",
298__FUNCTION__, process->pid, this->trdid, bad_vaddr );
299#endif
300                return EXCP_NON_FATAL;
301            } 
302            else if( error == EXCP_USER_ERROR )  // illegal write access
303            {
304                    printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
305                    "  %s : epc %x / badvaddr %x / is_ins %d\n",
306                    __FUNCTION__, this->process->pid, this->trdid, local_cxy,
307                    this->core->lid, (uint32_t)hal_get_cycles(),
308                    hal_mmu_exception_str(excp_code), excPC, bad_vaddr, is_ins );
309
310                            return EXCP_USER_ERROR;
311            }
312            else   // error == EXCP_KERNEL_PANIC
313            {
314                printk("\n[PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
315                "  %s : epc %x / badvaddr %x / is_ins %d\n",
316                __FUNCTION__, this->process->pid, this->trdid, local_cxy,
317                this->core->lid, (uint32_t)hal_get_cycles(),
318                hal_mmu_exception_str(excp_code), excPC, bad_vaddr, is_ins );
319
320                        return EXCP_USER_ERROR;
321            }
322        }
323        case MMU_READ_EXEC_VIOLATION:        // user error
324        {
325            printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
326            "  %s : epc %x / badvaddr %x / is_ins %d\n",
327            __FUNCTION__, this->process->pid, this->trdid, local_cxy,
328            this->core->lid, (uint32_t)hal_get_cycles(),
329            hal_mmu_exception_str(excp_code), excPC, bad_vaddr, is_ins );
330
331            return EXCP_USER_ERROR;
332        }
333        default:                             // this is a kernel error   
334        {
335            printk("\n[PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
336            "  %s : epc %x / badvaddr %x / is_ins %d\n",
337            __FUNCTION__, this->process->pid, this->trdid, local_cxy,
338            this->core->lid, (uint32_t)hal_get_cycles(),
339            hal_mmu_exception_str(excp_code), excPC, bad_vaddr, is_ins );
340
341            return EXCP_KERNEL_PANIC;
342        }
343    } 
344} // end hal_mmu_exception()
345
346//////////////////////////////////////////////////////////////////////////////////////////
347// This function prints on the kernel terminal the saved context (core registers)
348// and the thread state of a faulty thread.
349//////////////////////////////////////////////////////////////////////////////////////////
350// @ this     : pointer on faulty thread descriptor.
351//////////////////////////////////////////////////////////////////////////////////////////
352static void hal_exception_dump( thread_t * this )
353{
354    core_t    * core    = this->core;
355    process_t * process = this->process;
356    reg_t     * uzone   = this->uzone_current;
357
358    // get pointers on TXT0 chdev
359    xptr_t    txt0_xp  = chdev_dir.txt_tx[0];
360    cxy_t     txt0_cxy = GET_CXY( txt0_xp );
361    chdev_t * txt0_ptr = GET_PTR( txt0_xp );
362
363    // get extended pointer on remote TXT0 chdev lock
364    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
365
366    // get TXT0 lock in busy waiting mode
367    remote_busylock_acquire( lock_xp );
368
369    nolock_printk("\n=== thread(%x,%x) / core[%d] / cycle %d ===\n",
370    process->pid, this->trdid, core->lid, (uint32_t)hal_get_cycles() );
371
372        nolock_printk("busylocks = %d / blocked_vector = %X / flags = %X\n\n",
373    this->busylocks, this->blocked, this->flags );
374
375    nolock_printk("c0_cr   %X  c0_epc  %X  c0_sr  %X  c0_th  %X\n",
376    uzone[UZ_CR], uzone[UZ_EPC], uzone[UZ_SR], uzone[UZ_TH] );
377
378    nolock_printk("c2_mode %X  c2_ptpr %X\n",
379    uzone[UZ_MODE], uzone[UZ_PTPR] );
380
381    nolock_printk("at_01   %X  v0_2    %X  v1_3   %X  a0_4   %X  a1_5   %X\n",
382        uzone[UZ_AT], uzone[UZ_V0], uzone[UZ_V1], uzone[UZ_A0], uzone[UZ_A1] );
383
384    nolock_printk("a2_6    %X  a3_7    %X  t0_8   %X  t1_9   %X  t2_10  %X\n",
385        uzone[UZ_A2], uzone[UZ_A3], uzone[UZ_T0], uzone[UZ_T1], uzone[UZ_T2] );
386 
387    nolock_printk("t3_11   %X  t4_12   %X  t5_13  %X  t6_14  %X  t7_15  %X\n",
388        uzone[UZ_T3], uzone[UZ_T4], uzone[UZ_T5], uzone[UZ_T6], uzone[UZ_T7] );
389
390    nolock_printk("s0_16   %X  s1_17   %X  s2_18  %X  s3_19  %X  s4_20  %X\n",
391        uzone[UZ_S0], uzone[UZ_S1], uzone[UZ_S2], uzone[UZ_S3], uzone[UZ_S4] );
392 
393    nolock_printk("s5_21   %X  s6_22   %X  s7_23  %X  t8_24  %X  t9_25  %X\n",
394        uzone[UZ_S5], uzone[UZ_S6], uzone[UZ_S7], uzone[UZ_T8], uzone[UZ_T9] );
395
396    nolock_printk("gp_28   %X  sp_29   %X  S8_30  %X  ra_31  %X\n",
397        uzone[UZ_GP], uzone[UZ_SP], uzone[UZ_S8], uzone[UZ_RA] );
398
399    // release the lock
400    remote_busylock_release( lock_xp );
401
402}  // end hal_exception_dump()
403
404/////////////////////////////
405void hal_do_exception( void )
406{
407    uint32_t   * uzone;
408    thread_t   * this;
409        error_t      error;
410        uint32_t     excCode;                  // 4 bits XCODE from CP0_CR
411        uint32_t     excPC;                    // fauty instruction address
412
413    // get pointer on faulty thread uzone
414    this  = CURRENT_THREAD;
415    uzone = (uint32_t *)CURRENT_THREAD->uzone_current;
416
417    // get XCODE and EPC from UZONE
418        excCode        = (uzone[UZ_CR] >> 2) & 0xF;
419    excPC          = uzone[UZ_EPC];
420
421#if DEBUG_HAL_EXCEPTIONS
422uint32_t cycle = (uint32_t)hal_get_cycles();
423if( DEBUG_HAL_EXCEPTIONS < cycle )
424printk("\n[%s] thread[%x,%x] enter / core[%x,%d] / epc %x / xcode %x / cycle %d\n",
425__FUNCTION__, this->process->pid, this->trdid,
426local_cxy, this->core->lid, excPC, excCode, cycle );
427#endif
428
429        switch(excCode)
430        {
431        case XCODE_DBE:     // Data Bus Error : can be non fatal if page fault
432        {
433                    error = hal_mmu_exception( this , excPC , false );  // data MMU exception
434            break;
435        }
436            case XCODE_IBE:     // Instruction Bus Error : can be non fatal if page fault
437        {
438                    error = hal_mmu_exception( this , excPC , true );   // ins MMU exception
439                    break;
440        }
441            case XCODE_CPU:    // Coprocessor unavailable : can be non fatal if FPU
442        {
443            if( ((uzone[UZ_CR] >> 28) & 0x3) == 1 )             // FPU
444            {
445                error = hal_fpu_exception( this );
446            }
447            else                                                // undefined coprocessor
448            {
449                printk("\n[USER_ERROR] in %s for thread[%x,%x]\n"
450                "   undefined coprocessor / epc %x\n",
451                __FUNCTION__, this->process->pid, this->trdid, excPC );
452
453                        error = EXCP_USER_ERROR;
454            }
455                    break;
456        }
457        case XCODE_OVR:    // Arithmetic Overflow : user fatal error
458        {
459            printk("\n[USER_ERROR] in %s for thread[%x,%x]\n"
460            "   arithmetic overflow / epc %x\n",
461            __FUNCTION__, this->process->pid, this->trdid, excPC );
462
463                    error = EXCP_USER_ERROR;
464                break;
465        }
466        case XCODE_RI:     // Reserved Instruction : user fatal error
467        {
468            printk("\n[USER_ERROR] in %s for thread[%x,%x]\n"
469            "   reserved instruction / epc %x\n",
470            __FUNCTION__, this->process->pid, this->trdid, excPC );
471
472                    error = EXCP_USER_ERROR;
473                break;
474        }
475        case XCODE_ADEL:   // user fatal error
476        {
477            printk("\n[USER_ERROR] in %s for thread[%x,%x]\n"
478            "   illegal data load address / epc %x / bad_address %x\n",
479            __FUNCTION__, this->process->pid, this->trdid, excPC, hal_get_bad_vaddr() );
480
481                    error = EXCP_USER_ERROR;
482                break;
483        }
484        case XCODE_ADES:   //   user fatal error
485        {
486            printk("\n[USER_ERROR] in %s for thread[%x,%x]\n"
487            "   illegal data store address / epc %x / bad_address %x\n",
488            __FUNCTION__, this->process->pid, this->trdid, excPC, hal_get_bad_vaddr() );
489
490                    error = EXCP_USER_ERROR;
491                break;
492        }
493        default:
494        {
495                    error = EXCP_KERNEL_PANIC;
496        }
497        }
498   
499    // analyse error code
500        if( error == EXCP_USER_ERROR )          //  user error => kill user process
501        {
502        hal_exception_dump( this );
503
504        sys_exit( EXIT_FAILURE );
505        }
506    else if( error == EXCP_KERNEL_PANIC )   // kernel error => kernel panic
507    {
508        hal_exception_dump( this );
509
510        hal_core_sleep();
511    }
512
513#if DEBUG_HAL_EXCEPTIONS
514cycle = (uint32_t)hal_get_cycles();
515if( DEBUG_HAL_EXCEPTIONS < cycle )
516printk("\n[%s] thread[%x,%x] exit / core[%x,%d] / epc %x / xcode %x / cycle %d\n",
517__FUNCTION__, this->process->pid, this->trdid,
518local_cxy, this->core->lid, excPC, excCode, cycle );
519#endif
520
521}  // end hal_do_exception()
522
523
Note: See TracBrowser for help on using the repository browser.