From 7f05fa00f73038b425002566d3afe6c3ade2ccdb Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 22 Dec 2016 15:55:38 +0800 Subject: MdeModulePkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- .../Universal/DebugSupportDxe/X64/AsmFuncs.S | 551 +++++++++++++++++++ .../Universal/DebugSupportDxe/X64/AsmFuncs.asm | 596 +++++++++++++++++++++ .../Universal/DebugSupportDxe/X64/PlDebugSupport.h | 22 + .../DebugSupportDxe/X64/PlDebugSupportX64.c | 146 +++++ 4 files changed, 1315 insertions(+) create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h create mode 100644 Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c (limited to 'Core/MdeModulePkg/Universal/DebugSupportDxe/X64') diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S new file mode 100644 index 0000000000..7f0919ee1b --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S @@ -0,0 +1,551 @@ +///**@file +// Low leve x64 specific debug support functions. +// +// Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+// Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+// 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 diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm new file mode 100644 index 0000000000..bce49ef762 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.asm @@ -0,0 +1,596 @@ +;/** @file +; Low level x64 routines used by the debug support driver. +; +; Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
+; 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. +; +;**/ + +EXCPT64_DIVIDE_ERROR EQU 0 +EXCPT64_DEBUG EQU 1 +EXCPT64_NMI EQU 2 +EXCPT64_BREAKPOINT EQU 3 +EXCPT64_OVERFLOW EQU 4 +EXCPT64_BOUND EQU 5 +EXCPT64_INVALID_OPCODE EQU 6 +EXCPT64_DOUBLE_FAULT EQU 8 +EXCPT64_INVALID_TSS EQU 10 +EXCPT64_SEG_NOT_PRESENT EQU 11 +EXCPT64_STACK_FAULT EQU 12 +EXCPT64_GP_FAULT EQU 13 +EXCPT64_PAGE_FAULT EQU 14 +EXCPT64_FP_ERROR EQU 16 +EXCPT64_ALIGNMENT_CHECK EQU 17 +EXCPT64_MACHINE_CHECK EQU 18 +EXCPT64_SIMD EQU 19 + +FXSTOR_FLAG EQU 01000000h ; bit cpuid 24 of feature flags + +;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87, +;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver +;; MUST check the CPUID feature flags to see that these instructions are available +;; and fail to init if they are not. + +;; fxstor [rdi] +FXSTOR_RDI MACRO + db 0fh, 0aeh, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [rdi] +ENDM + +;; fxrstor [rsi] +FXRSTOR_RSI MACRO + db 0fh, 0aeh, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [rsi] +ENDM + +data SEGMENT + +public OrigVector, InterruptEntryStub, StubSize, CommonIdtEntry, FxStorSupport + +StubSize dd InterruptEntryStubEnd - InterruptEntryStub +AppRsp dq 1111111111111111h ; ? +DebugRsp dq 2222222222222222h ; ? +ExtraPush dq 3333333333333333h ; ? +ExceptData dq 4444444444444444h ; ? +Rflags dq 5555555555555555h ; ? +OrigVector dq 6666666666666666h ; ? + +;; 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 + +align 16 +DebugStackEnd db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment + dd 1ffch dup (000000000h) ;; 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. + ;; +ExceptionNumber dq ? ;; first entry will be the vector number pushed by the stub + +DebugStackBegin db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub + +data ENDS + +text SEGMENT + +externdef InterruptDistrubutionHub:near + +;------------------------------------------------------------------------------ +; BOOLEAN +; FxStorSupport ( +; void +; ) +; +; Abstract: Returns TRUE if FxStor instructions are supported +; +FxStorSupport PROC PUBLIC + +; +; cpuid corrupts rbx which must be preserved per the C calling convention +; + push rbx + mov rax, 1 + cpuid + mov eax, edx + and rax, FXSTOR_FLAG + shr rax, 24 + pop rbx + ret +FxStorSupport ENDP + +;------------------------------------------------------------------------------ +; void +; Vect2Desc ( +; IA32_IDT_GATE_DESCRIPTOR * DestDesc, // rcx +; void (*Vector) (void) // rdx +; ) +; +; Abstract: Encodes an IDT descriptor with the given physical address +; +Vect2Desc PROC PUBLIC + + mov rax, rdx + mov word ptr [rcx], ax ; write bits 15..0 of offset + mov dx, cs + mov word ptr [rcx+2], dx ; SYS_CODE_SEL from GDT + mov word ptr [rcx+4], 0e00h OR 8000h ; type = 386 interrupt gate, present + shr rax, 16 + mov word ptr [rcx+6], ax ; write bits 31..16 of offset + shr rax, 16 + mov dword ptr [rcx+8], eax ; write bits 63..32 of offset + + ret + +Vect2Desc ENDP + + + +;------------------------------------------------------------------------------ +; 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. +; +InterruptEntryStub:: + push 0 ; push vector number - will be modified before installed + db 0e9h ; jump rel32 + dd 0 ; fixed up to relative address of CommonIdtEntry +InterruptEntryStubEnd: + + + +;------------------------------------------------------------------------------ +; CommonIdtEntry +; +; Abstract: This code is not a function, but is the common part for all IDT +; vectors. +; +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 bit system context record + +;; NOTE: we save rsp here to prevent compiler put rip reference cause error AppRsp + push rax + mov rax, qword ptr [rsp][8] ; save vector number + mov ExceptionNumber, rax ; save vector number + pop rax + add rsp, 8 ; pop vector number + mov AppRsp, rsp ; save stack top + mov rsp, offset DebugStackBegin ; switch to debugger stack + sub rsp, 8 ; leave space for vector number + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi + +;; Save interrupt state rflags register... + pushfq + pop rax + mov qword ptr Rflags, rax + +;; 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. + cmp ExceptionNumber, EXCPT64_DOUBLE_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_INVALID_TSS + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_SEG_NOT_PRESENT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_STACK_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_GP_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_PAGE_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_ALIGNMENT_CHECK + jz ExtraPushOne + mov ExtraPush, 0 + mov ExceptData, 0 + jmp ExtraPushDone +ExtraPushOne: + mov ExtraPush, 1 + +;; If there's some extra data, save it also, and modify the saved AppRsp to effectively +;; pop this value off the application's stack. + mov rax, AppRsp + mov rbx, [rax] + mov ExceptData, rbx + add rax, 8 + mov AppRsp, rax + +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... + mov rax, [rsp + 24] + mov DebugRsp, rax + mov rax, AppRsp + mov rax, QWORD PTR [rax + 24] + ; application stack has ss, rsp, rflags, cs, & rip, so + ; last actual application stack entry is saved at offset + ; 24 bytes from stack top. + mov [rsp + 24], rax + +;; continue building context record +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov rax, ss + push rax + + ; CS from application is one entry back in application stack + mov rax, AppRsp + movzx rax, word ptr [rax + 8] + push rax + + mov rax, ds + push rax + mov rax, es + push rax + mov rax, fs + push rax + mov rax, gs + push rax + +;; UINT64 Rip; + ; Rip from application is on top of application stack + mov rax, AppRsp + push qword ptr [rax] + +;; UINT64 Gdtr[2], Idtr[2]; + push 0 + push 0 + sidt fword ptr [rsp] + push 0 + push 0 + sgdt fword ptr [rsp] + +;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + +;; UINT64 RFlags; +;; Rflags from application is two entries back in application stack + mov rax, AppRsp + push qword ptr [rax + 16] + +;; 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... + mov rax, cr8 + push rax + mov rax, cr4 + or rax, 208h + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + push 0 + mov rax, cr0 + push rax + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov rax, dr7 + push rax +;; clear Dr7 while executing debugger itself + xor rax, rax + mov dr7, rax + + mov rax, dr6 + push rax +;; insure all status bits in dr6 are clear... + xor rax, rax + mov dr6, rax + + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + +;; FX_SAVE_STATE_X64 FxSaveState; + sub rsp, 512 + mov rdi, rsp + ; 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 + +;; UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear + cld + +;; UINT64 ExceptionData; + mov rax, ExceptData + push rax + +; call to C code which will in turn call registered handler +; pass in the vector number + mov rdx, rsp + mov rcx, ExceptionNumber + sub rsp, 40 + call InterruptDistrubutionHub + add rsp, 40 + +; restore context... +;; UINT64 ExceptionData; + add rsp, 8 + +;; FX_SAVE_STATE_X64 FxSaveState; + mov rsi, rsp + FXRSTOR_RSI + add rsp, 512 + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop rax + mov dr0, rax + pop rax + mov dr1, rax + pop rax + mov dr2, rax + pop rax + mov dr3, rax +;; skip restore of dr6. We cleared dr6 during the context save. + add rsp, 8 + pop rax + mov dr7, rax + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + pop rax + mov cr0, rax + add rsp, 8 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + pop rax + mov cr8, rax + +;; UINT64 RFlags; + mov rax, AppRsp + pop qword ptr [rax + 16] + +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add rsp, 48 + +;; UINT64 Rip; + pop qword ptr [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. +;; + + pop rax + ; mov gs, rax + pop rax + ; mov fs, rax + pop rax + mov es, rax + pop rax + mov ds, rax + mov rax, AppRsp + pop qword ptr [rax + 8] + pop rax + mov ss, rax + +;; 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. + + mov rbx, [rsp + 24] ; move the potentially modified AppRsp into rbx + mov rax, AppRsp + mov rax, QWORD PTR [rax + 24] + cmp rbx, rax + je NoAppStackMove + + mov rax, AppRsp + mov rcx, [rax] ; RIP + mov [rbx], rcx + + mov rcx, [rax + 8] ; CS + mov [rbx + 8], rcx + + mov rcx, [rax + 16] ; RFLAGS + mov [rbx + 16], rcx + + mov rcx, [rax + 24] ; RSP + mov [rbx + 24], rcx + + mov rcx, [rax + 32] ; SS + mov [rbx + 32], rcx + + mov rax, rbx ; modify the saved AppRsp to the new AppRsp + mov AppRsp, rax +NoAppStackMove: + mov rax, DebugRsp ; restore the DebugRsp on the debug stack + ; so our "pop" will not cause a stack switch + mov [rsp + 24], rax + + cmp ExceptionNumber, 068h + 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. + mov rax, AppRsp + mov rbx, [rax + 40] + push rbx + mov rax, ss + push rax + mov rax, rsp + add rax, 16 + push rax + mov rax, AppRsp + mov rbx, [rax + 16] + and rbx, NOT 300h ; special handling for IF and TF + push rbx + mov rax, cs + push rax + mov rax, offset PhonyIretq + push rax + iretq +PhonyIretq: + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, AppRsp + +;; Jump to original handler + jmp OrigVector + +NoChain: +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, AppRsp + +;; We're outa here... + iretq +text ENDS + +END + + + diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h new file mode 100644 index 0000000000..044e5d9d07 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h @@ -0,0 +1,22 @@ +/** @file + X64 specific debug support macros. + +Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _PLDEBUG_SUPPORT_H_ +#define _PLDEBUG_SUPPORT_H_ + +#include "Ia32/DebugSupport.h" + +#define EFI_ISA IsaX64 + +#endif diff --git a/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c new file mode 100644 index 0000000000..fa8869d287 --- /dev/null +++ b/Core/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c @@ -0,0 +1,146 @@ +/** @file + X64 specific functions to support Debug Support protocol. + +Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+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. + +**/ + +#include "PlDebugSupport.h" + +IA32_IDT_GATE_DESCRIPTOR NullDesc = {{0,0}}; + +/** + Get Interrupt Handle from IDT Gate Descriptor. + + @param IdtGateDecriptor IDT Gate Descriptor. + + @return Interrupt Handle stored in IDT Gate Descriptor. + +**/ +UINTN +GetInterruptHandleFromIdt ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtGateDecriptor + ) +{ + UINTN InterruptHandle; + + // + // InterruptHandle 0-15 : OffsetLow + // InterruptHandle 16-31 : OffsetHigh + // InterruptHandle 32-63 : OffsetUpper + // + InterruptHandle = ((UINTN) IdtGateDecriptor->Bits.OffsetLow) | + (((UINTN) IdtGateDecriptor->Bits.OffsetHigh) << 16) | + (((UINTN) IdtGateDecriptor->Bits.OffsetUpper) << 32) ; + + return InterruptHandle; +} + +/** + Allocate pool for a new IDT entry stub. + + Copy the generic stub into the new buffer and fixup the vector number + and jump target address. + + @param ExceptionType This is the exception type that the new stub will be created + for. + @param Stub On successful exit, *Stub contains the newly allocated entry stub. + +**/ +VOID +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ) +{ + UINT8 *StubCopy; + + StubCopy = *Stub; + + // + // Fixup the stub code for this vector + // + + // The stub code looks like this: + // + // 00000000 6A 00 push 0 ; push vector number - will be modified before installed + // 00000002 E9 db 0e9h ; jump rel32 + // 00000003 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry + // + + // + // poke in the exception type so the second push pushes the exception type + // + StubCopy[0x1] = (UINT8) ExceptionType; + + // + // fixup the jump target to point to the common entry + // + *(UINT32 *) &StubCopy[0x3] = (UINT32)((UINTN) CommonIdtEntry - (UINTN) &StubCopy[StubSize]); + + return; +} + +/** + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + + @param NewCallback If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + @param ExceptionType Indicates which entry to manage. + + @retval EFI_SUCCESS Process is ok. + @retval EFI_INVALID_PARAMETER Requested uninstalling a handler from a vector that has + no handler registered for it + @retval EFI_ALREADY_STARTED Requested install to a vector that already has a handler registered. + @retval others Possible return values are passed through from UnHookEntry and HookEntry. + +**/ +EFI_STATUS +ManageIdtEntryTable ( + CALLBACK_FUNC NewCallback, + EFI_EXCEPTION_TYPE ExceptionType + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (CompareMem (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc, sizeof (IA32_IDT_GATE_DESCRIPTOR)) != 0) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + Status = EFI_ALREADY_STARTED; + } else { + UnhookEntry (ExceptionType); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback == NULL) { + // + // if the input handler is null, error + // + Status = EFI_INVALID_PARAMETER; + } else { + HookEntry (ExceptionType, NewCallback); + } + } + + return Status; +} -- cgit v1.2.3