xref: /aosp_15_r20/external/coreboot/payloads/libpayload/arch/x86/exception_asm_64.S (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1/*
2 *
3 * Copyright 2024 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29	.align 16
30	.global exception_stack_end
31exception_stack_end:
32	.quad 0
33	.global exception_state
34exception_state:
35	.quad 0
36
37/* Some temporary variables which are used while saving exception state. */
38vector:
39	.quad 0
40error_code:
41	.quad 0
42old_rax:
43	.quad 0
44old_rcx:
45	.quad 0
46
47	.align 16
48
49/*
50 * Each exception vector has a small stub associated with it which sets aside
51 * the error code, if any, records which vector we entered from, and calls
52 * the common exception entry point. Some exceptions have error codes and some
53 * don't, so we have a macro for each type.
54 */
55
56	.macro stub num
57exception_stub_\num:
58	movq $0, error_code
59	movq $\num, vector
60	jmp exception_common
61	.endm
62
63	.macro stub_err num
64exception_stub_\num:
65	pop error_code
66	movq $\num, vector
67	jmp exception_common
68	.endm
69
70	.altmacro
71	.macro user_defined_stubs from, to
72	stub \from
73	.if \to-\from
74	user_defined_stubs %(from+1),\to
75	.endif
76	.endm
77
78	stub 0
79	stub 1
80	stub 2
81	stub 3
82	stub 4
83	stub 5
84	stub 6
85	stub 7
86	stub_err 8
87	stub 9
88	stub_err 10
89	stub_err 11
90	stub_err 12
91	stub_err 13
92	stub_err 14
93	stub 15
94	stub 16
95	stub_err 17
96	stub 18
97	stub 19
98	stub 20
99	stub 21
100	stub 22
101	stub 23
102	stub 24
103	stub 25
104	stub 26
105	stub 27
106	stub 28
107	stub 29
108	stub_err 30
109	stub 31
110	/* Split the macro so we avoid a stack overflow. */
111	user_defined_stubs 32, 63
112	user_defined_stubs 64, 127
113	user_defined_stubs 128, 191
114	user_defined_stubs 192, 255
115
116exception_common:
117	/*
118	 * At this point, on x86-64, on the stack there is:
119	 * 0(%rsp) rip
120	 * 8(%rsp) cs
121	 * 16(%rsp) rflags
122	 * 24(%rsp) rsp
123	 * 32(%rsp) ss
124	 *
125	 * This section sets up the exception stack.
126	 * It saves the old stack pointer (rsp) to preserve RIP, CS, RFLAGS and SS.
127	 * Then sets up the new stack pointer to point to the exception stack area.
128	 */
129	movq %rax, old_rax
130	movq %rcx, old_rcx
131
132	mov %rsp, %rax
133	movq exception_stack_end, %rsp
134	/*
135	 * The `exception_state` struct is not 16-byte aligned.
136	 * Push an extra 8 bytes to ensure the stack pointer
137	 * is 16-byte aligned before calling exception_dispatch.
138	 */
139	push $0
140
141	/*
142	 * Push values onto the top of the exception stack to form an
143	 * exception state structure.
144	 */
145	push vector
146	push error_code
147
148	/* push of the gs, fs, es, ds, ss and cs */
149	mov %gs, %rcx
150	movl %ecx, -4(%rsp)   /* gs */
151	mov %fs, %rcx
152	movl %ecx, -8(%rsp)   /* fs */
153	movl $0, -12(%rsp)    /* es */
154	movl $0, -16(%rsp)    /* ds */
155	movq 32(%rax), %rcx
156	movl %ecx, -20(%rsp)  /* ss */
157	movq 8(%rax), %rcx
158	movl %ecx, -24(%rsp)  /* cs */
159	sub $24, %rsp
160
161	push 16(%rax)         /* rflags */
162	push (%rax)           /* rip */
163	push %r15
164	push %r14
165	push %r13
166	push %r12
167	push %r11
168	push %r10
169	push %r9
170	push %r8
171	push 24(%rax)         /* rsp */
172	push %rbp
173	push %rdi
174	push %rsi
175	push %rdx
176	push old_rcx          /* rcx */
177	push %rbx
178	push old_rax          /* rax */
179
180	/*
181	 * Call the C exception handler. It will find the exception state
182	 * using the exception_state global pointer. Not
183	 * passing parameters means we don't have to worry about what ABI
184	 * is being used.
185	 */
186	mov %rsp, exception_state
187	call exception_dispatch
188
189	/*
190	 * Restore state from the exception state structure, including any
191	 * changes that might have been made.
192	 */
193	pop old_rax
194	pop %rbx
195	pop old_rcx
196	pop %rdx
197	pop %rsi
198	pop %rdi
199	pop %rbp
200	lea exception_stack, %rax
201	pop 24(%rax)          /* rsp */
202	pop %r8
203	pop %r9
204	pop %r10
205	pop %r11
206	pop %r12
207	pop %r13
208	pop %r14
209	pop %r15
210	pop (%rax)            /* rip */
211	pop 16(%rax)          /* rflags */
212
213	/* pop of the gs, fs, es, ds, ss and cs */
214	movl (%rsp), %ecx
215	movq %rcx, 8(%rax)    /* cs */
216	movl 4(%rsp), %ecx
217	movq %rcx, 32(%rax)   /* ss */
218	movl 16(%rsp), %ecx
219	mov %rcx, %fs         /* fs */
220	movl 20(%rsp), %ecx
221	mov %rcx, %gs         /* gs */
222
223	mov %rax, %rsp
224	movq old_rax, %rax
225	movq old_rcx, %rcx
226
227	iretq
228
229/*
230 * We need segment selectors for the IDT, so we need to know where things are
231 * in the GDT. We set one up here which is pretty standard and largely copied
232 * from coreboot.
233 */
234	.align 16
235gdt:
236	/* selgdt 0, unused */
237	.word 0x0000, 0x0000
238	.byte 0x00, 0x00, 0x00, 0x00
239
240	/* selgdt 8, unused */
241	.word 0x0000, 0x0000
242	.byte 0x00, 0x00, 0x00, 0x00
243
244	/* selgdt 0x10, flat 4GB code segment */
245	.word 0xffff, 0x0000
246	.byte 0x00, 0x9b, 0xcf, 0x00
247
248	/* selgdt 0x18, flat 4GB data segment */
249	.word 0xffff, 0x0000
250	.byte 0x00, 0x92, 0xcf, 0x00
251
252	/* selgdt 0x20, flat x64 code segment */
253	.word 0xffff, 0x0000
254	.byte 0x00, 0x9b, 0xaf, 0x00
255gdt_end:
256
257/* GDT pointer for use with lgdt */
258.global gdt_ptr
259gdt_ptr:
260	.word gdt_end - gdt - 1
261	.quad gdt
262
263	/*
264	 * Record the target and construct the actual entry at init time. This
265	 * is necessary because the linker doesn't want to construct the entry
266	 * for us.
267	 */
268	.macro interrupt_gate target
269	.word 0       /* patchable */
270	.word 0x20    /* Target code segment selector */
271	.word 0xee00  /* Present, Type 64-bit Interrupt Gate */
272	.word 0       /* patchable */
273	.quad \target /* patchable */
274	.endm
275
276	.altmacro
277	.macro	user_defined_gates from, to
278	interrupt_gate	exception_stub_\from
279	.if	\to-\from
280	user_defined_gates	%(from+1),\to
281	.endif
282	.endm
283
284	.align 16
285	.global idt
286idt:
287	interrupt_gate exception_stub_0
288	interrupt_gate exception_stub_1
289	interrupt_gate exception_stub_2
290	interrupt_gate exception_stub_3
291	interrupt_gate exception_stub_4
292	interrupt_gate exception_stub_5
293	interrupt_gate exception_stub_6
294	interrupt_gate exception_stub_7
295	interrupt_gate exception_stub_8
296	interrupt_gate exception_stub_9
297	interrupt_gate exception_stub_10
298	interrupt_gate exception_stub_11
299	interrupt_gate exception_stub_12
300	interrupt_gate exception_stub_13
301	interrupt_gate exception_stub_14
302	interrupt_gate exception_stub_15
303	interrupt_gate exception_stub_16
304	interrupt_gate exception_stub_17
305	interrupt_gate exception_stub_18
306	interrupt_gate exception_stub_19
307	interrupt_gate exception_stub_20
308	interrupt_gate exception_stub_21
309	interrupt_gate exception_stub_22
310	interrupt_gate exception_stub_23
311	interrupt_gate exception_stub_24
312	interrupt_gate exception_stub_25
313	interrupt_gate exception_stub_26
314	interrupt_gate exception_stub_27
315	interrupt_gate exception_stub_28
316	interrupt_gate exception_stub_29
317	interrupt_gate exception_stub_30
318	interrupt_gate exception_stub_31
319	user_defined_gates 32, 63
320	user_defined_gates 64, 127
321	user_defined_gates 128, 191
322	user_defined_gates 192, 255
323idt_end:
324
325/* IDT pointer for use with lidt */
326idt_ptr:
327	.word idt_end - idt - 1
328	.quad idt
329
330.section .text.exception_init_asm
331.globl exception_init_asm
332.type exception_init_asm, @function
333
334exception_init_asm:
335	/* Set up IDT entries */
336	mov $idt, %rax
3371:
338	movq 8(%rax), %rdi
339	movw %di, (%rax)   /* procedure entry point offset bits 0..15 */
340	shr $16, %rdi
341	movw %di, 6(%rax)  /* procedure entry point offset bits 16..31 */
342	shr $16, %rdi
343	movl %edi, 8(%rax) /* procedure entry point offset bits 32..63 */
344	movl $0, 12(%rax)  /* reserved */
345	add $16, %rax
346	cmp $idt_end, %rax
347	jne 1b
348
349	/* Load the IDT */
350	lidt idt_ptr
351	ret
352