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