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