1306090Skib/*- 2306090Skib * Copyright (c) 2016 The FreeBSD Foundation 3306090Skib * All rights reserved. 4306090Skib * 5306090Skib * This software was developed by Konstantin Belousov under sponsorship 6306090Skib * from the FreeBSD Foundation. 7306090Skib * 8306090Skib * Redistribution and use in source and binary forms, with or without 9306090Skib * modification, are permitted provided that the following conditions 10306090Skib * are met: 11306090Skib * 1. Redistributions of source code must retain the above copyright 12306090Skib * notice, this list of conditions and the following disclaimer. 13306090Skib * 2. Redistributions in binary form must reproduce the above copyright 14306090Skib * notice, this list of conditions and the following disclaimer in the 15306090Skib * documentation and/or other materials provided with the distribution. 16306090Skib * 17306090Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18306090Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19306090Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20306090Skib * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21306090Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22306090Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23306090Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24306090Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25306090Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26306090Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27306090Skib * SUCH DAMAGE. 28306090Skib */ 29306090Skib 30306090Skib#include <sys/cdefs.h> 31306090Skib__FBSDID("$FreeBSD: stable/11/stand/efi/loader/arch/amd64/trap.c 329114 2018-02-11 02:27:50Z kevans $"); 32306090Skib 33306090Skib#include <stand.h> 34306090Skib#include <string.h> 35306090Skib#include <sys/param.h> 36306090Skib#include <machine/cpufunc.h> 37306090Skib#include <machine/psl.h> 38306090Skib#include <machine/segments.h> 39306090Skib#include <machine/frame.h> 40306090Skib#include <machine/tss.h> 41306090Skib 42306090Skib#include <efi.h> 43306090Skib#include <efilib.h> 44306090Skib 45306090Skib#include "bootstrap.h" 46306090Skib#include "loader_efi.h" 47306090Skib 48306090Skib#define NUM_IST 8 49306090Skib#define NUM_EXC 32 50306090Skib 51306090Skib/* 52306090Skib * This code catches exceptions but forwards hardware interrupts to 53306090Skib * handlers installed by firmware. It differentiates exceptions 54306090Skib * vs. interrupts by presence of the error code on the stack, which 55306090Skib * causes different stack pointer value on trap handler entry. 56306090Skib * 57306090Skib * Use kernel layout for the trapframe just to not be original. 58306090Skib * 59306090Skib * Use free IST slot in existing TSS, or create our own TSS if 60306090Skib * firmware did not configured any, to have stack switched to 61306090Skib * IST-specified one, e.g. to handle #SS. If hand-off cannot find 62306090Skib * unused IST slot, or create a new descriptor in GDT, we bail out. 63306090Skib */ 64306090Skib 65306090Skibstatic struct region_descriptor fw_idt; /* Descriptor for pristine fw IDT */ 66306090Skibstatic struct region_descriptor loader_idt;/* Descriptor for loader 67306090Skib shadow IDT */ 68306090Skibstatic EFI_PHYSICAL_ADDRESS lidt_pa; /* Address of loader shadow IDT */ 69306090Skibstatic EFI_PHYSICAL_ADDRESS tss_pa; /* Address of TSS */ 70306090Skibstatic EFI_PHYSICAL_ADDRESS exc_stack_pa;/* Address of IST stack for loader */ 71306090SkibEFI_PHYSICAL_ADDRESS exc_rsp; /* %rsp value on our IST stack when 72306090Skib exception happens */ 73306090SkibEFI_PHYSICAL_ADDRESS fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT 74306090Skib vectors */ 75306090Skibstatic int intercepted[NUM_EXC]; 76306090Skibstatic int ist; /* IST for exception handlers */ 77306090Skibstatic uint32_t tss_fw_seg; /* Fw TSS segment */ 78306090Skibstatic uint32_t loader_tss; /* Loader TSS segment */ 79306090Skibstatic struct region_descriptor fw_gdt; /* Descriptor of pristine GDT */ 80306090Skibstatic EFI_PHYSICAL_ADDRESS loader_gdt_pa; /* Address of loader shadow GDT */ 81306090Skib 82306090Skibvoid report_exc(struct trapframe *tf); 83306090Skibvoid 84306090Skibreport_exc(struct trapframe *tf) 85306090Skib{ 86306090Skib 87329114Skevans /* 88329114Skevans * printf() depends on loader runtime and UEFI firmware health 89329114Skevans * to produce the console output, in case of exception, the 90329114Skevans * loader or firmware runtime may fail to support the printf(). 91329114Skevans */ 92306090Skib printf("====================================================" 93306090Skib "============================\n"); 94306090Skib printf("Exception %u\n", tf->tf_trapno); 95306090Skib printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx " 96306090Skib "gs 0x%04hx\n", 97306090Skib (uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds, 98306090Skib (uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs); 99306090Skib printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n" 100306090Skib "rsp 0x%016lx rip 0x%016lx\n", 101306090Skib (uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr, 102306090Skib tf->tf_rsp, tf->tf_rip); 103306090Skib printf( 104306090Skib "rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n" 105306090Skib "rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n" 106306090Skib "rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n" 107306090Skib "r10 0x%016lx r11 0x%016lx r12 0x%016lx\n" 108306090Skib "r13 0x%016lx r14 0x%016lx r15 0x%016lx\n", 109306090Skib tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8, 110306090Skib tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10, 111306090Skib tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15); 112306090Skib printf("Machine stopped.\n"); 113306090Skib} 114306090Skib 115306090Skibstatic void 116306090Skibprepare_exception(unsigned idx, uint64_t my_handler, 117306090Skib int ist_use_table[static NUM_IST]) 118306090Skib{ 119306090Skib struct gate_descriptor *fw_idt_e, *loader_idt_e; 120306090Skib 121306090Skib fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx]; 122306090Skib loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx]; 123306090Skib fw_intr_handlers[idx] = fw_idt_e->gd_looffset + 124306090Skib (fw_idt_e->gd_hioffset << 16); 125306090Skib intercepted[idx] = 1; 126306090Skib ist_use_table[fw_idt_e->gd_ist]++; 127306090Skib loader_idt_e->gd_looffset = my_handler; 128306090Skib loader_idt_e->gd_hioffset = my_handler >> 16; 129329114Skevans /* 130329114Skevans * We reuse uefi selector for the code segment for the exception 131329114Skevans * handler code, while the reason for the fault might be the 132329114Skevans * corruption of that gdt entry. On the other hand, allocating 133329114Skevans * our own descriptor might be not much better, if gdt is corrupted. 134329114Skevans */ 135329114Skevans loader_idt_e->gd_selector = fw_idt_e->gd_selector; 136306090Skib loader_idt_e->gd_ist = 0; 137306090Skib loader_idt_e->gd_type = SDT_SYSIGT; 138306090Skib loader_idt_e->gd_dpl = 0; 139306090Skib loader_idt_e->gd_p = 1; 140306090Skib loader_idt_e->gd_xx = 0; 141306090Skib loader_idt_e->sd_xx1 = 0; 142306090Skib} 143306090Skib#define PREPARE_EXCEPTION(N) \ 144306090Skib extern char EXC##N##_handler[]; \ 145306090Skib prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table); 146306090Skib 147306090Skibstatic void 148306090Skibfree_tables(void) 149306090Skib{ 150306090Skib 151306090Skib if (lidt_pa != 0) { 152306090Skib BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit)); 153306090Skib lidt_pa = 0; 154306090Skib } 155306090Skib if (exc_stack_pa != 0) { 156306090Skib BS->FreePages(exc_stack_pa, 1); 157306090Skib exc_stack_pa = 0; 158306090Skib } 159306090Skib if (tss_pa != 0 && tss_fw_seg == 0) { 160306090Skib BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct 161306090Skib amd64tss))); 162306090Skib tss_pa = 0; 163306090Skib } 164306090Skib if (loader_gdt_pa != 0) { 165306090Skib BS->FreePages(tss_pa, 2); 166306090Skib loader_gdt_pa = 0; 167306090Skib } 168306090Skib ist = 0; 169306090Skib loader_tss = 0; 170306090Skib} 171306090Skib 172306090Skibstatic int 173306090Skibefi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx, 174306090Skib struct amd64tss **tss) 175306090Skib{ 176306090Skib EFI_STATUS status; 177306090Skib struct system_segment_descriptor *tss_desc; 178306090Skib 179306090Skib tss_desc = (struct system_segment_descriptor *)(gdt->rd_base + 180306090Skib (loader_tss_idx << 3)); 181306090Skib status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 182306090Skib EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa); 183306090Skib if (EFI_ERROR(status)) { 184306090Skib printf("efi_setup_tss: AllocatePages tss error %lu\n", 185306090Skib EFI_ERROR_CODE(status)); 186306090Skib return (0); 187306090Skib } 188306090Skib *tss = (struct amd64tss *)tss_pa; 189306090Skib bzero(*tss, sizeof(**tss)); 190306090Skib tss_desc->sd_lolimit = sizeof(struct amd64tss); 191306090Skib tss_desc->sd_lobase = tss_pa; 192306090Skib tss_desc->sd_type = SDT_SYSTSS; 193306090Skib tss_desc->sd_dpl = 0; 194306090Skib tss_desc->sd_p = 1; 195306090Skib tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16; 196306090Skib tss_desc->sd_gran = 0; 197306090Skib tss_desc->sd_hibase = tss_pa >> 24; 198306090Skib tss_desc->sd_xx0 = 0; 199306090Skib tss_desc->sd_xx1 = 0; 200306090Skib tss_desc->sd_mbz = 0; 201306090Skib tss_desc->sd_xx2 = 0; 202306090Skib return (1); 203306090Skib} 204306090Skib 205306090Skibstatic int 206306090Skibefi_redirect_exceptions(void) 207306090Skib{ 208306090Skib int ist_use_table[NUM_IST]; 209306090Skib struct gate_descriptor *loader_idt_e; 210306090Skib struct system_segment_descriptor *tss_desc, *gdt_desc; 211306090Skib struct amd64tss *tss; 212306090Skib struct region_descriptor *gdt_rd, loader_gdt; 213306090Skib uint32_t i; 214306090Skib EFI_STATUS status; 215306090Skib register_t rfl; 216306090Skib 217306090Skib sidt(&fw_idt); 218306090Skib status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 219306090Skib EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa); 220306090Skib if (EFI_ERROR(status)) { 221306090Skib printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n", 222306090Skib EFI_ERROR_CODE(status)); 223306090Skib lidt_pa = 0; 224306090Skib return (0); 225306090Skib } 226306090Skib status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1, 227306090Skib &exc_stack_pa); 228306090Skib if (EFI_ERROR(status)) { 229306090Skib printf("efi_redirect_exceptions: AllocatePages stk error %lu\n", 230306090Skib EFI_ERROR_CODE(status)); 231306090Skib exc_stack_pa = 0; 232306090Skib free_tables(); 233306090Skib return (0); 234306090Skib } 235306090Skib loader_idt.rd_limit = fw_idt.rd_limit; 236306090Skib bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base, 237306090Skib loader_idt.rd_limit); 238306090Skib bzero(ist_use_table, sizeof(ist_use_table)); 239306090Skib bzero(fw_intr_handlers, sizeof(fw_intr_handlers)); 240306090Skib bzero(intercepted, sizeof(intercepted)); 241306090Skib 242306090Skib sgdt(&fw_gdt); 243306090Skib tss_fw_seg = read_tr(); 244306090Skib gdt_rd = NULL; 245306090Skib if (tss_fw_seg == 0) { 246306090Skib for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit; 247306090Skib i += 2) { 248306090Skib gdt_desc = (struct system_segment_descriptor *)( 249306090Skib fw_gdt.rd_base + (i << 3)); 250306090Skib if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) { 251306090Skib gdt_rd = &fw_gdt; 252306090Skib break; 253306090Skib } 254306090Skib } 255306090Skib if (gdt_rd == NULL) { 256306090Skib if (i >= 8190) { 257306090Skib printf("efi_redirect_exceptions: all slots " 258306090Skib "in gdt are used\n"); 259306090Skib free_tables(); 260306090Skib return (0); 261306090Skib } 262306090Skib loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit + 263306090Skib sizeof(struct system_segment_descriptor), 264306090Skib sizeof(struct system_segment_descriptor)) - 1; 265306090Skib i = (loader_gdt.rd_limit + 1 - 266306090Skib sizeof(struct system_segment_descriptor)) / 267306090Skib sizeof(struct system_segment_descriptor) * 2; 268306090Skib status = BS->AllocatePages(AllocateAnyPages, 269306090Skib EfiLoaderData, 270306090Skib EFI_SIZE_TO_PAGES(loader_gdt.rd_limit), 271306090Skib &loader_gdt_pa); 272306090Skib if (EFI_ERROR(status)) { 273306090Skib printf("efi_setup_tss: AllocatePages gdt error " 274306090Skib "%lu\n", EFI_ERROR_CODE(status)); 275306090Skib loader_gdt_pa = 0; 276306090Skib free_tables(); 277306090Skib return (0); 278306090Skib } 279306090Skib loader_gdt.rd_base = loader_gdt_pa; 280306090Skib bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit); 281306090Skib bcopy((void *)fw_gdt.rd_base, 282306090Skib (void *)loader_gdt.rd_base, fw_gdt.rd_limit); 283306090Skib gdt_rd = &loader_gdt; 284306090Skib } 285306090Skib loader_tss = i << 3; 286306090Skib if (!efi_setup_tss(gdt_rd, i, &tss)) { 287306090Skib tss_pa = 0; 288306090Skib free_tables(); 289306090Skib return (0); 290306090Skib } 291306090Skib } else { 292306090Skib tss_desc = (struct system_segment_descriptor *)((char *) 293306090Skib fw_gdt.rd_base + tss_fw_seg); 294306090Skib if (tss_desc->sd_type != SDT_SYSTSS && 295306090Skib tss_desc->sd_type != SDT_SYSBSY) { 296306090Skib printf("LTR points to non-TSS descriptor\n"); 297306090Skib free_tables(); 298306090Skib return (0); 299306090Skib } 300306090Skib tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16); 301306090Skib tss = (struct amd64tss *)tss_pa; 302306090Skib tss_desc->sd_type = SDT_SYSTSS; /* unbusy */ 303306090Skib } 304306090Skib 305306090Skib PREPARE_EXCEPTION(0); 306306090Skib PREPARE_EXCEPTION(1); 307306090Skib PREPARE_EXCEPTION(2); 308306090Skib PREPARE_EXCEPTION(3); 309306090Skib PREPARE_EXCEPTION(4); 310306090Skib PREPARE_EXCEPTION(5); 311306090Skib PREPARE_EXCEPTION(6); 312306090Skib PREPARE_EXCEPTION(7); 313306090Skib PREPARE_EXCEPTION(8); 314306090Skib PREPARE_EXCEPTION(9); 315306090Skib PREPARE_EXCEPTION(10); 316306090Skib PREPARE_EXCEPTION(11); 317306090Skib PREPARE_EXCEPTION(12); 318306090Skib PREPARE_EXCEPTION(13); 319306090Skib PREPARE_EXCEPTION(14); 320306090Skib PREPARE_EXCEPTION(16); 321306090Skib PREPARE_EXCEPTION(17); 322306090Skib PREPARE_EXCEPTION(18); 323306090Skib PREPARE_EXCEPTION(19); 324306090Skib PREPARE_EXCEPTION(20); 325306090Skib 326306090Skib exc_rsp = exc_stack_pa + PAGE_SIZE - 327306090Skib (6 /* hw exception frame */ + 3 /* scratch regs */) * 8; 328306090Skib 329306090Skib /* Find free IST and use it */ 330306090Skib for (ist = 1; ist < NUM_IST; ist++) { 331306090Skib if (ist_use_table[ist] == 0) 332306090Skib break; 333306090Skib } 334306090Skib if (ist == NUM_IST) { 335306090Skib printf("efi_redirect_exceptions: all ISTs used\n"); 336306090Skib free_tables(); 337306090Skib lidt_pa = 0; 338306090Skib return (0); 339306090Skib } 340306090Skib for (i = 0; i < NUM_EXC; i++) { 341306090Skib loader_idt_e = &((struct gate_descriptor *)loader_idt. 342306090Skib rd_base)[i]; 343306090Skib if (intercepted[i]) 344306090Skib loader_idt_e->gd_ist = ist; 345306090Skib } 346306090Skib (&(tss->tss_ist1))[ist - 1] = exc_stack_pa + PAGE_SIZE; 347306090Skib 348306090Skib /* Switch to new IDT */ 349306090Skib rfl = intr_disable(); 350306090Skib if (loader_gdt_pa != 0) 351306090Skib bare_lgdt(&loader_gdt); 352306090Skib if (loader_tss != 0) 353306090Skib ltr(loader_tss); 354306090Skib lidt(&loader_idt); 355306090Skib intr_restore(rfl); 356306090Skib return (1); 357306090Skib} 358306090Skib 359306090Skibstatic void 360306090Skibefi_unredirect_exceptions(void) 361306090Skib{ 362306090Skib register_t rfl; 363306090Skib 364306090Skib if (lidt_pa == 0) 365306090Skib return; 366306090Skib 367306090Skib rfl = intr_disable(); 368306090Skib if (ist != 0) 369306090Skib (&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0; 370306090Skib if (loader_gdt_pa != 0) 371306090Skib bare_lgdt(&fw_gdt); 372306090Skib if (loader_tss != 0) 373306090Skib ltr(tss_fw_seg); 374306090Skib lidt(&fw_idt); 375306090Skib intr_restore(rfl); 376306090Skib free_tables(); 377306090Skib} 378306090Skib 379306090Skibstatic int 380306090Skibcommand_grab_faults(int argc, char *argv[]) 381306090Skib{ 382306090Skib int res; 383306090Skib 384306090Skib res = efi_redirect_exceptions(); 385306090Skib if (!res) 386306090Skib printf("failed\n"); 387306090Skib return (CMD_OK); 388306090Skib} 389306090SkibCOMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults); 390306090Skib 391306090Skibstatic int 392306090Skibcommand_ungrab_faults(int argc, char *argv[]) 393306090Skib{ 394306090Skib 395306090Skib efi_unredirect_exceptions(); 396306090Skib return (CMD_OK); 397306090Skib} 398306090SkibCOMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults", 399306090Skib command_ungrab_faults); 400306090Skib 401306090Skibstatic int 402306090Skibcommand_fault(int argc, char *argv[]) 403306090Skib{ 404306090Skib 405306090Skib __asm("ud2"); 406306090Skib return (CMD_OK); 407306090Skib} 408306090SkibCOMMAND_SET(fault, "fault", "generate fault", command_fault); 409