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
| /* SPDX-License-Identifier: GPL-2.0
| *
| * arch/sh/kernel/cpu/sh2a/entry.S
| *
| * The SH-2A exception entry
| *
| * Copyright (C) 2008 Yoshinori Sato
| * Based on arch/sh/kernel/cpu/sh2/entry.S
| */
|
| #include <linux/linkage.h>
| #include <asm/asm-offsets.h>
| #include <asm/thread_info.h>
| #include <cpu/mmu_context.h>
| #include <asm/unistd.h>
| #include <asm/errno.h>
| #include <asm/page.h>
|
| /* Offsets to the stack */
| OFF_R0 = 0 /* Return value. New ABI also arg4 */
| OFF_R1 = 4 /* New ABI: arg5 */
| OFF_R2 = 8 /* New ABI: arg6 */
| OFF_R3 = 12 /* New ABI: syscall_nr */
| OFF_R4 = 16 /* New ABI: arg0 */
| OFF_R5 = 20 /* New ABI: arg1 */
| OFF_R6 = 24 /* New ABI: arg2 */
| OFF_R7 = 28 /* New ABI: arg3 */
| OFF_SP = (15*4)
| OFF_PC = (16*4)
| OFF_SR = (16*4+2*4)
| OFF_TRA = (16*4+6*4)
|
| #include <asm/entry-macros.S>
|
| ENTRY(exception_handler)
| ! stack
| ! r0 <- point sp
| ! r1
| ! pc
| ! sr
| ! r0 = temporary
| ! r1 = vector (pseudo EXPEVT / INTEVT / TRA)
| mov.l r2,@-sp
| cli
| mov.l $cpu_mode,r2
| bld.b #6,@(0,r2) !previus SR.MD
| bst.b #6,@(4*4,r15) !set cpu mode to SR.MD
| bt 1f
| ! switch to kernel mode
| bset.b #6,@(0,r2) !set SR.MD
| mov.l $current_thread_info,r2
| mov.l @r2,r2
| mov #(THREAD_SIZE >> 8),r0
| shll8 r0
| add r2,r0 ! r0 = kernel stack tail
| mov r15,r2 ! r2 = user stack top
| mov r0,r15 ! switch kernel stack
| mov.l r1,@-r15 ! TRA
| sts.l macl, @-r15
| sts.l mach, @-r15
| stc.l gbr, @-r15
| mov.l @(4*4,r2),r0
| mov.l r0,@-r15 ! original SR
| sts.l pr,@-r15
| mov.l @(3*4,r2),r0
| mov.l r0,@-r15 ! original PC
| mov r2,r0
| add #(3+2)*4,r0 ! rewind r0 - r3 + exception frame
| lds r0,pr ! pr = original SP
| movmu.l r3,@-r15 ! save regs
| mov r2,r8 ! r8 = previus stack top
| mov r1,r9 ! r9 = interrupt vector
| ! restore previous stack
| mov.l @r8+,r2
| mov.l @r8+,r0
| mov.l @r8+,r1
| bra 2f
| movml.l r2,@-r15
| 1:
| ! in kernel exception
| mov r15,r2
| add #-((OFF_TRA + 4) - OFF_PC) + 5*4,r15
| movmu.l r3,@-r15
| mov r2,r8 ! r8 = previous stack top
| mov r1,r9 ! r9 = interrupt vector
| ! restore exception frame & regs
| mov.l @r8+,r2 ! old R2
| mov.l @r8+,r0 ! old R0
| mov.l @r8+,r1 ! old R1
| mov.l @r8+,r10 ! old PC
| mov.l @r8+,r11 ! old SR
| movml.l r2,@-r15
| mov.l r10,@(OFF_PC,r15)
| mov.l r11,@(OFF_SR,r15)
| mov.l r8,@(OFF_SP,r15) ! save old sp
| mov r15,r8
| add #OFF_TRA + 4,r8
| mov.l r9,@-r8
| sts.l macl,@-r8
| sts.l mach,@-r8
| stc.l gbr,@-r8
| add #-4,r8
| sts.l pr,@-r8
| 2:
| ! dispatch exception / interrupt
| mov #64,r8
| cmp/hs r8,r9
| bt interrupt_entry ! vec >= 64 is interrupt
| mov #31,r8
| cmp/hs r8,r9
| bt trap_entry ! 64 > vec >= 31 is trap
|
| mov.l 4f,r8
| mov r9,r4
| shll2 r9
| add r9,r8
| mov.l @r8,r8 ! exception handler address
| tst r8,r8
| bf 3f
| mov.l 8f,r8 ! unhandled exception
| 3:
| mov.l 5f,r10
| jmp @r8
| lds r10,pr
|
| interrupt_entry:
| mov r9,r4
| mov r15,r5
| mov.l 7f,r8
| mov.l 6f,r9
| jmp @r8
| lds r9,pr
|
| .align 2
| 4: .long exception_handling_table
| 5: .long ret_from_exception
| 6: .long ret_from_irq
| 7: .long do_IRQ
| 8: .long exception_error
|
| trap_entry:
| mov #0x30,r8
| cmp/ge r8,r9 ! vector 0x1f-0x2f is systemcall
| bt 1f
| mov #0x1f,r9 ! convert to unified SH2/3/4 trap number
| 1:
| shll2 r9 ! TRA
| bra system_call ! jump common systemcall entry
| mov r9,r8
|
| #if defined(CONFIG_SH_STANDARD_BIOS)
| /* Unwind the stack and jmp to the debug entry */
| ENTRY(sh_bios_handler)
| mov r15,r0
| add #(22-4)*4-4,r0
| ldc.l @r0+,gbr
| lds.l @r0+,mach
| lds.l @r0+,macl
| mov r15,r0
| mov.l @(OFF_SP,r0),r1
| mov.l @(OFF_SR,r2),r3
| mov.l r3,@-r1
| mov.l @(OFF_SP,r2),r3
| mov.l r3,@-r1
| mov r15,r0
| add #(22-4)*4-8,r0
| mov.l 1f,r2
| mov.l @r2,r2
| stc sr,r3
| mov.l r2,@r0
| mov.l r3,@(4,r0)
| mov.l r1,@(8,r0)
| movml.l @r15+,r14
| add #8,r15
| lds.l @r15+, pr
| mov.l @r15+,r15
| rte
| nop
| .align 2
| 1: .long gdb_vbr_vector
| #endif /* CONFIG_SH_STANDARD_BIOS */
|
| ENTRY(address_error_trap_handler)
| mov r15,r4 ! regs
| mov.l @(OFF_PC,r15),r6 ! pc
| mov.l 1f,r0
| jmp @r0
| mov #0,r5 ! writeaccess is unknown
|
| .align 2
| 1: .long do_address_error
|
| restore_all:
| stc sr,r0
| or #0xf0,r0
| ldc r0,sr ! all interrupt block (same BL = 1)
| ! restore special register
| ! overlap exception frame
| mov r15,r0
| add #17*4,r0
| lds.l @r0+,pr
| add #4,r0
| ldc.l @r0+,gbr
| lds.l @r0+,mach
| lds.l @r0+,macl
| mov r15,r0
| mov.l $cpu_mode,r2
| bld.b #6,@(OFF_SR,r15)
| bst.b #6,@(0,r2) ! save CPU mode
| mov.l @(OFF_SR,r0),r1
| shll2 r1
| shlr2 r1 ! clear MD bit
| mov.l @(OFF_SP,r0),r2
| add #-8,r2
| mov.l r2,@(OFF_SP,r0) ! point exception frame top
| mov.l r1,@(4,r2) ! set sr
| mov.l @(OFF_PC,r0),r1
| mov.l r1,@r2 ! set pc
| get_current_thread_info r0, r1
| mov.l $current_thread_info,r1
| mov.l r0,@r1
| movml.l @r15+,r14
| mov.l @r15,r15
| rte
| nop
|
| .align 2
| $current_thread_info:
| .long __current_thread_info
| $cpu_mode:
| .long __cpu_mode
|
| ! common exception handler
| #include "../../entry-common.S"
|
| .data
| ! cpu operation mode
| ! bit30 = MD (compatible SH3/4)
| __cpu_mode:
| .long 0x40000000
|
| .section .bss
| __current_thread_info:
| .long 0
|
| ENTRY(exception_handling_table)
| .space 4*32
|
|