master
s 478 lines 11 KB
Raw
1 /*
2 * Minimal AArch64 system boot code.
3 *
4 * Copyright Linaro Ltd 2019
5 *
6 * Loosely based on the newlib/libgloss setup stubs. Using semihosting
7 * for serial output and exit functions.
8 */
9
10 /*
11 * Semihosting interface on ARM AArch64
12 * See "Semihosting for AArch32 and AArch64 Release 2.0" by ARM
13 * w0 - semihosting call number
14 * x1 - semihosting parameter
15 */
16 #define semihosting_call hlt 0xf000
17 #define SYS_WRITEC 0x03 /* character to debug channel */
18 #define SYS_WRITE0 0x04 /* string to debug channel */
19 #define SYS_GET_CMDLINE 0x15 /* get command line */
20 #define SYS_EXIT 0x18
21
22 .align 12
23
24 .macro ventry label
25 .align 7
26 b \label
27 .endm
28
29 vector_table:
30 /* Current EL with SP0. */
31 ventry curr_sp0_sync /* Synchronous */
32 ventry curr_sp0_irq /* Irq/vIRQ */
33 ventry curr_sp0_fiq /* Fiq/vFIQ */
34 ventry curr_sp0_serror /* SError/VSError */
35
36 /* Current EL with SPx. */
37 ventry curr_spx_sync /* Synchronous */
38 ventry curr_spx_irq /* IRQ/vIRQ */
39 ventry curr_spx_fiq /* FIQ/vFIQ */
40 ventry curr_spx_serror /* SError/VSError */
41
42 /* Lower EL using AArch64. */
43 ventry lower_a64_sync /* Synchronous */
44 ventry lower_a64_irq /* IRQ/vIRQ */
45 ventry lower_a64_fiq /* FIQ/vFIQ */
46 ventry lower_a64_serror /* SError/VSError */
47
48 /* Lower EL using AArch32. */
49 ventry lower_a32_sync /* Synchronous */
50 ventry lower_a32_irq /* IRQ/vIRQ */
51 ventry lower_a32_fiq /* FIQ/vFIQ */
52 ventry lower_a32_serror /* SError/VSError */
53
54 .text
55 .align 4
56
57 /* Common vector handling for now */
58 curr_sp0_sync:
59 curr_sp0_irq:
60 curr_sp0_fiq:
61 curr_sp0_serror:
62 curr_spx_sync:
63 #ifdef LOGGING_VECTOR_TABLE
64 sub sp, sp, #16
65 stp x0, x1, [sp, #0]
66 mrs x0, ESR_EL3
67 lsr x0, x0, #26
68 and x0, x0, #0x3f
69 cmp x0, #37
70 beq data_fault
71 cmp x0, #30
72 beq gpc_fault
73 b generic_exception
74
75 data_fault:
76 mrs x0, FAR_EL3
77 adrp x1, exception_log
78 str x0, [x1]
79 ldr x0, =0x1001
80 str x0, [x1, #8]
81 b skip_return
82 gpc_fault:
83 mrs x0, FAR_EL3
84 adrp x1, exception_log
85 str x0, [x1]
86 ldr x0, =0x1002
87 str x0, [x1, #8]
88 /* Fall through */
89 skip_return:
90 mrs x0, ELR_EL3
91 add x0, x0, #4 /* Skip faulting instruction */
92 msr ELR_EL3, x0
93 ldp x0, x1, [sp, #0]
94 add sp, sp, #16
95 eret
96 #endif
97 curr_spx_irq:
98 curr_spx_fiq:
99 curr_spx_serror:
100 lower_a64_sync:
101 lower_a64_irq:
102 lower_a64_fiq:
103 lower_a64_serror:
104 lower_a32_sync:
105 lower_a32_irq:
106 lower_a32_fiq:
107 lower_a32_serror:
108 generic_exception:
109 adr x1, .unexp_excp
110 exit_msg:
111 mov x0, SYS_WRITE0
112 semihosting_call
113 mov x0, 1 /* EXIT_FAILURE */
114 bl _exit
115 /* never returns */
116
117 .section .rodata
118 .unexp_excp:
119 .string "Unexpected exception.\n"
120 .high_el_msg:
121 .string "Started in lower EL than requested.\n"
122 .unexp_el0:
123 .string "Started in invalid EL.\n"
124
125 .align 8
126 .get_cmd:
127 .quad cmdline
128 .quad 128
129
130 .text
131 .align 4
132 .global __start
133 __start:
134 /*
135 * Initialise the stack for whatever EL we are in before
136 * anything else, we need it to be able to _exit cleanly.
137 * It's smaller than the stack we pass to the C code but we
138 * don't need much.
139 */
140 adrp x0, system_stack_end
141 add x0, x0, :lo12:system_stack_end
142 mov sp, x0
143
144 /*
145 * The test can set the semihosting command line to the target
146 * EL needed for the test. However if no semihosting args are set we will
147 * end up with -kernel/-append data (see semihosting_arg_fallback).
148 * Keep the normalised target in w11.
149 */
150 mov x0, SYS_GET_CMDLINE
151 adr x1, .get_cmd
152 semihosting_call
153 adrp x10, cmdline
154 add x10, x10, :lo12:cmdline
155 ldrb w11, [x10]
156
157 /* sanity check, normalise char to EL, clamp to 1 if outside range */
158 subs w11, w11, #'0'
159 b.lt el_default
160 cmp w11, #3
161 b.gt el_default
162 b 1f
163
164 el_high:
165 adr x1, .high_el_msg
166 b exit_msg
167
168 el_default:
169 mov w11, #1
170
171 1:
172 /* Determine current Exception Level */
173 mrs x0, CurrentEL
174 lsr x0, x0, #2 /* CurrentEL[3:2] contains the current EL */
175
176 /* Are we already in a lower EL than we want? */
177 cmp w11, w0
178 bgt el_high
179
180 /* Branch based on current EL */
181 cmp x0, #3
182 b.eq setup_el3
183 cmp x0, #2
184 b.eq setup_el2
185 cmp x0, #1
186 b.eq at_testel /* Already at EL1, skip transition */
187
188 /* Should not be at EL0 - error out */
189 adr x1, .unexp_el0
190 b exit_msg
191
192 setup_el3:
193 /* Ensure we trap if we get anything wrong */
194 adr x0, vector_table
195 msr vbar_el3, x0
196
197 /* Does the test want to be at EL3? */
198 cmp w11, #3
199 beq at_testel
200
201 /* Configure EL3 to for lower states (EL2 or EL1) */
202 mrs x0, scr_el3
203 orr x0, x0, #(1 << 10) /* RW = 1: EL2/EL1 execution state is AArch64 */
204 orr x0, x0, #(1 << 0) /* NS = 1: Non-secure state */
205 msr scr_el3, x0
206
207 /*
208 * We need to check if EL2 is actually enabled via ID_AA64PFR0_EL1,
209 * otherwise we should just jump straight to EL1.
210 */
211 mrs x0, id_aa64pfr0_el1
212 ubfx x0, x0, #8, #4 /* Extract EL2 field (bits 11:8) */
213 cbz x0, el2_not_present /* If field is 0 no EL2 */
214
215
216 /* Prepare SPSR for exception return to EL2 */
217 mov x0, #0x3c9 /* DAIF bits and EL2h mode (9) */
218 msr spsr_el3, x0
219
220 /* Set EL2 entry point */
221 adr x0, setup_el2
222 msr elr_el3, x0
223
224 /* Return to EL2 */
225 eret
226
227 el2_not_present:
228 /* Initialize SCTLR_EL1 with reset value */
229 msr sctlr_el1, xzr
230
231 /* Set EL1 entry point */
232 adr x0, at_testel
233 msr elr_el3, x0
234
235 /* Prepare SPSR for exception return to EL1h with interrupts masked */
236 mov x0, #0x3c5 /* DAIF bits and EL1h mode (5) */
237 msr spsr_el3, x0
238
239 isb /* Synchronization barrier */
240 eret /* Jump to EL1 */
241
242 setup_el2:
243 /* Ensure we trap if we get anything wrong */
244 adr x0, vector_table
245 msr vbar_el2, x0
246
247 /* Does the test want to be at EL2? */
248 cmp w11, #2
249 beq at_testel
250
251 /* Configure EL2 to allow transition to EL1 */
252 mrs x0, hcr_el2
253 orr x0, x0, #(1 << 31) /* RW = 1: EL1 execution state is AArch64 */
254 msr hcr_el2, x0
255
256 /* Initialize SCTLR_EL1 with reset value */
257 msr sctlr_el1, xzr
258
259 /* Set EL1 entry point */
260 adr x0, at_testel
261 msr elr_el2, x0
262
263 /* Prepare SPSR for exception return to EL1 */
264 mov x0, #(0x5 << 0) /* EL1h (SPx), with interrupts disabled */
265 msr spsr_el2, x0
266
267 /* Return to EL1 */
268 eret
269
270 /*
271 * At the target EL for the test, usually EL1. Note we still
272 * set everything up as if we were at EL1.
273 */
274 at_testel:
275 /* Installs a table of exception vectors to catch and handle all
276 exceptions by terminating the process with a diagnostic. */
277 adr x0, vector_table
278 msr vbar_el1, x0
279
280 /* Page table setup (identity mapping). */
281 adrp x0, ttb
282 add x0, x0, :lo12:ttb
283 msr ttbr0_el1, x0
284
285 /*
286 * Setup a flat address mapping page-tables. Stage one simply
287 * maps RAM to the first Gb. The stage2 tables have two 2mb
288 * translation block entries covering a series of adjacent
289 * 4k pages.
290 */
291
292 /* Stage 1 entry: indexed by IA[38:30] */
293 adr x1, . /* phys address */
294 bic x1, x1, #(1 << 30) - 1 /* 1GB alignment*/
295 add x2, x0, x1, lsr #(30 - 3) /* offset in l1 page table */
296
297 /* point to stage 2 table [47:12] */
298 adrp x0, ttb_stage2
299 orr x1, x0, #3 /* ptr to stage 2 */
300 str x1, [x2]
301
302 /* Stage 2 entries: indexed by IA[29:21] */
303 ldr x5, =(((1 << 9) - 1) << 21)
304
305 /* First block: .text/RO/execute enabled */
306 adr x1, . /* phys address */
307 bic x1, x1, #(1 << 21) - 1 /* 2mb block alignment */
308 and x4, x1, x5 /* IA[29:21] */
309 add x2, x0, x4, lsr #(21 - 3) /* offset in l2 page table */
310 ldr x3, =0x401 /* attr(AF, block) */
311 orr x1, x1, x3
312 str x1, [x2] /* 1st 2mb (.text & rodata) */
313
314 /* Second block: .data/RW/no execute */
315 adrp x1, .data
316 add x1, x1, :lo12:.data
317 bic x1, x1, #(1 << 21) - 1 /* 2mb block alignment */
318 and x4, x1, x5 /* IA[29:21] */
319 add x2, x0, x4, lsr #(21 - 3) /* offset in l2 page table */
320 ldr x3, =(3 << 53) | 0x401 /* attr(AF, NX, block) */
321 orr x1, x1, x3
322 str x1, [x2] /* 2nd 2mb (.data & .bss)*/
323
324 /* Third block: at 'mte_page', set in kernel.ld */
325 adrp x1, mte_page
326 add x1, x1, :lo12:mte_page
327 bic x1, x1, #(1 << 21) - 1
328 and x4, x1, x5
329 add x2, x0, x4, lsr #(21 - 3)
330 /* attr(AF, NX, block, AttrIndx=Attr1) */
331 ldr x3, =(3 << 53) | 0x401 | (1 << 2)
332 orr x1, x1, x3
333 str x1, [x2]
334
335 /* Setup/enable the MMU. */
336
337 /*
338 * TCR_EL1 - Translation Control Registers
339 *
340 * IPS[34:32] = 40-bit PA, 1TB
341 * TG0[14:15] = b00 => 4kb granuale
342 * ORGN0[11:10] = Outer: Normal, WB Read-Alloc No Write-Alloc Cacheable
343 * IRGN0[9:8] = Inner: Normal, WB Read-Alloc No Write-Alloc Cacheable
344 * T0SZ[5:0] = 2^(64 - 25)
345 *
346 * The size of T0SZ controls what the initial lookup level. It
347 * would be nice to start at level 2 but unfortunately for a
348 * flat-mapping on the virt machine we need to handle IA's
349 * with at least 1gb range to see RAM. So we start with a
350 * level 1 lookup.
351 */
352 ldr x0, = (2 << 32) | 25 | (3 << 10) | (3 << 8)
353 msr tcr_el1, x0
354
355 mov x0, #0xee /* Inner/outer cacheable WB */
356 msr mair_el1, x0
357 isb
358
359 /*
360 * SCTLR_EL1 - System Control Register
361 *
362 * WXN[19] = 0 = no effect, Write does not imply XN (execute never)
363 * I[12] = Instruction cachability control
364 * SA[3] = SP alignment check
365 * C[2] = Data cachability control
366 * M[0] = 1, enable stage 1 address translation for EL0/1
367 */
368 mrs x0, sctlr_el1
369 ldr x1, =0x100d /* bits I(12) SA(3) C(2) M(0) */
370 bic x0, x0, #(1 << 1) /* clear bit A(1) */
371 bic x0, x0, #(1 << 19) /* clear WXN */
372 orr x0, x0, x1 /* set bits */
373
374 dsb sy
375 msr sctlr_el1, x0
376 isb
377
378 /*
379 * Enable FP/SVE registers. The standard C pre-amble will be
380 * saving these and A-profile compilers will use AdvSIMD
381 * registers unless we tell it not to.
382 */
383 mrs x0, cpacr_el1
384 orr x0, x0, #(3 << 20)
385 orr x0, x0, #(3 << 16)
386 msr cpacr_el1, x0
387
388 /*
389 * Setup some stack space before we enter the test code.
390 * Assume everything except the return value is garbage when we
391 * return, we won't need it.
392 */
393 adrp x0, stack_end
394 add x0, x0, :lo12:stack_end
395 mov sp, x0
396 bl main
397
398 /* pass return value to sys exit */
399 _exit:
400 mov x1, x0
401 ldr x0, =0x20026 /* ADP_Stopped_ApplicationExit */
402 stp x0, x1, [sp, #-16]!
403 mov x1, sp
404 mov x0, SYS_EXIT
405 semihosting_call
406 /* never returns */
407
408 /*
409 * Helper Functions
410 */
411
412 /* Output a single character to serial port */
413 .global __sys_outc
414 __sys_outc:
415 stp x0, x1, [sp, #-16]!
416 /* pass address of c on stack */
417 mov x1, sp
418 mov x0, SYS_WRITEC
419 semihosting_call
420 ldp x0, x1, [sp], #16
421 ret
422
423 .data
424
425 .align 8
426 cmdline:
427 .space 128, 0
428
429 .align 12
430
431 /* Translation table
432 * @4k granuale: 9 bit lookup, 512 entries
433 */
434 ttb:
435 .space 4096, 0
436
437 .align 12
438 ttb_stage2:
439 .space 4096, 0
440
441 .align 12
442 .global realms_gpt0
443 /* GPT stage 0 table */
444 realms_gpt0:
445 .space 4096, 0
446 .align 17
447 .global realms_gpt1
448 /* GPT stage 1 table, initialised to all 0xFF (full access) */
449 realms_gpt1:
450 .space 524288, 0xFF
451 #ifdef LOGGING_VECTOR_TABLE
452 .align 12
453 .global exception_fault_address
454 .type exception_fault_address, @object
455 .size exception_fault_address, 8
456 .global exception_type_code
457 .type exception_type_code, @object
458 .size exception_type_code, 8
459 /*
460 * These fields record details of the last exception, if
461 * LOGGING_VECTOR_TABLE is defined.
462 */
463 exception_log:
464 exception_fault_address:
465 /* The contents of FAR_EL3 when an exception is taken. */
466 .space 8, 0
467 exception_type_code:
468 /* A generic code indicating what type of exception occurred. */
469 .space 8, 0
470 #endif
471 .align 12
472 system_stack:
473 .space 4096, 0
474 system_stack_end:
475
476 stack:
477 .space 65536, 0
478 stack_end: