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
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
66 loader_idt; /* Descriptor for loader 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
71 exc_rsp; /* %rsp value on our IST stack when exception happens */
72 EFI_PHYSICAL_ADDRESS
73 fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT 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 void report_exc(struct trapframe *tf);
82 void
report_exc(struct trapframe * tf)83 report_exc(struct trapframe *tf)
84 {
85
86 /*
87 * printf() depends on loader runtime and UEFI firmware health
88 * to produce the console output, in case of exception, the
89 * loader or firmware runtime may fail to support the printf().
90 */
91 printf("===================================================="
92 "============================\n");
93 printf("Exception %u\n", tf->tf_trapno);
94 printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "
95 "gs 0x%04hx\n",
96 (uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,
97 (uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);
98 printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"
99 "rsp 0x%016lx rip 0x%016lx\n",
100 (uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,
101 tf->tf_rsp, tf->tf_rip);
102 printf(
103 "rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"
104 "rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n"
105 "rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"
106 "r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"
107 "r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",
108 tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,
109 tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,
110 tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
111 printf("Machine stopped.\n");
112 }
113
114 static void
prepare_exception(unsigned idx,uint64_t my_handler,int ist_use_table[static NUM_IST])115 prepare_exception(unsigned idx, uint64_t my_handler,
116 int ist_use_table[static NUM_IST])
117 {
118 struct gate_descriptor *fw_idt_e, *loader_idt_e;
119
120 fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];
121 loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];
122 fw_intr_handlers[idx] = fw_idt_e->gd_looffset +
123 (fw_idt_e->gd_hioffset << 16);
124 intercepted[idx] = 1;
125 ist_use_table[fw_idt_e->gd_ist]++;
126 loader_idt_e->gd_looffset = my_handler;
127 loader_idt_e->gd_hioffset = my_handler >> 16;
128 /*
129 * We reuse uefi selector for the code segment for the exception
130 * handler code, while the reason for the fault might be the
131 * corruption of that gdt entry. On the other hand, allocating
132 * our own descriptor might be not much better, if gdt is corrupted.
133 */
134 loader_idt_e->gd_selector = fw_idt_e->gd_selector;
135 loader_idt_e->gd_ist = 0;
136 loader_idt_e->gd_type = SDT_SYSIGT;
137 loader_idt_e->gd_dpl = 0;
138 loader_idt_e->gd_p = 1;
139 loader_idt_e->gd_xx = 0;
140 loader_idt_e->sd_xx1 = 0;
141 }
142 #define PREPARE_EXCEPTION(N) \
143 extern char EXC##N##_handler[]; \
144 prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);
145
146 static void
free_tables(void)147 free_tables(void)
148 {
149
150 if (lidt_pa != 0) {
151 BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));
152 lidt_pa = 0;
153 }
154 if (exc_stack_pa != 0) {
155 BS->FreePages(exc_stack_pa, 1);
156 exc_stack_pa = 0;
157 }
158 if (tss_pa != 0 && tss_fw_seg == 0) {
159 BS->FreePages(tss_pa,
160 EFI_SIZE_TO_PAGES(sizeof (struct amd64tss)));
161 tss_pa = 0;
162 }
163 if (loader_gdt_pa != 0) {
164 BS->FreePages(tss_pa, 2);
165 loader_gdt_pa = 0;
166 }
167 ist = 0;
168 loader_tss = 0;
169 }
170
171 static int
efi_setup_tss(struct region_descriptor * gdt,uint32_t loader_tss_idx,struct amd64tss ** tss)172 efi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,
173 struct amd64tss **tss)
174 {
175 EFI_STATUS status;
176 struct system_segment_descriptor *tss_desc;
177
178 tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +
179 (loader_tss_idx << 3));
180 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
181 EFI_SIZE_TO_PAGES(sizeof (struct amd64tss)), &tss_pa);
182 if (EFI_ERROR(status)) {
183 printf("efi_setup_tss: AllocatePages tss error %lu\n",
184 DECODE_ERROR(status));
185 return (0);
186 }
187 *tss = (struct amd64tss *)tss_pa;
188 bzero(*tss, sizeof (**tss));
189 tss_desc->sd_lolimit = sizeof (struct amd64tss);
190 tss_desc->sd_lobase = tss_pa;
191 tss_desc->sd_type = SDT_SYSTSS;
192 tss_desc->sd_dpl = 0;
193 tss_desc->sd_p = 1;
194 tss_desc->sd_hilimit = sizeof (struct amd64tss) >> 16;
195 tss_desc->sd_gran = 0;
196 tss_desc->sd_hibase = tss_pa >> 24;
197 tss_desc->sd_xx0 = 0;
198 tss_desc->sd_xx1 = 0;
199 tss_desc->sd_mbz = 0;
200 tss_desc->sd_xx2 = 0;
201 return (1);
202 }
203
204 static int
efi_redirect_exceptions(void)205 efi_redirect_exceptions(void)
206 {
207 int ist_use_table[NUM_IST];
208 struct gate_descriptor *loader_idt_e;
209 struct system_segment_descriptor *tss_desc, *gdt_desc;
210 struct amd64tss *tss;
211 struct region_descriptor *gdt_rd, loader_gdt;
212 uint32_t i;
213 EFI_STATUS status;
214 register_t rfl;
215
216 sidt(&fw_idt);
217 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
218 EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);
219 if (EFI_ERROR(status)) {
220 printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",
221 DECODE_ERROR(status));
222 lidt_pa = 0;
223 return (0);
224 }
225 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,
226 &exc_stack_pa);
227 if (EFI_ERROR(status)) {
228 printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",
229 DECODE_ERROR(status));
230 exc_stack_pa = 0;
231 free_tables();
232 return (0);
233 }
234 loader_idt.rd_limit = fw_idt.rd_limit;
235 bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,
236 loader_idt.rd_limit);
237 bzero(ist_use_table, sizeof (ist_use_table));
238 bzero(fw_intr_handlers, sizeof (fw_intr_handlers));
239 bzero(intercepted, sizeof (intercepted));
240
241 sgdt(&fw_gdt);
242 tss_fw_seg = read_tr();
243 gdt_rd = NULL;
244 if (tss_fw_seg == 0) {
245 for (i = 2; (i << 3) + sizeof (*gdt_desc) <= fw_gdt.rd_limit;
246 i += 2) {
247 gdt_desc = (struct system_segment_descriptor *)(
248 fw_gdt.rd_base + (i << 3));
249 if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {
250 gdt_rd = &fw_gdt;
251 break;
252 }
253 }
254 if (gdt_rd == NULL) {
255 if (i >= 8190) {
256 printf("efi_redirect_exceptions: all slots "
257 "in gdt are used\n");
258 free_tables();
259 return (0);
260 }
261 loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +
262 sizeof (struct system_segment_descriptor),
263 sizeof (struct system_segment_descriptor)) - 1;
264 i = (loader_gdt.rd_limit + 1 -
265 sizeof (struct system_segment_descriptor)) /
266 sizeof (struct system_segment_descriptor) * 2;
267 status = BS->AllocatePages(AllocateAnyPages,
268 EfiLoaderData,
269 EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),
270 &loader_gdt_pa);
271 if (EFI_ERROR(status)) {
272 printf("efi_setup_tss: AllocatePages gdt error "
273 "%lu\n", DECODE_ERROR(status));
274 loader_gdt_pa = 0;
275 free_tables();
276 return (0);
277 }
278 loader_gdt.rd_base = loader_gdt_pa;
279 bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);
280 bcopy((void *)fw_gdt.rd_base,
281 (void *)loader_gdt.rd_base, fw_gdt.rd_limit);
282 gdt_rd = &loader_gdt;
283 }
284 loader_tss = i << 3;
285 if (!efi_setup_tss(gdt_rd, i, &tss)) {
286 tss_pa = 0;
287 free_tables();
288 return (0);
289 }
290 } else {
291 tss_desc = (struct system_segment_descriptor *)((char *)
292 fw_gdt.rd_base + tss_fw_seg);
293 if (tss_desc->sd_type != SDT_SYSTSS &&
294 tss_desc->sd_type != SDT_SYSBSY) {
295 printf("LTR points to non-TSS descriptor\n");
296 free_tables();
297 return (0);
298 }
299 tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);
300 tss = (struct amd64tss *)tss_pa;
301 tss_desc->sd_type = SDT_SYSTSS; /* unbusy */
302 }
303
304 PREPARE_EXCEPTION(0);
305 PREPARE_EXCEPTION(1);
306 PREPARE_EXCEPTION(2);
307 PREPARE_EXCEPTION(3);
308 PREPARE_EXCEPTION(4);
309 PREPARE_EXCEPTION(5);
310 PREPARE_EXCEPTION(6);
311 PREPARE_EXCEPTION(7);
312 PREPARE_EXCEPTION(8);
313 PREPARE_EXCEPTION(9);
314 PREPARE_EXCEPTION(10);
315 PREPARE_EXCEPTION(11);
316 PREPARE_EXCEPTION(12);
317 PREPARE_EXCEPTION(13);
318 PREPARE_EXCEPTION(14);
319 PREPARE_EXCEPTION(16);
320 PREPARE_EXCEPTION(17);
321 PREPARE_EXCEPTION(18);
322 PREPARE_EXCEPTION(19);
323 PREPARE_EXCEPTION(20);
324
325 exc_rsp = exc_stack_pa + PAGE_SIZE -
326 (6 /* hw exception frame */ + 3 /* scratch regs */) * 8;
327
328 /* Find free IST and use it */
329 for (ist = 1; ist < NUM_IST; ist++) {
330 if (ist_use_table[ist] == 0)
331 break;
332 }
333 if (ist == NUM_IST) {
334 printf("efi_redirect_exceptions: all ISTs used\n");
335 free_tables();
336 lidt_pa = 0;
337 return (0);
338 }
339 for (i = 0; i < NUM_EXC; i++) {
340 loader_idt_e = &((struct gate_descriptor *)loader_idt.
341 rd_base)[i];
342 if (intercepted[i])
343 loader_idt_e->gd_ist = ist;
344 }
345 (&(tss->tss_ist1))[ist - 1] = exc_stack_pa + PAGE_SIZE;
346
347 /* Switch to new IDT */
348 rfl = intr_disable();
349 if (loader_gdt_pa != 0)
350 bare_lgdt(&loader_gdt);
351 if (loader_tss != 0)
352 ltr(loader_tss);
353 lidt(&loader_idt);
354 intr_restore(rfl);
355 return (1);
356 }
357
358 static void
efi_unredirect_exceptions(void)359 efi_unredirect_exceptions(void)
360 {
361 register_t rfl;
362
363 if (lidt_pa == 0)
364 return;
365
366 rfl = intr_disable();
367 if (ist != 0)
368 (&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;
369 if (loader_gdt_pa != 0)
370 bare_lgdt(&fw_gdt);
371 if (loader_tss != 0)
372 ltr(tss_fw_seg);
373 lidt(&fw_idt);
374 intr_restore(rfl);
375 free_tables();
376 }
377
378 static int
command_grab_faults(int argc __unused,char * argv[]__unused)379 command_grab_faults(int argc __unused, char *argv[] __unused)
380 {
381 int res;
382
383 res = efi_redirect_exceptions();
384 if (!res)
385 printf("failed\n");
386 return (CMD_OK);
387 }
388 COMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);
389
390 static int
command_ungrab_faults(int argc __unused,char * argv[]__unused)391 command_ungrab_faults(int argc __unused, char *argv[] __unused)
392 {
393
394 efi_unredirect_exceptions();
395 return (CMD_OK);
396 }
397 COMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",
398 command_ungrab_faults);
399
400 static int
command_fault(int argc __unused,char * argv[]__unused)401 command_fault(int argc __unused, char *argv[] __unused)
402 {
403
404 __asm("ud2");
405 return (CMD_OK);
406 }
407 COMMAND_SET(fault, "fault", "generate fault", command_fault);
408