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 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 void report_exc(struct trapframe *tf); 82 void 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 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 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, EFI_SIZE_TO_PAGES(sizeof(struct 160 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 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 EFI_ERROR_CODE(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 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 EFI_ERROR_CODE(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 EFI_ERROR_CODE(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", EFI_ERROR_CODE(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 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 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 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 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