summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel/monitor.S
blob: c6f5ec8f1fd1c795712aee43f74c48adfd79d25b (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
/*
 * monitor.S - the EL3 secure monitor, the resident layer real
 * firmware ships. the loader drops to non-secure and never
 * returns, but the kernel keeps calling into firmware: PSCI
 * through the SMC conduit, and on hardware with the security
 * extension the group routing of the interrupt controller is
 * only writable from here.
 *
 * the entry path runs once per PE: configure EL3, install the
 * monitor vectors, hand the next stage non-secure EL2 in the
 * manual's boot state. SMCCC calls from the kernel trap into
 * the SMC slot, the C dispatcher behind it is the same one the
 * hvc path uses.
 *
 * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
 */

/*
 * the monitor stack. SP_EL3 needs memory no non-secure stage
 * will touch, the region after the loader stack, sixteen
 * bytes a call deep at most.
 */
.section .bss.el3stack, "aw", %nobits
.align 4
.globl __el3_stack_bottom
__el3_stack_bottom:
	.quad 0, 0, 0, 0
	.quad 0, 0, 0, 0
.globl __el3_stack_top
__el3_stack_top:

/*
 * EL3 vectors, same sixteen slot layout every exception level
 * uses. only the lower EL sync slot carries work, the SMC
 * conduit, everything else parks.
 */
.balign 2048
.globl tb_el3_vectors
tb_el3_vectors:
	/* 0x000: current EL, SP_EL0, unused */
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_park

	/* 0x200: current EL, SP_ELx, unused */
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_park

	/* 0x400: lower EL, AArch64, the SMC conduit lives here */
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_smc

	/* 0x600: lower EL, AArch32, unused */
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_park
	.align 7
	b	el3_park

/*
 * one time per PE, from the reset path. x30 = the next stage
 * entry in non-secure EL2, x0 = the dtb pointer.
 */
.globl tb_monitor_init
tb_monitor_init:
	/* SP_EL3 on its own region */
	adr	x1, __el3_stack_top
	msr	spsel, #0
	mov	sp, x1
	msr	spsel, #1

	/* the monitor vectors */
	adr	x1, tb_el3_vectors
	msr	vbar_el3, x1
	isb

	/*
	 * SMC as the conduit, SVE traps off, no interrupt routing
	 * into EL3: FIQ/IRQ stay whatever SCR_EL3.SCR left them,
	 * the kernel owns the world below.
	 */
	mrs	x1, scr_el3
	bic	x1, x1, #(1 << 2)	/* SMD, SMC enabled */
	msr	scr_el3, x1
	isb

	ret

el3_park:
	b	el3_park

/*
 * the SMC trap from lower EL. the SMCCC calling convention is
 * the SMC register set, function id in x0, arguments x1 to
 * x3, results in x0 to x3. x17 and x18 are caller save in
 * this convention, the dispatcher clobbers x0 to x18.
 */
el3_smc:
	stp	x29, x30, [sp, #-16]!
	mov	x29, sp
	stp	x19, x20, [sp, #-16]!
	stp	x21, x22, [sp, #-16]!
	stp	x23, x24, [sp, #-16]!

	bl	tb_psci_dispatch

	ldp	x23, x24, [sp], #16
	ldp	x21, x22, [sp], #16
	ldp	x19, x20, [sp], #16
	ldp	x29, x30, [sp], #16

	eret