source: trunk/hal/tsar_mips32/hal_do_exceptions.c @ 26

Last change on this file since 26 was 1, checked in by alain, 8 years ago

First import

File size: 15.6 KB
Line 
1/*
2 * hal_do_exception.c - implementation of exception handler for TSAR-MIPS32
3 *
4 * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
5 *          Alain Greiner (2016)
6 *
7 * Copyright (c) UPMC Sorbonne Universites
8 *
9 * This file is part of ALMOS-MKH.
10 *
11 * ALMOS-MKH.is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2.0 of the License.
14 *
15 * ALMOS-MKH.is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with ALMOS-MKH.; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25#include <types.h>
26#include <task.h>
27#include <thread.h>
28#include <kdmsg.h>
29#include <pmm.h>
30#include <vmm.h>
31#include <errno.h>
32#include <scheduler.h>
33#include <cpu.h>
34#include <spinlock.h>
35#include <distlock.h>
36#include <cpu-trace.h>
37#include <cpu-regs.h>
38
39//////////////////////////////////////////////////////////////////////////////////////////
40// This enum defines the relevant values for the XCODE field from CP0_CR register.
41//////////////////////////////////////////////////////////////////////////////////////////
42
43typedef enum
44{
45    XCODE_ADEL = 4,        // Illegal address for data load
46    XCODE_ADES = 5,        // Illegal address for data store
47    XCODE_IBE  = 6,        // Instruction MMU exception
48    XCODE_DBE  = 7,        // Data MMU exception
49    XCODE_RI   = 10,       // Reserved instruction exception
50    XCODE_FPU  = 11,       // FPU coprocessor exception
51    XCODE_OVR  = 12        // Arithmetic Overflow exception
52}
53xcode_values_t;
54
55//////////////////////////////////////////////////////////////////////////////////////////
56// This defines the masks used to analyse the TSAR MMU exception code
57//////////////////////////////////////////////////////////////////////////////////////////
58
59#define    TSAR_MMU_PAGE_UNMAPPED   0x0003    // page fault (PTE unmapped)
60#define    TSAR_MMU_USER_PRIVILEGE  0x0004    // user access to a kernel segment
61#define    TSAR_MMU_USER_WRITE      0x0008    // user access to non writable segment
62#define    TSAR_MMU_USER_EXEC       0x0010    // user access to non executable segment
63#define    TSAR_MMU_KERNEL_XTN      0x0020    // kernel illegal external access   
64#define    TSAR_MMU_KERNEL_PT1      0x0040    // kernel illegal PT1 access   
65#define    TSAR_MMU_KERNEL_PT2      0x0080    // kernel illegal PT2 access   
66#define    TSAR_MMU_KERNEL_DATA     0x0100    // kernel illegal data access
67
68//////////////////////////////////////////////////////////////////////////////////////////
69// This defines the masks used to get the TSAR MMU PTE attributes
70//////////////////////////////////////////////////////////////////////////////////////////
71
72#define    TSAR_MMU_PTE_V           0x80000000  // Valid
73#define    TSAR_MMU_PTE_T           0x40000000  // Small Page
74#define    TSAR_MMU_PTE_C           0x08000000  // Cachable 
75#define    TSAR_MMU_PTE_W           0x04000000  // Writable 
76#define    TSAR_MMU_PTE_X           0x02000000  // eXecutable
77#define    TSAR_MMU_PTE_U           0x01000000  // User accessible
78
79//////////////////////////////////////////////////////////////////////////////////////////
80// This enum defines the various types of error code returned to the hal_do_exception()
81// function by the mmu_exception_handler() and fpu_exception_handler().
82//////////////////////////////////////////////////////////////////////////////////////////
83
84typedef enum
85{
86        EXCP_SOLVED        = 0,   // No error => the unmapped PTE has been mapped
87        EXCP_USER_ERROR    = 1,   // User error => user process will receive a SIGSEGV
88    EXCP_KERNEL_PANIC  = 2,   // Kernel error => kernel panic
89}
90mmu_excp_t;
91
92/////////////////////////////////////////////////////////////////////////////////////////
93// This remote_spinlock is a global variable defined in all clusters,
94// but only the spinlock implemented in the boot cluster is used.
95/////////////////////////////////////////////////////////////////////////////////////////
96
97remote_spinlock_t  exception_lock;
98
99
100/////////////////////////////////////////////////////////////////////////////////////////
101// This function is called by the hal_do_exception() function when a "FPU unusable"
102// exception has been detected by the calling thread.
103// This function check in CP0_CR register that the unavailable CPU is actually CP1,
104// it saves the FPU context in the owner thread descriptor, and restore the FPU context
105// from the calling thread descriptor.
106/////////////////////////////////////////////////////////////////////////////////////////
107static error_t fpu_exception_handler( reg_t    * regs_tbl )
108{
109    thread_t * this = CURRENT_THREAD;    // calling thread
110        core_t   * core = this->core;        // associated core
111
112    // check coprocessor index
113        if( ((regs_tbl[CR] >> 28) & 0x3) != 1 )
114    {
115                printk(WARNING, "%s for thread %x : bad coprocessor indexn",
116                       __FUNCTION__ , this->trdid );
117                return EXCP_KERNEL_PANIC;
118    }
119 
120        hal_fpu_enable();
121
122        if( (core->fpu_owner != NULL) && (core->fpu_owner != this) )
123        {
124                hal_fpu_context_save ( &core->fpu_owner->uzone );
125        }
126
127        hal_fpu_context_restore( &this->uzone );
128        cpu->fpu_owner = this;
129
130        return EXCP_SOLVED;
131}
132
133
134///////////////////////////////////////////////////////////////////////////////////
135// This function is called by the hal_do_exception() function when a TSAR-MMU
136// exception has been detected. There is three possible actions :
137// 1) simple page fault => page table is updated and thread resume.
138// 2) user error        => user process is killed.
139// 3) kernel error      => system crash.
140///////////////////////////////////////////////////////////////////////////////////
141// @ excp_code  : generic exception code returned by TSAR-MMU
142// @ bad_vaddr  : faulty virtual address
143// @ return EXCP_RESOLVED / MMU_EXCP_USER_ERROR / MMU_EXCP_KERNEL_PANIC
144///////////////////////////////////////////////////////////////////////////////////
145static error_t mmu_exception_handler( uint32_t   excp_code, 
146                                      uint32_t   bad_vaddr )
147{
148        thread_t       * this;        // calling thread pointer
149        vseg_t         * vseg;        // vseg containing the bad_vaddr
150        process_t      * process;     // local process descriptor
151        vmm_t          * vmm;         // VMM for calling thread
152    vpn_t            vpn;         // VPN for bad_vaddr
153    uint32_t         flags;       // vseg flags
154    error_t          error;       // return value
155
156        this        = CURRENT_THREAD;
157        process     = this->process;
158        vmm         = &process->vmm;
159    vpn         = bad_vaddr>>CONFIG_PPM_PAGE_SHIFT;
160
161        vmm_dmsg(2, "%s enters for thread %x in process %x / bad_vaddr = %x / excep_code = %x\n", 
162                 __FUNCTION__, this->trdid , process->pid , bad_vaddr , excep_code );
163
164    // a kernel thread should not rise an MMU exception
165        if( this->type != T_USER )
166        {
167                printk(WARNING, "%s for thread %x : it's a kernel thread / vaddr = %x\n",
168                       __FUNCTION__ , this->trdid , bad_vaddr );
169                return EXCP_KERNEL_PANIC;
170        }
171 
172    // enable IRQs
173        hal_enable_irq( NULL );
174
175    // update user_time
176        tm_usr_compute( this );
177
178    // vaddr must be contained in a registered vseg
179    vseg = vmm_get_vseg( process , bad_vaddr );
180
181    if( vseg == NULL )   // vseg not found
182        {
183        if( cxy != cxy_ref )  // try to get vseg from reference VMM
184        {
185            rpc_vmm_get_ref_vseg_client( cxy_ref , process_ref , bad_vaddr , &vseg );
186        }
187
188        if( vseg == NULL )    // illegal user vaddr => return user error
189        {
190            printk(WARNING, "%s for thread %x : no vseg for vaddr = %x\n",
191                   __FUNCTION__ , this->trdid , bad_vaddr ); 
192            hal_disable_irq( NULL );
193                    return EXCP_USER_ERROR;
194        }
195        else                  // legal vaddr => get vseg flags
196        {
197            flags = vseg->flags;
198        }
199    }
200
201        vmm_dmsg(2, "%s found vseg for thread %x / vseg_base = %x / vseg_flags = %x\n", 
202                         __FUNCTION__ , this->trdid , vseg->begin , vseg->flags );
203
204    // analyse TSAR MMU exception code
205    if( excp_code & TSAR_MMU_UNMAPPED )
206    {
207        // try to map the unmapped PTE
208        error = vmm_handle_page_fault( process , vseg , vpn );
209        if( error )
210        {
211            printk(WARNING, "%s for thread %x : cannot allocate memory for new PTE\n",
212                   __FUNCTION__ , this->trdid , bad_vaddr ); 
213            hal_disable_irq( NULL );
214                    return EXCP_KERNEL_PANIC;
215        }
216        else
217        {
218            vmm_dmsg(2, "%s page fault succesfully handled for vaddr = %x in thread %x\n",
219                             __FUNCTION__ , bad_vaddr , this->trdid );
220 
221            // page fault successfully handled
222            hal_disable_irq( NULL );
223
224            hal_yield();  // TODO Pourquoi ce yield ?
225
226            // update kernel_time
227            tm_sys_compute(this);
228
229            return EXCP_SOLVED;
230        }
231    }
232    else if( excp_code & TSAR_MMU_USER_PRIVILEGE )
233    {
234        printk(WARNING,"%s for thread %x : user access to kernel vseg at vaddr = %x\n",
235               __FUNCTION__ , thread->trdid , bad_vaddr );
236        return EXCP_USER_ERROR;
237    }
238    else if( excp_code & TSAR_MMU_USER_EXEC )
239    {
240        printk(WARNING,"%s for thread %x : access to non-exec vseg at vaddr = %x\n"
241               __FUNCTION__ , thread->trdid , bad_vaddr );
242        return EXCP_USER_ERROR;
243    }
244    else if( excp_code & TSAR_MMU_USER_WRITE )
245    {
246        printk(WARNING,"%s for thread %x : write to non-writable vseg at vaddr = %x\n"
247               __FUNCTION__ , thread->trdid , bad_vaddr );
248        return EXCP_USER_ERROR;
249    }
250    else if( excp_code & TSAR_MMU_KERNEL_XTN )
251    {
252        printk(WARNING,"%s for thread %x : kernel illegal access to external address = %x\n"
253               __FUNCTION__ , thread->trdid , bad_vaddr );
254        return EXCP_KERNEL_PANIC;
255    }
256    else if( excp_code & TSAR_MMU_KERNEL_PT1 )
257    {
258        printk(WARNING,"%s for thread %x : kernel bus error accessing PT1 / vaddr = %x\n"
259               __FUNCTION__ , thread->trdid , bad_vaddr );
260        return EXCP_KERNEL_PANIC;
261    }
262    else if( excp_code & TSAR_MMU_KERNEL_PT2 )
263    {
264        printk(WARNING,"%s for thread %x : kernel bus error accessing PT2 / vaddr = %x\n"
265               __FUNCTION__ , thread->trdid , bad_vaddr );
266        return EXCP_KERNEL_PANIC;
267    }
268    else if( excp_code & TSAR_MMU_KERNEL_DATA )
269    {
270        printk(WARNING,"%s for thread %x : kernel bus error accessing DATA / vaddr = %x\n"
271               __FUNCTION__ , thread->trdid , bad_vaddr );
272        return EXCP_KERNEL_PANIC;
273    }
274    else
275    {
276        printk(WARNING,"%s for thread %x : undefined MMUexception code ??? / vaddr = %x\n"
277               __FUNCTION__ , thread->trdid , bad_vaddr );
278        return EXCP_KERNEL_PANIC;
279    }
280 
281} // end mmu_exception_handler()
282
283
284///////////////////////////////////////
285void hal_do_exception( thread_t * this, 
286                       gid_t      gid, 
287                       reg_t    * regs_tbl )
288{
289        error_t             error;
290        uint32_t            excCode;               // XCODE from CP0_CR
291        uint32_t            mmu_iexcp_code;        // MMU IEXCP_CODE from CP2
292        uint32_t            mmu_ibad_vaddr;        // MMU IBAD_VADDR from CP2
293        uint32_t            mmu_dexcp_code;        // MMU DEXCP_CODE from CP2
294        uint32_t            mmu_dbad_vaddr;        // MMU BDAD_VADDR from CP2
295        bool_t              isInKernelMode;
296        mmu_except_info_t * entry;
297        hal_except_info_t * execErr;
298
299    // get XCODE from CP0_CR register
300        excCode        = (regs_tbl[CR] >> 2) & 0x1F;
301
302    // get relevant values from CP2 registers
303        mmu_iexcp_code = mips_get_cp2(MMU_IETR, 0);
304        mmu_ibad_vaddr = mips_get_cp2(MMU_IBVAR, 0);
305        mmu_dexcp_code = mips_get_cp2(MMU_DETR, 0);
306        mmu_dbad_vaddr = mips_get_cp2(MMU_DBVAR, 0);
307
308        switch(excCode)
309        {
310        case XCODE_IBE:
311                error = mmu_exception_handler( mmu_iexcp_code , mmu_ibad_vaddr );
312                break;
313        case XCODE_DBE:
314                error = mmu_exception_handler( mmu_dexcp_code , mmu_dbad_vaddr );
315                break;
316        case XCODE_CPU:
317                error = fpu_exception_handler( regs_tbl );
318                break;
319        default:
320                error = EXCP_KERNEL_PANIC;
321                break;
322        }
323
324    // analyse error code
325        if( error == EXCP_SOLVED )          // page fault successfully handled => just return
326    { 
327       return;
328    }
329
330        else if( error == EXCP_USER_ERROR ) //  user error => kill the user process and return
331        {
332//      TODO [AG]
333//              uspace_start = (uint32_t) &__uspace_start;
334//              uspace_end   = (uint32_t) &__uspace_end;
335//
336//              if((regs_tbl[EPC] >= uspace_start) && (regs_tbl[EPC] <= uspace_end))
337//              {
338//                      regs_tbl[EPC] = (reg_t) &hal_uspace_error;
339//                      regs_tbl[MMU_MD] = (reg_t) 0x3;//MMU_MODE OFF
340//                      return;
341//              }
342        }
343    else                                // kernel error => kernel panic
344    {
345        // take the exception_lock located in boot_cluster
346        cxy_t  boot_cxy = LOCAL_CLUSTER->boot_cxy;
347            remote_spinlock_lock( XPTR( boot_cxy , &exception_lock ) );
348
349        thread_t  * this    = CURRENT_THREAD;
350        process_t * process = this->process;
351
352        // dump registers values
353
354        except_dmsg("====================================================================\n");
355        except_dmsg("Kernel Panic: thread %x in process %x on core %x at cycle %d\n",
356                this->trdid , process->pid , gid , hal_time_stamp() );
357
358        except_dmsg("Processor State:\n");
359 
360        except_dmsg("CR:   %x\tEPC:  %x\tSR:   %x\tSP:    %x\tUSR SP %x\n",
361                    regs_tbl[CR],regs_tbl[EPC],regs_tbl[SR],regs_tbl[SP],this->uzone.regs[SP]);
362
363        except_dmsg("at_1  %x\tv0_2  %x\t\tv1_3  %x\ta0_4   %x\ta1_5   %x\n",
364                    regs_tbl[AT],regs_tbl[V0],regs_tbl[V1],regs_tbl[A0],regs_tbl[A1]);
365
366        except_dmsg("a2_6  %x\t\ta3_7  %x\tt0_8  %x\tt1_9   %x\tt2_10  %x\n",
367                    regs_tbl[A2],regs_tbl[A3],regs_tbl[T0],regs_tbl[T1],regs_tbl[T2]);
368 
369        except_dmsg("t3_11 %x\tt4_12 %x\t\tt5_13 %x\tt6_14  %x\tt7_15  %x\n",
370                    regs_tbl[T3],regs_tbl[T4],regs_tbl[T5],regs_tbl[T6],regs_tbl[T7]);
371
372        except_dmsg("t8_24 %x\t\tt9_25 %x\tgp_28 %x\tc0_hi  %x\tc0_lo  %x\n",
373                    regs_tbl[T8],regs_tbl[T9],regs_tbl[GP],regs_tbl[HI],regs_tbl[LO]);
374
375        except_dmsg("s0_16 %x\ts1_17 %x\ts2_18 %x\ts3_19  %x\ts4_20  %x\n",
376                    regs_tbl[S0],regs_tbl[S1],regs_tbl[S2],regs_tbl[S3],regs_tbl[S4]);
377 
378        except_dmsg("s5_21 %x\ts6_22 %x\t\ts7_23 %x\ts8_30  %x\tra_31  %x\n\n",
379                    regs_tbl[S5],regs_tbl[S6],regs_tbl[S7],regs_tbl[S8],regs_tbl[RA]);
380
381        except_dmsg("Thread State %x\n"
382                "\tsys_stack_top = %x\n"
383                "tusr_stack      = %x\n"
384                "\tutls          = %x\n"
385                "\tstate         = %s\n"
386                "\tlocks         = %d\n",
387                    this->trdid,
388                        this->uzone.regs[KSP], 
389                this->uzone.regs[SP], 
390                this->uzone.regs[TLS_K1],
391                thread_get_state_name( this->state ), 
392                this->locks_count);
393
394        isInKernelMode = (regs_tbl[SR] & 0x10) ? false : true;
395
396        except_dmsg("\nIs in kernel: %s\n", (isInKernelMode) ? "YES" : "NO");
397
398        if(isInKernelMode)
399        {
400                execErr = hal_except_get_entry(excCode);
401                if(regs_tbl[EPC] >= __ktext_start && regs_tbl[EPC] <= __ktext_end)
402                        instContent = *((uint32_t*) regs_tbl[EPC]);
403                else
404                        instContent = 0;
405
406                except_dmsg("Pid %d, Cpu %d, Inst. %x, Exception : code %d, name %s, description %s, bad address %x\n",
407                            this->task->pid,
408                            gid,
409                            instContent,
410                            excCode, 
411                            execErr->name, 
412                            execErr->desc, 
413                            hal_get_bad_vaddr()
414                                );
415       
416        }
417
418
419        except_dmsg("====================================================================\n");
420
421    // release exception lock
422        remote_spinlock_unlock( XPTR( boot_cxy , &exception_lock ) );
423
424        sched_exit(this);        // TODO ??? [AG]
425        while(entry != NULL);
426}
Note: See TracBrowser for help on using the repository browser.