Path: blob/main/stand/efi/loader/arch/amd64/trap.c
34914 views
/*-1* Copyright (c) 2016 The FreeBSD Foundation2*3* This software was developed by Konstantin Belousov under sponsorship4* from the FreeBSD Foundation.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <stand.h>29#include <string.h>30#include <sys/param.h>31#include <machine/cpufunc.h>32#include <machine/psl.h>33#include <machine/segments.h>34#include <machine/frame.h>35#include <machine/tss.h>3637#include <efi.h>38#include <efilib.h>3940#include "bootstrap.h"41#include "loader_efi.h"4243#define NUM_IST 844#define NUM_EXC 324546/*47* This code catches exceptions but forwards hardware interrupts to48* handlers installed by firmware. It differentiates exceptions49* vs. interrupts by presence of the error code on the stack, which50* causes different stack pointer value on trap handler entry.51*52* Use kernel layout for the trapframe just to not be original.53*54* Use free IST slot in existing TSS, or create our own TSS if55* firmware did not configured any, to have stack switched to56* IST-specified one, e.g. to handle #SS. If hand-off cannot find57* unused IST slot, or create a new descriptor in GDT, we bail out.58*/5960static struct region_descriptor fw_idt; /* Descriptor for pristine fw IDT */61static struct region_descriptor loader_idt;/* Descriptor for loader62shadow IDT */63static EFI_PHYSICAL_ADDRESS lidt_pa; /* Address of loader shadow IDT */64static EFI_PHYSICAL_ADDRESS tss_pa; /* Address of TSS */65static EFI_PHYSICAL_ADDRESS exc_stack_pa;/* Address of IST stack for loader */66EFI_PHYSICAL_ADDRESS exc_rsp; /* %rsp value on our IST stack when67exception happens */68EFI_PHYSICAL_ADDRESS fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT69vectors */70static int intercepted[NUM_EXC];71static int ist; /* IST for exception handlers */72static uint32_t tss_fw_seg; /* Fw TSS segment */73static uint32_t loader_tss; /* Loader TSS segment */74static struct region_descriptor fw_gdt; /* Descriptor of pristine GDT */75static EFI_PHYSICAL_ADDRESS loader_gdt_pa; /* Address of loader shadow GDT */7677struct frame {78struct frame *fr_savfp;79uintptr_t fr_savpc;80};8182void report_exc(struct trapframe *tf);83void84report_exc(struct trapframe *tf)85{86struct frame *fp;87uintptr_t pc, base;88char buf[80];89int ret;9091base = (uintptr_t)boot_img->ImageBase;92/*93* printf() depends on loader runtime and UEFI firmware health94* to produce the console output, in case of exception, the95* loader or firmware runtime may fail to support the printf().96*/97printf("===================================================="98"============================\n");99printf("Exception %u\n", tf->tf_trapno);100printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "101"gs 0x%04hx\n",102(uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,103(uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);104printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"105"rsp 0x%016lx rip 0x%016lx\n",106(uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,107tf->tf_rsp, tf->tf_rip);108printf(109"rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"110"rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n"111"rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"112"r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"113"r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",114tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,115tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,116tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);117118fp = (struct frame *)tf->tf_rbp;119pc = tf->tf_rip;120121printf("Stack trace:\n");122pager_open();123while (fp != NULL || pc != 0) {124char *source = "PC";125126if (pc >= base && pc < base + boot_img->ImageSize) {127pc -= base;128source = "loader PC";129}130(void) snprintf(buf, sizeof (buf), "FP %016lx: %s 0x%016lx\n",131(uintptr_t)fp, source, pc);132if (pager_output(buf))133break;134135if (fp != NULL)136fp = fp->fr_savfp;137138if (fp != NULL)139pc = fp->fr_savpc;140else141pc = 0;142}143pager_close();144printf("Machine stopped.\n");145}146147static void148prepare_exception(unsigned idx, uint64_t my_handler,149int ist_use_table[static NUM_IST])150{151struct gate_descriptor *fw_idt_e, *loader_idt_e;152153fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];154loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];155fw_intr_handlers[idx] = fw_idt_e->gd_looffset +156(fw_idt_e->gd_hioffset << 16);157intercepted[idx] = 1;158ist_use_table[fw_idt_e->gd_ist]++;159loader_idt_e->gd_looffset = my_handler;160loader_idt_e->gd_hioffset = my_handler >> 16;161/*162* We reuse uefi selector for the code segment for the exception163* handler code, while the reason for the fault might be the164* corruption of that gdt entry. On the other hand, allocating165* our own descriptor might be not much better, if gdt is corrupted.166*/167loader_idt_e->gd_selector = fw_idt_e->gd_selector;168loader_idt_e->gd_ist = 0;169loader_idt_e->gd_type = SDT_SYSIGT;170loader_idt_e->gd_dpl = 0;171loader_idt_e->gd_p = 1;172loader_idt_e->gd_xx = 0;173loader_idt_e->sd_xx1 = 0;174}175#define PREPARE_EXCEPTION(N) \176extern char EXC##N##_handler[]; \177prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);178179static void180free_tables(void)181{182183if (lidt_pa != 0) {184BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));185lidt_pa = 0;186}187if (exc_stack_pa != 0) {188BS->FreePages(exc_stack_pa, 1);189exc_stack_pa = 0;190}191if (tss_pa != 0 && tss_fw_seg == 0) {192BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct193amd64tss)));194tss_pa = 0;195}196if (loader_gdt_pa != 0) {197BS->FreePages(tss_pa, 2);198loader_gdt_pa = 0;199}200ist = 0;201loader_tss = 0;202}203204static int205efi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,206struct amd64tss **tss)207{208EFI_STATUS status;209struct system_segment_descriptor *tss_desc;210211tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +212(loader_tss_idx << 3));213status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,214EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa);215if (EFI_ERROR(status)) {216printf("efi_setup_tss: AllocatePages tss error %lu\n",217EFI_ERROR_CODE(status));218return (0);219}220*tss = (struct amd64tss *)tss_pa;221bzero(*tss, sizeof(**tss));222tss_desc->sd_lolimit = sizeof(struct amd64tss);223tss_desc->sd_lobase = tss_pa;224tss_desc->sd_type = SDT_SYSTSS;225tss_desc->sd_dpl = 0;226tss_desc->sd_p = 1;227tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16;228tss_desc->sd_gran = 0;229tss_desc->sd_hibase = tss_pa >> 24;230tss_desc->sd_xx0 = 0;231tss_desc->sd_xx1 = 0;232tss_desc->sd_mbz = 0;233tss_desc->sd_xx2 = 0;234return (1);235}236237static int238efi_redirect_exceptions(void)239{240int ist_use_table[NUM_IST];241struct gate_descriptor *loader_idt_e;242struct system_segment_descriptor *tss_desc, *gdt_desc;243struct amd64tss *tss;244struct region_descriptor *gdt_rd, loader_gdt;245uint32_t i;246EFI_STATUS status;247register_t rfl;248249sidt(&fw_idt);250status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,251EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);252if (EFI_ERROR(status)) {253printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",254EFI_ERROR_CODE(status));255lidt_pa = 0;256return (0);257}258status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,259&exc_stack_pa);260if (EFI_ERROR(status)) {261printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",262EFI_ERROR_CODE(status));263exc_stack_pa = 0;264free_tables();265return (0);266}267loader_idt.rd_limit = fw_idt.rd_limit;268bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,269loader_idt.rd_limit);270bzero(ist_use_table, sizeof(ist_use_table));271bzero(fw_intr_handlers, sizeof(fw_intr_handlers));272bzero(intercepted, sizeof(intercepted));273274sgdt(&fw_gdt);275tss_fw_seg = read_tr();276gdt_rd = NULL;277if (tss_fw_seg == 0) {278for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit;279i += 2) {280gdt_desc = (struct system_segment_descriptor *)(281fw_gdt.rd_base + (i << 3));282if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {283gdt_rd = &fw_gdt;284break;285}286}287if (gdt_rd == NULL) {288if (i >= 8190) {289printf("efi_redirect_exceptions: all slots "290"in gdt are used\n");291free_tables();292return (0);293}294loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +295sizeof(struct system_segment_descriptor),296sizeof(struct system_segment_descriptor)) - 1;297i = (loader_gdt.rd_limit + 1 -298sizeof(struct system_segment_descriptor)) /299sizeof(struct system_segment_descriptor) * 2;300status = BS->AllocatePages(AllocateAnyPages,301EfiLoaderData,302EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),303&loader_gdt_pa);304if (EFI_ERROR(status)) {305printf("efi_setup_tss: AllocatePages gdt error "306"%lu\n", EFI_ERROR_CODE(status));307loader_gdt_pa = 0;308free_tables();309return (0);310}311loader_gdt.rd_base = loader_gdt_pa;312bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);313bcopy((void *)fw_gdt.rd_base,314(void *)loader_gdt.rd_base, fw_gdt.rd_limit);315gdt_rd = &loader_gdt;316}317loader_tss = i << 3;318if (!efi_setup_tss(gdt_rd, i, &tss)) {319tss_pa = 0;320free_tables();321return (0);322}323} else {324tss_desc = (struct system_segment_descriptor *)((char *)325fw_gdt.rd_base + tss_fw_seg);326if (tss_desc->sd_type != SDT_SYSTSS &&327tss_desc->sd_type != SDT_SYSBSY) {328printf("LTR points to non-TSS descriptor\n");329free_tables();330return (0);331}332tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);333tss = (struct amd64tss *)tss_pa;334tss_desc->sd_type = SDT_SYSTSS; /* unbusy */335}336337PREPARE_EXCEPTION(0);338PREPARE_EXCEPTION(1);339PREPARE_EXCEPTION(2);340PREPARE_EXCEPTION(3);341PREPARE_EXCEPTION(4);342PREPARE_EXCEPTION(5);343PREPARE_EXCEPTION(6);344PREPARE_EXCEPTION(7);345PREPARE_EXCEPTION(8);346PREPARE_EXCEPTION(9);347PREPARE_EXCEPTION(10);348PREPARE_EXCEPTION(11);349PREPARE_EXCEPTION(12);350PREPARE_EXCEPTION(13);351PREPARE_EXCEPTION(14);352PREPARE_EXCEPTION(16);353PREPARE_EXCEPTION(17);354PREPARE_EXCEPTION(18);355PREPARE_EXCEPTION(19);356PREPARE_EXCEPTION(20);357358exc_rsp = exc_stack_pa + EFI_PAGE_SIZE -359(6 /* hw exception frame */ + 3 /* scratch regs */) * 8;360361/* Find free IST and use it */362for (ist = 1; ist < NUM_IST; ist++) {363if (ist_use_table[ist] == 0)364break;365}366if (ist == NUM_IST) {367printf("efi_redirect_exceptions: all ISTs used\n");368free_tables();369lidt_pa = 0;370return (0);371}372for (i = 0; i < NUM_EXC; i++) {373loader_idt_e = &((struct gate_descriptor *)loader_idt.374rd_base)[i];375if (intercepted[i])376loader_idt_e->gd_ist = ist;377}378(&(tss->tss_ist1))[ist - 1] = exc_stack_pa + EFI_PAGE_SIZE;379380/* Switch to new IDT */381rfl = intr_disable();382if (loader_gdt_pa != 0)383bare_lgdt(&loader_gdt);384if (loader_tss != 0)385ltr(loader_tss);386lidt(&loader_idt);387intr_restore(rfl);388return (1);389}390391static void392efi_unredirect_exceptions(void)393{394register_t rfl;395396if (lidt_pa == 0)397return;398399rfl = intr_disable();400if (ist != 0)401(&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;402if (loader_gdt_pa != 0)403bare_lgdt(&fw_gdt);404if (loader_tss != 0)405ltr(tss_fw_seg);406lidt(&fw_idt);407intr_restore(rfl);408free_tables();409}410411static int412command_grab_faults(int argc, char *argv[])413{414int res;415416res = efi_redirect_exceptions();417if (!res)418printf("failed\n");419return (CMD_OK);420}421COMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);422423static int424command_ungrab_faults(int argc, char *argv[])425{426427efi_unredirect_exceptions();428return (CMD_OK);429}430COMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",431command_ungrab_faults);432433static int434command_fault(int argc, char *argv[])435{436437__asm("ud2");438return (CMD_OK);439}440COMMAND_SET(fault, "fault", "generate fault", command_fault);441442443