summaryrefslogtreecommitdiff
path: root/ArmPkg/Drivers/CpuDxe/Arm/ExceptionSupport.S
blob: 673b931297369c11d3e24ae130e2d73681d91039 (plain)
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
#------------------------------------------------------------------------------
#
# Use ARMv6 instruction to operate on a single stack
#
# Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
# Copyright (c) 2014, ARM Limited. All rights reserved.<BR>
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution.  The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#------------------------------------------------------------------------------

#include <Library/PcdLib.h>

/*

This is the stack constructed by the exception handler (low address to high address)
                # R0 - IFAR is EFI_SYSTEM_CONTEXT for ARM
  Reg   Offset
  ===   ======
  R0    0x00    # stmfd     SP!,{R0-R12}
  R1    0x04
  R2    0x08
  R3    0x0c
  R4    0x10
  R5    0x14
  R6    0x18
  R7    0x1c
  R8    0x20
  R9    0x24
  R10   0x28
  R11   0x2c
  R12   0x30
  SP    0x34    # reserved via subtraction 0x20 (32) from SP
  LR    0x38
  PC    0x3c
  CPSR  0x40
  DFSR  0x44
  DFAR  0x48
  IFSR  0x4c
  IFAR  0x50

  LR    0x54    # SVC Link register (we need to restore it)

  LR    0x58    # pushed by srsfd
  CPSR  0x5c

 */


GCC_ASM_EXPORT(ExceptionHandlersStart)
GCC_ASM_EXPORT(ExceptionHandlersEnd)
GCC_ASM_EXPORT(CommonExceptionEntry)
GCC_ASM_EXPORT(AsmCommonExceptionEntry)
GCC_ASM_EXPORT(CommonCExceptionHandler)

.text
.syntax unified
#if !defined(__APPLE__)
.fpu neon    @ makes vpush/vpop assemble
#endif
.align 5


//
// This code gets copied to the ARM vector table
// ExceptionHandlersStart - ExceptionHandlersEnd gets copied
//
ASM_PFX(ExceptionHandlersStart):

ASM_PFX(Reset):
  b ASM_PFX(ResetEntry)

ASM_PFX(UndefinedInstruction):
  b ASM_PFX(UndefinedInstructionEntry)

ASM_PFX(SoftwareInterrupt):
  b ASM_PFX(SoftwareInterruptEntry)

ASM_PFX(PrefetchAbort):
  b ASM_PFX(PrefetchAbortEntry)

ASM_PFX(DataAbort):
  b ASM_PFX(DataAbortEntry)

ASM_PFX(ReservedException):
  b ASM_PFX(ReservedExceptionEntry)

ASM_PFX(Irq):
  b ASM_PFX(IrqEntry)

ASM_PFX(Fiq):
  b ASM_PFX(FiqEntry)

ASM_PFX(ResetEntry):
  srsdb     #0x13!                    @ Store return state on SVC stack
                                      @ We are already in SVC mode

  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state

  mov       R0,#0                     @ ExceptionType
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

ASM_PFX(UndefinedInstructionEntry):
  sub       LR, LR, #4                @ Only -2 for Thumb, adjust in CommonExceptionEntry
  srsdb     #0x13!                    @ Store return state on SVC stack
  cps       #0x13                     @ Switch to SVC for common stack
  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state

  mov       R0,#1                     @ ExceptionType
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

ASM_PFX(SoftwareInterruptEntry):
  srsdb     #0x13!                    @ Store return state on SVC stack
                                      @ We are already in SVC mode
  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state

  mov       R0,#2                     @ ExceptionType
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

ASM_PFX(PrefetchAbortEntry):
  sub       LR,LR,#4
  srsdb     #0x13!                    @ Store return state on SVC stack
  cps       #0x13                     @ Switch to SVC for common stack
  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state

  mov       R0,#3                     @ ExceptionType
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

ASM_PFX(DataAbortEntry):
  sub       LR,LR,#8
  srsdb     #0x13!                    @ Store return state on SVC stack
  cps       #0x13                     @ Switch to SVC for common stack
  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state

  mov       R0,#4
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

ASM_PFX(ReservedExceptionEntry):
  srsdb     #0x13!                    @ Store return state on SVC stack
  cps       #0x13                     @ Switch to SVC for common stack
  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state

  mov       R0,#5
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

ASM_PFX(IrqEntry):
  sub       LR,LR,#4
  srsdb     #0x13!                    @ Store return state on SVC stack
  cps       #0x13                     @ Switch to SVC for common stack
  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state

  mov       R0,#6                     @ ExceptionType
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

ASM_PFX(FiqEntry):
  sub       LR,LR,#4
  srsdb     #0x13!                    @ Store return state on SVC stack
  cps       #0x13                     @ Switch to SVC for common stack
  stmfd     SP!,{LR}                  @ Store the link register for the current mode
  sub       SP,SP,#0x20               @ Save space for SP, LR, PC, IFAR - CPSR
  stmfd     SP!,{R0-R12}              @ Store the register state
                                      @ Since we have already switch to SVC R8_fiq - R12_fiq
                                      @ never get used or saved
  mov       R0,#7                     @ ExceptionType
  ldr       R1,ASM_PFX(CommonExceptionEntry)
  bx        R1

//
// This gets patched by the C code that patches in the vector table
//
ASM_PFX(CommonExceptionEntry):
  .word       ASM_PFX(AsmCommonExceptionEntry)

ASM_PFX(ExceptionHandlersEnd):

//
// This code runs from CpuDxe driver loaded address. It is patched into
// CommonExceptionEntry.
//
ASM_PFX(AsmCommonExceptionEntry):
  mrc       p15, 0, R1, c6, c0, 2   @ Read IFAR
  str       R1, [SP, #0x50]         @ Store it in EFI_SYSTEM_CONTEXT_ARM.IFAR

  mrc       p15, 0, R1, c5, c0, 1   @ Read IFSR
  str       R1, [SP, #0x4c]         @ Store it in EFI_SYSTEM_CONTEXT_ARM.IFSR

  mrc       p15, 0, R1, c6, c0, 0   @ Read DFAR
  str       R1, [SP, #0x48]         @ Store it in EFI_SYSTEM_CONTEXT_ARM.DFAR

  mrc       p15, 0, R1, c5, c0, 0   @ Read DFSR
  str       R1, [SP, #0x44]         @ Store it in EFI_SYSTEM_CONTEXT_ARM.DFSR

  ldr       R1, [SP, #0x5c]         @ srsdb saved pre-exception CPSR on the stack
  str       R1, [SP, #0x40]         @ Store it in EFI_SYSTEM_CONTEXT_ARM.CPSR

  add       R2, SP, #0x38           @ Make R2 point to EFI_SYSTEM_CONTEXT_ARM.LR
  and       R3, R1, #0x1f           @ Check CPSR to see if User or System Mode
  cmp       R3, #0x1f               @ if ((CPSR == 0x10) || (CPSR == 0x1f))
  cmpne     R3, #0x10               @
  stmdaeq   R2, {lr}^               @   save unbanked lr
                                    @ else
  stmdane   R2, {lr}                @   save SVC lr


  ldr       R5, [SP, #0x58]         @ PC is the LR pushed by srsfd
                                    @ Check to see if we have to adjust for Thumb entry
  sub       r4, r0, #1              @ if (ExceptionType == 1 || ExceptionType == 2)) {
  cmp       r4, #1                  @   // UND & SVC have differnt LR adjust for Thumb
  bhi       NoAdjustNeeded

  tst       r1, #0x20               @   if ((CPSR & T)) == T) {  // Thumb Mode on entry
  addne     R5, R5, #2              @     PC += 2;
  strne     R5,[SP,#0x58]           @ Update LR value pushed by srsfd

NoAdjustNeeded:

  str       R5, [SP, #0x3c]         @ Store it in EFI_SYSTEM_CONTEXT_ARM.PC

  add       R1, SP, #0x60           @ We pushed 0x60 bytes on the stack
  str       R1, [SP, #0x34]         @ Store it in EFI_SYSTEM_CONTEXT_ARM.SP

                                    @ R0 is ExceptionType
  mov       R1,SP                   @ R1 is SystemContext

#if (FixedPcdGet32(PcdVFPEnabled))
  vpush     {d0-d15}                @ save vstm registers in case they are used in optimizations
#endif

  mov       R4, SP                  @ Save current SP
  tst       R4, #4
  subne     SP, SP, #4              @ Adjust SP if not 8-byte aligned

/*
VOID
EFIAPI
CommonCExceptionHandler (
  IN     EFI_EXCEPTION_TYPE           ExceptionType,   R0
  IN OUT EFI_SYSTEM_CONTEXT           SystemContext    R1
  )

*/
  blx       ASM_PFX(CommonCExceptionHandler)  @ Call exception handler

  mov       SP, R4                  @ Restore SP

#if (FixedPcdGet32(PcdVFPEnabled))
  vpop      {d0-d15}
#endif

  ldr       R1, [SP, #0x4c]         @ Restore EFI_SYSTEM_CONTEXT_ARM.IFSR
  mcr       p15, 0, R1, c5, c0, 1   @ Write IFSR

  ldr       R1, [SP, #0x44]         @ Restore EFI_SYSTEM_CONTEXT_ARM.DFSR
  mcr       p15, 0, R1, c5, c0, 0   @ Write DFSR

  ldr       R1,[SP,#0x3c]           @ EFI_SYSTEM_CONTEXT_ARM.PC
  str       R1,[SP,#0x58]           @ Store it back to srsfd stack slot so it can be restored

  ldr       R1,[SP,#0x40]           @ EFI_SYSTEM_CONTEXT_ARM.CPSR
  str       R1,[SP,#0x5c]           @ Store it back to srsfd stack slot so it can be restored

  add       R3, SP, #0x54           @ Make R3 point to SVC LR saved on entry
  add       R2, SP, #0x38           @ Make R2 point to EFI_SYSTEM_CONTEXT_ARM.LR
  and       R1, R1, #0x1f           @ Check to see if User or System Mode
  cmp       R1, #0x1f               @ if ((CPSR == 0x10) || (CPSR == 0x1f))
  cmpne     R1, #0x10               @
  ldmibeq   R2, {lr}^               @   restore unbanked lr
                                    @ else
  ldmibne   R3, {lr}                @   restore SVC lr, via ldmfd SP!, {LR}

  ldmfd     SP!,{R0-R12}            @ Restore general purpose registers
                                    @ Exception handler can not change SP

  add       SP,SP,#0x20             @ Clear out the remaining stack space
  ldmfd     SP!,{LR}                @ restore the link register for this context
  rfefd     SP!                     @ return from exception via srsfd stack slot