-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathentry.S
More file actions
278 lines (233 loc) · 8.13 KB
/
entry.S
File metadata and controls
278 lines (233 loc) · 8.13 KB
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
/**
* Bareflank EL2 boot stub: entry points
* This file contains all entry points (e.g. when launching or switching ELs).
*
* Copyright (C) Assured Information Security, Inc.
* Author: Kate J. Temkin <[email protected]>
*
* <insert license here>
*/
.section ".text"
/*
* x0 contains the FDT blob PA, which we don't use
*/
.globl _header
_header:
b _start // branch to kernel start, magic
.long 0 // reserved
.quad 0 // Image load offset from start of RAM
.quad 0x2000000 // Image size to be processed, little endian (32MiB, default for Pixel C)
.quad 0 // reserved
.quad 0 // reserved
.quad 0 // reserved
.quad 0 // reserved
.byte 0x41 // Magic number, "ARM\x64"
.byte 0x52
.byte 0x4d
.byte 0x64
.word 0 // reserved
/**
* Simple macro to help with generating vector table entries.
*/
.macro ventry label
.align 7
b \label
.endm
/*
* Vector table for interrupts/exceptions that reach EL2.
*/
.align 11
.global el2_vector_table;
el2_vector_table:
ventry _unhandled_vector // Synchronous EL2t
ventry _unhandled_vector // IRQ EL2t
ventry _unhandled_vector // FIQ EL2t
ventry _unhandled_vector // Error EL2t
ventry _unhandled_vector // Synchronous EL2h
ventry _unhandled_vector // IRQ EL2h
ventry _unhandled_vector // FIQ EL2h
ventry _unhandled_vector // Error EL2h
ventry _handle_hypercall // Synchronous 64-bit EL0/EL1
ventry _unhandled_vector // IRQ 64-bit EL0/EL1
ventry _unhandled_vector // FIQ 64-bit EL0/EL1
ventry _unhandled_vector // Error 64-bit EL0/EL1
ventry _unhandled_vector // Synchronous 32-bit EL0/EL1
ventry _unhandled_vector // IRQ 32-bit EL0/EL1
ventry _unhandled_vector // FIQ 32-bit EL0/EL1
ventry _unhandled_vector // Error 32-bit EL0/EL1
/**
* Start of day code. This is the first code that executes after we're launched
* by the bootloader. We use this only to set up a C environment.
*/
_start:
// Reminder: do not clobber x0, as it contains the location of our
// Flattened Device Tree / FDT. If you need to use x0, stash the value
// (e.g. on the stack), and then put it back before main.
// Create a simple stack for the bfstub, while executing in EL2.
ldr x1, =el2_stack_end
mov sp, x1
// Clear out our binary's bss.
stp x0, x1, [sp, #-16]!
bl _clear_bss
ldp x0, x1, [sp], #16
// Run the main routine. This shouldn't return.
b main
// We shouldn't ever reach here; trap.
1: b 1b
/*
* Switch down to EL1 and then execute the second half of our stub.
* Implemented in assembly, as this manipulates the stack.
*
* Obliterates the stack, but leaves the rest of memory intact. This should be
* fine, as we should be hiding the EL2 memory from the rest of the system.
*
* x0: The location of the device tree to be passed into EL0.
*/
.global switch_to_el1
switch_to_el1:
// Set up a post-EL1-switch return address...
ldr x2, =_post_el1_switch
msr elr_el2, x2
// .. and set up the CPSR after we switch to EL1.
// We overwrite the saved program status register. Note that setting
// this with the EL = EL1 is what actually causes the switch.
mov x2, #0x3c5 // EL1_SP1 | D | A | I | F
msr spsr_el2, x2
// Reset the stack pointer to the very end of the stack, so it's
// fresh and clean for when we jump back up into EL2.
ldr x2, =el2_stack_end
mov sp, x2
// ... and switch down to EL1. (This essentially asks the processor
// to switch down to EL1 and then load ELR_EL2 to the PC.)
eret
/*
* Entry point after the switch to EL1.
*
* x0: The location of the device tree.
* x2: The C code to return to after the EL1 switch.
*/
.global _post_el1_switch
_post_el1_switch:
// Create a simple stack for the bfstub to use while at EL1.
ldr x2, =el1_stack_end
mov sp, x2
// Run the main routine. This shouldn't return.
b main_el1
// We shouldn't ever reach here; trap.
1: b 1b
/**
* Push and pop 'psuedo-op' macros that simplify the ARM syntax to make the below pretty.
*/
.macro push, xreg1, xreg2
stp \xreg1, \xreg2, [sp, #-16]!
.endm
.macro pop, xreg1, xreg2
ldp \xreg1, \xreg2, [sp], #16
.endm
/**
* Macro that saves registers onto the stack when entering an exception handler--
* effectively saving the guest state. Once this method is complete, *sp will
* point to a struct guest_state.
*
* You can modify this to save whatever you'd like, but:
* 1) We can only push in pairs due to armv8 architecture quirks.
* 2) Be careful not to trounce registers until after you've saved them.
* 3) r31 is your stack pointer, and doesn't need to be saved. You'll want to
* save the lesser EL's stack pointers separately.
* 4) Make sure any changes you make are reflected both in _restore_registers_
* and in struct guest_state, or things will break pretty badly.
*/
.macro save_registers
// General purpose registers x1 - x30
push x29, x30
push x27, x28
push x25, x26
push x23, x24
push x21, x22
push x19, x20
push x17, x18
push x15, x16
push x13, x14
push x11, x12
push x9, x10
push x7, x8
push x5, x6
push x3, x4
push x1, x2
// x0 and the el2_esr
mrs x20, esr_el2
push x20, x0
// the el1_sp and el0_sp
mrs x0, sp_el0
mrs x1, sp_el1
push x0, x1
// the el1 elr/spsr
mrs x0, elr_el1
mrs x1, spsr_el1
push x0, x1
// the el2 elr/spsr
mrs x0, elr_el2
mrs x1, spsr_el2
push x0, x1
.endm
/**
* Macro that restores registers when returning from EL2.
* Mirrors save_registers.
*/
.macro restore_registers
// the el2 elr/spsr
pop x0, x1
msr elr_el2, x0
msr spsr_el2, x1
// the el1 elr/spsr
pop x0, x1
msr elr_el1, x0
msr spsr_el1, x1
// the el1_sp and el0_sp
pop x0, x1
msr sp_el0, x0
msr sp_el1, x1
// x0, and the el2_esr
// Note that we don't restore el2_esr, as this wouldn't
// have any meaning.
pop x20, x0
// General purpose registers x1 - x30
pop x1, x2
pop x3, x4
pop x5, x6
pop x7, x8
pop x9, x10
pop x11, x12
pop x13, x14
pop x15, x16
pop x17, x18
pop x19, x20
pop x21, x22
pop x23, x24
pop x25, x26
pop x27, x28
pop x29, x30
.endm
/*
* Handler for any vector we're not equipped to handle.
*/
_unhandled_vector:
// TODO: Save interrupt state and turn off interrupts.
save_registers
// Point x0 at our saved registers, and then call our C handler.
mov x0, sp
bl unhandled_vector
restore_registers
eret
/*
* Handler for any synchronous event coming from the guest (any trap-to-EL2).
* This _stub_ only uses this to handle hypercalls-- hence the name.
*/
_handle_hypercall:
// TODO: Save interrupt state and turn off interrupts.
save_registers
// Point x0 at our saved registers, and then call our C handler.
mov x0, sp
bl handle_hypercall
restore_registers
eret