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