summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel/boot.S
blob: d8b888ff73dea3358281dff32d2d003b9b146c4b (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
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * boot.S - the final jump to the payload. x0 = dtb, x1 = x2 = x3 = 0,
 * MMU and caches off, D cache flushed, I cache invalidated. that is
 * the whole contract from Documentation/arch/arm64/booting.rst.
 *
 * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
 */

#include <asm/linkage.h>

.pushsection .text.tb_boot_linux, "ax"
ENTRY(tb_boot_linux)
	/* ep in x0, dtb in x1, per the kernel boot protocol */

	/*
	 * cache maintenance first, register setup last. x0-x18 are
	 * caller saved per the AAPCS, the flush helpers are free to
	 * clobber them, so the args ride in x20/x21 across the calls.
	 */
	mov	x20, x0		/* entry point */
	mov	x21, x1		/* dtb */

	bl	tb_flush_dcache_all
	bl	tb_invalidate_icache_all

	mov	x8, x20
	mov	x0, x21
	mov	x1, xzr
	mov	x2, xzr
	mov	x3, xzr

	/*
	 * raise to EL2 for the payload when EL2 exists, the kernel
	 * prefers it there (booting.rst). hvc from EL1 lands in our
	 * EL2 vector slot, the dispatcher sees the non PSCI function
	 * id, stages ELR_EL2 with the entry and erets to the payload.
	 * on an EL1 only machine this is a straight branch.
	 */
	mrs	x9, CurrentEL
	lsr	x9, x9, #2
	cmp	x9, #2
	b.lt	5f
	hvc	#0
5:

	/* MMU off, caches off, the kernel sets up its own state */
	mrs	x9, sctlr_el1
	bic	x9, x9, #(1 << 0)	/* M, MMU */
	bic	x9, x9, #(1 << 2)	/* C, D-cache */
	bic	x9, x9, #(1 << 12)	/* I, I-cache */
	msr	sctlr_el1, x9
	isb

	/*
	 * if we entered at EL2, the kernel prefers it there. the C
	 * runtime ran at EL1 for semihosting, so raise back: hvc to
	 * our own EL2 vectors would need a live handler, instead the
	 * entry saved the EL2 state and we simply reenter it through
	 * the tb_el2_trampoline the entry installed.
	 */


	br	x8
ENDPROC(tb_boot_linux)
.popsection