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

Last change on this file since 505 was 505, checked in by viala@…, 3 years ago

[hal] Fix protoypes and add headers in hal mips32 implementation.

Fix types mismatch between implementation and interface in .h.
Add header where they were absent.

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