1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
|
/*
* Exception handling for Microblaze
*
* Rewriten interrupt handling
*
* Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
* Copyright (C) 2008-2009 PetaLogix
*
* uClinux customisation (C) 2005 John Williams
*
* MMU code derived from arch/ppc/kernel/head_4xx.S:
* Copyright (C) 1995-1996 Gary Thomas <gdt@linuxppc.org>
* Initial PowerPC version.
* Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu>
* Rewritten for PReP
* Copyright (C) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
* Low-level exception handers, MMU support, and rewrite.
* Copyright (C) 1997 Dan Malek <dmalek@jlc.net>
* PowerPC 8xx modifications.
* Copyright (C) 1998-1999 TiVo, Inc.
* PowerPC 403GCX modifications.
* Copyright (C) 1999 Grant Erickson <grant@lcse.umn.edu>
* PowerPC 403GCX/405GP modifications.
* Copyright 2000 MontaVista Software Inc.
* PPC405 modifications
* PowerPC 403GCX/405GP modifications.
* Author: MontaVista Software, Inc.
* frank_rowand@mvista.com or source@mvista.com
* debbie_chu@mvista.com
*
* Original code
* Copyright (C) 2004 Xilinx, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
/*
* Here are the handlers which don't require enabling translation
* and calling other kernel code thus we can keep their design very simple
* and do all processing in real mode. All what they need is a valid current
* (that is an issue for the CONFIG_REGISTER_TASK_PTR case)
* This handlers use r3,r4,r5,r6 and optionally r[current] to work therefore
* these registers are saved/restored
* The handlers which require translation are in entry.S --KAA
*
* Microblaze HW Exception Handler
* - Non self-modifying exception handler for the following exception conditions
* - Unalignment
* - Instruction bus error
* - Data bus error
* - Illegal instruction opcode
* - Divide-by-zero
*
* Note we disable interrupts during exception handling, otherwise we will
* possibly get multiple re-entrancy if interrupt handles themselves cause
* exceptions. JW
*/
#include <asm/exceptions.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include <asm/entry.h>
#include <asm/current.h>
#include <linux/linkage.h>
#include <asm/mmu.h>
#include <asm/pgtable.h>
#include <asm/asm-offsets.h>
/* Helpful Macros */
#define EX_HANDLER_STACK_SIZ (4*19)
#define NUM_TO_REG(num) r ## num
#define LWREG_NOP \
bri ex_handler_unhandled; \
nop;
#define SWREG_NOP \
bri ex_handler_unhandled; \
nop;
/* FIXME this is weird - for noMMU kernel is not possible to use brid
* instruction which can shorten executed time
*/
/* r3 is the source */
#define R3_TO_LWREG_V(regnum) \
swi r3, r1, 4 * regnum; \
bri ex_handler_done;
/* r3 is the source */
#define R3_TO_LWREG(regnum) \
or NUM_TO_REG (regnum), r0, r3; \
bri ex_handler_done;
/* r3 is the target */
#define SWREG_TO_R3_V(regnum) \
lwi r3, r1, 4 * regnum; \
bri ex_sw_tail;
/* r3 is the target */
#define SWREG_TO_R3(regnum) \
or r3, r0, NUM_TO_REG (regnum); \
bri ex_sw_tail;
.extern other_exception_handler /* Defined in exception.c */
/*
* hw_exception_handler - Handler for exceptions
*
* Exception handler notes:
* - Handles all exceptions
* - Does not handle unaligned exceptions during load into r17, r1, r0.
* - Does not handle unaligned exceptions during store from r17 (cannot be
* done) and r1 (slows down common case)
*
* Relevant register structures
*
* EAR - |----|----|----|----|----|----|----|----|
* - < ## 32 bit faulting address ## >
*
* ESR - |----|----|----|----|----| - | - |-----|-----|
* - W S REG EXC
*
*
* STACK FRAME STRUCTURE (for NO_MMU)
* ---------------------------------
*
* +-------------+ + 0
* | MSR |
* +-------------+ + 4
* | r1 |
* | . |
* | . |
* | . |
* | . |
* | r18 |
* +-------------+ + 76
* | . |
* | . |
*
* NO_MMU kernel use the same r0_ram pointed space - look to vmlinux.lds.S
* which is used for storing register values - old style was, that value were
* stored in stack but in case of failure you lost information about register.
* Currently you can see register value in memory in specific place.
* In compare to with previous solution the speed should be the same.
*
* MMU exception handler has different handling compare to no MMU kernel.
* Exception handler use jump table for directing of what happen. For MMU kernel
* is this approach better because MMU relate exception are handled by asm code
* in this file. In compare to with MMU expect of unaligned exception
* is everything handled by C code.
*/
/*
* every of these handlers is entered having R3/4/5/6/11/current saved on stack
* and clobbered so care should be taken to restore them if someone is going to
* return from exception
*/
/* wrappers to restore state before coming to entry.S */
.global _hw_exception_handler
.section .text
.align 4
.ent _hw_exception_handler
_hw_exception_handler:
addik r1, r1, -(EX_HANDLER_STACK_SIZ); /* Create stack frame */
swi r3, r1, PT_R3
swi r4, r1, PT_R4
swi r5, r1, PT_R5
swi r6, r1, PT_R6
mfs r5, rmsr;
nop
swi r5, r1, 0;
mfs r4, rbtr /* Save BTR before jumping to handler */
nop
mfs r3, resr
nop
andi r5, r3, 0x1000; /* Check ESR[DS] */
beqi r5, not_in_delay_slot; /* Branch if ESR[DS] not set */
mfs r17, rbtr; /* ESR[DS] set - return address in BTR */
nop
not_in_delay_slot:
swi r17, r1, PT_R17
andi r5, r3, 0x1F; /* Extract ESR[EXC] */
/* Exceptions enabled here. This will allow nested exceptions */
mfs r6, rmsr;
nop
swi r6, r1, 0; /* RMSR_OFFSET */
ori r6, r6, 0x100; /* Turn ON the EE bit */
andi r6, r6, ~2; /* Disable interrupts */
mts rmsr, r6;
nop
xori r6, r5, 1; /* 00001 = Unaligned Exception */
/* Jump to unalignment exception handler */
beqi r6, handle_unaligned_ex;
handle_other_ex: /* Handle Other exceptions here */
/* Save other volatiles before we make procedure calls below */
swi r7, r1, PT_R7
swi r8, r1, PT_R8
swi r9, r1, PT_R9
swi r10, r1, PT_R10
swi r11, r1, PT_R11
swi r12, r1, PT_R12
swi r14, r1, PT_R14
swi r15, r1, PT_R15
swi r18, r1, PT_R18
or r5, r1, r0
andi r6, r3, 0x1F; /* Load ESR[EC] */
lwi r7, r0, PER_CPU(KM) /* MS: saving current kernel mode to regs */
swi r7, r1, PT_MODE
mfs r7, rfsr
nop
addk r8, r17, r0; /* Load exception address */
bralid r15, full_exception; /* Branch to the handler */
nop;
/*
* Trigger execution of the signal handler by enabling
* interrupts and calling an invalid syscall.
*/
mfs r5, rmsr;
nop
ori r5, r5, 2;
mts rmsr, r5; /* enable interrupt */
nop
addi r12, r0, __NR_syscalls;
brki r14, 0x08;
mfs r5, rmsr; /* disable interrupt */
nop
andi r5, r5, ~2;
mts rmsr, r5;
nop
lwi r7, r1, PT_R7
lwi r8, r1, PT_R8
lwi r9, r1, PT_R9
lwi r10, r1, PT_R10
lwi r11, r1, PT_R11
lwi r12, r1, PT_R12
lwi r14, r1, PT_R14
lwi r15, r1, PT_R15
lwi r18, r1, PT_R18
bri ex_handler_done; /* Complete exception handling */
/* 0x01 - Unaligned data access exception
* This occurs when a word access is not aligned on a word boundary,
* or when a 16-bit access is not aligned on a 16-bit boundary.
* This handler perform the access, and returns, except for MMU when
* the unaligned address is last on a 4k page or the physical address is
* not found in the page table, in which case unaligned_data_trap is called.
*/
handle_unaligned_ex:
/* Working registers already saved: R3, R4, R5, R6
* R3 = ESR
* R4 = BTR
*/
mfs r4, rear;
nop
andi r6, r3, 0x3E0; /* Mask and extract the register operand */
srl r6, r6; /* r6 >> 5 */
srl r6, r6;
srl r6, r6;
srl r6, r6;
srl r6, r6;
/* Store the register operand in a temporary location */
sbi r6, r0, TOPHYS(ex_reg_op);
andi r6, r3, 0x400; /* Extract ESR[S] */
bnei r6, ex_sw;
ex_lw:
andi r6, r3, 0x800; /* Extract ESR[W] */
beqi r6, ex_lhw;
lbui r5, r4, 0; /* Exception address in r4 */
/* Load a word, byte-by-byte from destination address
and save it in tmp space */
sbi r5, r0, TOPHYS(ex_tmp_data_loc_0);
lbui r5, r4, 1;
sbi r5, r0, TOPHYS(ex_tmp_data_loc_1);
lbui r5, r4, 2;
sbi r5, r0, TOPHYS(ex_tmp_data_loc_2);
lbui r5, r4, 3;
sbi r5, r0, TOPHYS(ex_tmp_data_loc_3);
/* Get the destination register value into r3 */
lwi r3, r0, TOPHYS(ex_tmp_data_loc_0);
bri ex_lw_tail;
ex_lhw:
lbui r5, r4, 0; /* Exception address in r4 */
/* Load a half-word, byte-by-byte from destination
address and save it in tmp space */
sbi r5, r0, TOPHYS(ex_tmp_data_loc_0);
lbui r5, r4, 1;
sbi r5, r0, TOPHYS(ex_tmp_data_loc_1);
/* Get the destination register value into r3 */
lhui r3, r0, TOPHYS(ex_tmp_data_loc_0);
ex_lw_tail:
/* Get the destination register number into r5 */
lbui r5, r0, TOPHYS(ex_reg_op);
/* Form load_word jump table offset (lw_table + (8 * regnum)) */
la r6, r0, TOPHYS(lw_table);
addk r5, r5, r5;
addk r5, r5, r5;
addk r5, r5, r5;
addk r5, r5, r6;
bra r5;
ex_lw_end: /* Exception handling of load word, ends */
ex_sw:
/* Get the destination register number into r5 */
lbui r5, r0, TOPHYS(ex_reg_op);
/* Form store_word jump table offset (sw_table + (8 * regnum)) */
la r6, r0, TOPHYS(sw_table);
add r5, r5, r5;
add r5, r5, r5;
add r5, r5, r5;
add r5, r5, r6;
bra r5;
ex_sw_tail:
mfs r6, resr;
nop
andi r6, r6, 0x800; /* Extract ESR[W] */
beqi r6, ex_shw;
/* Get the word - delay slot */
swi r3, r0, TOPHYS(ex_tmp_data_loc_0);
/* Store the word, byte-by-byte into destination address */
lbui r3, r0, TOPHYS(ex_tmp_data_loc_0);
sbi r3, r4, 0;
lbui r3, r0, TOPHYS(ex_tmp_data_loc_1);
sbi r3, r4, 1;
lbui r3, r0, TOPHYS(ex_tmp_data_loc_2);
sbi r3, r4, 2;
lbui r3, r0, TOPHYS(ex_tmp_data_loc_3);
sbi r3, r4, 3;
bri ex_handler_done;
ex_shw:
/* Store the lower half-word, byte-by-byte into destination address */
swi r3, r0, TOPHYS(ex_tmp_data_loc_0);
lbui r3, r0, TOPHYS(ex_tmp_data_loc_2);
sbi r3, r4, 0;
lbui r3, r0, TOPHYS(ex_tmp_data_loc_3);
sbi r3, r4, 1;
ex_sw_end: /* Exception handling of store word, ends. */
ex_handler_done:
lwi r5, r1, 0 /* RMSR */
mts rmsr, r5
nop
lwi r3, r1, PT_R3
lwi r4, r1, PT_R4
lwi r5, r1, PT_R5
lwi r6, r1, PT_R6
lwi r17, r1, PT_R17
rted r17, 0
addik r1, r1, (EX_HANDLER_STACK_SIZ); /* Restore stack frame */
.end _hw_exception_handler
ex_handler_unhandled:
/* FIXME add handle function for unhandled exception - dump register */
bri 0
.section .text
.align 4
lw_table:
lw_r0: R3_TO_LWREG (0);
lw_r1: LWREG_NOP;
lw_r2: R3_TO_LWREG (2);
lw_r3: R3_TO_LWREG_V (3);
lw_r4: R3_TO_LWREG_V (4);
lw_r5: R3_TO_LWREG_V (5);
lw_r6: R3_TO_LWREG_V (6);
lw_r7: R3_TO_LWREG (7);
lw_r8: R3_TO_LWREG (8);
lw_r9: R3_TO_LWREG (9);
lw_r10: R3_TO_LWREG (10);
lw_r11: R3_TO_LWREG (11);
lw_r12: R3_TO_LWREG (12);
lw_r13: R3_TO_LWREG (13);
lw_r14: R3_TO_LWREG (14);
lw_r15: R3_TO_LWREG (15);
lw_r16: R3_TO_LWREG (16);
lw_r17: LWREG_NOP;
lw_r18: R3_TO_LWREG (18);
lw_r19: R3_TO_LWREG (19);
lw_r20: R3_TO_LWREG (20);
lw_r21: R3_TO_LWREG (21);
lw_r22: R3_TO_LWREG (22);
lw_r23: R3_TO_LWREG (23);
lw_r24: R3_TO_LWREG (24);
lw_r25: R3_TO_LWREG (25);
lw_r26: R3_TO_LWREG (26);
lw_r27: R3_TO_LWREG (27);
lw_r28: R3_TO_LWREG (28);
lw_r29: R3_TO_LWREG (29);
lw_r30: R3_TO_LWREG (30);
lw_r31: R3_TO_LWREG (31);
sw_table:
sw_r0: SWREG_TO_R3 (0);
sw_r1: SWREG_NOP;
sw_r2: SWREG_TO_R3 (2);
sw_r3: SWREG_TO_R3_V (3);
sw_r4: SWREG_TO_R3_V (4);
sw_r5: SWREG_TO_R3_V (5);
sw_r6: SWREG_TO_R3_V (6);
sw_r7: SWREG_TO_R3 (7);
sw_r8: SWREG_TO_R3 (8);
sw_r9: SWREG_TO_R3 (9);
sw_r10: SWREG_TO_R3 (10);
sw_r11: SWREG_TO_R3 (11);
sw_r12: SWREG_TO_R3 (12);
sw_r13: SWREG_TO_R3 (13);
sw_r14: SWREG_TO_R3 (14);
sw_r15: SWREG_TO_R3 (15);
sw_r16: SWREG_TO_R3 (16);
sw_r17: SWREG_NOP;
sw_r18: SWREG_TO_R3 (18);
sw_r19: SWREG_TO_R3 (19);
sw_r20: SWREG_TO_R3 (20);
sw_r21: SWREG_TO_R3 (21);
sw_r22: SWREG_TO_R3 (22);
sw_r23: SWREG_TO_R3 (23);
sw_r24: SWREG_TO_R3 (24);
sw_r25: SWREG_TO_R3 (25);
sw_r26: SWREG_TO_R3 (26);
sw_r27: SWREG_TO_R3 (27);
sw_r28: SWREG_TO_R3 (28);
sw_r29: SWREG_TO_R3 (29);
sw_r30: SWREG_TO_R3 (30);
sw_r31: SWREG_TO_R3 (31);
/* Temporary data structures used in the handler */
.section .data
.align 4
ex_tmp_data_loc_0:
.byte 0
ex_tmp_data_loc_1:
.byte 0
ex_tmp_data_loc_2:
.byte 0
ex_tmp_data_loc_3:
.byte 0
ex_reg_op:
.byte 0
|