///**@file
// Low leve x64 specific debug support functions.
//
// Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
// Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
// This program and the accompanying materials
// are licensed and made available under the terms and conditions of the BSD License
// which accompanies this distribution.  The full text of the license may be found at
// http://opensource.org/licenses/bsd-license.php
//
// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
//
//**/

ASM_GLOBAL ASM_PFX(OrigVector)
ASM_GLOBAL ASM_PFX(InterruptEntryStub)
ASM_GLOBAL ASM_PFX(StubSize)
ASM_GLOBAL ASM_PFX(CommonIdtEntry)
ASM_GLOBAL ASM_PFX(FxStorSupport)

.data

ASM_PFX(StubSize):         .long   ASM_PFX(InterruptEntryStubEnd) - ASM_PFX(InterruptEntryStub)
ASM_PFX(AppRsp):           .long   0x11111111 # ?
                           .long   0x11111111 # ?
ASM_PFX(DebugRsp):         .long   0x22222222 # ?
                           .long   0x22222222 # ?
ASM_PFX(ExtraPush):        .long   0x33333333 # ?
                           .long   0x33333333 # ?
ASM_PFX(ExceptData):       .long   0x44444444 # ?
                           .long   0x44444444 # ?
ASM_PFX(Rflags):           .long   0x55555555 # ?
                           .long   0x55555555 # ?
ASM_PFX(OrigVector):       .long   0x66666666 # ?
                           .long   0x66666666 # ?

// The declarations below define the memory region that will be used for the debug stack.
// The context record will be built by pushing register values onto this stack.
// It is imparitive that alignment be carefully managed, since the FXSTOR and
// FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned.
//
// The stub will switch stacks from the application stack to the debuger stack
// and pushes the exception number.
//
// Then we building the context record on the stack. Since the stack grows down,
// we push the fields of the context record from the back to the front.  There
// are 336 bytes of stack used prior allocating the 512 bytes of stack to be
// used as the memory buffer for the fxstor instruction. Therefore address of
// the buffer used for the FXSTOR instruction is &Eax - 336 - 512, which
// must be 16 byte aligned.
//
// We carefully locate the stack to make this happen.
//
// For reference, the context structure looks like this:
//      struct {
//        UINT64            ExceptionData;
//        FX_SAVE_STATE_X64 FxSaveState;    // 512 bytes, must be 16 byte aligned
//        UINT64            Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
//        UINT64            Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
//        UINT64            RFlags;
//        UINT64            Ldtr, Tr;
//        UINT64            Gdtr[2], Idtr[2];
//        UINT64            Rip;
//        UINT64            Gs, Fs, Es, Ds, Cs, Ss;
//        UINT64            Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
//        UINT64            R8, R9, R10, R11, R12, R13, R14, R15;
//      } SYSTEM_CONTEXT_X64;  // 64 bit system context record

.p2align 4
DebugStackEnd : .ascii     "DbgStkEnd >>>>>>"    # 16 byte long string - must be 16 bytes to preserve alignment
                .fill 0x1ffc, 4, 0x00000000
                                                 # 32K should be enough stack
                                                 #   This allocation is coocked to insure
                                                 #   that the the buffer for the FXSTORE instruction
                                                 #   will be 16 byte aligned also.
                                                 #
ASM_PFX(ExceptionNumber):  .long   0x77777777    # first entry will be the vector number pushed by the stub
                           .long   0x77777777    # ?

DebugStackBegin : .ascii    "<<<< DbgStkBegin"   # initial debug ESP == DebugStackBegin, set in stub


.text

//------------------------------------------------------------------------------
// BOOLEAN
// FxStorSupport (
//   void
//   )
//
// Abstract: Returns TRUE if FxStor instructions are supported
//
ASM_GLOBAL ASM_PFX(FxStorSupport)
ASM_PFX(FxStorSupport):
//
// cpuid corrupts rbx which must be preserved per the C calling convention
//
                pushq   %rbx
                movq    $1, %rax
                cpuid
                movl    %edx, %eax
                andq    $0x01000000, %rax
                shrq    $24, %rax
                popq    %rbx
                ret
//------------------------------------------------------------------------------
// void
// Vect2Desc (
//   IA32_IDT_GATE_DESCRIPTOR * DestDesc,  // rcx
//   void (*Vector) (void)   // rdx
//   )
//
// Abstract: Encodes an IDT descriptor with the given physical address
//
ASM_GLOBAL ASM_PFX(Vect2Desc)
ASM_PFX(Vect2Desc):
                movq    %rdx, %rax
                movw    %ax, (%rcx)                  # write bits 15..0 of offset
                movw    %cs, %dx
                movw    %dx, 2(%rcx)                 # SYS_CODE_SEL from GDT
                movw    $(0x0e00 | 0x8000), 4(%rcx)  # type = 386 interrupt gate, present
                shrq    $16, %rax
                movw    %ax, 6(%rcx)                 # write bits 31..16 of offset
                shrq    $16, %rax
                movl    %eax, 8(%rcx)                # write bits 63..32 of offset

                ret

//------------------------------------------------------------------------------
// InterruptEntryStub
//
// Abstract: This code is not a function, but is a small piece of code that is
//               copied and fixed up once for each IDT entry that is hooked.
//
ASM_GLOBAL ASM_PFX(InterruptEntryStub)
ASM_PFX(InterruptEntryStub):

                pushq  $0                       # push vector number - will be modified before installed
                jmp     ASM_PFX(CommonIdtEntry)

ASM_GLOBAL ASM_PFX(InterruptEntryStubEnd)
ASM_PFX(InterruptEntryStubEnd):

//------------------------------------------------------------------------------
// CommonIdtEntry
//
// Abstract: This code is not a function, but is the common part for all IDT
//               vectors.
//
ASM_GLOBAL ASM_PFX(CommonIdtEntry)
//
// At this point, the stub has saved the current application stack esp into AppRsp
// and switched stacks to the debug stack, where it pushed the vector number
//
// The application stack looks like this:
//
//              ...
//              (last application stack entry)
//              [16 bytes alignment, do not care it]
//              SS from interrupted task
//              RSP from interrupted task
//              rflags from interrupted task
//              CS from interrupted task
//              RIP from interrupted task
//              Error code <-------------------- Only present for some exeption types
//
//              Vector Number <----------------- pushed in our IDT Entry
//


// The stub switched us to the debug stack and pushed the interrupt number.
//
// Next, construct the context record.  It will be build on the debug stack by
// pushing the registers in the correct order so as to create the context structure
// on the debug stack.  The context record must be built from the end back to the
// beginning because the stack grows down...
//
// For reference, the context record looks like this:
//
// typedef
// struct {
//   UINT64            ExceptionData;
//   FX_SAVE_STATE_X64 FxSaveState;
//   UINT64            Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
//   UINT64            Cr0, Cr2, Cr3, Cr4, Cr8;
//   UINT64            RFlags;
//   UINT64            Ldtr, Tr;
//   UINT64            Gdtr[2], Idtr[2];
//   UINT64            Rip;
//   UINT64            Gs, Fs, Es, Ds, Cs, Ss;
//   UINT64            Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
//   UINT64            R8, R9, R10, R11, R12, R13, R14, R15;
// } SYSTEM_CONTEXT_X64;  // 64
ASM_PFX(CommonIdtEntry):
// NOTE: we save rsp here to prevent compiler put rip reference cause error AppRsp
                pushq   %rax
                movq    (8)(%rsp), %rax                  # save vector number
                movq    %rax, ASM_PFX(ExceptionNumber)(%rip)   # save vector number
                popq    %rax
                addq    $8, %rsp                         # pop vector number
                movq    %rsp, ASM_PFX(AppRsp)(%rip)      # save stack top
                movq    DebugStackBegin(%rip), %rsp      # switch to debugger stack
                subq    $8, %rsp                         # leave space for vector number
// UINT64  Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
// UINT64  R8, R9, R10, R11, R12, R13, R14, R15;
                pushq    %r15
                pushq    %r14
                pushq    %r13
                pushq    %r12
                pushq    %r11
                pushq    %r10
                pushq    %r9
                pushq    %r8
                pushq    %rax
                pushq    %rcx
                pushq    %rdx
                pushq    %rbx
                pushq    %rsp
                pushq    %rbp
                pushq    %rsi
                pushq    %rdi
// Save interrupt state rflags register...
                pushfq
                popq    %rax
                movq    %rax, ASM_PFX(Rflags)(%rip)
// We need to determine if any extra data was pushed by the exception, and if so, save it
// To do this, we check the exception number pushed by the stub, and cache the
// result in a variable since we'll need this again.
                cmpl    $0, ASM_PFX(ExceptionNumber)(%rip)
                jz      ExtraPushOne
                cmpl    $10, ASM_PFX(ExceptionNumber)(%rip)
                jz      ExtraPushOne
                cmpl    $11, ASM_PFX(ExceptionNumber)(%rip)
                jz      ExtraPushOne
                cmpl    $12, ASM_PFX(ExceptionNumber)(%rip)
                jz      ExtraPushOne
                cmpl    $13, ASM_PFX(ExceptionNumber)(%rip)
                jz      ExtraPushOne
                cmpl    $14, ASM_PFX(ExceptionNumber)(%rip)
                jz      ExtraPushOne
                cmpl    $17, ASM_PFX(ExceptionNumber)(%rip)
                jz      ExtraPushOne
                movl    $0, ASM_PFX(ExtraPush)(%rip)
                movl    $0, ASM_PFX(ExceptData)(%rip)
                jmp     ExtraPushDone
ExtraPushOne:
                movl  $1, ASM_PFX(ExtraPush)(%rip)

// If there's some extra data, save it also, and modify the saved AppRsp to effectively
// pop this value off the application's stack.
                movq    ASM_PFX(AppRsp)(%rip), %rax
                movq    (%rax), %rbx
                movq    %rbx, ASM_PFX(ExceptData)(%rip)
                addq    $8, %rax
                movq    %rax, ASM_PFX(AppRsp)(%rip)

ExtraPushDone:

// The "push" above pushed the debug stack rsp.  Since what we're actually doing
// is building the context record on the debug stack, we need to save the pushed
// debug RSP, and replace it with the application's last stack entry...
                movq    24(%rsp), %rax
                movq    %rax, ASM_PFX(DebugRsp)(%rip)
                movq    ASM_PFX(AppRsp)(%rip), %rax
                movq    24(%rax), %rax
                # application stack has ss, rsp, rflags, cs, & rip, so
                # last actual application stack entry is saved at offset
                # 24 bytes from stack top.
                movq    %rax, 24(%rsp)

// continue building context record
// UINT64  Gs, Fs, Es, Ds, Cs, Ss;  insure high 16 bits of each is zero
                mov     %ss, %rax
                pushq   %rax
                # CS from application is one entry back in application stack
                movq    ASM_PFX(AppRsp)(%rip), %rax
                movzxw  8(%rax), %rax
                pushq   %rax

                mov     %ds, %rax
                pushq   %rax
                mov     %es, %rax
                pushq   %rax
                mov     %fs, %rax
                pushq   %rax
                mov     %gs, %rax
                pushq   %rax
// UINT64  Rip;
                # Rip from application is on top of application stack
                movq    ASM_PFX(AppRsp)(%rip), %rax
                pushq   (%rax)
// UINT64  Gdtr[2], Idtr[2];
                push    $0
                push    $0
                sidtq   (%rsp)
                push    $0
                push    $0
                sgdtq   (%rsp)

// UINT64  Ldtr, Tr;
                xorq    %rax, %rax
                str     %ax
                pushq   %rax
                sldt    %ax
                pushq   %rax

// UINT64  RFlags;
// Rflags from application is two entries back in application stack
                movq    ASM_PFX(AppRsp)(%rip), %rax
                pushq   16(%rax)
// UINT64  Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
// insure FXSAVE/FXRSTOR is enabled in CR4...
// ... while we're at it, make sure DE is also enabled...
                movq    %cr8, %rax
                pushq   %rax
                movq    %cr4, %rax
                orq     $0x208, %rax
                movq    %rax, %cr4
                pushq   %rax
                movq    %cr3, %rax
                pushq   %rax
                movq    %cr2, %rax
                pushq   %rax
                push    $0
                movq    %cr0, %rax
                pushq   %rax
// UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
                movq    %dr7, %rax
                pushq   %rax
// clear Dr7 while executing debugger itself
                xorq    %rax, %rax
                movq    %rax, %dr7

                movq    %dr6, %rax
                pushq   %rax
// insure all status bits in dr6 are clear...
                xorq    %rax, %rax
                movq    %rax, %dr6

                movq    %dr3, %rax
                pushq   %rax
                movq    %dr2, %rax
                pushq   %rax
                movq    %dr1, %rax
                pushq   %rax
                movq    %dr0, %rax
                pushq   %rax

// FX_SAVE_STATE_X64 FxSaveState;
                subq    $512, %rsp
                movq    %rsp, %rdi
                # IMPORTANT!! The debug stack has been carefully constructed to
                # insure that rsp and rdi are 16 byte aligned when we get here.
                # They MUST be.  If they are not, a GP fault will occur.

                #     FXSTOR_RDI
                fxsave      (%rdi)

// UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear
                cld
                
// UINT64  ExceptionData;
                movq    ASM_PFX(ExceptData)(%rip),  %rax
                pushq   %rax

// call to C code which will in turn call registered handler
// pass in the vector number
                movq    %rsp, %rdx
                movq    ASM_PFX(ExceptionNumber)(%rip), %rcx
                subq    $40, %rsp
                call    ASM_PFX(InterruptDistrubutionHub)
                addq    $40, %rsp
// restore context...
// UINT64  ExceptionData;
                addq    $8, %rsp

// FX_SAVE_STATE_X64 FxSaveState;
                movq    %rsp, %rsi

                #    FXRSTOR_RSI
                fxrstor     (%rsi)

                addq    $512, %rsp

// UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
                popq    %rax
                movq    %rax, %dr0
                popq    %rax
                movq    %rax, %dr1
                popq    %rax
                movq    %rax, %dr2
                popq    %rax
                movq    %rax, %dr3

// skip restore of dr6.  We cleared dr6 during the context save.
                addq    $8, %rsp
                popq    %rax
                movq    %rax, %dr7

// UINT64  Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
                popq    %rax
                movq    %rax, %cr0
                addq    $8, %rsp
                popq    %rax
                movq    %rax, %cr2
                popq    %rax
                movq    %rax, %cr3
                popq    %rax
                movq    %rax, %cr4
                popq    %rax
                movq    %rax, %cr8
// UINT64  RFlags;
                movq    ASM_PFX(AppRsp)(%rip), %rax
                popq    16(%rax)
// UINT64  Ldtr, Tr;
// UINT64  Gdtr[2], Idtr[2];
// Best not let anyone mess with these particular registers...
                addq    $48, %rsp
// UINT64  Rip;
                popq    (%rax)

// UINT64  Gs, Fs, Es, Ds, Cs, Ss;
// NOTE - modified segment registers could hang the debugger...  We
//        could attempt to insulate ourselves against this possibility,
//        but that poses risks as well.
//

                popq    %rax
                # mov     %rax, %gs
                popq    %rax
                # mov     %rax, %fs
                popq    %rax
                mov     %rax, %es
                popq    %rax
                mov     %rax, %ds
                movq    ASM_PFX(AppRsp)(%rip), %rax
                popq    8(%rax)
                popq    %rax
                mov     %rax, %ss
## The next stuff to restore is the general purpose registers that were pushed
## using the "push" instruction.
##
## The value of RSP as stored in the context record is the application RSP
## including the 5 entries on the application stack caused by the exception
## itself. It may have been modified by the debug agent, so we need to
## determine if we need to relocate the application stack.

                movq    24(%rsp), %rbx  # move the potentially modified AppRsp into rbx
                movq    ASM_PFX(AppRsp)(%rip), %rax
                movq    24(%rax), %rax
                cmpq    %rax, %rbx
                je      NoAppStackMove

                movq    ASM_PFX(AppRsp)(%rip), %rax
                movq    (%rax), %rcx     # RIP
                movq    %rcx, (%rbx)

                movq    8(%rax), %rcx    # CS
                movq    %rcx, 8(%rbx)

                movq    16(%rax), %rcx   # RFLAGS
                movq    %rcx, 16(%rbx)

                movq    24(%rax), %rcx   # RSP
                movq    %rcx, 24(%rbx)

                movq    32(%rax), %rcx   # SS
                movq    %rcx, 32(%rbx)

                movq    %rbx, %rax       # modify the saved AppRsp to the new AppRsp
                movq    %rax, ASM_PFX(AppRsp)(%rip)
NoAppStackMove:
                movq    ASM_PFX(DebugRsp)(%rip), %rax   # restore the DebugRsp on the debug stack
                                         # so our "pop" will not cause a stack switch
                movq    %rax, 24(%rsp)

                cmpl    $0x068, ASM_PFX(ExceptionNumber)(%rip)
                jne     NoChain

Chain:

// Restore rflags so when we chain, the flags will be exactly as if we were never here.
// We gin up the stack to do an iretq so we can get ALL the flags.
                movq    ASM_PFX(AppRsp)(%rip), %rax
                movq    40(%rax), %rbx
                pushq   %rbx
                mov     %ss, %rax
                pushq   %rax
                movq    %rsp, %rax
                addq    $16, %rax
                pushq   %rax
                movq    ASM_PFX(AppRsp)(%rip), %rax
                movq    16(%rax), %rbx
                andq    $0xfffffffffffffcff, %rbx  # special handling for IF and TF
                pushq   %rbx
                mov     %cs, %rax
                pushq   %rax
                movq    PhonyIretq(%rip), %rax
                pushq   %rax
                iretq
PhonyIretq:

// UINT64  Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
// UINT64  R8, R9, R10, R11, R12, R13, R14, R15;
                popq    %rdi
                popq    %rsi
                popq    %rbp
                popq    %rsp
                popq    %rbx
                popq    %rdx
                popq    %rcx
                popq    %rax
                popq    %r8
                popq    %r9
                popq    %r10
                popq    %r11
                popq    %r12
                popq    %r13
                popq    %r14
                popq    %r15

// Switch back to application stack
                movq    ASM_PFX(AppRsp)(%rip), %rsp
// Jump to original handler
                jmp     ASM_PFX(OrigVector)
NoChain:
// UINT64  Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
// UINT64  R8, R9, R10, R11, R12, R13, R14, R15;
                popq    %rdi
                popq    %rsi
                popq    %rbp
                popq    %rsp
                popq    %rbx
                popq    %rdx
                popq    %rcx
                popq    %rax
                popq    %r8
                popq    %r9
                popq    %r10
                popq    %r11
                popq    %r12
                popq    %r13
                popq    %r14
                popq    %r15

// Switch back to application stack
                movq    ASM_PFX(AppRsp)(%rip), %rsp

// We're outa here...
                iret
