xref: /freebsd/stand/efi/loader/arch/amd64/trap.c (revision f4ca0fdbe69db4ab8147406e753d869d77485c2c)
1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh  * Copyright (c) 2016 The FreeBSD Foundation
3ca987d46SWarner Losh  *
4ca987d46SWarner Losh  * This software was developed by Konstantin Belousov under sponsorship
5ca987d46SWarner Losh  * from the FreeBSD Foundation.
6ca987d46SWarner Losh  *
7ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
8ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
9ca987d46SWarner Losh  * are met:
10ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
11ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
12ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
13ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
14ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
15ca987d46SWarner Losh  *
16ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17ca987d46SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18ca987d46SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ca987d46SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20ca987d46SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21ca987d46SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22ca987d46SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23ca987d46SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24ca987d46SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25ca987d46SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26ca987d46SWarner Losh  * SUCH DAMAGE.
27ca987d46SWarner Losh  */
28ca987d46SWarner Losh 
29ca987d46SWarner Losh #include <sys/cdefs.h>
30ca987d46SWarner Losh __FBSDID("$FreeBSD$");
31ca987d46SWarner Losh 
32ca987d46SWarner Losh #include <stand.h>
33ca987d46SWarner Losh #include <string.h>
34ca987d46SWarner Losh #include <sys/param.h>
35ca987d46SWarner Losh #include <machine/cpufunc.h>
36ca987d46SWarner Losh #include <machine/psl.h>
37ca987d46SWarner Losh #include <machine/segments.h>
38ca987d46SWarner Losh #include <machine/frame.h>
39ca987d46SWarner Losh #include <machine/tss.h>
40ca987d46SWarner Losh 
41ca987d46SWarner Losh #include <efi.h>
42ca987d46SWarner Losh #include <efilib.h>
43ca987d46SWarner Losh 
44ca987d46SWarner Losh #include "bootstrap.h"
45ca987d46SWarner Losh #include "loader_efi.h"
46ca987d46SWarner Losh 
47ca987d46SWarner Losh #define	NUM_IST	8
48ca987d46SWarner Losh #define	NUM_EXC	32
49ca987d46SWarner Losh 
50ca987d46SWarner Losh /*
51ca987d46SWarner Losh  * This code catches exceptions but forwards hardware interrupts to
52ca987d46SWarner Losh  * handlers installed by firmware.  It differentiates exceptions
53ca987d46SWarner Losh  * vs. interrupts by presence of the error code on the stack, which
54ca987d46SWarner Losh  * causes different stack pointer value on trap handler entry.
55ca987d46SWarner Losh  *
56ca987d46SWarner Losh  * Use kernel layout for the trapframe just to not be original.
57ca987d46SWarner Losh  *
58ca987d46SWarner Losh  * Use free IST slot in existing TSS, or create our own TSS if
59ca987d46SWarner Losh  * firmware did not configured any, to have stack switched to
60ca987d46SWarner Losh  * IST-specified one, e.g. to handle #SS.  If hand-off cannot find
61ca987d46SWarner Losh  * unused IST slot, or create a new descriptor in GDT, we bail out.
62ca987d46SWarner Losh  */
63ca987d46SWarner Losh 
64ca987d46SWarner Losh static struct region_descriptor fw_idt;	/* Descriptor for pristine fw IDT */
65ca987d46SWarner Losh static struct region_descriptor loader_idt;/* Descriptor for loader
66ca987d46SWarner Losh 					   shadow IDT */
67ca987d46SWarner Losh static EFI_PHYSICAL_ADDRESS lidt_pa;	/* Address of loader shadow IDT */
68ca987d46SWarner Losh static EFI_PHYSICAL_ADDRESS tss_pa;	/* Address of TSS */
69ca987d46SWarner Losh static EFI_PHYSICAL_ADDRESS exc_stack_pa;/* Address of IST stack for loader */
70ca987d46SWarner Losh EFI_PHYSICAL_ADDRESS exc_rsp;	/* %rsp value on our IST stack when
71ca987d46SWarner Losh 				   exception happens */
72ca987d46SWarner Losh EFI_PHYSICAL_ADDRESS fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT
73ca987d46SWarner Losh 						   vectors */
74ca987d46SWarner Losh static int intercepted[NUM_EXC];
75ca987d46SWarner Losh static int ist;				/* IST for exception handlers */
76ca987d46SWarner Losh static uint32_t tss_fw_seg;		/* Fw TSS segment */
77ca987d46SWarner Losh static uint32_t loader_tss;		/* Loader TSS segment */
78ca987d46SWarner Losh static struct region_descriptor fw_gdt;	/* Descriptor of pristine GDT */
79ca987d46SWarner Losh static EFI_PHYSICAL_ADDRESS loader_gdt_pa; /* Address of loader shadow GDT */
80ca987d46SWarner Losh 
81*f4ca0fdbSToomas Soome struct frame {
82*f4ca0fdbSToomas Soome 	struct frame	*fr_savfp;
83*f4ca0fdbSToomas Soome 	uintptr_t	fr_savpc;
84*f4ca0fdbSToomas Soome };
85*f4ca0fdbSToomas Soome 
86ca987d46SWarner Losh void report_exc(struct trapframe *tf);
87ca987d46SWarner Losh void
88ca987d46SWarner Losh report_exc(struct trapframe *tf)
89ca987d46SWarner Losh {
90*f4ca0fdbSToomas Soome 	struct frame *fp;
91*f4ca0fdbSToomas Soome 	uintptr_t pc, base;
92*f4ca0fdbSToomas Soome 	char buf[80];
93*f4ca0fdbSToomas Soome 	int ret;
94ca987d46SWarner Losh 
95*f4ca0fdbSToomas Soome 	base = (uintptr_t)boot_img->ImageBase;
96ca987d46SWarner Losh 	/*
97ca987d46SWarner Losh 	 * printf() depends on loader runtime and UEFI firmware health
98ca987d46SWarner Losh 	 * to produce the console output, in case of exception, the
99ca987d46SWarner Losh 	 * loader or firmware runtime may fail to support the printf().
100ca987d46SWarner Losh 	 */
101ca987d46SWarner Losh 	printf("===================================================="
102ca987d46SWarner Losh 	    "============================\n");
103ca987d46SWarner Losh 	printf("Exception %u\n", tf->tf_trapno);
104ca987d46SWarner Losh 	printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "
105ca987d46SWarner Losh 	    "gs 0x%04hx\n",
106ca987d46SWarner Losh 	    (uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,
107ca987d46SWarner Losh 	    (uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);
108ca987d46SWarner Losh 	printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"
109ca987d46SWarner Losh 	    "rsp 0x%016lx rip 0x%016lx\n",
110ca987d46SWarner Losh 	    (uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,
111ca987d46SWarner Losh 	    tf->tf_rsp, tf->tf_rip);
112ca987d46SWarner Losh 	printf(
113ca987d46SWarner Losh 	    "rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"
114ca987d46SWarner Losh 	    "rcx 0x%016lx r8  0x%016lx r9  0x%016lx\n"
115ca987d46SWarner Losh 	    "rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"
116ca987d46SWarner Losh 	    "r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"
117ca987d46SWarner Losh 	    "r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",
118ca987d46SWarner Losh 	    tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,
119ca987d46SWarner Losh 	    tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,
120ca987d46SWarner Losh 	    tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
121*f4ca0fdbSToomas Soome 
122*f4ca0fdbSToomas Soome 	fp = (struct frame *)tf->tf_rbp;
123*f4ca0fdbSToomas Soome 	pc = tf->tf_rip;
124*f4ca0fdbSToomas Soome 
125*f4ca0fdbSToomas Soome 	printf("Stack trace:\n");
126*f4ca0fdbSToomas Soome 	pager_open();
127*f4ca0fdbSToomas Soome 	while (fp != NULL || pc != 0) {
128*f4ca0fdbSToomas Soome 		char *source = "PC";
129*f4ca0fdbSToomas Soome 
130*f4ca0fdbSToomas Soome 		if (pc >= base && pc < base + boot_img->ImageSize) {
131*f4ca0fdbSToomas Soome 			pc -= base;
132*f4ca0fdbSToomas Soome 			source = "loader PC";
133*f4ca0fdbSToomas Soome 		}
134*f4ca0fdbSToomas Soome 		(void) snprintf(buf, sizeof (buf), "FP %016lx: %s 0x%016lx\n",
135*f4ca0fdbSToomas Soome 		    (uintptr_t)fp, source, pc);
136*f4ca0fdbSToomas Soome 		if (pager_output(buf))
137*f4ca0fdbSToomas Soome 			break;
138*f4ca0fdbSToomas Soome 
139*f4ca0fdbSToomas Soome 		if (fp != NULL)
140*f4ca0fdbSToomas Soome 			fp = fp->fr_savfp;
141*f4ca0fdbSToomas Soome 
142*f4ca0fdbSToomas Soome 		if (fp != NULL)
143*f4ca0fdbSToomas Soome 			pc = fp->fr_savpc;
144*f4ca0fdbSToomas Soome 		else
145*f4ca0fdbSToomas Soome 			pc = 0;
146*f4ca0fdbSToomas Soome 	}
147*f4ca0fdbSToomas Soome 	pager_close();
148ca987d46SWarner Losh 	printf("Machine stopped.\n");
149ca987d46SWarner Losh }
150ca987d46SWarner Losh 
151ca987d46SWarner Losh static void
152ca987d46SWarner Losh prepare_exception(unsigned idx, uint64_t my_handler,
153ca987d46SWarner Losh     int ist_use_table[static NUM_IST])
154ca987d46SWarner Losh {
155ca987d46SWarner Losh 	struct gate_descriptor *fw_idt_e, *loader_idt_e;
156ca987d46SWarner Losh 
157ca987d46SWarner Losh 	fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];
158ca987d46SWarner Losh 	loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];
159ca987d46SWarner Losh 	fw_intr_handlers[idx] = fw_idt_e->gd_looffset +
160ca987d46SWarner Losh 	    (fw_idt_e->gd_hioffset << 16);
161ca987d46SWarner Losh 	intercepted[idx] = 1;
162ca987d46SWarner Losh 	ist_use_table[fw_idt_e->gd_ist]++;
163ca987d46SWarner Losh 	loader_idt_e->gd_looffset = my_handler;
164ca987d46SWarner Losh 	loader_idt_e->gd_hioffset = my_handler >> 16;
165ca987d46SWarner Losh 	/*
166ca987d46SWarner Losh 	 * We reuse uefi selector for the code segment for the exception
167ca987d46SWarner Losh 	 * handler code, while the reason for the fault might be the
168ca987d46SWarner Losh 	 * corruption of that gdt entry. On the other hand, allocating
169ca987d46SWarner Losh 	 * our own descriptor might be not much better, if gdt is corrupted.
170ca987d46SWarner Losh 	 */
171ca987d46SWarner Losh 	loader_idt_e->gd_selector = fw_idt_e->gd_selector;
172ca987d46SWarner Losh 	loader_idt_e->gd_ist = 0;
173ca987d46SWarner Losh 	loader_idt_e->gd_type = SDT_SYSIGT;
174ca987d46SWarner Losh 	loader_idt_e->gd_dpl = 0;
175ca987d46SWarner Losh 	loader_idt_e->gd_p = 1;
176ca987d46SWarner Losh 	loader_idt_e->gd_xx = 0;
177ca987d46SWarner Losh 	loader_idt_e->sd_xx1 = 0;
178ca987d46SWarner Losh }
179ca987d46SWarner Losh #define	PREPARE_EXCEPTION(N)						\
180ca987d46SWarner Losh     extern char EXC##N##_handler[];					\
181ca987d46SWarner Losh     prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);
182ca987d46SWarner Losh 
183ca987d46SWarner Losh static void
184ca987d46SWarner Losh free_tables(void)
185ca987d46SWarner Losh {
186ca987d46SWarner Losh 
187ca987d46SWarner Losh 	if (lidt_pa != 0) {
188ca987d46SWarner Losh 		BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));
189ca987d46SWarner Losh 		lidt_pa = 0;
190ca987d46SWarner Losh 	}
191ca987d46SWarner Losh 	if (exc_stack_pa != 0) {
192ca987d46SWarner Losh 		BS->FreePages(exc_stack_pa, 1);
193ca987d46SWarner Losh 		exc_stack_pa = 0;
194ca987d46SWarner Losh 	}
195ca987d46SWarner Losh 	if (tss_pa != 0 && tss_fw_seg == 0) {
196ca987d46SWarner Losh 		BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct
197ca987d46SWarner Losh 		    amd64tss)));
198ca987d46SWarner Losh 		tss_pa = 0;
199ca987d46SWarner Losh 	}
200ca987d46SWarner Losh 	if (loader_gdt_pa != 0) {
201ca987d46SWarner Losh 		BS->FreePages(tss_pa, 2);
202ca987d46SWarner Losh 		loader_gdt_pa = 0;
203ca987d46SWarner Losh 	}
204ca987d46SWarner Losh 	ist = 0;
205ca987d46SWarner Losh 	loader_tss = 0;
206ca987d46SWarner Losh }
207ca987d46SWarner Losh 
208ca987d46SWarner Losh static int
209ca987d46SWarner Losh efi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,
210ca987d46SWarner Losh     struct amd64tss **tss)
211ca987d46SWarner Losh {
212ca987d46SWarner Losh 	EFI_STATUS status;
213ca987d46SWarner Losh 	struct system_segment_descriptor *tss_desc;
214ca987d46SWarner Losh 
215ca987d46SWarner Losh 	tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +
216ca987d46SWarner Losh 	    (loader_tss_idx << 3));
217ca987d46SWarner Losh 	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
218ca987d46SWarner Losh 	    EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa);
219ca987d46SWarner Losh 	if (EFI_ERROR(status)) {
220ca987d46SWarner Losh 		printf("efi_setup_tss: AllocatePages tss error %lu\n",
221ca987d46SWarner Losh 		    EFI_ERROR_CODE(status));
222ca987d46SWarner Losh 		return (0);
223ca987d46SWarner Losh 	}
224ca987d46SWarner Losh 	*tss = (struct amd64tss *)tss_pa;
225ca987d46SWarner Losh 	bzero(*tss, sizeof(**tss));
226ca987d46SWarner Losh 	tss_desc->sd_lolimit = sizeof(struct amd64tss);
227ca987d46SWarner Losh 	tss_desc->sd_lobase = tss_pa;
228ca987d46SWarner Losh 	tss_desc->sd_type = SDT_SYSTSS;
229ca987d46SWarner Losh 	tss_desc->sd_dpl = 0;
230ca987d46SWarner Losh 	tss_desc->sd_p = 1;
231ca987d46SWarner Losh 	tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16;
232ca987d46SWarner Losh 	tss_desc->sd_gran = 0;
233ca987d46SWarner Losh 	tss_desc->sd_hibase = tss_pa >> 24;
234ca987d46SWarner Losh 	tss_desc->sd_xx0 = 0;
235ca987d46SWarner Losh 	tss_desc->sd_xx1 = 0;
236ca987d46SWarner Losh 	tss_desc->sd_mbz = 0;
237ca987d46SWarner Losh 	tss_desc->sd_xx2 = 0;
238ca987d46SWarner Losh 	return (1);
239ca987d46SWarner Losh }
240ca987d46SWarner Losh 
241ca987d46SWarner Losh static int
242ca987d46SWarner Losh efi_redirect_exceptions(void)
243ca987d46SWarner Losh {
244ca987d46SWarner Losh 	int ist_use_table[NUM_IST];
245ca987d46SWarner Losh 	struct gate_descriptor *loader_idt_e;
246ca987d46SWarner Losh 	struct system_segment_descriptor *tss_desc, *gdt_desc;
247ca987d46SWarner Losh 	struct amd64tss *tss;
248ca987d46SWarner Losh 	struct region_descriptor *gdt_rd, loader_gdt;
249ca987d46SWarner Losh 	uint32_t i;
250ca987d46SWarner Losh 	EFI_STATUS status;
251ca987d46SWarner Losh 	register_t rfl;
252ca987d46SWarner Losh 
253ca987d46SWarner Losh 	sidt(&fw_idt);
254ca987d46SWarner Losh 	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
255ca987d46SWarner Losh 	    EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);
256ca987d46SWarner Losh 	if (EFI_ERROR(status)) {
257ca987d46SWarner Losh 		printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",
258ca987d46SWarner Losh 		    EFI_ERROR_CODE(status));
259ca987d46SWarner Losh 		lidt_pa = 0;
260ca987d46SWarner Losh 		return (0);
261ca987d46SWarner Losh 	}
262ca987d46SWarner Losh 	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,
263ca987d46SWarner Losh 	    &exc_stack_pa);
264ca987d46SWarner Losh 	if (EFI_ERROR(status)) {
265ca987d46SWarner Losh 		printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",
266ca987d46SWarner Losh 		    EFI_ERROR_CODE(status));
267ca987d46SWarner Losh 		exc_stack_pa = 0;
268ca987d46SWarner Losh 		free_tables();
269ca987d46SWarner Losh 		return (0);
270ca987d46SWarner Losh 	}
271ca987d46SWarner Losh 	loader_idt.rd_limit = fw_idt.rd_limit;
272ca987d46SWarner Losh 	bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,
273ca987d46SWarner Losh 	    loader_idt.rd_limit);
274ca987d46SWarner Losh 	bzero(ist_use_table, sizeof(ist_use_table));
275ca987d46SWarner Losh 	bzero(fw_intr_handlers, sizeof(fw_intr_handlers));
276ca987d46SWarner Losh 	bzero(intercepted, sizeof(intercepted));
277ca987d46SWarner Losh 
278ca987d46SWarner Losh 	sgdt(&fw_gdt);
279ca987d46SWarner Losh 	tss_fw_seg = read_tr();
280ca987d46SWarner Losh 	gdt_rd = NULL;
281ca987d46SWarner Losh 	if (tss_fw_seg == 0) {
282ca987d46SWarner Losh 		for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit;
283ca987d46SWarner Losh 		    i += 2) {
284ca987d46SWarner Losh 			gdt_desc = (struct system_segment_descriptor *)(
285ca987d46SWarner Losh 			    fw_gdt.rd_base + (i << 3));
286ca987d46SWarner Losh 			if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {
287ca987d46SWarner Losh 				gdt_rd = &fw_gdt;
288ca987d46SWarner Losh 				break;
289ca987d46SWarner Losh 			}
290ca987d46SWarner Losh 		}
291ca987d46SWarner Losh 		if (gdt_rd == NULL) {
292ca987d46SWarner Losh 			if (i >= 8190) {
293ca987d46SWarner Losh 				printf("efi_redirect_exceptions: all slots "
294ca987d46SWarner Losh 				    "in gdt are used\n");
295ca987d46SWarner Losh 				free_tables();
296ca987d46SWarner Losh 				return (0);
297ca987d46SWarner Losh 			}
298ca987d46SWarner Losh 			loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +
299ca987d46SWarner Losh 			    sizeof(struct system_segment_descriptor),
300ca987d46SWarner Losh 			    sizeof(struct system_segment_descriptor)) - 1;
301ca987d46SWarner Losh 			i = (loader_gdt.rd_limit + 1 -
302ca987d46SWarner Losh 			    sizeof(struct system_segment_descriptor)) /
303ca987d46SWarner Losh 			    sizeof(struct system_segment_descriptor) * 2;
304ca987d46SWarner Losh 			status = BS->AllocatePages(AllocateAnyPages,
305ca987d46SWarner Losh 			    EfiLoaderData,
306ca987d46SWarner Losh 			    EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),
307ca987d46SWarner Losh 			    &loader_gdt_pa);
308ca987d46SWarner Losh 			if (EFI_ERROR(status)) {
309ca987d46SWarner Losh 				printf("efi_setup_tss: AllocatePages gdt error "
310ca987d46SWarner Losh 				    "%lu\n",  EFI_ERROR_CODE(status));
311ca987d46SWarner Losh 				loader_gdt_pa = 0;
312ca987d46SWarner Losh 				free_tables();
313ca987d46SWarner Losh 				return (0);
314ca987d46SWarner Losh 			}
315ca987d46SWarner Losh 			loader_gdt.rd_base = loader_gdt_pa;
316ca987d46SWarner Losh 			bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);
317ca987d46SWarner Losh 			bcopy((void *)fw_gdt.rd_base,
318ca987d46SWarner Losh 			    (void *)loader_gdt.rd_base, fw_gdt.rd_limit);
319ca987d46SWarner Losh 			gdt_rd = &loader_gdt;
320ca987d46SWarner Losh 		}
321ca987d46SWarner Losh 		loader_tss = i << 3;
322ca987d46SWarner Losh 		if (!efi_setup_tss(gdt_rd, i, &tss)) {
323ca987d46SWarner Losh 			tss_pa = 0;
324ca987d46SWarner Losh 			free_tables();
325ca987d46SWarner Losh 			return (0);
326ca987d46SWarner Losh 		}
327ca987d46SWarner Losh 	} else {
328ca987d46SWarner Losh 		tss_desc = (struct system_segment_descriptor *)((char *)
329ca987d46SWarner Losh 		    fw_gdt.rd_base + tss_fw_seg);
330ca987d46SWarner Losh 		if (tss_desc->sd_type != SDT_SYSTSS &&
331ca987d46SWarner Losh 		    tss_desc->sd_type != SDT_SYSBSY) {
332ca987d46SWarner Losh 			printf("LTR points to non-TSS descriptor\n");
333ca987d46SWarner Losh 			free_tables();
334ca987d46SWarner Losh 			return (0);
335ca987d46SWarner Losh 		}
336ca987d46SWarner Losh 		tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);
337ca987d46SWarner Losh 		tss = (struct amd64tss *)tss_pa;
338ca987d46SWarner Losh 		tss_desc->sd_type = SDT_SYSTSS; /* unbusy */
339ca987d46SWarner Losh 	}
340ca987d46SWarner Losh 
341ca987d46SWarner Losh 	PREPARE_EXCEPTION(0);
342ca987d46SWarner Losh 	PREPARE_EXCEPTION(1);
343ca987d46SWarner Losh 	PREPARE_EXCEPTION(2);
344ca987d46SWarner Losh 	PREPARE_EXCEPTION(3);
345ca987d46SWarner Losh 	PREPARE_EXCEPTION(4);
346ca987d46SWarner Losh 	PREPARE_EXCEPTION(5);
347ca987d46SWarner Losh 	PREPARE_EXCEPTION(6);
348ca987d46SWarner Losh 	PREPARE_EXCEPTION(7);
349ca987d46SWarner Losh 	PREPARE_EXCEPTION(8);
350ca987d46SWarner Losh 	PREPARE_EXCEPTION(9);
351ca987d46SWarner Losh 	PREPARE_EXCEPTION(10);
352ca987d46SWarner Losh 	PREPARE_EXCEPTION(11);
353ca987d46SWarner Losh 	PREPARE_EXCEPTION(12);
354ca987d46SWarner Losh 	PREPARE_EXCEPTION(13);
355ca987d46SWarner Losh 	PREPARE_EXCEPTION(14);
356ca987d46SWarner Losh 	PREPARE_EXCEPTION(16);
357ca987d46SWarner Losh 	PREPARE_EXCEPTION(17);
358ca987d46SWarner Losh 	PREPARE_EXCEPTION(18);
359ca987d46SWarner Losh 	PREPARE_EXCEPTION(19);
360ca987d46SWarner Losh 	PREPARE_EXCEPTION(20);
361ca987d46SWarner Losh 
362ca987d46SWarner Losh 	exc_rsp = exc_stack_pa + PAGE_SIZE -
363ca987d46SWarner Losh 	    (6 /* hw exception frame */ + 3 /* scratch regs */) * 8;
364ca987d46SWarner Losh 
365ca987d46SWarner Losh 	/* Find free IST and use it */
366ca987d46SWarner Losh 	for (ist = 1; ist < NUM_IST; ist++) {
367ca987d46SWarner Losh 		if (ist_use_table[ist] == 0)
368ca987d46SWarner Losh 			break;
369ca987d46SWarner Losh 	}
370ca987d46SWarner Losh 	if (ist == NUM_IST) {
371ca987d46SWarner Losh 		printf("efi_redirect_exceptions: all ISTs used\n");
372ca987d46SWarner Losh 		free_tables();
373ca987d46SWarner Losh 		lidt_pa = 0;
374ca987d46SWarner Losh 		return (0);
375ca987d46SWarner Losh 	}
376ca987d46SWarner Losh 	for (i = 0; i < NUM_EXC; i++) {
377ca987d46SWarner Losh 		loader_idt_e = &((struct gate_descriptor *)loader_idt.
378ca987d46SWarner Losh 		    rd_base)[i];
379ca987d46SWarner Losh 		if (intercepted[i])
380ca987d46SWarner Losh 			loader_idt_e->gd_ist = ist;
381ca987d46SWarner Losh 	}
382ca987d46SWarner Losh 	(&(tss->tss_ist1))[ist - 1] = exc_stack_pa + PAGE_SIZE;
383ca987d46SWarner Losh 
384ca987d46SWarner Losh 	/* Switch to new IDT */
385ca987d46SWarner Losh 	rfl = intr_disable();
386ca987d46SWarner Losh 	if (loader_gdt_pa != 0)
387ca987d46SWarner Losh 		bare_lgdt(&loader_gdt);
388ca987d46SWarner Losh 	if (loader_tss != 0)
389ca987d46SWarner Losh 		ltr(loader_tss);
390ca987d46SWarner Losh 	lidt(&loader_idt);
391ca987d46SWarner Losh 	intr_restore(rfl);
392ca987d46SWarner Losh 	return (1);
393ca987d46SWarner Losh }
394ca987d46SWarner Losh 
395ca987d46SWarner Losh static void
396ca987d46SWarner Losh efi_unredirect_exceptions(void)
397ca987d46SWarner Losh {
398ca987d46SWarner Losh 	register_t rfl;
399ca987d46SWarner Losh 
400ca987d46SWarner Losh 	if (lidt_pa == 0)
401ca987d46SWarner Losh 		return;
402ca987d46SWarner Losh 
403ca987d46SWarner Losh 	rfl = intr_disable();
404ca987d46SWarner Losh 	if (ist != 0)
405ca987d46SWarner Losh 		(&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;
406ca987d46SWarner Losh 	if (loader_gdt_pa != 0)
407ca987d46SWarner Losh 		bare_lgdt(&fw_gdt);
408ca987d46SWarner Losh 	if (loader_tss != 0)
409ca987d46SWarner Losh 		ltr(tss_fw_seg);
410ca987d46SWarner Losh 	lidt(&fw_idt);
411ca987d46SWarner Losh 	intr_restore(rfl);
412ca987d46SWarner Losh 	free_tables();
413ca987d46SWarner Losh }
414ca987d46SWarner Losh 
415ca987d46SWarner Losh static int
416ca987d46SWarner Losh command_grab_faults(int argc, char *argv[])
417ca987d46SWarner Losh {
418ca987d46SWarner Losh 	int res;
419ca987d46SWarner Losh 
420ca987d46SWarner Losh 	res = efi_redirect_exceptions();
421ca987d46SWarner Losh 	if (!res)
422ca987d46SWarner Losh 		printf("failed\n");
423ca987d46SWarner Losh 	return (CMD_OK);
424ca987d46SWarner Losh }
425ca987d46SWarner Losh COMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);
426ca987d46SWarner Losh 
427ca987d46SWarner Losh static int
428ca987d46SWarner Losh command_ungrab_faults(int argc, char *argv[])
429ca987d46SWarner Losh {
430ca987d46SWarner Losh 
431ca987d46SWarner Losh 	efi_unredirect_exceptions();
432ca987d46SWarner Losh 	return (CMD_OK);
433ca987d46SWarner Losh }
434ca987d46SWarner Losh COMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",
435ca987d46SWarner Losh     command_ungrab_faults);
436ca987d46SWarner Losh 
437ca987d46SWarner Losh static int
438ca987d46SWarner Losh command_fault(int argc, char *argv[])
439ca987d46SWarner Losh {
440ca987d46SWarner Losh 
441ca987d46SWarner Losh 	__asm("ud2");
442ca987d46SWarner Losh 	return (CMD_OK);
443ca987d46SWarner Losh }
444ca987d46SWarner Losh COMMAND_SET(fault, "fault", "generate fault", command_fault);
445