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