OpenCores
URL https://opencores.org/ocsvn/c0or1k/c0or1k/trunk

Subversion Repositories c0or1k

[/] [c0or1k/] [trunk/] [src/] [arch/] [arm/] [exception-common.c] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 drasko
/*
2
 * Common exception handling code
3
 *
4
 * Copyright (C) 2008 - 2010 B Labs Ltd.
5
 * Written by Bahadir Balban
6
 */
7
#include <l4/generic/scheduler.h>
8
#include <l4/generic/thread.h>
9
#include <l4/api/thread.h>
10
#include <l4/generic/space.h>
11
#include <l4/generic/tcb.h>
12
#include <l4/generic/platform.h>
13
#include <l4/generic/debug.h>
14
#include <l4/lib/printk.h>
15
#include <l4/api/ipc.h>
16
#include <l4/api/kip.h>
17
#include <l4/api/errno.h>
18
#include INC_ARCH(exception.h)
19
#include INC_GLUE(memlayout.h)
20
#include INC_GLUE(memory.h)
21
#include INC_GLUE(mapping.h)
22
#include INC_GLUE(message.h)
23
#include INC_GLUE(ipc.h)
24
#include INC_SUBARCH(mm.h)
25
 
26
 
27
void abort_die(void)
28
{
29
        disable_irqs();
30
        print_early("Unhandled kernel abort.\n");
31
        print_early("Kernel panic.\n");
32
        print_early("Halting system...\n");
33
        while (1)
34
                ;
35
}
36
 
37
struct ipc_state {
38
        u32 mr[MR_TOTAL];
39
        unsigned int flags;
40
};
41
 
42
void ipc_save_state(struct ipc_state *state)
43
{
44
        unsigned int *mr0_current = KTCB_REF_MR0(current);
45
 
46
        BUG_ON(!mr0_current);
47
 
48
        /* Save primary message registers */
49
        for (int i = 0; i < MR_TOTAL; i++)
50
                state->mr[i] = mr0_current[i];
51
 
52
        /* Save ipc flags */
53
        state->flags = tcb_get_ipc_flags(current);
54
}
55
 
56
void ipc_restore_state(struct ipc_state *state)
57
{
58
        unsigned int *mr0_current = KTCB_REF_MR0(current);
59
 
60
        BUG_ON(!mr0_current);
61
 
62
        /* Restore primary message registers */
63
        for (int i = 0; i < MR_TOTAL; i++)
64
                mr0_current[i] = state->mr[i];
65
 
66
        /* Restore ipc flags */
67
        tcb_set_ipc_flags(current, state->flags);
68
}
69
 
70
/* Send data fault ipc to the faulty task's pager */
71
int __attribute__((optimize("O0")))
72
fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far, u32 ipc_tag)
73
{
74
        int err;
75
 
76
        /* mr[0] has the fault tag. The rest is the fault structure */
77
        u32 mr[MR_TOTAL] = {
78
                [MR_TAG] = ipc_tag,
79
                [MR_SENDER] = current->tid
80
        };
81
 
82
        fault_kdata_t *fault = (fault_kdata_t *)&mr[MR_UNUSED_START];
83
 
84
        /* Fill in fault information to pass over during ipc */
85
        fault->faulty_pc = faulty_pc;
86
        fault->fsr = fsr;
87
        fault->far = far;
88
 
89
        /*
90
         * Write pte of the abort address,
91
         * which is different on pabt/dabt
92
         */
93
        if (is_prefetch_abort(fsr))
94
                fault->pte = virt_to_pte(faulty_pc);
95
        else
96
                fault->pte = virt_to_pte(far);
97
 
98
        /*
99
         * System calls save arguments (and message registers)
100
         * on the kernel stack. They are then referenced from
101
         * the caller's ktcb. Here, we forge a fault structure
102
         * as if an ipc syscall has occured. Then the reference
103
         * to the fault structure is set in the ktcb such that
104
         * it lies on the mr0 offset when referred as the syscall
105
         * context.
106
         */
107
 
108
        /*
109
         * Assign fault such that it overlaps
110
         * as the MR0 reference in ktcb.
111
         */
112
        current->syscall_regs = (syscall_context_t *)
113
                                ((unsigned long)&mr[0] -
114
                                 offsetof(syscall_context_t, r3));
115
 
116
        /* Set current flags to short ipc */
117
        tcb_set_ipc_flags(current, IPC_FLAGS_SHORT);
118
 
119
        /* Detect if a pager is self-faulting */
120
        if (current->tid == current->pagerid) {
121
                printk("Pager (%d) faulted on itself. "
122
                       "FSR: 0x%x, FAR: 0x%x, PC: 0x%x pte: 0x%x CPU%d Exiting.\n",
123
                       current->tid, fault->fsr, fault->far,
124
                       fault->faulty_pc, fault->pte, smp_get_cpuid());
125
                thread_destroy(current);
126
        }
127
 
128
        /* Send ipc to the task's pager */
129
        if ((err = ipc_sendrecv(current->pagerid,
130
                                current->pagerid, 0)) < 0) {
131
                        BUG_ON(current->nlocks);
132
 
133
                /* Return on interrupt */
134
                if (err == -EINTR) {
135
                        printk("Thread (%d) page-faulted "
136
                               "and got interrupted by its pager.\n",
137
                               current->tid);
138
                        return err;
139
                } else { /* Suspend on any other error */
140
                        printk("Thread (%d) faulted in kernel "
141
                               "and an error occured during "
142
                               "page-fault ipc. err=%d. "
143
                               "Suspending task.\n",
144
                               current->tid, err);
145
                        current->flags |= TASK_SUSPENDING;
146
                        sched_suspend_sync();
147
                }
148
        }
149
        return 0;
150
}
151
 
152
/*
153
 * When a task calls the kernel and the supplied user buffer is
154
 * not mapped, the kernel generates a page fault to the task's
155
 * pager so that the pager can make the decision on mapping the
156
 * buffer. Remember that if a task maps its own user buffer to
157
 * itself this way, the kernel can access it, since it shares
158
 * that task's page table.
159
 */
160
int pager_pagein_request(unsigned long addr, unsigned long size,
161
                         unsigned int flags)
162
{
163
        int err;
164
        u32 abort = 0;
165
        unsigned long npages = __pfn(align_up(size, PAGE_SIZE));
166
        struct ipc_state ipc_state;
167
 
168
        set_abort_type(abort, ABORT_TYPE_DATA);
169
 
170
        /* Save current ipc state */
171
        ipc_save_state(&ipc_state);
172
 
173
        /* For every page to be used by the
174
         * kernel send a page-in request */
175
        for (int i = 0; i < npages; i++)
176
                if ((err = fault_ipc_to_pager(0, abort,
177
                                              addr + (i * PAGE_SIZE),
178
                                              L4_IPC_TAG_PFAULT)) < 0)
179
                        return err;
180
 
181
        /* Restore ipc state */
182
        ipc_restore_state(&ipc_state);
183
 
184
        return 0;
185
}
186
 
187
/*
188
 * @r0: The address where the program counter was during the fault.
189
 * @r1: Contains the fault status register
190
 * @r2: Contains the fault address register
191
 */
192
void data_abort_handler(u32 faulted_pc, u32 dfsr, u32 dfar, u32 spsr)
193
{
194
        int ret;
195
 
196
        system_account_dabort();
197
 
198
        /* Indicate abort type on dfsr */
199
        set_abort_type(dfsr, ABORT_TYPE_DATA);
200
 
201
        dbg_abort("Data abort PC:0x%x, FAR: 0x%x, FSR: 0x%x, CPU%d\n",
202
                  faulted_pc, dfar, dfsr, smp_get_cpuid());
203
 
204
        /*
205
         * Check abort type and tell
206
         * if it's an irrecoverable fault
207
         */
208
        if ((ret = check_abort_type(faulted_pc, dfsr, dfar, spsr)) < 0)
209
                goto die; /* Die if irrecoverable */
210
        else if (ret == ABORT_HANDLED)
211
                return;
212
 
213
        /* Notify the pager */
214
        fault_ipc_to_pager(faulted_pc, dfsr, dfar, L4_IPC_TAG_PFAULT);
215
 
216
        /*
217
         * FIXME:
218
         * Check return value of pager, and also make a record of
219
         * the fault that has occured. We ought to expect progress
220
         * from the pager. If the same fault is occuring a number
221
         * of times consecutively, we might want to kill the pager.
222
         */
223
 
224
        /* See if current task has various flags set by its pager */
225
        if (current->flags & TASK_SUSPENDING) {
226
                BUG_ON(current->nlocks);
227
                sched_suspend_sync();
228
        }
229
 
230
        return;
231
die:
232
        dprintk("FAR:", dfar);
233
        dprintk("PC:", faulted_pc);
234
        abort_die();
235
}
236
 
237
void prefetch_abort_handler(u32 faulted_pc, u32 ifsr, u32 ifar, u32 spsr)
238
{
239
        int ret;
240
 
241
        system_account_pabort();
242
 
243
        /* Indicate abort type on dfsr */
244
        set_abort_type(ifsr, ABORT_TYPE_PREFETCH);
245
 
246
        dbg_abort("Prefetch abort PC:0x%x, FAR: 0x%x, FSR: 0x%x, CPU%d\n",
247
                  faulted_pc, ifar, ifsr, smp_get_cpuid());
248
 
249
        /*
250
         * Check abort type and tell
251
         * if it's an irrecoverable fault
252
         */
253
 
254
        if ((ret = check_abort_type(0, ifsr, ifar, spsr)) < 0)
255
                goto die; /* Die if irrecoverable */
256
        else if (ret == ABORT_HANDLED)
257
                return; /* Return if handled internally */
258
 
259
        /* Notify the pager */
260
        fault_ipc_to_pager(faulted_pc, ifsr, ifar, L4_IPC_TAG_PFAULT);
261
 
262
        /*
263
         * FIXME:
264
         * Check return value of pager, and also make a record of
265
         * the fault that has occured. We ought to expect progress
266
         * from the pager. If the same fault is occuring a number
267
         * of times consecutively, we might want to kill the pager.
268
         */
269
 
270
        /* See if current task has various flags set by its pager */
271
        if (current->flags & TASK_SUSPENDING) {
272
                BUG_ON(current->nlocks);
273
                sched_suspend_sync();
274
        }
275
 
276
        return;
277
die:
278
        dprintk("FAR:", ifar);
279
        abort_die();
280
 
281
}
282
 
283
void undefined_instr_handler(u32 undefined_address, u32 spsr, u32 lr)
284
{
285
        dbg_abort("Undefined instruction. PC:0x%x", undefined_address);
286
 
287
        system_account_undef_abort();
288
 
289
        fault_ipc_to_pager(undefined_address, 0, undefined_address,
290
                           L4_IPC_TAG_UNDEF_FAULT);
291
 
292
        if (!is_user_mode(spsr)) {
293
                dprintk("Undefined instruction occured in "
294
                        "non-user mode. addr=", undefined_address);
295
                goto die;
296
        }
297
 
298
        /* See if current task has various flags set by its pager */
299
        if (current->flags & TASK_SUSPENDING) {
300
                BUG_ON(current->nlocks);
301
                sched_suspend_sync();
302
        }
303
 
304
        return;
305
 
306
die:
307
        abort_die();
308
}
309
 
310
extern int current_irq_nest_count;
311
 
312
/*
313
 * This is called right where the nest count is increased
314
 * in case the nesting is beyond the predefined max limit.
315
 * It is another matter whether this limit is enough to
316
 * guarantee the kernel stack is not overflown.
317
 *
318
 * FIXME: Take measures to recover. (E.g. disable irqs etc)
319
 *
320
 * Note that this is called in irq context, and it *also*
321
 * thrashes the designated irq stack which is only 12 bytes.
322
 *
323
 * It really is assumed the system has come to a halt when
324
 * this happens.
325
 */
326
void irq_overnest_error(void)
327
{
328
        printk("Irqs nested beyond limit. Current count: %d",
329
                current_irq_nest_count);
330
        print_early("System halted...\n");
331
        while(1)
332
                ;
333
}
334
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.