source: trunk/kernel/kern/do_exception.c @ 375

Last change on this file since 375 was 369, checked in by max@…, 7 years ago

style

File size: 10.4 KB
RevLine 
[16]1/*
2 * do_exception.c - architecture independant exception handler implementation.
[369]3 *
[16]4 * Author        Alain Greiner (2016)
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_types.h>
25#include <hal_irqmask.h>
26#include <kernel_config.h>
27#include <thread.h>
28#include <printk.h>
29#include <do_exception.h>
30
31//////////////////////////////////////////////////////////////////////////////////////////
32// This file containss the architecture independant exception handler.
33//
34// It is called by the architecture specific hal_do_exception() function.
35// The fatal errors are mostly handled by the architecture specific handler,
36// and this generic exception handler handles only two non fatal exceptions types:
37// - MMU page fault
38// - FPU unavailable
39//
40// As some MMU exceptions can be fatal, the returned error code can take three values:
41// - Non Fatal Exception
42// - Fatal User Error
43// - Kernel Panic Error
44//////////////////////////////////////////////////////////////////////////////////////////
45
46/////////////////////////////////////////////////////////////////////////////////////////
[369]47// This remote_spinlock is a kernel global variable defined in all clusters.
[16]48// It must be used by the architecture-specific exception handlers to display
49// error messages on the kernel terminal. Only the spinlock in the I/O cluster is used.
50/////////////////////////////////////////////////////////////////////////////////////////
51
52remote_spinlock_t  exception_lock;
53
54
55//////////////////////////////////////////////////////////////////////////////////////////
56// This static function is called by the generic do_exception() handler when an MMU
[369]57// exception has been detected.
[16]58// It get the relevant exception arguments from the MMU.
59// It signal a fatal error in case of illegal access. In case of page fault,
60// it checks that the faulty address belongs to a registered vseg. It update the local
61// vseg list from the reference cluster if required, and signal a fatal user error
62// in case of illegal virtual address. Finally, it updates the local page table from the
63// reference cluster.
64//////////////////////////////////////////////////////////////////////////////////////////
65// @ this     : pointer on faulty thread descriptor.
66// @ return EXCP_NON_FATAL / EXCP_USER_ERROR / EXCP_KERNEL_PANIC
67//////////////////////////////////////////////////////////////////////////////////////////
[369]68static error_t mmu_exception( thread_t * this )
[16]69{
[369]70    vseg_t         * vseg;        // vseg containing the bad_vaddr
71    process_t      * process;     // local process descriptor
[16]72    error_t          error;       // return value
73
74    reg_t            mmu_ins_excp_code;
75    reg_t            mmu_ins_bad_vaddr;
76    reg_t            mmu_dat_excp_code;
77    reg_t            mmu_dat_bad_vaddr;
78
79    intptr_t         bad_vaddr;
80    uint32_t         excp_code;
[369]81
[16]82    process     = this->process;
83
84    // get relevant values from MMU
[369]85    hal_get_mmu_excp( &mmu_ins_excp_code,
86                      &mmu_ins_bad_vaddr,
87                      &mmu_dat_excp_code,
88                      &mmu_dat_bad_vaddr );
[16]89
90    // get exception code and faulty vaddr
91    if( mmu_ins_excp_code )
92    {
93        excp_code = mmu_ins_excp_code;
94        bad_vaddr = mmu_ins_bad_vaddr;
95    }
96    else if( mmu_dat_excp_code )
97    {
98        excp_code = mmu_dat_excp_code;
99        bad_vaddr = mmu_dat_bad_vaddr;
100    }
101    else
102    {
103         return EXCP_NON_FATAL;
104    }
105
[369]106    vmm_dmsg("\n[INFO] %s : enters for thread %x / process %x"
107             " / bad_vaddr = %x / excep_code = %x\n",
108             __FUNCTION__, this->trdid , process->pid , bad_vaddr , excp_code );
[16]109
110    // a kernel thread should not rise an MMU exception
[369]111    if( this->type != THREAD_USER )
112    {
113        printk("\n[PANIC] in %s : thread %x is a kernel thread / vaddr = %x\n",
114               __FUNCTION__ , this->trdid , bad_vaddr );
115        return EXCP_KERNEL_PANIC;
116    }
[16]117
[369]118    // enable IRQs
119    hal_enable_irq( NULL );
120
[16]121    // vaddr must be contained in a registered vseg
122    vseg = vmm_get_vseg( process , bad_vaddr );
123
124    if( vseg == NULL )   // vseg not found in local cluster
[369]125    {
[16]126        // get extended pointer on reference process
127        xptr_t ref_xp = process->ref_xp;
128
[369]129        // get cluster and local pointer on reference process
[16]130        cxy_t       ref_cxy = GET_CXY( ref_xp );
131        process_t * ref_ptr = (process_t *)GET_PTR( ref_xp );
132
133        if( local_cxy != ref_cxy )   // reference process is remote
134        {
135            // get extended pointer on reference vseg
136            xptr_t vseg_xp;
137            rpc_vmm_get_ref_vseg_client( ref_cxy , ref_ptr , bad_vaddr , &vseg_xp );
[369]138
139            if( vseg == NULL )          // vseg not found => illegal user vaddr
[16]140            {
141                printk("\n[ERROR] in %s for thread %x : illegal vaddr = %x\n",
142                       __FUNCTION__ , this->trdid , bad_vaddr );
143
144                hal_disable_irq( NULL );
[369]145                return EXCP_USER_ERROR;
[16]146            }
147            else                        // vseg found => make a local copy
148            {
149                // allocate a vseg in local cluster
150                vseg = vseg_alloc();
151
152                if( vseg == NULL )
153                {
[369]154                    printk("\n[PANIC] in %s : no memory for vseg / thread = %x\n",
155                           __FUNCTION__ , this->trdid );
[16]156                    hal_disable_irq( NULL );
[369]157                    return EXCP_KERNEL_PANIC;
[16]158                }
159
160                // initialise local vseg from reference
161                vseg_init_from_ref( vseg , ref_xp );
162
163                // register local vseg in local VMM
164                error = vseg_attach( &process->vmm , vseg );
165            }
166        }
167        else                   // reference is local => illegal user vaddr
168        {
169            printk("\n[ERROR] in %s for thread %x : illegal vaddr = %x\n",
170                   __FUNCTION__ , this->trdid , bad_vaddr );
171
172            hal_disable_irq( NULL );
[369]173            return EXCP_USER_ERROR;
[16]174        }
175    }
176
[369]177    vmm_dmsg("\n[INFO] %s : found vseg for thread %x / vseg_min = %x / vseg_max = %x\n",
178             __FUNCTION__ , this->trdid , vseg->min , vseg->max );
[16]179
180    // analyse exception code
181    if( excp_code & MMU_EXCP_PAGE_UNMAPPED )
182    {
183        // try to map the unmapped PTE
[369]184        error = vmm_handle_page_fault( process,
[16]185                                       vseg,
186                                       bad_vaddr >> CONFIG_PPM_PAGE_SHIFT );  // vpn
187
188        if( error )
189        {
190            printk("\n[PANIC] in %s for thread %x : cannot map legal vaddr = %x\n",
191               __FUNCTION__ , this->trdid , bad_vaddr );
192
193            hal_disable_irq( NULL );
[369]194            return EXCP_KERNEL_PANIC;
[16]195        }
196        else
197        {
198            vmm_dmsg("\n[INFO] %s : page fault handled for vaddr = %x in thread %x\n",
[369]199                     __FUNCTION__ , bad_vaddr , this->trdid );
200
[16]201            // page fault successfully handled
202            hal_disable_irq( NULL );
203
204            // TODO Pourquoi ce yield ? [AG]
[369]205            // sched_yield();
[16]206
207            return EXCP_NON_FATAL;
208        }
209    }
210    else if( excp_code & MMU_EXCP_USER_PRIVILEGE )
211    {
212        printk("\n[ERROR] in %s for thread %x : user access to kernel vseg at vaddr = %x\n",
213               __FUNCTION__ , this->trdid , bad_vaddr );
214
215        hal_disable_irq( NULL );
216        return EXCP_USER_ERROR;
217    }
218    else if( excp_code & MMU_EXCP_USER_EXEC )
219    {
220        printk("\n[ERROR] in %s for thread %x : access to non-exec vseg at vaddr = %x\n",
221               __FUNCTION__ , this->trdid , bad_vaddr );
222
223        hal_disable_irq( NULL );
224        return EXCP_USER_ERROR;
225    }
226    else if( excp_code & MMU_EXCP_USER_WRITE )
227    {
228        printk("\n[ERROR] in %s for thread %x : write to non-writable vseg at vaddr = %x\n",
229               __FUNCTION__ , this->trdid , bad_vaddr );
230
231        hal_disable_irq( NULL );
232        return EXCP_USER_ERROR;
233    }
234
[369]235    else  // this is a kernel error => panic
[16]236    {
237        printk("\n[PANIC] in %s for thread %x : kernel exception = %x / vaddr = %x\n",
238               __FUNCTION__ , this->trdid , excp_code , bad_vaddr );
239
240        hal_disable_irq( NULL );
241        return EXCP_KERNEL_PANIC;
242    }
[369]243
[16]244} // end mmu_exception_handler()
245
246//////////////////////////////////////////////////////////////////////////////////////////
[369]247// This static function is called by the generic do_exception() handler when a
248// FPU Coprocessor Unavailable exception has been detected by the calling thread.
249// It enables the FPU, It saves the current FPU context in the current owner thread
[16]250// descriptor if required, and restore the FPU context from the calling thread descriptor.
251//////////////////////////////////////////////////////////////////////////////////////////
252// @ thread     : pointer on faulty thread descriptor.
253// @ return always EXCP_NON_FATAL
254//////////////////////////////////////////////////////////////////////////////////////////
255static error_t fpu_exception( thread_t * this )
256{
[369]257    core_t   * core = this->core;
[16]258
[369]259    // enable FPU
260    hal_fpu_enable();
[16]261
[369]262    // save FPU context in current owner thread if required
263    if( core->fpu_owner != NULL )
[16]264    {
265        if( core->fpu_owner != this )
[369]266        {
267            hal_fpu_context_save ( core->fpu_owner->fpu_context );
[16]268        }
[369]269    }
[16]270
271    // attach the FPU to the requesting thread
[369]272    hal_fpu_context_restore( this->fpu_context );
273    core->fpu_owner = this;
[16]274
[369]275    return EXCP_NON_FATAL;
[16]276}
277
278
279//////////////////////////////////////
280error_t do_exception( thread_t * this,
281                      bool_t     is_mmu )
282{
283    error_t error;
284
[369]285    // update user time
286    thread_user_time_update( this );
[16]287
288    // try to handle non fatal exception
289    if( is_mmu ) error = mmu_exception( this );
290    else         error = fpu_exception( this );
[369]291
[16]292    // handle pending signals for interrupted thread
293    thread_signals_handle( this );
294
[369]295    // update kernel time
296    thread_kernel_time_update( this );
[16]297
298    return error;
299}
Note: See TracBrowser for help on using the repository browser.