text: /** @file
    x64 CPU Exception Handler.
  
    Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR>
    SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include "CpuExceptionCommon.h"
  
  /**
    Return address map of exception handler template so that C code can generate
    exception tables.
  
    @param IdtEntry          Pointer to IDT entry to be updated.
    @param InterruptHandler  IDT handler value.
  **/
  VOID
  ArchUpdateIdtEntry (
    OUT IA32_IDT_GATE_DESCRIPTOR  *IdtEntry,
    IN  UINTN                     InterruptHandler
    )
  {
    IdtEntry->Bits.OffsetLow   = (UINT16)(UINTN)InterruptHandler;
    IdtEntry->Bits.OffsetHigh  = (UINT16)((UINTN)InterruptHandler >> 16);
    IdtEntry->Bits.OffsetUpper = (UINT32)((UINTN)InterruptHandler >> 32);
    IdtEntry->Bits.GateType    = IA32_IDT_GATE_TYPE_INTERRUPT_32;
  }
  
  /**
    Read IDT handler value from IDT entry.
  
    @param IdtEntry          Pointer to IDT entry to be read.
  
  **/
  UINTN
  ArchGetIdtHandler (
    IN IA32_IDT_GATE_DESCRIPTOR  *IdtEntry
    )
  {
    return IdtEntry->Bits.OffsetLow + (((UINTN)IdtEntry->Bits.OffsetHigh)  << 16) +
           (((UINTN)IdtEntry->Bits.OffsetUpper) << 32);
  }
  
  /**
    Save CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case.
  
    @param[in] ExceptionType        Exception type.
    @param[in] SystemContext        Pointer to EFI_SYSTEM_CONTEXT.
    @param[in] ExceptionHandlerData Pointer to exception handler data.
  **/
  VOID
  ArchSaveExceptionContext (
    IN UINTN                   ExceptionType,
    IN EFI_SYSTEM_CONTEXT      SystemContext,
    IN EXCEPTION_HANDLER_DATA  *ExceptionHandlerData
    )
  {
    IA32_EFLAGS32          Eflags;
    RESERVED_VECTORS_DATA  *ReservedVectors;
  
    ReservedVectors = ExceptionHandlerData->ReservedVectors;
    //
    // Save Exception context in global variable in first entry of the exception handler.
    // So when original exception handler returns to the new exception handler (second entry),
    // the Eflags/Cs/Eip/ExceptionData can be used.
    //
    ReservedVectors[ExceptionType].OldSs         = SystemContext.SystemContextX64->Ss;
    ReservedVectors[ExceptionType].OldSp         = SystemContext.SystemContextX64->Rsp;
    ReservedVectors[ExceptionType].OldFlags      = SystemContext.SystemContextX64->Rflags;
    ReservedVectors[ExceptionType].OldCs         = SystemContext.SystemContextX64->Cs;
    ReservedVectors[ExceptionType].OldIp         = SystemContext.SystemContextX64->Rip;
    ReservedVectors[ExceptionType].ExceptionData = SystemContext.SystemContextX64->ExceptionData;
    //
    // Clear IF flag to avoid old IDT handler enable interrupt by IRET
    //
    Eflags.UintN                           = SystemContext.SystemContextX64->Rflags;
    Eflags.Bits.IF                         = 0;
    SystemContext.SystemContextX64->Rflags = Eflags.UintN;
    //
    // Modify the EIP in stack, then old IDT handler will return to HookAfterStubBegin.
    //
    SystemContext.SystemContextX64->Rip = (UINTN)ReservedVectors[ExceptionType].HookAfterStubHeaderCode;
  }
  
  /**
    Restore CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case.
  
    @param[in] ExceptionType        Exception type.
    @param[in] SystemContext        Pointer to EFI_SYSTEM_CONTEXT.
    @param[in] ExceptionHandlerData Pointer to exception handler data.
  **/
  VOID
  ArchRestoreExceptionContext (
    IN UINTN                   ExceptionType,
    IN EFI_SYSTEM_CONTEXT      SystemContext,
    IN EXCEPTION_HANDLER_DATA  *ExceptionHandlerData
    )
  {
    RESERVED_VECTORS_DATA  *ReservedVectors;
  
    ReservedVectors                               = ExceptionHandlerData->ReservedVectors;
    SystemContext.SystemContextX64->Ss            = ReservedVectors[ExceptionType].OldSs;
    SystemContext.SystemContextX64->Rsp           = ReservedVectors[ExceptionType].OldSp;
    SystemContext.SystemContextX64->Rflags        = ReservedVectors[ExceptionType].OldFlags;
    SystemContext.SystemContextX64->Cs            = ReservedVectors[ExceptionType].OldCs;
    SystemContext.SystemContextX64->Rip           = ReservedVectors[ExceptionType].OldIp;
    SystemContext.SystemContextX64->ExceptionData = ReservedVectors[ExceptionType].ExceptionData;
  }
  
  /**
    Setup separate stack for given exceptions.
  
    @param[in] StackSwitchData      Pointer to data required for setuping up
                                    stack switch.
  
    @retval EFI_SUCCESS             The exceptions have been successfully
                                    initialized with new stack.
    @retval EFI_INVALID_PARAMETER   StackSwitchData contains invalid content.
  
  **/
  EFI_STATUS
  ArchSetupExceptionStack (
    IN CPU_EXCEPTION_INIT_DATA  *StackSwitchData
    )
  {
    IA32_DESCRIPTOR           Gdtr;
    IA32_DESCRIPTOR           Idtr;
    IA32_IDT_GATE_DESCRIPTOR  *IdtTable;
    IA32_TSS_DESCRIPTOR       *TssDesc;
    IA32_TASK_STATE_SEGMENT   *Tss;
    UINTN                     StackTop;
    UINTN                     Index;
    UINTN                     Vector;
    UINTN                     TssBase;
    UINTN                     GdtSize;
  
    if ((StackSwitchData == NULL) ||
        (StackSwitchData->Ia32.Revision != CPU_EXCEPTION_INIT_DATA_REV) ||
        (StackSwitchData->X64.KnownGoodStackTop == 0) ||
        (StackSwitchData->X64.KnownGoodStackSize == 0) ||
        (StackSwitchData->X64.StackSwitchExceptions == NULL) ||
        (StackSwitchData->X64.StackSwitchExceptionNumber == 0) ||
        (StackSwitchData->X64.StackSwitchExceptionNumber > CPU_EXCEPTION_NUM) ||
        (StackSwitchData->X64.GdtTable == NULL) ||
        (StackSwitchData->X64.IdtTable == NULL) ||
        (StackSwitchData->X64.ExceptionTssDesc == NULL) ||
        (StackSwitchData->X64.ExceptionTss == NULL))
    {
      return EFI_INVALID_PARAMETER;
    }
  
    //
    // The caller is responsible for that the GDT table, no matter the existing
    // one or newly allocated, has enough space to hold descriptors for exception
    // task-state segments.
    //
    if (((UINTN)StackSwitchData->X64.GdtTable & (IA32_GDT_ALIGNMENT - 1)) != 0) {
      return EFI_INVALID_PARAMETER;
    }
  
    if ((UINTN)StackSwitchData->X64.ExceptionTssDesc < (UINTN)(StackSwitchData->X64.GdtTable)) {
      return EFI_INVALID_PARAMETER;
    }
  
    if (((UINTN)StackSwitchData->X64.ExceptionTssDesc + StackSwitchData->X64.ExceptionTssDescSize) >
        ((UINTN)(StackSwitchData->X64.GdtTable) + StackSwitchData->X64.GdtTableSize))
    {
      return EFI_INVALID_PARAMETER;
    }
  
    //
    // One task gate descriptor and one task-state segment are needed.
    //
    if (StackSwitchData->X64.ExceptionTssDescSize < sizeof (IA32_TSS_DESCRIPTOR)) {
      return EFI_INVALID_PARAMETER;
    }
  
    if (StackSwitchData->X64.ExceptionTssSize < sizeof (IA32_TASK_STATE_SEGMENT)) {
      return EFI_INVALID_PARAMETER;
    }
  
    //
    // Interrupt stack table supports only 7 vectors.
    //
    TssDesc = StackSwitchData->X64.ExceptionTssDesc;
    Tss     = StackSwitchData->X64.ExceptionTss;
    if (StackSwitchData->X64.StackSwitchExceptionNumber > ARRAY_SIZE (Tss->IST)) {
      return EFI_INVALID_PARAMETER;
    }
  
    //
    // Initialize new GDT table and/or IDT table, if any
    //
    AsmReadIdtr (&Idtr);
    AsmReadGdtr (&Gdtr);
  
    GdtSize = (UINTN)TssDesc + sizeof (IA32_TSS_DESCRIPTOR) -
              (UINTN)(StackSwitchData->X64.GdtTable);
    if ((UINTN)StackSwitchData->X64.GdtTable != Gdtr.Base) {
      CopyMem (StackSwitchData->X64.GdtTable, (VOID *)Gdtr.Base, Gdtr.Limit + 1);
      Gdtr.Base  = (UINTN)StackSwitchData->X64.GdtTable;
      Gdtr.Limit = (UINT16)GdtSize - 1;
    }
  
    if ((UINTN)StackSwitchData->X64.IdtTable != Idtr.Base) {
      Idtr.Base = (UINTN)StackSwitchData->X64.IdtTable;
    }
  
    if (StackSwitchData->X64.IdtTableSize > 0) {
      Idtr.Limit = (UINT16)(StackSwitchData->X64.IdtTableSize - 1);
    }
  
    //
    // Fixup current task descriptor. Task-state segment for current task will
    // be filled by processor during task switching.
    //
    TssBase = (UINTN)Tss;
  
    TssDesc->Uint128.Uint64   = 0;
    TssDesc->Uint128.Uint64_1 = 0;
    TssDesc->Bits.LimitLow    = sizeof (IA32_TASK_STATE_SEGMENT) - 1;
    TssDesc->Bits.BaseLow     = (UINT16)TssBase;
    TssDesc->Bits.BaseMidl    = (UINT8)(TssBase >> 16);
    TssDesc->Bits.Type        = IA32_GDT_TYPE_TSS;
    TssDesc->Bits.P           = 1;
    TssDesc->Bits.LimitHigh   = 0;
    TssDesc->Bits.BaseMidh    = (UINT8)(TssBase >> 24);
    TssDesc->Bits.BaseHigh    = (UINT32)(TssBase >> 32);
  
    //
    // Fixup exception task descriptor and task-state segment
    //
    ZeroMem (Tss, sizeof (*Tss));
    StackTop = StackSwitchData->X64.KnownGoodStackTop - CPU_STACK_ALIGNMENT;
    StackTop = (UINTN)ALIGN_POINTER (StackTop, CPU_STACK_ALIGNMENT);
    IdtTable = StackSwitchData->X64.IdtTable;
    for (Index = 0; Index < StackSwitchData->X64.StackSwitchExceptionNumber; ++Index) {
      //
      // Fixup IST
      //
      Tss->IST[Index] = StackTop;
      StackTop       -= StackSwitchData->X64.KnownGoodStackSize;
  
      //
      // Set the IST field to enable corresponding IST
      //
      Vector = StackSwitchData->X64.StackSwitchExceptions[Index];
      if ((Vector >= CPU_EXCEPTION_NUM) ||
          (Vector >= (Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)))
      {
        continue;
      }
  
      IdtTable[Vector].Bits.Reserved_0 = (UINT8)(Index + 1);
    }
  
    //
    // Publish GDT
    //
    AsmWriteGdtr (&Gdtr);
  
    //
    // Load current task
    //
    AsmWriteTr ((UINT16)((UINTN)StackSwitchData->X64.ExceptionTssDesc - Gdtr.Base));
  
    //
    // Publish IDT
    //
    AsmWriteIdtr (&Idtr);
  
    return EFI_SUCCESS;
  }
  
  /**
    Display CPU information.
  
    @param ExceptionType  Exception type.
    @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
  **/
  VOID
  EFIAPI
  DumpCpuContext (
    IN EFI_EXCEPTION_TYPE  ExceptionType,
    IN EFI_SYSTEM_CONTEXT  SystemContext
    )
  {
    InternalPrintMessage (
      "!!!! X64 Exception Type - %02x(%a)  CPU Apic ID - %08x !!!!\n",
      ExceptionType,
      GetExceptionNameStr (ExceptionType),
      GetApicId ()
      );
    if ((mErrorCodeFlag & (1 << ExceptionType)) != 0) {
      InternalPrintMessage (
        "ExceptionData - %016lx",
        SystemContext.SystemContextX64->ExceptionData
        );
      if (ExceptionType == EXCEPT_IA32_PAGE_FAULT) {
        InternalPrintMessage (
          "  I:%x R:%x U:%x W:%x P:%x PK:%x SS:%x SGX:%x",
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID)   != 0,
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_RSVD) != 0,
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_US)   != 0,
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_WR)   != 0,
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_P)    != 0,
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_PK)   != 0,
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SS)   != 0,
          (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SGX)  != 0
          );
      }
  
      InternalPrintMessage ("\n");
    }
  
    InternalPrintMessage (
      "RIP  - %016lx, CS  - %016lx, RFLAGS - %016lx\n",
      SystemContext.SystemContextX64->Rip,
      SystemContext.SystemContextX64->Cs,
      SystemContext.SystemContextX64->Rflags
      );
    InternalPrintMessage (
      "RAX  - %016lx, RCX - %016lx, RDX - %016lx\n",
      SystemContext.SystemContextX64->Rax,
      SystemContext.SystemContextX64->Rcx,
      SystemContext.SystemContextX64->Rdx
      );
    InternalPrintMessage (
      "RBX  - %016lx, RSP - %016lx, RBP - %016lx\n",
      SystemContext.SystemContextX64->Rbx,
      SystemContext.SystemContextX64->Rsp,
      SystemContext.SystemContextX64->Rbp
      );
    InternalPrintMessage (
      "RSI  - %016lx, RDI - %016lx\n",
      SystemContext.SystemContextX64->Rsi,
      SystemContext.SystemContextX64->Rdi
      );
    InternalPrintMessage (
      "R8   - %016lx, R9  - %016lx, R10 - %016lx\n",
      SystemContext.SystemContextX64->R8,
      SystemContext.SystemContextX64->R9,
      SystemContext.SystemContextX64->R10
      );
    InternalPrintMessage (
      "R11  - %016lx, R12 - %016lx, R13 - %016lx\n",
      SystemContext.SystemContextX64->R11,
      SystemContext.SystemContextX64->R12,
      SystemContext.SystemContextX64->R13
      );
    InternalPrintMessage (
      "R14  - %016lx, R15 - %016lx\n",
      SystemContext.SystemContextX64->R14,
      SystemContext.SystemContextX64->R15
      );
    InternalPrintMessage (
      "DS   - %016lx, ES  - %016lx, FS  - %016lx\n",
      SystemContext.SystemContextX64->Ds,
      SystemContext.SystemContextX64->Es,
      SystemContext.SystemContextX64->Fs
      );
    InternalPrintMessage (
      "GS   - %016lx, SS  - %016lx\n",
      SystemContext.SystemContextX64->Gs,
      SystemContext.SystemContextX64->Ss
      );
    InternalPrintMessage (
      "CR0  - %016lx, CR2 - %016lx, CR3 - %016lx\n",
      SystemContext.SystemContextX64->Cr0,
      SystemContext.SystemContextX64->Cr2,
      SystemContext.SystemContextX64->Cr3
      );
    InternalPrintMessage (
      "CR4  - %016lx, CR8 - %016lx\n",
      SystemContext.SystemContextX64->Cr4,
      SystemContext.SystemContextX64->Cr8
      );
    InternalPrintMessage (
      "DR0  - %016lx, DR1 - %016lx, DR2 - %016lx\n",
      SystemContext.SystemContextX64->Dr0,
      SystemContext.SystemContextX64->Dr1,
      SystemContext.SystemContextX64->Dr2
      );
    InternalPrintMessage (
      "DR3  - %016lx, DR6 - %016lx, DR7 - %016lx\n",
      SystemContext.SystemContextX64->Dr3,
      SystemContext.SystemContextX64->Dr6,
      SystemContext.SystemContextX64->Dr7
      );
    InternalPrintMessage (
      "GDTR - %016lx %016lx, LDTR - %016lx\n",
      SystemContext.SystemContextX64->Gdtr[0],
      SystemContext.SystemContextX64->Gdtr[1],
      SystemContext.SystemContextX64->Ldtr
      );
    InternalPrintMessage (
      "IDTR - %016lx %016lx,   TR - %016lx\n",
      SystemContext.SystemContextX64->Idtr[0],
      SystemContext.SystemContextX64->Idtr[1],
      SystemContext.SystemContextX64->Tr
      );
    InternalPrintMessage (
      "FXSAVE_STATE - %016lx\n",
      &SystemContext.SystemContextX64->FxSaveState
      );
  }
  
  /**
    Display CPU information.
  
    @param ExceptionType  Exception type.
    @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
  **/
  VOID
  DumpImageAndCpuContent (
    IN EFI_EXCEPTION_TYPE  ExceptionType,
    IN EFI_SYSTEM_CONTEXT  SystemContext
    )
  {
    DumpCpuContext (ExceptionType, SystemContext);
    //
    // Dump module image base and module entry point by RIP
    //
    if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
        ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0))
    {
      //
      // The RIP in SystemContext could not be used
      // if it is page fault with I/D set.
      //
      DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp));
    } else {
      DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
    }
  }
  