1306090Skib/*-
2306090Skib * Copyright (c) 2016 The FreeBSD Foundation
3306090Skib * All rights reserved.
4306090Skib *
5306090Skib * This software was developed by Konstantin Belousov under sponsorship
6306090Skib * from the FreeBSD Foundation.
7306090Skib *
8306090Skib * Redistribution and use in source and binary forms, with or without
9306090Skib * modification, are permitted provided that the following conditions
10306090Skib * are met:
11306090Skib * 1. Redistributions of source code must retain the above copyright
12306090Skib *    notice, this list of conditions and the following disclaimer.
13306090Skib * 2. Redistributions in binary form must reproduce the above copyright
14306090Skib *    notice, this list of conditions and the following disclaimer in the
15306090Skib *    documentation and/or other materials provided with the distribution.
16306090Skib *
17306090Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18306090Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19306090Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20306090Skib * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21306090Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22306090Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23306090Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24306090Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25306090Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26306090Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27306090Skib * SUCH DAMAGE.
28306090Skib */
29306090Skib
30306090Skib#include <sys/cdefs.h>
31306090Skib__FBSDID("$FreeBSD: stable/11/stand/efi/loader/arch/amd64/trap.c 329114 2018-02-11 02:27:50Z kevans $");
32306090Skib
33306090Skib#include <stand.h>
34306090Skib#include <string.h>
35306090Skib#include <sys/param.h>
36306090Skib#include <machine/cpufunc.h>
37306090Skib#include <machine/psl.h>
38306090Skib#include <machine/segments.h>
39306090Skib#include <machine/frame.h>
40306090Skib#include <machine/tss.h>
41306090Skib
42306090Skib#include <efi.h>
43306090Skib#include <efilib.h>
44306090Skib
45306090Skib#include "bootstrap.h"
46306090Skib#include "loader_efi.h"
47306090Skib
48306090Skib#define	NUM_IST	8
49306090Skib#define	NUM_EXC	32
50306090Skib
51306090Skib/*
52306090Skib * This code catches exceptions but forwards hardware interrupts to
53306090Skib * handlers installed by firmware.  It differentiates exceptions
54306090Skib * vs. interrupts by presence of the error code on the stack, which
55306090Skib * causes different stack pointer value on trap handler entry.
56306090Skib *
57306090Skib * Use kernel layout for the trapframe just to not be original.
58306090Skib *
59306090Skib * Use free IST slot in existing TSS, or create our own TSS if
60306090Skib * firmware did not configured any, to have stack switched to
61306090Skib * IST-specified one, e.g. to handle #SS.  If hand-off cannot find
62306090Skib * unused IST slot, or create a new descriptor in GDT, we bail out.
63306090Skib */
64306090Skib
65306090Skibstatic struct region_descriptor fw_idt;	/* Descriptor for pristine fw IDT */
66306090Skibstatic struct region_descriptor loader_idt;/* Descriptor for loader
67306090Skib					   shadow IDT */
68306090Skibstatic EFI_PHYSICAL_ADDRESS lidt_pa;	/* Address of loader shadow IDT */
69306090Skibstatic EFI_PHYSICAL_ADDRESS tss_pa;	/* Address of TSS */
70306090Skibstatic EFI_PHYSICAL_ADDRESS exc_stack_pa;/* Address of IST stack for loader */
71306090SkibEFI_PHYSICAL_ADDRESS exc_rsp;	/* %rsp value on our IST stack when
72306090Skib				   exception happens */
73306090SkibEFI_PHYSICAL_ADDRESS fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT
74306090Skib						   vectors */
75306090Skibstatic int intercepted[NUM_EXC];
76306090Skibstatic int ist;				/* IST for exception handlers */
77306090Skibstatic uint32_t tss_fw_seg;		/* Fw TSS segment */
78306090Skibstatic uint32_t loader_tss;		/* Loader TSS segment */
79306090Skibstatic struct region_descriptor fw_gdt;	/* Descriptor of pristine GDT */
80306090Skibstatic EFI_PHYSICAL_ADDRESS loader_gdt_pa; /* Address of loader shadow GDT */
81306090Skib
82306090Skibvoid report_exc(struct trapframe *tf);
83306090Skibvoid
84306090Skibreport_exc(struct trapframe *tf)
85306090Skib{
86306090Skib
87329114Skevans	/*
88329114Skevans	 * printf() depends on loader runtime and UEFI firmware health
89329114Skevans	 * to produce the console output, in case of exception, the
90329114Skevans	 * loader or firmware runtime may fail to support the printf().
91329114Skevans	 */
92306090Skib	printf("===================================================="
93306090Skib	    "============================\n");
94306090Skib	printf("Exception %u\n", tf->tf_trapno);
95306090Skib	printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "
96306090Skib	    "gs 0x%04hx\n",
97306090Skib	    (uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,
98306090Skib	    (uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);
99306090Skib	printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"
100306090Skib	    "rsp 0x%016lx rip 0x%016lx\n",
101306090Skib	    (uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,
102306090Skib	    tf->tf_rsp, tf->tf_rip);
103306090Skib	printf(
104306090Skib	    "rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"
105306090Skib	    "rcx 0x%016lx r8  0x%016lx r9  0x%016lx\n"
106306090Skib	    "rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"
107306090Skib	    "r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"
108306090Skib	    "r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",
109306090Skib	    tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,
110306090Skib	    tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,
111306090Skib	    tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
112306090Skib	printf("Machine stopped.\n");
113306090Skib}
114306090Skib
115306090Skibstatic void
116306090Skibprepare_exception(unsigned idx, uint64_t my_handler,
117306090Skib    int ist_use_table[static NUM_IST])
118306090Skib{
119306090Skib	struct gate_descriptor *fw_idt_e, *loader_idt_e;
120306090Skib
121306090Skib	fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];
122306090Skib	loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];
123306090Skib	fw_intr_handlers[idx] = fw_idt_e->gd_looffset +
124306090Skib	    (fw_idt_e->gd_hioffset << 16);
125306090Skib	intercepted[idx] = 1;
126306090Skib	ist_use_table[fw_idt_e->gd_ist]++;
127306090Skib	loader_idt_e->gd_looffset = my_handler;
128306090Skib	loader_idt_e->gd_hioffset = my_handler >> 16;
129329114Skevans	/*
130329114Skevans	 * We reuse uefi selector for the code segment for the exception
131329114Skevans	 * handler code, while the reason for the fault might be the
132329114Skevans	 * corruption of that gdt entry. On the other hand, allocating
133329114Skevans	 * our own descriptor might be not much better, if gdt is corrupted.
134329114Skevans	 */
135329114Skevans	loader_idt_e->gd_selector = fw_idt_e->gd_selector;
136306090Skib	loader_idt_e->gd_ist = 0;
137306090Skib	loader_idt_e->gd_type = SDT_SYSIGT;
138306090Skib	loader_idt_e->gd_dpl = 0;
139306090Skib	loader_idt_e->gd_p = 1;
140306090Skib	loader_idt_e->gd_xx = 0;
141306090Skib	loader_idt_e->sd_xx1 = 0;
142306090Skib}
143306090Skib#define	PREPARE_EXCEPTION(N)						\
144306090Skib    extern char EXC##N##_handler[];					\
145306090Skib    prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);
146306090Skib
147306090Skibstatic void
148306090Skibfree_tables(void)
149306090Skib{
150306090Skib
151306090Skib	if (lidt_pa != 0) {
152306090Skib		BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));
153306090Skib		lidt_pa = 0;
154306090Skib	}
155306090Skib	if (exc_stack_pa != 0) {
156306090Skib		BS->FreePages(exc_stack_pa, 1);
157306090Skib		exc_stack_pa = 0;
158306090Skib	}
159306090Skib	if (tss_pa != 0 && tss_fw_seg == 0) {
160306090Skib		BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct
161306090Skib		    amd64tss)));
162306090Skib		tss_pa = 0;
163306090Skib	}
164306090Skib	if (loader_gdt_pa != 0) {
165306090Skib		BS->FreePages(tss_pa, 2);
166306090Skib		loader_gdt_pa = 0;
167306090Skib	}
168306090Skib	ist = 0;
169306090Skib	loader_tss = 0;
170306090Skib}
171306090Skib
172306090Skibstatic int
173306090Skibefi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,
174306090Skib    struct amd64tss **tss)
175306090Skib{
176306090Skib	EFI_STATUS status;
177306090Skib	struct system_segment_descriptor *tss_desc;
178306090Skib
179306090Skib	tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +
180306090Skib	    (loader_tss_idx << 3));
181306090Skib	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
182306090Skib	    EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa);
183306090Skib	if (EFI_ERROR(status)) {
184306090Skib		printf("efi_setup_tss: AllocatePages tss error %lu\n",
185306090Skib		    EFI_ERROR_CODE(status));
186306090Skib		return (0);
187306090Skib	}
188306090Skib	*tss = (struct amd64tss *)tss_pa;
189306090Skib	bzero(*tss, sizeof(**tss));
190306090Skib	tss_desc->sd_lolimit = sizeof(struct amd64tss);
191306090Skib	tss_desc->sd_lobase = tss_pa;
192306090Skib	tss_desc->sd_type = SDT_SYSTSS;
193306090Skib	tss_desc->sd_dpl = 0;
194306090Skib	tss_desc->sd_p = 1;
195306090Skib	tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16;
196306090Skib	tss_desc->sd_gran = 0;
197306090Skib	tss_desc->sd_hibase = tss_pa >> 24;
198306090Skib	tss_desc->sd_xx0 = 0;
199306090Skib	tss_desc->sd_xx1 = 0;
200306090Skib	tss_desc->sd_mbz = 0;
201306090Skib	tss_desc->sd_xx2 = 0;
202306090Skib	return (1);
203306090Skib}
204306090Skib
205306090Skibstatic int
206306090Skibefi_redirect_exceptions(void)
207306090Skib{
208306090Skib	int ist_use_table[NUM_IST];
209306090Skib	struct gate_descriptor *loader_idt_e;
210306090Skib	struct system_segment_descriptor *tss_desc, *gdt_desc;
211306090Skib	struct amd64tss *tss;
212306090Skib	struct region_descriptor *gdt_rd, loader_gdt;
213306090Skib	uint32_t i;
214306090Skib	EFI_STATUS status;
215306090Skib	register_t rfl;
216306090Skib
217306090Skib	sidt(&fw_idt);
218306090Skib	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
219306090Skib	    EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);
220306090Skib	if (EFI_ERROR(status)) {
221306090Skib		printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",
222306090Skib		    EFI_ERROR_CODE(status));
223306090Skib		lidt_pa = 0;
224306090Skib		return (0);
225306090Skib	}
226306090Skib	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,
227306090Skib	    &exc_stack_pa);
228306090Skib	if (EFI_ERROR(status)) {
229306090Skib		printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",
230306090Skib		    EFI_ERROR_CODE(status));
231306090Skib		exc_stack_pa = 0;
232306090Skib		free_tables();
233306090Skib		return (0);
234306090Skib	}
235306090Skib	loader_idt.rd_limit = fw_idt.rd_limit;
236306090Skib	bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,
237306090Skib	    loader_idt.rd_limit);
238306090Skib	bzero(ist_use_table, sizeof(ist_use_table));
239306090Skib	bzero(fw_intr_handlers, sizeof(fw_intr_handlers));
240306090Skib	bzero(intercepted, sizeof(intercepted));
241306090Skib
242306090Skib	sgdt(&fw_gdt);
243306090Skib	tss_fw_seg = read_tr();
244306090Skib	gdt_rd = NULL;
245306090Skib	if (tss_fw_seg == 0) {
246306090Skib		for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit;
247306090Skib		    i += 2) {
248306090Skib			gdt_desc = (struct system_segment_descriptor *)(
249306090Skib			    fw_gdt.rd_base + (i << 3));
250306090Skib			if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {
251306090Skib				gdt_rd = &fw_gdt;
252306090Skib				break;
253306090Skib			}
254306090Skib		}
255306090Skib		if (gdt_rd == NULL) {
256306090Skib			if (i >= 8190) {
257306090Skib				printf("efi_redirect_exceptions: all slots "
258306090Skib				    "in gdt are used\n");
259306090Skib				free_tables();
260306090Skib				return (0);
261306090Skib			}
262306090Skib			loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +
263306090Skib			    sizeof(struct system_segment_descriptor),
264306090Skib			    sizeof(struct system_segment_descriptor)) - 1;
265306090Skib			i = (loader_gdt.rd_limit + 1 -
266306090Skib			    sizeof(struct system_segment_descriptor)) /
267306090Skib			    sizeof(struct system_segment_descriptor) * 2;
268306090Skib			status = BS->AllocatePages(AllocateAnyPages,
269306090Skib			    EfiLoaderData,
270306090Skib			    EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),
271306090Skib			    &loader_gdt_pa);
272306090Skib			if (EFI_ERROR(status)) {
273306090Skib				printf("efi_setup_tss: AllocatePages gdt error "
274306090Skib				    "%lu\n",  EFI_ERROR_CODE(status));
275306090Skib				loader_gdt_pa = 0;
276306090Skib				free_tables();
277306090Skib				return (0);
278306090Skib			}
279306090Skib			loader_gdt.rd_base = loader_gdt_pa;
280306090Skib			bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);
281306090Skib			bcopy((void *)fw_gdt.rd_base,
282306090Skib			    (void *)loader_gdt.rd_base, fw_gdt.rd_limit);
283306090Skib			gdt_rd = &loader_gdt;
284306090Skib		}
285306090Skib		loader_tss = i << 3;
286306090Skib		if (!efi_setup_tss(gdt_rd, i, &tss)) {
287306090Skib			tss_pa = 0;
288306090Skib			free_tables();
289306090Skib			return (0);
290306090Skib		}
291306090Skib	} else {
292306090Skib		tss_desc = (struct system_segment_descriptor *)((char *)
293306090Skib		    fw_gdt.rd_base + tss_fw_seg);
294306090Skib		if (tss_desc->sd_type != SDT_SYSTSS &&
295306090Skib		    tss_desc->sd_type != SDT_SYSBSY) {
296306090Skib			printf("LTR points to non-TSS descriptor\n");
297306090Skib			free_tables();
298306090Skib			return (0);
299306090Skib		}
300306090Skib		tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);
301306090Skib		tss = (struct amd64tss *)tss_pa;
302306090Skib		tss_desc->sd_type = SDT_SYSTSS; /* unbusy */
303306090Skib	}
304306090Skib
305306090Skib	PREPARE_EXCEPTION(0);
306306090Skib	PREPARE_EXCEPTION(1);
307306090Skib	PREPARE_EXCEPTION(2);
308306090Skib	PREPARE_EXCEPTION(3);
309306090Skib	PREPARE_EXCEPTION(4);
310306090Skib	PREPARE_EXCEPTION(5);
311306090Skib	PREPARE_EXCEPTION(6);
312306090Skib	PREPARE_EXCEPTION(7);
313306090Skib	PREPARE_EXCEPTION(8);
314306090Skib	PREPARE_EXCEPTION(9);
315306090Skib	PREPARE_EXCEPTION(10);
316306090Skib	PREPARE_EXCEPTION(11);
317306090Skib	PREPARE_EXCEPTION(12);
318306090Skib	PREPARE_EXCEPTION(13);
319306090Skib	PREPARE_EXCEPTION(14);
320306090Skib	PREPARE_EXCEPTION(16);
321306090Skib	PREPARE_EXCEPTION(17);
322306090Skib	PREPARE_EXCEPTION(18);
323306090Skib	PREPARE_EXCEPTION(19);
324306090Skib	PREPARE_EXCEPTION(20);
325306090Skib
326306090Skib	exc_rsp = exc_stack_pa + PAGE_SIZE -
327306090Skib	    (6 /* hw exception frame */ + 3 /* scratch regs */) * 8;
328306090Skib
329306090Skib	/* Find free IST and use it */
330306090Skib	for (ist = 1; ist < NUM_IST; ist++) {
331306090Skib		if (ist_use_table[ist] == 0)
332306090Skib			break;
333306090Skib	}
334306090Skib	if (ist == NUM_IST) {
335306090Skib		printf("efi_redirect_exceptions: all ISTs used\n");
336306090Skib		free_tables();
337306090Skib		lidt_pa = 0;
338306090Skib		return (0);
339306090Skib	}
340306090Skib	for (i = 0; i < NUM_EXC; i++) {
341306090Skib		loader_idt_e = &((struct gate_descriptor *)loader_idt.
342306090Skib		    rd_base)[i];
343306090Skib		if (intercepted[i])
344306090Skib			loader_idt_e->gd_ist = ist;
345306090Skib	}
346306090Skib	(&(tss->tss_ist1))[ist - 1] = exc_stack_pa + PAGE_SIZE;
347306090Skib
348306090Skib	/* Switch to new IDT */
349306090Skib	rfl = intr_disable();
350306090Skib	if (loader_gdt_pa != 0)
351306090Skib		bare_lgdt(&loader_gdt);
352306090Skib	if (loader_tss != 0)
353306090Skib		ltr(loader_tss);
354306090Skib	lidt(&loader_idt);
355306090Skib	intr_restore(rfl);
356306090Skib	return (1);
357306090Skib}
358306090Skib
359306090Skibstatic void
360306090Skibefi_unredirect_exceptions(void)
361306090Skib{
362306090Skib	register_t rfl;
363306090Skib
364306090Skib	if (lidt_pa == 0)
365306090Skib		return;
366306090Skib
367306090Skib	rfl = intr_disable();
368306090Skib	if (ist != 0)
369306090Skib		(&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;
370306090Skib	if (loader_gdt_pa != 0)
371306090Skib		bare_lgdt(&fw_gdt);
372306090Skib	if (loader_tss != 0)
373306090Skib		ltr(tss_fw_seg);
374306090Skib	lidt(&fw_idt);
375306090Skib	intr_restore(rfl);
376306090Skib	free_tables();
377306090Skib}
378306090Skib
379306090Skibstatic int
380306090Skibcommand_grab_faults(int argc, char *argv[])
381306090Skib{
382306090Skib	int res;
383306090Skib
384306090Skib	res = efi_redirect_exceptions();
385306090Skib	if (!res)
386306090Skib		printf("failed\n");
387306090Skib	return (CMD_OK);
388306090Skib}
389306090SkibCOMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);
390306090Skib
391306090Skibstatic int
392306090Skibcommand_ungrab_faults(int argc, char *argv[])
393306090Skib{
394306090Skib
395306090Skib	efi_unredirect_exceptions();
396306090Skib	return (CMD_OK);
397306090Skib}
398306090SkibCOMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",
399306090Skib    command_ungrab_faults);
400306090Skib
401306090Skibstatic int
402306090Skibcommand_fault(int argc, char *argv[])
403306090Skib{
404306090Skib
405306090Skib	__asm("ud2");
406306090Skib	return (CMD_OK);
407306090Skib}
408306090SkibCOMMAND_SET(fault, "fault", "generate fault", command_fault);
409