Path: blob/main/stand/efi/loader/arch/amd64/trap.c
105930 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];8990base = (uintptr_t)boot_img->ImageBase;91/*92* printf() depends on loader runtime and UEFI firmware health93* to produce the console output, in case of exception, the94* loader or firmware runtime may fail to support the printf().95*/96printf("===================================================="97"============================\n");98printf("Exception %u\n", tf->tf_trapno);99printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "100"gs 0x%04hx\n",101(uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,102(uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);103printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"104"rsp 0x%016lx rip 0x%016lx\n",105(uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,106tf->tf_rsp, tf->tf_rip);107printf(108"rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"109"rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n"110"rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"111"r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"112"r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",113tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,114tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,115tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);116117fp = (struct frame *)tf->tf_rbp;118pc = tf->tf_rip;119120printf("Stack trace:\n");121pager_open();122while (fp != NULL || pc != 0) {123char *source = "PC";124125if (pc >= base && pc < base + boot_img->ImageSize) {126pc -= base;127source = "loader PC";128}129(void) snprintf(buf, sizeof (buf), "FP %016lx: %s 0x%016lx\n",130(uintptr_t)fp, source, pc);131if (pager_output(buf))132break;133134if (fp != NULL)135fp = fp->fr_savfp;136137if (fp != NULL)138pc = fp->fr_savpc;139else140pc = 0;141}142pager_close();143printf("Machine stopped.\n");144}145146static void147prepare_exception(unsigned idx, uint64_t my_handler,148int ist_use_table[static NUM_IST])149{150struct gate_descriptor *fw_idt_e, *loader_idt_e;151152fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];153loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];154fw_intr_handlers[idx] = fw_idt_e->gd_looffset +155(fw_idt_e->gd_hioffset << 16);156intercepted[idx] = 1;157ist_use_table[fw_idt_e->gd_ist]++;158loader_idt_e->gd_looffset = my_handler;159loader_idt_e->gd_hioffset = my_handler >> 16;160/*161* We reuse uefi selector for the code segment for the exception162* handler code, while the reason for the fault might be the163* corruption of that gdt entry. On the other hand, allocating164* our own descriptor might be not much better, if gdt is corrupted.165*/166loader_idt_e->gd_selector = fw_idt_e->gd_selector;167loader_idt_e->gd_ist = 0;168loader_idt_e->gd_type = SDT_SYSIGT;169loader_idt_e->gd_dpl = 0;170loader_idt_e->gd_p = 1;171loader_idt_e->gd_xx = 0;172loader_idt_e->sd_xx1 = 0;173}174#define PREPARE_EXCEPTION(N) \175extern char EXC##N##_handler[]; \176prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);177178static void179free_tables(void)180{181182if (lidt_pa != 0) {183BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));184lidt_pa = 0;185}186if (exc_stack_pa != 0) {187BS->FreePages(exc_stack_pa, 1);188exc_stack_pa = 0;189}190if (tss_pa != 0 && tss_fw_seg == 0) {191BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct192amd64tss)));193tss_pa = 0;194}195if (loader_gdt_pa != 0) {196BS->FreePages(tss_pa, 2);197loader_gdt_pa = 0;198}199ist = 0;200loader_tss = 0;201}202203static int204efi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,205struct amd64tss **tss)206{207EFI_STATUS status;208struct system_segment_descriptor *tss_desc;209210tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +211(loader_tss_idx << 3));212status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,213EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa);214if (EFI_ERROR(status)) {215printf("efi_setup_tss: AllocatePages tss error %lu\n",216DECODE_ERROR(status));217return (0);218}219*tss = (struct amd64tss *)tss_pa;220bzero(*tss, sizeof(**tss));221tss_desc->sd_lolimit = sizeof(struct amd64tss);222tss_desc->sd_lobase = tss_pa;223tss_desc->sd_type = SDT_SYSTSS;224tss_desc->sd_dpl = 0;225tss_desc->sd_p = 1;226tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16;227tss_desc->sd_gran = 0;228tss_desc->sd_hibase = tss_pa >> 24;229tss_desc->sd_xx0 = 0;230tss_desc->sd_xx1 = 0;231tss_desc->sd_mbz = 0;232tss_desc->sd_xx2 = 0;233return (1);234}235236static int237efi_redirect_exceptions(void)238{239int ist_use_table[NUM_IST];240struct gate_descriptor *loader_idt_e;241struct system_segment_descriptor *tss_desc, *gdt_desc;242struct amd64tss *tss;243struct region_descriptor *gdt_rd, loader_gdt;244uint32_t i;245EFI_STATUS status;246register_t rfl;247248sidt(&fw_idt);249status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,250EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);251if (EFI_ERROR(status)) {252printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",253DECODE_ERROR(status));254lidt_pa = 0;255return (0);256}257status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,258&exc_stack_pa);259if (EFI_ERROR(status)) {260printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",261DECODE_ERROR(status));262exc_stack_pa = 0;263free_tables();264return (0);265}266loader_idt.rd_limit = fw_idt.rd_limit;267bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,268loader_idt.rd_limit);269bzero(ist_use_table, sizeof(ist_use_table));270bzero(fw_intr_handlers, sizeof(fw_intr_handlers));271bzero(intercepted, sizeof(intercepted));272273sgdt(&fw_gdt);274tss_fw_seg = read_tr();275gdt_rd = NULL;276if (tss_fw_seg == 0) {277for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit;278i += 2) {279gdt_desc = (struct system_segment_descriptor *)(280fw_gdt.rd_base + (i << 3));281if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {282gdt_rd = &fw_gdt;283break;284}285}286if (gdt_rd == NULL) {287if (i >= 8190) {288printf("efi_redirect_exceptions: all slots "289"in gdt are used\n");290free_tables();291return (0);292}293loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +294sizeof(struct system_segment_descriptor),295sizeof(struct system_segment_descriptor)) - 1;296i = (loader_gdt.rd_limit + 1 -297sizeof(struct system_segment_descriptor)) /298sizeof(struct system_segment_descriptor) * 2;299status = BS->AllocatePages(AllocateAnyPages,300EfiLoaderData,301EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),302&loader_gdt_pa);303if (EFI_ERROR(status)) {304printf("efi_setup_tss: AllocatePages gdt error "305"%lu\n", DECODE_ERROR(status));306loader_gdt_pa = 0;307free_tables();308return (0);309}310loader_gdt.rd_base = loader_gdt_pa;311bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);312bcopy((void *)fw_gdt.rd_base,313(void *)loader_gdt.rd_base, fw_gdt.rd_limit);314gdt_rd = &loader_gdt;315}316loader_tss = i << 3;317if (!efi_setup_tss(gdt_rd, i, &tss)) {318tss_pa = 0;319free_tables();320return (0);321}322} else {323tss_desc = (struct system_segment_descriptor *)((char *)324fw_gdt.rd_base + tss_fw_seg);325if (tss_desc->sd_type != SDT_SYSTSS &&326tss_desc->sd_type != SDT_SYSBSY) {327printf("LTR points to non-TSS descriptor\n");328free_tables();329return (0);330}331tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);332tss = (struct amd64tss *)tss_pa;333tss_desc->sd_type = SDT_SYSTSS; /* unbusy */334}335336PREPARE_EXCEPTION(0);337PREPARE_EXCEPTION(1);338PREPARE_EXCEPTION(2);339PREPARE_EXCEPTION(3);340PREPARE_EXCEPTION(4);341PREPARE_EXCEPTION(5);342PREPARE_EXCEPTION(6);343PREPARE_EXCEPTION(7);344PREPARE_EXCEPTION(8);345PREPARE_EXCEPTION(9);346PREPARE_EXCEPTION(10);347PREPARE_EXCEPTION(11);348PREPARE_EXCEPTION(12);349PREPARE_EXCEPTION(13);350PREPARE_EXCEPTION(14);351PREPARE_EXCEPTION(16);352PREPARE_EXCEPTION(17);353PREPARE_EXCEPTION(18);354PREPARE_EXCEPTION(19);355PREPARE_EXCEPTION(20);356357exc_rsp = exc_stack_pa + EFI_PAGE_SIZE -358(6 /* hw exception frame */ + 3 /* scratch regs */) * 8;359360/* Find free IST and use it */361for (ist = 1; ist < NUM_IST; ist++) {362if (ist_use_table[ist] == 0)363break;364}365if (ist == NUM_IST) {366printf("efi_redirect_exceptions: all ISTs used\n");367free_tables();368lidt_pa = 0;369return (0);370}371for (i = 0; i < NUM_EXC; i++) {372loader_idt_e = &((struct gate_descriptor *)loader_idt.373rd_base)[i];374if (intercepted[i])375loader_idt_e->gd_ist = ist;376}377(&(tss->tss_ist1))[ist - 1] = exc_stack_pa + EFI_PAGE_SIZE;378379/* Switch to new IDT */380rfl = intr_disable();381if (loader_gdt_pa != 0)382bare_lgdt(&loader_gdt);383if (loader_tss != 0)384ltr(loader_tss);385lidt(&loader_idt);386intr_restore(rfl);387return (1);388}389390static void391efi_unredirect_exceptions(void)392{393register_t rfl;394395if (lidt_pa == 0)396return;397398rfl = intr_disable();399if (ist != 0)400(&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;401if (loader_gdt_pa != 0)402bare_lgdt(&fw_gdt);403if (loader_tss != 0)404ltr(tss_fw_seg);405lidt(&fw_idt);406intr_restore(rfl);407free_tables();408}409410static int411command_grab_faults(int argc, char *argv[])412{413int res;414415res = efi_redirect_exceptions();416if (!res)417printf("failed\n");418return (CMD_OK);419}420COMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);421422static int423command_ungrab_faults(int argc, char *argv[])424{425426efi_unredirect_exceptions();427return (CMD_OK);428}429COMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",430command_ungrab_faults);431432static int433command_fault(int argc, char *argv[])434{435436__asm("ud2");437return (CMD_OK);438}439COMMAND_SET(fault, "fault", "generate fault", command_fault);440441442