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