diff options
author | andrewfish <andrewfish@6f19259b-4bc3-4df7-8a09-765794883524> | 2010-04-03 00:41:42 +0000 |
---|---|---|
committer | andrewfish <andrewfish@6f19259b-4bc3-4df7-8a09-765794883524> | 2010-04-03 00:41:42 +0000 |
commit | 969eba7b0df70c9aa261eaf005085568b88de87c (patch) | |
tree | bdbc700471085d43dfb70929159f32ddf231a24c /EmbeddedPkg/Library | |
parent | 66c0b4461add95d84f74ab8348430adb62ae9e1f (diff) | |
download | edk2-969eba7b0df70c9aa261eaf005085568b88de87c.tar.gz edk2-969eba7b0df70c9aa261eaf005085568b88de87c.tar.bz2 edk2-969eba7b0df70c9aa261eaf005085568b88de87c.zip |
Add the beginning of a GDB based Debug Agent. IA-32 and X64 don't have low level interrupt code yet. I've been testing on ARM.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10334 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'EmbeddedPkg/Library')
11 files changed, 4771 insertions, 0 deletions
diff --git a/EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLib.c b/EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLib.c new file mode 100755 index 0000000000..1aa2d2938f --- /dev/null +++ b/EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLib.c @@ -0,0 +1,64 @@ +/** @file
+ Null Debug Agent timer.
+
+ The debug agent uses the timer so the debugger can break into running programs.
+ If you link against this library you will not be able to break into a running
+ program with the debugger.
+
+ Copyright (c) 2008-2010, Apple Inc. All rights reserved.
+
+ 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.
+
+**/
+
+
+/**
+ Setup all the hardware needed for the debug agents timer.
+
+ This function is used to set up debug enviroment. It may enable interrupts.
+
+**/
+VOID
+EFIAPI
+DebugAgentTimerIntialize (
+ VOID
+ )
+{
+}
+
+
+/**
+ Set the period for the debug agent timer. Zero means disable the timer.
+
+ @param[in] TimerPeriodMilliseconds Frequency of the debug agent timer.
+
+**/
+VOID
+EFIAPI
+DebugAgentTimerSetPeriod (
+ IN UINT32 TimerPeriodMilliseconds
+ )
+{
+}
+
+
+/**
+ Perform End Of Interrupt for the debug agent timer. This is called in the
+ interrupt handler after the interrupt has been processed.
+
+**/
+VOID
+EFIAPI
+DebugAgentTimerEndOfInterrupt (
+ VOID
+ )
+{
+}
+
+
\ No newline at end of file diff --git a/EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLibNull.inf b/EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLibNull.inf new file mode 100755 index 0000000000..4df40a3703 --- /dev/null +++ b/EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLibNull.inf @@ -0,0 +1,37 @@ +#/** @file
+# Component description file for Base PCI Cf8 Library.
+#
+# PCI CF8 Library that uses I/O ports 0xCF8 and 0xCFC to perform PCI Configuration cycles.
+# Layers on top of an I/O Library instance.
+# Copyright (c) 2007, 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.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DebugAgentTimerLibNull
+ FILE_GUID = 02f04694-2c0a-4f1e-b0ce-64be25890b03
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DebugAgentTimerLib|SEC BASE DXE_CORE
+
+
+[Sources.common]
+ DebugAgentTimerLib.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+
+[LibraryClasses]
+ IoLib
+
diff --git a/EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.S b/EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.S new file mode 100755 index 0000000000..bb4d17c5ad --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.S @@ -0,0 +1,258 @@ +#------------------------------------------------------------------------------ +# +# Use ARMv6 instruction to operate on a single stack +# +# Copyright (c) 2008-2010 Apple Inc. All rights reserved. +# +# 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. +# +#------------------------------------------------------------------------------ + +/* + +This is the stack constructed by the exception handler (low address to high address) + # R0 - IFAR is EFI_SYSTEM_CONTEXT for ARM + Reg Offset + === ====== + R0 0x00 # stmfd SP!,{R0-R12} + R1 0x04 + R2 0x08 + R3 0x0c + R4 0x10 + R5 0x14 + R6 0x18 + R7 0x1c + R8 0x20 + R9 0x24 + R10 0x28 + R11 0x2c + R12 0x30 + SP 0x34 # reserved via adding 0x20 (32) to the SP + LR 0x38 + PC 0x3c + CPSR 0x40 + DFSR 0x44 + DFAR 0x48 + IFSR 0x4c + IFAR 0x50 + + LR 0x54 # SVC Link register (we need to restore it) + + LR 0x58 # pushed by srsfd + CPSR 0x5c + + */ + + +.globl ASM_PFX(ExceptionHandlersStart) +.globl ASM_PFX(ExceptionHandlersEnd) +.globl ASM_PFX(CommonExceptionEntry) +.globl ASM_PFX(AsmCommonExceptionEntry) +.globl ASM_PFX(GdbExceptionHandler) + +.text +.align 3 + + +// +// This code gets copied to the ARM vector table +// ExceptionHandlersStart - ExceptionHandlersEnd gets copied +// +ASM_PFX(ExceptionHandlersStart): + +ASM_PFX(Reset): + b ASM_PFX(Reset) + +ASM_PFX(UndefinedInstruction): + b ASM_PFX(UndefinedInstructionEntry) + +ASM_PFX(SoftwareInterrupt): + b ASM_PFX(SoftwareInterruptEntry) + +ASM_PFX(PrefetchAbort): + b ASM_PFX(PrefetchAbortEntry) + +ASM_PFX(DataAbort): + b ASM_PFX(DataAbortEntry) + +ASM_PFX(ReservedException): + b ASM_PFX(ReservedExceptionEntry) + +ASM_PFX(Irq): + b ASM_PFX(Irq) + +ASM_PFX(Fiq): + b ASM_PFX(FiqEntry) + + +ASM_PFX(UndefinedInstructionEntry): + sub LR, LR, #4 @ Only -2 for Thumb, adjust in CommonExceptionEntry + srsdb #0x13! @ Store return state on SVC stack + cps #0x13 @ Switch to SVC for common stack + stmfd SP!,{LR} @ Store the link register for the current mode + sub SP,SP,#0x20 @ Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} @ Store the register state + + mov R0,#1 @ ExceptionType + ldr R1,ASM_PFX(CommonExceptionEntry) + bx R1 + +ASM_PFX(SoftwareInterruptEntry): + sub LR, LR, #4 @ Only -2 for Thumb, adjust in CommonExceptionEntry + srsdb #0x13! @ Store return state on SVC stack + @ We are already in SVC mode + stmfd SP!,{LR} @ Store the link register for the current mode + sub SP,SP,#0x20 @ Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} @ Store the register state + + mov R0,#2 @ ExceptionType + ldr R1,ASM_PFX(CommonExceptionEntry) + bx R1 + +ASM_PFX(PrefetchAbortEntry): + sub LR,LR,#4 + srsdb #0x13! @ Store return state on SVC stack + cps #0x13 @ Switch to SVC for common stack + stmfd SP!,{LR} @ Store the link register for the current mode + sub SP,SP,#0x20 @ Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} @ Store the register state + + mov R0,#3 @ ExceptionType + ldr R1,ASM_PFX(CommonExceptionEntry) + bx R1 + +ASM_PFX(DataAbortEntry): + sub LR,LR,#8 + srsdb #0x13! @ Store return state on SVC stack + cps #0x13 @ Switch to SVC for common stack + stmfd SP!,{LR} @ Store the link register for the current mode + sub SP,SP,#0x20 @ Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} @ Store the register state + + mov R0,#4 + ldr R1,ASM_PFX(CommonExceptionEntry) + bx R1 + +ASM_PFX(ReservedExceptionEntry): + srsdb #0x13! @ Store return state on SVC stack + cps #0x13 @ Switch to SVC for common stack + stmfd SP!,{LR} @ Store the link register for the current mode + sub SP,SP,#0x20 @ Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} @ Store the register state + + mov R0,#5 + ldr R1,ASM_PFX(CommonExceptionEntry) + bx R1 + +ASM_PFX(FiqEntry): + sub LR,LR,#4 + srsdb #0x13! @ Store return state on SVC stack + cps #0x13 @ Switch to SVC for common stack + stmfd SP!,{LR} @ Store the link register for the current mode + sub SP,SP,#0x20 @ Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} @ Store the register state + @ Since we have already switch to SVC R8_fiq - R12_fiq + @ never get used or saved + mov R0,#7 @ ExceptionType + ldr R1,ASM_PFX(CommonExceptionEntry) + bx R1 + +// +// This gets patched by the C code that patches in the vector table +// +ASM_PFX(CommonExceptionEntry): + .byte 0x12 + .byte 0x34 + .byte 0x56 + .byte 0x78 + +ASM_PFX(ExceptionHandlersEnd): + +// +// This code runs from CpuDxe driver loaded address. It is patched into +// CommonExceptionEntry. +// +ASM_PFX(AsmCommonExceptionEntry): + mrc p15, 0, R1, c6, c0, 2 @ Read IFAR + str R1, [SP, #0x50] @ Store it in EFI_SYSTEM_CONTEXT_ARM.IFAR + + mrc p15, 0, R1, c5, c0, 1 @ Read IFSR + str R1, [SP, #0x4c] @ Store it in EFI_SYSTEM_CONTEXT_ARM.IFSR + + mrc p15, 0, R1, c6, c0, 0 @ Read DFAR + str R1, [SP, #0x48] @ Store it in EFI_SYSTEM_CONTEXT_ARM.DFAR + + mrc p15, 0, R1, c5, c0, 0 @ Read DFSR + str R1, [SP, #0x44] @ Store it in EFI_SYSTEM_CONTEXT_ARM.DFSR + + ldr R1, [SP, #0x5c] @ srsdb saved pre-exception CPSR on the stack + str R1, [SP, #0x40] @ Store it in EFI_SYSTEM_CONTEXT_ARM.CPSR + + add R2, SP, #0x38 @ Make R2 point to EFI_SYSTEM_CONTEXT_ARM.LR + and R3, R1, #0x1f @ Check CPSR to see if User or System Mode + cmp R3, #0x1f @ if ((CPSR == 0x10) || (CPSR == 0x1df)) + cmpne R3, #0x10 @ + stmeqed R2, {lr}^ @ save unbanked lr + @ else + stmneed R2, {lr} @ save SVC lr + + + ldr R5, [SP, #0x58] @ PC is the LR pushed by srsfd + @ Check to see if we have to adjust for Thumb entry + sub r4, r0, #1 @ if (ExceptionType == 1 || ExceptionType ==2)) { + cmp r4, #1 @ // UND & SVC have differnt LR adjust for Thumb + bhi NoAdjustNeeded + + tst r1, #0x20 @ if ((CPSR & T)) == T) { // Thumb Mode on entry + addne R5, R5, #2 @ PC += 2@ + str R5,[SP,#0x58] @ Update LR value pused by srsfd + +NoAdjustNeeded: + + str R5, [SP, #0x3c] @ Store it in EFI_SYSTEM_CONTEXT_ARM.PC + + sub R1, SP, #0x60 @ We pused 0x60 bytes on the stack + str R1, [SP, #0x34] @ Store it in EFI_SYSTEM_CONTEXT_ARM.SP + + @ R0 is ExceptionType + mov R1,SP @ R1 is SystemContext + +/* +VOID +EFIAPI +GdbExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, R0 + IN OUT EFI_SYSTEM_CONTEXT SystemContext R1 + ) + +*/ + blx ASM_PFX(GdbExceptionHandler) @ Call exception handler + + ldr R1,[SP,#0x3c] @ EFI_SYSTEM_CONTEXT_ARM.PC + str R1,[SP,#0x58] @ Store it back to srsfd stack slot so it can be restored + + ldr R1,[SP,#0x40] @ EFI_SYSTEM_CONTEXT_ARM.CPSR + str R1,[SP,#0x5c] @ Store it back to srsfd stack slot so it can be restored + + add R3, SP, #0x54 @ Make R3 point to SVC LR saved on entry + add R2, SP, #0x38 @ Make R2 point to EFI_SYSTEM_CONTEXT_ARM.LR + and R1, R1, #0x1f @ Check to see if User or System Mode + cmp R1, #0x1f @ if ((CPSR == 0x10) || (CPSR == 0x1f)) + cmpne R1, #0x10 @ + ldmeqed R2, {lr}^ @ restore unbanked lr + @ else + ldmneed R3, {lr} @ restore SVC lr, via ldmfd SP!, {LR} + + ldmfd SP!,{R0-R12} @ Restore general purpose registers + @ Exception handler can not change SP + + add SP,SP,#0x20 @ Clear out the remaining stack space + ldmfd SP!,{LR} @ restore the link register for this context + rfefd SP! @ return from exception via srsfd stack slot + diff --git a/EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.asm b/EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.asm new file mode 100755 index 0000000000..3dccf5b077 --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.asm @@ -0,0 +1,259 @@ +//------------------------------------------------------------------------------ +// +// Use ARMv6 instruction to operate on a single stack +// +// Copyright (c) 2008-2010 Apple Inc. All rights reserved. +// +// 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. +// +//------------------------------------------------------------------------------ + + + +/* + +This is the stack constructed by the exception handler (low address to high address) + # R0 - IFAR is EFI_SYSTEM_CONTEXT for ARM + Reg Offset + === ====== + R0 0x00 # stmfd SP!,{R0-R12} + R1 0x04 + R2 0x08 + R3 0x0c + R4 0x10 + R5 0x14 + R6 0x18 + R7 0x1c + R8 0x20 + R9 0x24 + R10 0x28 + R11 0x2c + R12 0x30 + SP 0x34 # reserved via adding 0x20 (32) to the SP + LR 0x38 + PC 0x3c + CPSR 0x40 + DFSR 0x44 + DFAR 0x48 + IFSR 0x4c + IFAR 0x50 + + LR 0x54 # SVC Link register (we need to restore it) + + LR 0x58 # pushed by srsfd + CPSR 0x5c + + */ + + + EXPORT ExceptionHandlersStart + EXPORT ExceptionHandlersEnd + EXPORT CommonExceptionEntry + EXPORT AsmCommonExceptionEntry + IMPORT GdbExceptionHandler + + PRESERVE8 + AREA DxeExceptionHandlers, CODE, READONLY + +// +// This code gets copied to the ARM vector table +// ExceptionHandlersStart - ExceptionHandlersEnd gets copied +// +ExceptionHandlersStart + +Reset + b Reset + +UndefinedInstruction + b UndefinedInstructionEntry + +SoftwareInterrupt + b SoftwareInterruptEntry + +PrefetchAbort + b PrefetchAbortEntry + +DataAbort + b DataAbortEntry + +ReservedException + b ReservedExceptionEntry + +Irq + b Irq + +Fiq + b FiqEntry + + +UndefinedInstructionEntry + sub LR, LR, #4 ; Only -2 for Thumb, adjust in CommonExceptionEntry + srsfd #0x13! ; Store return state on SVC stack + cps #0x13 ; Switch to SVC for common stack + stmfd SP!,{LR} ; Store the link register for the current mode + sub SP,SP,#0x20 ; Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} ; Store the register state + + mov R0,#1 ; ExceptionType + ldr R1,CommonExceptionEntry; + bx R1 + +SoftwareInterruptEntry + sub LR, LR, #4 ; Only -2 for Thumb, adjust in CommonExceptionEntry + srsfd #0x13! ; Store return state on SVC stack + ; We are already in SVC mode + stmfd SP!,{LR} ; Store the link register for the current mode + sub SP,SP,#0x20 ; Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} ; Store the register state + + mov R0,#2 ; ExceptionType + ldr R1,CommonExceptionEntry + bx R1 + +PrefetchAbortEntry + sub LR,LR,#4 + srsfd #0x13! ; Store return state on SVC stack + cps #0x13 ; Switch to SVC for common stack + stmfd SP!,{LR} ; Store the link register for the current mode + sub SP,SP,#0x20 ; Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} ; Store the register state + + mov R0,#3 ; ExceptionType + ldr R1,CommonExceptionEntry + bx R1 + +DataAbortEntry + sub LR,LR,#8 + srsfd #0x13! ; Store return state on SVC stack + cps #0x13 ; Switch to SVC for common stack + stmfd SP!,{LR} ; Store the link register for the current mode + sub SP,SP,#0x20 ; Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} ; Store the register state + + mov R0,#4 ; ExceptionType + ldr R1,CommonExceptionEntry + bx R1 + +ReservedExceptionEntry + srsfd #0x13! ; Store return state on SVC stack + cps #0x13 ; Switch to SVC for common stack + stmfd SP!,{LR} ; Store the link register for the current mode + sub SP,SP,#0x20 ; Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} ; Store the register state + + mov R0,#5 ; ExceptionType + ldr R1,CommonExceptionEntry + bx R1 + +FiqEntry + sub LR,LR,#4 + srsfd #0x13! ; Store return state on SVC stack + cps #0x13 ; Switch to SVC for common stack + stmfd SP!,{LR} ; Store the link register for the current mode + sub SP,SP,#0x20 ; Save space for SP, LR, PC, IFAR - CPSR + stmfd SP!,{R0-R12} ; Store the register state + ; Since we have already switch to SVC R8_fiq - R12_fiq + ; never get used or saved + mov R0,#7 ; ExceptionType + ldr R1,CommonExceptionEntry + bx R1 + +// +// This gets patched by the C code that patches in the vector table +// +CommonExceptionEntry + dcd 0x12345678 + +ExceptionHandlersEnd + +// +// This code runs from CpuDxe driver loaded address. It is patched into +// CommonExceptionEntry. +// +AsmCommonExceptionEntry + mrc p15, 0, R1, c6, c0, 2 ; Read IFAR + str R1, [SP, #0x50] ; Store it in EFI_SYSTEM_CONTEXT_ARM.IFAR + + mrc p15, 0, R1, c5, c0, 1 ; Read IFSR + str R1, [SP, #0x4c] ; Store it in EFI_SYSTEM_CONTEXT_ARM.IFSR + + mrc p15, 0, R1, c6, c0, 0 ; Read DFAR + str R1, [SP, #0x48] ; Store it in EFI_SYSTEM_CONTEXT_ARM.DFAR + + mrc p15, 0, R1, c5, c0, 0 ; Read DFSR + str R1, [SP, #0x44] ; Store it in EFI_SYSTEM_CONTEXT_ARM.DFSR + + ldr R1, [SP, #0x5c] ; srsfd saved pre-exception CPSR on the stack + str R1, [SP, #0x40] ; Store it in EFI_SYSTEM_CONTEXT_ARM.CPSR + + add R2, SP, #0x38 ; Make R2 point to EFI_SYSTEM_CONTEXT_ARM.LR + and R3, R1, #0x1f ; Check CPSR to see if User or System Mode + cmp R3, #0x1f ; if ((CPSR == 0x10) || (CPSR == 0x1df)) + cmpne R3, #0x10 ; + stmeqed R2, {lr}^ ; save unbanked lr + ; else + stmneed R2, {lr} ; save SVC lr + + + ldr R5, [SP, #0x58] ; PC is the LR pushed by srsfd + ; Check to see if we have to adjust for Thumb entry + sub r4, r0, #1 ; if (ExceptionType == 1 || ExceptionType ==2)) { + cmp r4, #1 ; // UND & SVC have differnt LR adjust for Thumb + bhi NoAdjustNeeded + + tst r1, #0x20 ; if ((CPSR & T)) == T) { // Thumb Mode on entry + addne R5, R5, #2 ; PC += 2; + str R5,[SP,#0x58] ; Update LR value pused by srsfd + +NoAdjustNeeded + + str R5, [SP, #0x3c] ; Store it in EFI_SYSTEM_CONTEXT_ARM.PC + + sub R1, SP, #0x60 ; We pused 0x60 bytes on the stack + str R1, [SP, #0x34] ; Store it in EFI_SYSTEM_CONTEXT_ARM.SP + + ; R0 is ExceptionType + mov R1,SP ; R1 is SystemContext + +/* +VOID +EFIAPI +GdbExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, R0 + IN OUT EFI_SYSTEM_CONTEXT SystemContext R1 + ) + +*/ + blx GdbExceptionHandler ; Call exception handler + + ldr R1,[SP,#0x3c] ; EFI_SYSTEM_CONTEXT_ARM.PC + str R1,[SP,#0x58] ; Store it back to srsfd stack slot so it can be restored + + ldr R1,[SP,#0x40] ; EFI_SYSTEM_CONTEXT_ARM.CPSR + str R1,[SP,#0x5c] ; Store it back to srsfd stack slot so it can be restored + + add R3, SP, #0x54 ; Make R3 point to SVC LR saved on entry + add R2, SP, #0x38 ; Make R2 point to EFI_SYSTEM_CONTEXT_ARM.LR + and R1, R1, #0x1f ; Check to see if User or System Mode + cmp R1, #0x1f ; if ((CPSR == 0x10) || (CPSR == 0x1f)) + cmpne R1, #0x10 ; + ldmeqed R2, {lr}^ ; restore unbanked lr + ; else + ldmneed R3, {lr} ; restore SVC lr, via ldmfd SP!, {LR} + + ldmfd SP!,{R0-R12} ; Restore general purpose registers + ; Exception handler can not change SP + + add SP,SP,#0x20 ; Clear out the remaining stack space + ldmfd SP!,{LR} ; restore the link register for this context + rfefd SP! ; return from exception via srsfd stack slot + + END + + diff --git a/EmbeddedPkg/Library/GdbDebugAgent/Arm/Processor.c b/EmbeddedPkg/Library/GdbDebugAgent/Arm/Processor.c new file mode 100755 index 0000000000..e09e18d6c1 --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/Arm/Processor.c @@ -0,0 +1,630 @@ +/** @file
+ Processor specific parts of the GDB stub
+
+ Copyright (c) 2008-2010, Apple Inc. All rights reserved.
+
+ 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 <GdbDebugAgent.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/PrintLib.h>
+#include <Library/ArmLib.h>
+
+//
+// Externs from the exception handler assembly file
+//
+VOID
+ExceptionHandlersStart (
+ VOID
+ );
+
+VOID
+ExceptionHandlersEnd (
+ VOID
+ );
+
+VOID
+CommonExceptionEntry (
+ VOID
+ );
+
+VOID
+AsmCommonExceptionEntry (
+ VOID
+ );
+
+
+//
+// Array of exception types that need to be hooked by the debugger
+// (efi, gdb) //efi number
+//
+EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {
+ { EXCEPT_ARM_SOFTWARE_INTERRUPT, GDB_SIGTRAP },
+ { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },
+ { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },
+ { EXCEPT_ARM_DATA_ABORT, GDB_SIGTRAP }, // GDB_SIGEMT
+ { EXCEPT_ARM_RESERVED, GDB_SIGTRAP }, // GDB_SIGILL
+ { EXCEPT_ARM_FIQ, GDB_SIGINT } // Used for ctrl-c
+};
+
+// Shut up some annoying RVCT warnings
+#ifdef __CC_ARM
+#pragma diag_suppress 1296
+#endif
+
+UINTN gRegisterOffsets[] = {
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R0),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R1),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R2),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R3),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R4),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R5),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R6),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R7),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R8),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R9),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R10),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R11),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R12),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, SP),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, LR),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, PC),
+ 0x00000F01, // f0
+ 0x00000F02,
+ 0x00000F03,
+ 0x00000F11, // f1
+ 0x00000F12,
+ 0x00000F13,
+ 0x00000F21, // f2
+ 0x00000F22,
+ 0x00000F23,
+ 0x00000F31, // f3
+ 0x00000F32,
+ 0x00000F33,
+ 0x00000F41, // f4
+ 0x00000F42,
+ 0x00000F43,
+ 0x00000F51, // f5
+ 0x00000F52,
+ 0x00000F53,
+ 0x00000F61, // f6
+ 0x00000F62,
+ 0x00000F63,
+ 0x00000F71, // f7
+ 0x00000F72,
+ 0x00000F73,
+ 0x00000FFF, // fps
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)
+};
+
+// restore warnings for RVCT
+#ifdef __CC_ARM
+#pragma diag_default 1296
+#endif
+
+
+/**
+ Return the number of entries in the gExceptionType[]
+
+ @retval UINTN, the number of entries in the gExceptionType[] array.
+ **/
+UINTN
+MaxEfiException (
+ VOID
+ )
+{
+ return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY);
+}
+
+
+
+
+/**
+ Check to see if the ISA is supported.
+ ISA = Instruction Set Architecture
+
+ @retval TRUE if Isa is supported
+
+**/
+BOOLEAN
+CheckIsa (
+ IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
+ )
+{
+ if (Isa == IsaArm) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+/**
+ This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
+ It is, by default, set to find the register pointer of the ARM member
+ @param SystemContext Register content at time of the exception
+ @param RegNumber The register to which we want to find a pointer
+ @retval the pointer to the RegNumber-th pointer
+ **/
+UINTN *
+FindPointerToRegister(
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber
+ )
+{
+ UINT8 *TempPtr;
+ ASSERT(gRegisterOffsets[RegNumber] < 0xF00);
+ TempPtr = ((UINT8 *)SystemContext.SystemContextArm) + gRegisterOffsets[RegNumber];
+ return (UINT32 *)TempPtr;
+}
+
+
+/**
+ Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
+ @param SystemContext Register content at time of the exception
+ @param RegNumber the number of the register that we want to read
+ @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
+ @retval the pointer to the next character of the output buffer that is available to be written on.
+ **/
+CHAR8 *
+BasicReadRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber,
+ IN CHAR8 *OutBufPtr
+ )
+{
+ UINTN RegSize;
+ CHAR8 Char;
+
+ if (gRegisterOffsets[RegNumber] > 0xF00) {
+ AsciiSPrint(OutBufPtr, 9, "00000000");
+ OutBufPtr += 8;
+ return OutBufPtr;
+ }
+
+ RegSize = 0;
+ while (RegSize < 32) {
+ Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)];
+ if ((Char >= 'A') && (Char <= 'F')) {
+ Char = Char - 'A' + 'a';
+ }
+ *OutBufPtr++ = Char;
+
+ Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)];
+ if ((Char >= 'A') && (Char <= 'F')) {
+ Char = Char - 'A' + 'a';
+ }
+ *OutBufPtr++ = Char;
+
+ RegSize = RegSize + 8;
+ }
+ return OutBufPtr;
+}
+
+
+/** ‘p n’
+ Reads the n-th register's value into an output buffer and sends it as a packet
+ @param SystemContext Register content at time of the exception
+ @param InBuffer Pointer to the input buffer received from gdb server
+ **/
+VOID
+ReadNthRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ )
+{
+ UINTN RegNumber;
+ CHAR8 OutBuffer[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
+ CHAR8 *OutBufPtr; // pointer to the output buffer
+
+ RegNumber = AsciiStrHexToUintn (&InBuffer[1]);
+
+ if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {
+ SendError (GDB_EINVALIDREGNUM);
+ return;
+ }
+
+ OutBufPtr = OutBuffer;
+ OutBufPtr = BasicReadRegister (SystemContext, RegNumber, OutBufPtr);
+
+ *OutBufPtr = '\0'; // the end of the buffer
+ SendPacket(OutBuffer);
+}
+
+
+/** ‘g’
+ Reads the general registers into an output buffer and sends it as a packet
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+EFIAPI
+ReadGeneralRegisters (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ UINTN Index;
+ // a UINT32 takes 8 ascii characters
+ CHAR8 OutBuffer[(sizeof (gRegisterOffsets) * 2) + 1];
+ CHAR8 *OutBufPtr;
+
+ // It is not safe to allocate pool here....
+ OutBufPtr = OutBuffer;
+ for (Index = 0; Index < (sizeof (gRegisterOffsets)/sizeof (UINTN)); Index++) {
+ OutBufPtr = BasicReadRegister (SystemContext, Index, OutBufPtr);
+ }
+
+ *OutBufPtr = '\0';
+ SendPacket(OutBuffer);
+}
+
+
+/**
+ Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
+ @param SystemContext Register content at time of the exception
+ @param RegNumber the number of the register that we want to write
+ @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
+ @retval the pointer to the next character of the input buffer that can be used
+ **/
+CHAR8 *
+BasicWriteRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber,
+ IN CHAR8 *InBufPtr
+ )
+{
+ UINTN RegSize;
+ UINTN TempValue; // the value transferred from a hex char
+ UINT32 NewValue; // the new value of the RegNumber-th Register
+
+ if (gRegisterOffsets[RegNumber] > 0xF00) {
+ return InBufPtr + 8;
+ }
+
+ NewValue = 0;
+ RegSize = 0;
+ while (RegSize < 32) {
+ TempValue = HexCharToInt(*InBufPtr++);
+
+ if ((INTN)TempValue < 0) {
+ SendError (GDB_EBADMEMDATA);
+ return NULL;
+ }
+
+ NewValue += (TempValue << (RegSize+4));
+ TempValue = HexCharToInt(*InBufPtr++);
+
+ if ((INTN)TempValue < 0) {
+ SendError (GDB_EBADMEMDATA);
+ return NULL;
+ }
+
+ NewValue += (TempValue << RegSize);
+ RegSize = RegSize + 8;
+ }
+ *(FindPointerToRegister(SystemContext, RegNumber)) = NewValue;
+ return InBufPtr;
+}
+
+
+/** ‘P n...=r...’
+ Writes the new value of n-th register received into the input buffer to the n-th register
+ @param SystemContext Register content at time of the exception
+ @param InBuffer Ponter to the input buffer received from gdb server
+ **/
+VOID
+WriteNthRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ )
+{
+ UINTN RegNumber;
+ CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array
+ CHAR8 *RegNumBufPtr;
+ CHAR8 *InBufPtr; // pointer to the input buffer
+
+ // find the register number to write
+ InBufPtr = &InBuffer[1];
+ RegNumBufPtr = RegNumBuffer;
+ while (*InBufPtr != '=') {
+ *RegNumBufPtr++ = *InBufPtr++;
+ }
+ *RegNumBufPtr = '\0';
+ RegNumber = AsciiStrHexToUintn (RegNumBuffer);
+
+ // check if this is a valid Register Number
+ if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {
+ SendError (GDB_EINVALIDREGNUM);
+ return;
+ }
+ InBufPtr++; // skips the '=' character
+ BasicWriteRegister (SystemContext, RegNumber, InBufPtr);
+ SendSuccess();
+}
+
+
+/** ‘G XX...’
+ Writes the new values received into the input buffer to the general registers
+ @param SystemContext Register content at time of the exception
+ @param InBuffer Pointer to the input buffer received from gdb server
+ **/
+
+VOID
+EFIAPI
+WriteGeneralRegisters (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ )
+{
+ UINTN i;
+ CHAR8 *InBufPtr; /// pointer to the input buffer
+ UINTN MinLength;
+ UINTN RegisterCount = (sizeof (gRegisterOffsets)/sizeof (UINTN));
+
+ MinLength = (RegisterCount * 8) + 1; // 'G' plus the registers in ASCII format
+
+ if (AsciiStrLen(InBuffer) < MinLength) {
+ //Bad message. Message is not the right length
+ SendError (GDB_EBADBUFSIZE);
+ return;
+ }
+
+ InBufPtr = &InBuffer[1];
+
+ // Read the new values for the registers from the input buffer to an array, NewValueArray.
+ // The values in the array are in the gdb ordering
+ for(i = 0; i < RegisterCount; i++) {
+ InBufPtr = BasicWriteRegister (SystemContext, i, InBufPtr);
+ }
+
+ SendSuccess ();
+}
+
+
+
+
+/** ‘c [addr ]’
+ Continue. addr is Address to resume. If addr is omitted, resume at current
+ Address.
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+EFIAPI
+ContinueAtAddress (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ if (PacketData[1] != '\0') {
+ SystemContext.SystemContextArm->PC = AsciiStrHexToUintn(&PacketData[1]);
+ }
+}
+
+
+/** ‘s [addr ]’
+ Single step. addr is the Address at which to resume. If addr is omitted, resume
+ at same Address.
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+EFIAPI
+SingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ SendNotSupported();
+}
+
+
+VOID
+EFIAPI
+InsertBreakPoint (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ SendNotSupported ();
+}
+
+VOID
+EFIAPI
+RemoveBreakPoint (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ SendNotSupported ();
+}
+
+
+/** + Send the T signal with the given exception type (in gdb order) and possibly + with n:r pairs related to the watchpoints + + @param SystemContext Register content at time of the exception + @param GdbExceptionType GDB exception type + **/ +VOID +ProcessorSendTSignal ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINT8 GdbExceptionType, + IN OUT CHAR8 *TSignalPtr, + IN UINTN SizeOfBuffer + ) +{ + *TSignalPtr = '\0'; +} +
+/**
+ Check to see if this exception is related to ctrl-c handling.
+
+ In this scheme we dedicate FIQ to the ctrl-c handler so it is
+ independent of the rest of the system.
+
+ SaveAndSetDebugTimerInterrupt () can be used to
+
+ @param ExceptionType Exception that is being processed
+ @param SystemContext Register content at time of the exception
+
+ @return TRUE This was a ctrl-c check that did not find a ctrl-c
+ @return FALSE This was not a ctrl-c check or some one hit ctrl-c
+ **/
+BOOLEAN
+ProcessorControlC (
+ IN EFI_EXCEPTION_TYPE ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ BOOLEAN Return = TRUE;
+
+ if (ExceptionType != EXCEPT_ARM_FIQ) {
+ // Skip it as it is not related to ctrl-c
+ return FALSE;
+ }
+
+ while (TRUE) { + if (!GdbIsCharAvailable ()) { + // + // No characters are pending so exit the loop + // + Return = TRUE; + break; + } + + if (GdbGetChar () == 0x03) { + // + // We have a ctrl-c so exit and process exception for ctrl-c + // + Return = FALSE; + break; + } + } +
+ DebugAgentTimerEndOfInterrupt ();
+
+ // Force an exit from the exception handler as we are done
+ return Return;
+}
+
+
+/**
+ Enable/Disable the interrupt of debug timer and return the interrupt state
+ prior to the operation.
+
+ If EnableStatus is TRUE, enable the interrupt of debug timer.
+ If EnableStatus is FALSE, disable the interrupt of debug timer.
+
+ @param[in] EnableStatus Enable/Disable.
+
+ @return FALSE always.
+
+**/
+BOOLEAN
+EFIAPI
+SaveAndSetDebugTimerInterrupt (
+ IN BOOLEAN EnableStatus
+ )
+{
+ BOOLEAN FiqEnabled;
+
+ FiqEnabled = ArmGetFiqState ();
+
+ if (EnableStatus) {
+ DebugAgentTimerSetPeriod (100);
+ ArmEnableFiq ();
+ } else {
+ DebugAgentTimerSetPeriod (0);
+ ArmDisableFiq ();
+ }
+
+ return FiqEnabled;
+}
+
+VOID
+GdbFPutString (
+ IN CHAR8 *String
+ );
+
+/**
+ Initialize debug agent.
+
+ This function is used to set up debug enviroment. It may enable interrupts.
+
+ @param[in] InitFlag Init flag is used to decide initialize process.
+ @param[in] Context Context needed according to InitFlag, it was optional.
+
+**/
+VOID
+EFIAPI
+InitializeDebugAgent (
+ IN UINT32 InitFlag,
+ IN VOID *Context OPTIONAL
+ )
+{ + UINTN Offset;
+ UINTN Length;
+ BOOLEAN IrqEnabled;
+ BOOLEAN FiqEnabled;
+ UINT32 *VectorBase;
+
+ + //
+ // Disable interrupts
+ //
+ IrqEnabled = ArmGetInterruptState ();
+ ArmDisableInterrupts ();
+
+ //
+ // EFI does not use the FIQ, but a debugger might so we must disable
+ // as we take over the exception vectors.
+ //
+ FiqEnabled = ArmGetFiqState ();
+ ArmDisableFiq ();
+
+ //
+ // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.
+ //
+ Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
+
+ //
+ // Reserve space for the exception handlers
+ //
+ VectorBase = (UINT32 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress);
+
+
+ // Copy our assembly code into the page that contains the exception vectors.
+ CopyMem ((VOID *)VectorBase, (VOID *)ExceptionHandlersStart, Length);
+
+ //
+ // Patch in the common Assembly exception handler
+ //
+ Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;
+ *(UINTN *) (((UINT8 *)VectorBase) + Offset) = (UINTN)AsmCommonExceptionEntry;
+
+ // Flush Caches since we updated executable stuff
+ InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);
+
+ DebugAgentTimerIntialize ();
+
+ if (FiqEnabled) {
+ ArmEnableFiq ();
+ }
+
+ if (IrqEnabled) {
+ ArmEnableInterrupts ();
+ }
+
+ return;
+}
+
diff --git a/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.c b/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.c new file mode 100755 index 0000000000..d7f6dd9577 --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.c @@ -0,0 +1,816 @@ +/** @file
+ Debug Agent library implementition with empty functions.
+
+ Copyright (c) 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 "GdbDebugAgent.h"
+
+
+UINTN gMaxProcessorIndex = 0;
+
+//
+// Buffers for basic gdb communication
+//
+CHAR8 gInBuffer[MAX_BUF_SIZE];
+CHAR8 gOutBuffer[MAX_BUF_SIZE];
+
+
+//
+// Globals for returning XML from qXfer:libraries:read packet
+//
+UINTN gPacketqXferLibraryOffset = 0;
+UINTN gEfiDebugImageTableEntry = 0;
+CHAR8 gXferLibraryBuffer[2000];
+
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mHexToStr[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+
+// add-symbol-file c:/work/edk2/Build/BeagleBoard/DEBUG_ARMGCC/ARM/BeagleBoardPkg/Sec/Sec/DEBUG/BeagleBoardSec.dll 0x80008360
+CHAR8 *qXferHack = "<library name=\"c:/work/edk2/Build/BeagleBoard/DEBUG_ARMGCC/ARM/BeagleBoardPkg/Sec/Sec/DEBUG/BeagleBoardSec.dll\"><segment address=\"0x80008360\"/></library>";
+
+UINTN
+gXferObjectReadResponse (
+ IN CHAR8 Type,
+ IN CHAR8 *Str
+ )
+{
+ CHAR8 *OutBufPtr; // pointer to the output buffer
+ CHAR8 Char;
+ UINTN Count;
+
+ // responce starts with 'm' or 'l' if it is the end
+ OutBufPtr = gOutBuffer;
+ *OutBufPtr++ = Type;
+ Count = 1;
+
+ // Binary data encoding
+ OutBufPtr = gOutBuffer;
+ while (*Str != '\0') {
+ Char = *Str++;
+ if ((Char == 0x7d) || (Char == 0x23) || (Char == 0x24) || (Char == 0x2a)) {
+ // escape character
+ *OutBufPtr++ = 0x7d;
+
+ Char ^= 0x20;
+ }
+ *OutBufPtr++ = Char;
+ Count++;
+ }
+
+ *OutBufPtr = '\0' ; // the end of the buffer
+ SendPacket (gOutBuffer);
+
+ return Count;
+}
+
+/**
+ Process "qXfer:object:read:annex:offset,length" request.
+
+ Returns an XML document that contains loaded libraries. In our case it is
+ infomration in the EFI Debug Inmage Table converted into an XML document.
+
+ GDB will call with an arbitrary length (it can't know the real length and
+ will reply with chunks of XML that are easy for us to deal with. Gdb will
+ keep calling until we say we are done. XML doc looks like:
+
+ <library-list>
+ <library name="/a/a/c/d.dSYM"><segment address="0x10000000"/></library>
+ <library name="/a/m/e/e.pdb"><segment address="0x20000000"/></library>
+ <library name="/a/l/f/f.dll"><segment address="0x30000000"/></library>
+ </library-list>
+
+ Since we can not allocate memory in interupt context this module has
+ assumptions about how it will get called:
+ 1) Length will generally be max remote packet size (big enough)
+ 2) First Offset of an XML document read needs to be 0
+ 3) This code will return back small chunks of the XML document on every read.
+ Each subseqent call will ask for the next availble part of the document.
+
+ Note: The only variable size element in the XML is:
+ " <library name=\"%s\"><segment address=\"%p\"/></library>\n" and it is
+ based on the file path and name of the symbol file. If the symbol file name
+ is bigger than the max gdb remote packet size we could update this code
+ to respond back in chunks.
+
+ @param Offset offset into special data area
+ @param Length number of bytes to read starting at Offset
+
+ **/
+VOID
+QxferLibrary (
+ IN UINTN Offset,
+ IN UINTN Length
+ )
+{
+ gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', "<library-list>\n");
+ gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', qXferHack);
+ gXferObjectReadResponse ('l', "</library-list>\n");
+ gPacketqXferLibraryOffset = 0;
+}
+
+/**
+ Transfer length bytes of input buffer, starting at Address, to memory.
+
+ @param length the number of the bytes to be transferred/written
+ @param *address the start address of the transferring/writing the memory
+ @param *new_data the new data to be written to memory
+ **/
+
+VOID
+TransferFromInBufToMem (
+ IN UINTN Length,
+ IN unsigned char *Address,
+ IN CHAR8 *NewData
+ )
+{
+ CHAR8 c1;
+ CHAR8 c2;
+
+ while (Length-- > 0) {
+ c1 = (CHAR8)HexCharToInt (*NewData++);
+ c2 = (CHAR8)HexCharToInt (*NewData++);
+
+ if ((c1 < 0) || (c2 < 0)) {
+ SendError (GDB_EBADMEMDATA);
+ return;
+ }
+ *Address++ = (UINT8)((c1 << 4) + c2);
+ }
+
+ SendSuccess();
+}
+
+
+/**
+ Transfer Length bytes of memory starting at Address to an output buffer, OutBuffer. This function will finally send the buffer
+ as a packet.
+
+ @param Length the number of the bytes to be transferred/read
+ @param *address pointer to the start address of the transferring/reading the memory
+ **/
+
+VOID
+TransferFromMemToOutBufAndSend (
+ IN UINTN Length,
+ IN unsigned char *Address
+ )
+{
+ // there are Length bytes and every byte is represented as 2 hex chars
+ CHAR8 OutBuffer[MAX_BUF_SIZE];
+ CHAR8 *OutBufPtr; // pointer to the output buffer
+ CHAR8 Char;
+
+ OutBufPtr = OutBuffer;
+ while (Length > 0) {
+
+ Char = mHexToStr[*Address >> 4];
+ if ((Char >= 'A') && (Char <= 'F')) {
+ Char = Char - 'A' + 'a';
+ }
+ *OutBufPtr++ = Char;
+
+ Char = mHexToStr[*Address & 0x0f];
+ if ((Char >= 'A') && (Char <= 'F')) {
+ Char = Char - 'A' + 'a';
+ }
+ *OutBufPtr++ = Char;
+
+ Address++;
+ Length--;
+ }
+
+ *OutBufPtr = '\0' ; // the end of the buffer
+ SendPacket (OutBuffer);
+}
+
+
+
+/**
+ Send a GDB Remote Serial Protocol Packet
+
+ $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$',
+ the packet teminating character '#' and the two digit checksum.
+
+ If an ack '+' is not sent resend the packet, but timeout eventually so we don't end up
+ in an infinit loop. This is so if you unplug the debugger code just keeps running
+
+ @param PacketData Payload data for the packet
+
+
+ @retval Number of bytes of packet data sent.
+
+**/
+UINTN
+SendPacket (
+ IN CHAR8 *PacketData
+ )
+{
+ UINT8 CheckSum;
+ UINTN Timeout;
+ CHAR8 *Ptr;
+ CHAR8 TestChar;
+ UINTN Count;
+
+ Timeout = PcdGet32 (PcdGdbMaxPacketRetryCount);
+
+ Count = 0;
+ do {
+
+ Ptr = PacketData;
+
+ if (Timeout-- == 0) {
+ // Only try a finite number of times so we don't get stuck in the loop
+ return Count;
+ }
+
+ // Packet prefix
+ GdbPutChar ('$');
+
+ for (CheckSum = 0, Count =0 ; *Ptr != '\0'; Ptr++, Count++) {
+ GdbPutChar (*Ptr);
+ CheckSum = CheckSum + *Ptr;
+ }
+
+ // Packet terminating character and checksum
+ GdbPutChar ('#');
+ GdbPutChar (mHexToStr[CheckSum >> 4]);
+ GdbPutChar (mHexToStr[CheckSum & 0x0F]);
+
+ TestChar = GdbGetChar ();
+ } while (TestChar != '+');
+
+ return Count;
+}
+
+/**
+ Receive a GDB Remote Serial Protocol Packet
+
+ $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$',
+ the packet teminating character '#' and the two digit checksum.
+
+ If host re-starts sending a packet without ending the previous packet, only the last valid packet is proccessed.
+ (In other words, if received packet is '$12345$12345$123456#checksum', only '$123456#checksum' will be processed.)
+
+ If an ack '+' is not sent resend the packet
+
+ @param PacketData Payload data for the packet
+
+ @retval Number of bytes of packet data received.
+
+**/
+UINTN
+ReceivePacket (
+ OUT CHAR8 *PacketData,
+ IN UINTN PacketDataSize
+ )
+{
+ UINT8 CheckSum;
+ UINTN Index;
+ CHAR8 Char;
+ CHAR8 SumString[3];
+ CHAR8 TestChar;
+
+ ZeroMem (PacketData, PacketDataSize);
+
+ for (;;) {
+ // wait for the start of a packet
+ TestChar = GdbGetChar ();
+ while (TestChar != '$') {
+ TestChar = GdbGetChar ();
+ };
+
+ retry:
+ for (Index = 0, CheckSum = 0; Index < (PacketDataSize - 1); Index++) {
+ Char = GdbGetChar ();
+ if (Char == '$') {
+ goto retry;
+ }
+ if (Char == '#') {
+ break;
+ }
+
+ PacketData[Index] = Char;
+ CheckSum = CheckSum + Char;
+ }
+ PacketData[Index] = '\0';
+
+ if (Index == PacketDataSize) {
+ continue;
+ }
+
+ SumString[0] = GdbGetChar ();
+ SumString[1] = GdbGetChar ();
+ SumString[2] = '\0';
+
+ if (AsciiStrHexToUintn (SumString) == CheckSum) {
+ // Ack: Success
+ GdbPutChar ('+');
+
+ // Null terminate the callers string
+ PacketData[Index] = '\0';
+ return Index;
+ } else {
+ // Ack: Failure
+ GdbPutChar ('-');
+ }
+ }
+
+ //return 0;
+}
+
+
+/**
+ Empties the given buffer
+ @param Buf pointer to the first element in buffer to be emptied
+ **/
+VOID
+EmptyBuffer (
+ IN CHAR8 *Buf
+ )
+{
+ *Buf = '\0';
+}
+
+
+/**
+ Converts an 8-bit Hex Char into a INTN.
+
+ @param Char the hex character to be converted into UINTN
+ @retval a INTN, from 0 to 15, that corressponds to Char
+ -1 if Char is not a hex character
+ **/
+INTN
+HexCharToInt (
+ IN CHAR8 Char
+ )
+{
+ if ((Char >= 'A') && (Char <= 'F')) {
+ return Char - 'A' + 10;
+ } else if ((Char >= 'a') && (Char <= 'f')) {
+ return Char - 'a' + 10;
+ } else if ((Char >= '0') && (Char <= '9')) {
+ return Char - '0';
+ } else { // if not a hex value, return a negative value
+ return -1;
+ }
+}
+
+ // 'E' + the biggest error number is 255, so its 2 hex digits + buffer end
+CHAR8 *gError = "E__";
+
+/** 'E NN'
+ Send an error with the given error number after converting to hex.
+ The error number is put into the buffer in hex. '255' is the biggest errno we can send.
+ ex: 162 will be sent as A2.
+
+ @param errno the error number that will be sent
+ **/
+VOID
+EFIAPI
+SendError (
+ IN UINT8 ErrorNum
+ )
+{
+ //
+ // Replace _, or old data, with current errno
+ //
+ gError[1] = mHexToStr [ErrorNum >> 4];
+ gError[2] = mHexToStr [ErrorNum & 0x0f];
+
+ SendPacket (gError); // send buffer
+}
+
+
+
+/**
+ Send 'OK' when the function is done executing successfully.
+ **/
+VOID
+EFIAPI
+SendSuccess (
+ VOID
+ )
+{
+ SendPacket ("OK"); // send buffer
+}
+
+
+/**
+ Send empty packet to specify that particular command/functionality is not supported.
+ **/
+VOID
+EFIAPI
+SendNotSupported (
+ VOID
+ )
+{
+ SendPacket ("");
+}
+
+
+
+
+
+/**
+ Translates the EFI mapping to GDB mapping
+
+ @param EFIExceptionType EFI Exception that is being processed
+ @retval UINTN that corresponds to EFIExceptionType's GDB exception type number
+ **/
+UINT8
+ConvertEFItoGDBtype (
+ IN EFI_EXCEPTION_TYPE EFIExceptionType
+ )
+{
+ UINTN i;
+
+ for (i=0; i < MaxEfiException() ; i++) {
+ if (gExceptionType[i].Exception == EFIExceptionType) {
+ return gExceptionType[i].SignalNo;
+ }
+ }
+ return GDB_SIGTRAP; // this is a GDB trap
+}
+
+
+/** "m addr,length"
+ Find the Length of the area to read and the start addres. Finally, pass them to
+ another function, TransferFromMemToOutBufAndSend, that will read from that memory space and
+ send it as a packet.
+ **/
+
+VOID
+EFIAPI
+ReadFromMemory (
+ CHAR8 *PacketData
+ )
+{
+ UINTN Address;
+ UINTN Length;
+ CHAR8 AddressBuffer[MAX_ADDR_SIZE]; // the buffer that will hold the address in hex chars
+ CHAR8 *AddrBufPtr; // pointer to the address buffer
+ CHAR8 *InBufPtr; /// pointer to the input buffer
+
+ AddrBufPtr = AddressBuffer;
+ InBufPtr = &PacketData[1];
+ while (*InBufPtr != ',') {
+ *AddrBufPtr++ = *InBufPtr++;
+ }
+ *AddrBufPtr = '\0';
+
+ InBufPtr++; // this skips ',' in the buffer
+
+ /* Error checking */
+ if (AsciiStrLen(AddressBuffer) >= MAX_ADDR_SIZE) {
+ SendError (GDB_EBADMEMADDRBUFSIZE);
+ return;
+ }
+
+ // 2 = 'm' + ','
+ if (AsciiStrLen(PacketData) - AsciiStrLen(AddressBuffer) - 2 >= MAX_LENGTH_SIZE) {
+ SendError (GDB_EBADMEMLENGTH);
+ return;
+ }
+
+ Address = AsciiStrHexToUintn (AddressBuffer);
+ Length = AsciiStrHexToUintn (InBufPtr);
+
+ TransferFromMemToOutBufAndSend (Length, (unsigned char *)Address);
+}
+
+
+/** "M addr,length :XX..."
+ Find the Length of the area in bytes to write and the start addres. Finally, pass them to
+ another function, TransferFromInBufToMem, that will write to that memory space the info in
+ the input buffer.
+ **/
+VOID
+EFIAPI
+WriteToMemory (
+ IN CHAR8 *PacketData
+ )
+{
+ UINTN Address;
+ UINTN Length;
+ UINTN MessageLength;
+ CHAR8 AddressBuffer[MAX_ADDR_SIZE]; // the buffer that will hold the Address in hex chars
+ CHAR8 LengthBuffer[MAX_LENGTH_SIZE]; // the buffer that will hold the Length in hex chars
+ CHAR8 *AddrBufPtr; // pointer to the Address buffer
+ CHAR8 *LengthBufPtr; // pointer to the Length buffer
+ CHAR8 *InBufPtr; /// pointer to the input buffer
+
+ AddrBufPtr = AddressBuffer;
+ LengthBufPtr = LengthBuffer;
+ InBufPtr = &PacketData[1];
+
+ while (*InBufPtr != ',') {
+ *AddrBufPtr++ = *InBufPtr++;
+ }
+ *AddrBufPtr = '\0';
+
+ InBufPtr++; // this skips ',' in the buffer
+
+ while (*InBufPtr != ':') {
+ *LengthBufPtr++ = *InBufPtr++;
+ }
+ *LengthBufPtr = '\0';
+
+ InBufPtr++; // this skips ':' in the buffer
+
+ Address = AsciiStrHexToUintn (AddressBuffer);
+ Length = AsciiStrHexToUintn (LengthBuffer);
+
+ /* Error checking */
+
+ //Check if Address is not too long.
+ if (AsciiStrLen(AddressBuffer) >= MAX_ADDR_SIZE) {
+ SendError (GDB_EBADMEMADDRBUFSIZE);
+ return;
+ }
+
+ //Check if message length is not too long
+ if (AsciiStrLen(LengthBuffer) >= MAX_LENGTH_SIZE) {
+ SendError (GDB_EBADMEMLENGBUFSIZE);
+ return;
+ }
+
+ // Check if Message is not too long/short.
+ // 3 = 'M' + ',' + ':'
+ MessageLength = (AsciiStrLen(PacketData) - AsciiStrLen(AddressBuffer) - AsciiStrLen(LengthBuffer) - 3);
+ if (MessageLength != (2*Length)) {
+ //Message too long/short. New data is not the right size.
+ SendError (GDB_EBADMEMDATASIZE);
+ return;
+ }
+ TransferFromInBufToMem (Length, (unsigned char *)Address, InBufPtr);
+}
+
+/**
+ Parses breakpoint packet data and captures Breakpoint type, Address and length.
+ In case of an error, function returns particular error code. Returning 0 meaning
+ no error.
+
+ @param PacketData Pointer to the payload data for the packet.
+ @param Type Breakpoint type
+ @param Address Breakpoint address
+ @param Length Breakpoint length in Bytes (1 byte, 2 byte, 4 byte)
+
+ @retval 1 Success
+ @retval {other} Particular error code
+
+**/
+UINTN
+ParseBreakpointPacket (
+ IN CHAR8 *PacketData,
+ OUT UINTN *Type,
+ OUT UINTN *Address,
+ OUT UINTN *Length
+ )
+{
+ CHAR8 AddressBuffer[MAX_ADDR_SIZE];
+ CHAR8 *AddressBufferPtr;
+ CHAR8 *PacketDataPtr;
+
+ PacketDataPtr = &PacketData[1];
+ AddressBufferPtr = AddressBuffer;
+
+ *Type = AsciiStrHexToUintn (PacketDataPtr);
+
+ //Breakpoint/watchpoint type should be between 0 to 4
+ if (*Type > 4) {
+ return 22; //EINVAL: Invalid argument.
+ }
+
+ //Skip ',' in the buffer.
+ while (*PacketDataPtr++ != ',');
+
+ //Parse Address information
+ while (*PacketDataPtr != ',') {
+ *AddressBufferPtr++ = *PacketDataPtr++;
+ }
+ *AddressBufferPtr = '\0';
+
+ //Check if Address is not too long.
+ if (AsciiStrLen(AddressBuffer) >= MAX_ADDR_SIZE) {
+ return 40; //EMSGSIZE: Message size too long.
+ }
+
+ *Address = AsciiStrHexToUintn (AddressBuffer);
+
+ PacketDataPtr++; //This skips , in the buffer
+
+ //Parse Length information
+ *Length = AsciiStrHexToUintn (PacketDataPtr);
+
+ //Length should be 1, 2 or 4 bytes
+ if (*Length > 4) {
+ return 22; //EINVAL: Invalid argument
+ }
+
+ return 0; //0 = No error
+}
+
+
+
+/**
+ Send the T signal with the given exception type (in gdb order) and possibly with n:r pairs related to the watchpoints
+
+ @param SystemContext Register content at time of the exception
+ @param GdbExceptionType GDB exception type
+ **/
+VOID
+GdbSendTSignal (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINT8 GdbExceptionType
+ )
+{
+ CHAR8 TSignalBuffer[128];
+ CHAR8 *TSignalPtr;
+
+ TSignalPtr = &TSignalBuffer[0];
+
+ //Construct TSignal packet
+ *TSignalPtr++ = 'T';
+
+ //
+ // replace _, or previous value, with Exception type
+ //
+ *TSignalPtr++ = mHexToStr [GdbExceptionType >> 4];
+ *TSignalPtr++ = mHexToStr [GdbExceptionType & 0x0f];
+
+ ProcessorSendTSignal (SystemContext, GdbExceptionType, TSignalPtr, sizeof (TSignalBuffer) - 2);
+
+ SendPacket (TSignalBuffer);
+}
+
+VOID
+GdbFWrite (
+ IN UINTN Fd,
+ IN CHAR8 *Data,
+ IN UINTN DataSize
+ )
+{
+ CHAR8 Buffer[128];
+
+ AsciiSPrint (Buffer, sizeof (Buffer), "Fwrite,%x,%x,%x", Fd, Data, DataSize);
+ SendPacket (Buffer);
+
+ for( ; ; ) {
+ ReceivePacket (gInBuffer, MAX_BUF_SIZE);
+
+ switch (gInBuffer[0]) {
+ case 'm':
+ ReadFromMemory (gInBuffer);
+ break;
+
+ case 'M':
+ WriteToMemory (gInBuffer);
+ break;
+
+ case 'F':
+ return;
+ break;
+ }
+ }
+}
+
+
+VOID
+GdbFPutString (
+ IN CHAR8 *String
+ )
+{
+ UINTN Len = AsciiStrSize (String);
+
+ GdbFWrite (2, String, Len);
+}
+
+
+/**
+ Exception Hanldler for GDB. It will be called for all exceptions
+ registered via the gExceptionType[] array.
+
+ @param ExceptionType Exception that is being processed
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+EFIAPI
+GdbExceptionHandler (
+ IN EFI_EXCEPTION_TYPE ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ UINT8 GdbExceptionType;
+ CHAR8 *Ptr;
+
+ if (ProcessorControlC (ExceptionType, SystemContext)) {
+ // We tried to process a control C handler and there is nothing to do
+ return;
+ }
+
+ GdbExceptionType = ConvertEFItoGDBtype (ExceptionType);
+ GdbSendTSignal (SystemContext, GdbExceptionType);
+
+ for( ; ; ) {
+ ReceivePacket (gInBuffer, MAX_BUF_SIZE);
+
+ switch (gInBuffer[0]) {
+ case '?':
+ GdbSendTSignal (SystemContext, GdbExceptionType);
+ break;
+
+ case 'c':
+ ContinueAtAddress (SystemContext, gInBuffer);
+ return;
+
+ case 'D':
+ // gdb wants to disconnect so return "OK" packet since.
+ SendSuccess ();
+ return;
+
+ case 'g':
+ ReadGeneralRegisters (SystemContext);
+ break;
+
+ case 'G':
+ WriteGeneralRegisters (SystemContext, gInBuffer);
+ break;
+
+ case 'H':
+ //Return "OK" packet since we don't have more than one thread.
+ SendSuccess ();
+ break;
+
+ case 'm':
+ ReadFromMemory (gInBuffer);
+ break;
+
+ case 'M':
+ WriteToMemory (gInBuffer);
+ break;
+
+ case 'P':
+ WriteNthRegister (SystemContext, gInBuffer);
+ break;
+
+ //
+ // Still debugging this code. Not used in Darwin
+ //
+ case 'q':
+ // General Query Packets
+ if (AsciiStrnCmp (gInBuffer, "qSupported", 10) == 0) {
+ // return what we currently support, we don't parse what gdb suports
+ AsciiSPrint (gOutBuffer, MAX_BUF_SIZE, "qXfer:libraries:read+;PacketSize=%d", MAX_BUF_SIZE);
+ SendPacket (gOutBuffer);
+ } else if (AsciiStrnCmp (gInBuffer, "qXfer:libraries:read::", 22) == 0) {
+ // ‘qXfer:libraries:read::offset,length
+ // gInBuffer[22] is offset string, ++Ptr is length string’
+ for (Ptr = &gInBuffer[22]; *Ptr != ','; Ptr++);
+
+ // Not sure if multi-radix support is required. Currently only support decimal
+ QxferLibrary (AsciiStrHexToUintn (&gInBuffer[22]), AsciiStrHexToUintn (++Ptr));
+ } else if (AsciiStrnCmp (gInBuffer, "qOffsets", 8) == 0) {
+ AsciiSPrint (gOutBuffer, MAX_BUF_SIZE, "Text=1000;Data=f000;Bss=f000");
+ SendPacket (gOutBuffer);
+ } else if (AsciiStrnCmp (gInBuffer, "qAttached", 9) == 0) {
+ // remote server attached to an existing process
+ SendPacket ("1");
+ } else {
+ //Send empty packet
+ SendNotSupported ();
+ }
+ break;
+
+ case 's':
+ SingleStep (SystemContext, gInBuffer);
+ return;
+
+ case 'z':
+ RemoveBreakPoint (SystemContext, gInBuffer);
+ break;
+
+ case 'Z':
+ InsertBreakPoint (SystemContext, gInBuffer);
+ break;
+
+ default:
+ //Send empty packet
+ SendNotSupported ();
+ break;
+ }
+ }
+}
+
+
+
+
+
diff --git a/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.h b/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.h new file mode 100755 index 0000000000..6cc2f6244f --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.h @@ -0,0 +1,727 @@ +/** @file + Private include file for GDB stub + + Copyright (c) 2008-2009 Apple Inc. All rights reserved.<BR> + + 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 __GCC_DEBUG_AGENT_INTERNAL__ +#define __GCC_DEBUG_AGENT_INTERNAL__ + +#include <Uefi.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include <Library/PcdLib.h> +#include <Library/GdbSerialLib.h> +#include <Library/PrintLib.h> +#include <Library/CacheMaintenanceLib.h> +#include <Library/DebugAgentTimerLib.h> + +#include <IndustryStandard/PeImage.h> +#include <Protocol/DebugSupport.h> + +extern CONST CHAR8 mHexToStr[]; + +// maximum size of input and output buffers +// This value came from the show remote command of the gdb we tested against +#define MAX_BUF_SIZE 2000 + +// maximum size of address buffer +#define MAX_ADDR_SIZE 32 + +// maximum size of register number buffer +#define MAX_REG_NUM_BUF_SIZE 32 + +// maximum size of length buffer +#define MAX_LENGTH_SIZE 32 + +// maximum size of T signal members +#define MAX_T_SIGNAL_SIZE 64 + +// the mask used to clear all the cache +#define TF_BIT 0x00000100 + + +// +// GDB Signal definitions - generic names for interrupts +// +#define GDB_SIGINT 2 // Interrupt process via ctrl-c +#define GDB_SIGILL 4 // Illegal instruction +#define GDB_SIGTRAP 5 // Trace Trap (Breakpoint and SingleStep) +#define GDB_SIGEMT 7 // Emulator Trap +#define GDB_SIGFPE 8 // Floating point exception +#define GDB_SIGSEGV 11 // Setgment violation, page fault + + +// +// GDB File I/O Error values, zero means no error +// Includes all general GDB Unix like error values +// +#define GDB_EBADMEMADDRBUFSIZE 11 // the buffer that stores memory Address to be read from/written to is not the right size +#define GDB_EBADMEMLENGBUFSIZE 12 // the buffer that stores Length is not the right size +#define GDB_EBADMEMLENGTH 13 // Length, the given number of bytes to read or write, is not the right size +#define GDB_EBADMEMDATA 14 // one of the bytes or nibbles of the memory is leess than 0 +#define GDB_EBADMEMDATASIZE 15 // the memory data, 'XX..', is too short or too long +#define GDB_EBADBUFSIZE 21 // the buffer created is not the correct size +#define GDB_EINVALIDARG 31 // argument is invalid +#define GDB_ENOSPACE 41 // +#define GDB_EINVALIDBRKPOINTTYPE 51 // the breakpoint type is not recognized +#define GDB_EINVALIDREGNUM 61 // given register number is not valid: either <0 or >=Number of Registers +#define GDB_EUNKNOWN 255 // unknown + + +// +// These devices are open by GDB so we can just read and write to them +// +#define GDB_STDIN 0x00 +#define GDB_STDOUT 0x01 +#define GDB_STDERR 0x02 + +// +//Define Register size for different architectures +// +#if defined (MDE_CPU_IA32) +#define REG_SIZE 32 +#elif defined (MDE_CPU_X64) +#define REG_SIZE 64 +#elif defined (MDE_CPU_ARM) +#define REG_SIZE 32 +#endif + + +typedef struct { + EFI_EXCEPTION_TYPE Exception; + UINT8 SignalNo; +} EFI_EXCEPTION_TYPE_ENTRY; + + +#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) + +// +// Byte packed structure for DR6 +// 32-bits on IA-32 +// 64-bits on X64. The upper 32-bits on X64 are reserved +// +typedef union { + struct { + UINT32 B0:1; // Breakpoint condition detected + UINT32 B1:1; // Breakpoint condition detected + UINT32 B2:1; // Breakpoint condition detected + UINT32 B3:1; // Breakpoint condition detected + UINT32 Reserved_1:9; // Reserved + UINT32 BD:1; // Debug register access detected + UINT32 BS:1; // Single step + UINT32 BT:1; // Task switch + UINT32 Reserved_2:16; // Reserved + } Bits; + UINTN UintN; +} IA32_DR6; + +// +// Byte packed structure for DR7 +// 32-bits on IA-32 +// 64-bits on X64. The upper 32-bits on X64 are reserved +// +typedef union { + struct { + UINT32 L0:1; // Local breakpoint enable + UINT32 G0:1; // Global breakpoint enable + UINT32 L1:1; // Local breakpoint enable + UINT32 G1:1; // Global breakpoint enable + UINT32 L2:1; // Local breakpoint enable + UINT32 G2:1; // Global breakpoint enable + UINT32 L3:1; // Local breakpoint enable + UINT32 G3:1; // Global breakpoint enable + UINT32 LE:1; // Local exact breakpoint enable + UINT32 GE:1; // Global exact breakpoint enable + UINT32 Reserved_1:3; // Reserved + UINT32 GD:1; // Global detect enable + UINT32 Reserved_2:2; // Reserved + UINT32 RW0:2; // Read/Write field + UINT32 LEN0:2; // Length field + UINT32 RW1:2; // Read/Write field + UINT32 LEN1:2; // Length field + UINT32 RW2:2; // Read/Write field + UINT32 LEN2:2; // Length field + UINT32 RW3:2; // Read/Write field + UINT32 LEN3:2; // Length field + } Bits; + UINTN UintN; +} IA32_DR7; + +#endif /* if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) */ + +typedef enum { + InstructionExecution, //Hardware breakpoint + DataWrite, //watch + DataRead, //rwatch + DataReadWrite, //awatch + SoftwareBreakpoint, //Software breakpoint + NotSupported +} BREAK_TYPE; + +// +// Array of exception types that need to be hooked by the debugger +// +extern EFI_EXCEPTION_TYPE_ENTRY gExceptionType[]; + +// +// If the periodic callback is called while we are processing an F packet we need +// to let the callback know to not read from the serail stream as it could steal +// characters from the F reponse packet +// +extern BOOLEAN gProcessingFPacket; + + +/** + Return the number of entries in the gExceptionType[] + + @retval UINTN, the number of entries in the gExceptionType[] array. + **/ +UINTN +MaxEfiException ( + VOID + ); + + +/** + Check to see if the ISA is supported. + ISA = Instruction Set Architecture + + @retval TRUE if Isa is supported, + FALSE otherwise. + **/ +BOOLEAN +CheckIsa ( + IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa + ); + + +/** + Send the T signal with the given exception type (in gdb order) and possibly with n:r pairs related to the watchpoints + + @param SystemContext Register content at time of the exception + @param GdbExceptionType GDB exception type + **/ + +VOID +GdbSendTSignal ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINT8 GdbExceptionType + ); + + +/** + Translates the EFI mapping to GDB mapping + + @param EFIExceptionType EFI Exception that is being processed + @retval UINTN that corresponds to EFIExceptionType's GDB exception type number + **/ +UINT8 +ConvertEFItoGDBtype ( + IN EFI_EXCEPTION_TYPE EFIExceptionType + ); + + +/** + Empties the given buffer + @param *Buf pointer to the first element in buffer to be emptied + **/ +VOID +EmptyBuffer ( + IN CHAR8 *Buf + ); + + +/** + Converts an 8-bit Hex Char into a INTN. + + @param Char - the hex character to be converted into UINTN + @retval a INTN, from 0 to 15, that corressponds to Char + -1 if Char is not a hex character + **/ +INTN +HexCharToInt ( + IN CHAR8 Char + ); + + +/** 'E NN' + Send an error with the given error number after converting to hex. + The error number is put into the buffer in hex. '255' is the biggest errno we can send. + ex: 162 will be sent as A2. + + @param errno the error number that will be sent + **/ +VOID +EFIAPI +SendError ( + IN UINT8 ErrorNum + ); + + +/** + Send 'OK' when the function is done executing successfully. + **/ +VOID +SendSuccess ( + VOID + ); + + +/** + Send empty packet to specify that particular command/functionality is not supported. + **/ +VOID +SendNotSupported ( + VOID + ); + +/** ‘p n’ + Reads the n-th register's value into an output buffer and sends it as a packet + @param SystemContext Register content at time of the exception + @param InBuffer This is the input buffer received from gdb server + **/ +VOID +ReadNthRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *InBuffer + ); + + +/** ‘g’ + Reads the general registers into an output buffer and sends it as a packet + @param SystemContext Register content at time of the exception + **/ +VOID +ReadGeneralRegisters ( + IN EFI_SYSTEM_CONTEXT SystemContext + ); + + +/** ‘P n...=r...’ + Writes the new value of n-th register received into the input buffer to the n-th register + @param SystemContext Register content at time of the exception + @param InBuffer This is the input buffer received from gdb server + **/ +VOID +WriteNthRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *InBuffer + ); + + +/** ‘G XX...’ + Writes the new values received into the input buffer to the general registers + @param SystemContext Register content at time of the exception + @param InBuffer Pointer to the input buffer received from gdb server + **/ + +VOID +WriteGeneralRegisters ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *InBuffer + ); + + +/** ‘m addr,length ’ + Find the Length of the area to read and the start addres. Finally, pass them to + another function, TransferFromMemToOutBufAndSend, that will read from that memory space and + send it as a packet. + + @param *PacketData Pointer to Payload data for the packet + **/ +VOID +ReadFromMemory ( + IN CHAR8 *PacketData + ); + + +/** ‘M addr,length :XX...’ + Find the Length of the area in bytes to write and the start addres. Finally, pass them to + another function, TransferFromInBufToMem, that will write to that memory space the info in + the input buffer. + + @param PacketData Pointer to Payload data for the packet + **/ +VOID +WriteToMemory ( + IN CHAR8 *PacketData + ); + + +/** ‘c [addr ]’ + Continue. addr is Address to resume. If addr is omitted, resume at current + Address. + + @param SystemContext Register content at time of the exception + @param *PacketData Pointer to PacketData + **/ + +VOID +ContinueAtAddress ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ); + + +/** ‘s [addr ]’ + Single step. addr is the Address at which to resume. If addr is omitted, resume + at same Address. + + @param SystemContext Register content at time of the exception + @param PacketData Pointer to Payload data for the packet + **/ +VOID +SingleStep ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ); + +/** + Insert Single Step in the SystemContext + + @param SystemContext Register content at time of the exception + **/ +VOID +AddSingleStep ( + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + Remove Single Step in the SystemContext + + @param SystemContext Register content at time of the exception + **/ +VOID +RemoveSingleStep ( + IN EFI_SYSTEM_CONTEXT SystemContext + ); + + +/** + ‘Z1, [addr], [length]’ + ‘Z2, [addr], [length]’ + ‘Z3, [addr], [length]’ + ‘Z4, [addr], [length]’ + + Insert hardware breakpoint/watchpoint at address addr of size length + + @param SystemContext Register content at time of the exception + @param *PacketData Pointer to the Payload data for the packet + +**/ +VOID +EFIAPI +InsertBreakPoint( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ); + + +/** + ‘z1, [addr], [length]’ + ‘z2, [addr], [length]’ + ‘z3, [addr], [length]’ + ‘z4, [addr], [length]’ + + Remove hardware breakpoint/watchpoint at address addr of size length + + @param SystemContext Register content at time of the exception + @param *PacketData Pointer to the Payload data for the packet + +**/ +VOID +EFIAPI +RemoveBreakPoint( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ); + + +/** + Exception Hanldler for GDB. It will be called for all exceptions + registered via the gExceptionType[] array. + + @param ExceptionType Exception that is being processed + @param SystemContext Register content at time of the exception + + **/ +VOID +EFIAPI +GdbExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + + +/** + Periodic callback for GDB. This function is used to catch a ctrl-c or other + break in type command from GDB. + + @param SystemContext Register content at time of the call + + **/ +VOID +EFIAPI +GdbPeriodicCallBack ( + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + + +/** + Make two serail consoles: 1) StdIn and StdOut via GDB. 2) StdErr via GDB. + + These console show up on the remote system running GDB + +**/ + +VOID +GdbInitializeSerialConsole ( + VOID + ); + + +/** + Send a GDB Remote Serial Protocol Packet + + $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$', + the packet teminating character '#' and the two digit checksum. + + If an ack '+' is not sent resend the packet, but timeout eventually so we don't end up + in an infinit loop. This is so if you unplug the debugger code just keeps running + + @param PacketData Payload data for the packet + + @retval Number of bytes of packet data sent. + +**/ +UINTN +SendPacket ( + IN CHAR8 *PacketData + ); + + +/** + Receive a GDB Remote Serial Protocol Packet + + $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$', + the packet teminating character '#' and the two digit checksum. + + If host re-starts sending a packet without ending the previous packet, only the last valid packet is proccessed. + (In other words, if received packet is '$12345$12345$123456#checksum', only '$123456#checksum' will be processed.) + + If an ack '+' is not sent resend the packet + + @param PacketData Payload data for the packet + + @retval Number of bytes of packet data received. + + **/ +UINTN +ReceivePacket ( + OUT CHAR8 *PacketData, + IN UINTN PacketDataSize + ); + + +/** + Read data from a FileDescriptor. On success number of bytes read is returned. Zero indicates + the end of a file. On error -1 is returned. If count is zero, GdbRead returns zero. + + @param FileDescriptor Device to talk to. + @param Buffer Buffer to hold Count bytes that were read + @param Count Number of bytes to transfer. + + @retval -1 Error + @retval {other} Number of bytes read. + +**/ +INTN +GdbRead ( + IN INTN FileDescriptor, + OUT VOID *Buffer, + IN UINTN Count + ); + + +/** + Write data to a FileDescriptor. On success number of bytes written is returned. Zero indicates + nothing was written. On error -1 is returned. + + @param FileDescriptor Device to talk to. + @param Buffer Buffer to hold Count bytes that are to be written + @param Count Number of bytes to transfer. + + @retval -1 Error + @retval {other} Number of bytes written. + +**/ +INTN +GdbWrite ( + IN INTN FileDescriptor, + OUT CONST VOID *Buffer, + IN UINTN Count + ); + +UINTN * +FindPointerToRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN RegNumber + ); + +CHAR8 * +BasicReadRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN RegNumber, + IN CHAR8 *OutBufPtr + ); + +VOID +TransferFromInBufToMem ( + IN UINTN Length, + IN UINT8 *Address, + IN CHAR8 *NewData + ); + +VOID +TransferFromMemToOutBufAndSend ( + IN UINTN Length, + IN UINT8 *Address + ); + +CHAR8 * +BasicWriteRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN RegNumber, + IN CHAR8 *InBufPtr + ); + +VOID +PrintReg ( + EFI_SYSTEM_CONTEXT SystemContext + ); + +UINTN +ParseBreakpointPacket ( + IN CHAR8 *PacketData, + OUT UINTN *Type, + OUT UINTN *Address, + OUT UINTN *Length + ); + +UINTN +GetBreakpointDataAddress ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN BreakpointNumber + ); + +UINTN +GetBreakpointDetected ( + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +BREAK_TYPE +GetBreakpointType ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN BreakpointNumber + ); + +UINTN +ConvertLengthData ( + IN UINTN Length + ); + +EFI_STATUS +FindNextFreeDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT UINTN *Register + ); + +EFI_STATUS +EnableDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN Register, + IN UINTN Address, + IN UINTN Length, + IN UINTN Type + ); + +EFI_STATUS +FindMatchingDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN Address, + IN UINTN Length, + IN UINTN Type, + OUT UINTN *Register + ); + +EFI_STATUS +DisableDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN Register + ); + +VOID +InitializeProcessor ( + VOID + ); + +/** + Send the T signal with the given exception type (in gdb order) and possibly with n:r pairs related to the watchpoints + + @param SystemContext Register content at time of the exception + @param GdbExceptionType GDB exception type + **/ +VOID +ProcessorSendTSignal ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINT8 GdbExceptionType, + IN OUT CHAR8 *TSignalPtr, + IN UINTN SizeOfBuffer + ); + +/**
+ Check to see if this exception is related to ctrl-c handling.
+
+ @param ExceptionType Exception that is being processed
+ @param SystemContext Register content at time of the exception
+
+ @return TRUE This was a ctrl-c check that did not find a ctrl-c
+ @return FALSE This was not a ctrl-c check or some one hit ctrl-c
+ **/
+BOOLEAN
+ProcessorControlC (
+ IN EFI_EXCEPTION_TYPE ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ ); + + +/**
+ Initialize debug agent.
+
+ This function is used to set up debug enviroment. It may enable interrupts.
+
+ @param[in] InitFlag Init flag is used to decide initialize process.
+ @param[in] Context Context needed according to InitFlag, it was optional.
+
+**/
+VOID
+EFIAPI
+DebugAgentHookExceptions (
+ IN UINT32 InitFlag,
+ IN VOID *Context OPTIONAL
+ );
+ + +#endif diff --git a/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.inf b/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.inf new file mode 100755 index 0000000000..9ccb90bf4b --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.inf @@ -0,0 +1,69 @@ +#/** @file
+# Null instance of Debug Agent Library with empty functions.
+#
+# Copyright (c) 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.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = GdbDebugAgent
+ FILE_GUID = b9f10c17-6ca0-40b5-9b44-6253cfc7d24b
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DebugAgentLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ GdbDebugAgent.c
+
+[Sources.arm]
+ Arm/Processor.c
+ Arm/ExceptionSupport.ARMv6.S
+ Arm/ExceptionSupport.ARMv6.asm
+
+[Sources.X64]
+ Ia32/Processor.c
+
+[Sources.Ia32]
+ X64/Processor.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+
+[Packages.arm]
+ ArmPkg/ArmPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ PcdLib
+ GdbSerialLib
+ CacheMaintenanceLib
+ DebugAgentTimerLib
+
+[FeaturePcd.common]
+ gEmbeddedTokenSpaceGuid.PcdGdbSerial
+
+[FixedPcd.common]
+ gEmbeddedTokenSpaceGuid.PcdGdbMaxPacketRetryCount
+
+[FixedPcd.arm]
+ gArmTokenSpaceGuid.PcdCpuVectorBaseAddress
diff --git a/EmbeddedPkg/Library/GdbDebugAgent/Ia32/Processor.c b/EmbeddedPkg/Library/GdbDebugAgent/Ia32/Processor.c new file mode 100755 index 0000000000..d81e70f959 --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/Ia32/Processor.c @@ -0,0 +1,933 @@ +/** @file + Processor specific parts of the GDB stub + + Copyright (c) 2008-2009, Apple Inc. All rights reserved. + + 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 <GdbDebugAgent.h>
+ +// +// Array of exception types that need to be hooked by the debugger +// {EFI mapping, GDB mapping} +// +EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = { + { EXCEPT_IA32_DIVIDE_ERROR, GDB_SIGFPE }, + { EXCEPT_IA32_DEBUG, GDB_SIGTRAP }, + { EXCEPT_IA32_NMI, GDB_SIGEMT }, + { EXCEPT_IA32_BREAKPOINT, GDB_SIGTRAP }, + { EXCEPT_IA32_OVERFLOW, GDB_SIGSEGV }, + { EXCEPT_IA32_BOUND, GDB_SIGSEGV }, + { EXCEPT_IA32_INVALID_OPCODE, GDB_SIGILL }, + { EXCEPT_IA32_DOUBLE_FAULT, GDB_SIGEMT }, + { EXCEPT_IA32_STACK_FAULT, GDB_SIGSEGV }, + { EXCEPT_IA32_GP_FAULT, GDB_SIGSEGV }, + { EXCEPT_IA32_PAGE_FAULT, GDB_SIGSEGV }, + { EXCEPT_IA32_FP_ERROR, GDB_SIGEMT }, + { EXCEPT_IA32_ALIGNMENT_CHECK, GDB_SIGEMT }, + { EXCEPT_IA32_MACHINE_CHECK, GDB_SIGEMT } +}; + + +// The offsets of registers SystemContext. +// The fields in the array are in the gdb ordering. +// +//16 regs +UINTN gRegisterOffsets[] = { + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eax), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ecx), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Edx), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ebx), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Esp), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ebp), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Esi), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Edi), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eip), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eflags), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Cs), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ss), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ds), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Es), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Fs), + OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Gs) +}; + + +//Debug only.. +VOID +PrintReg ( + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + Print ((CHAR16 *)L"EAX: %x ", SystemContext.SystemContextIa32->Eax); + Print ((CHAR16 *)L"ECX: %x ", SystemContext.SystemContextIa32->Ecx); + Print ((CHAR16 *)L"EDX: %x ", SystemContext.SystemContextIa32->Edx); + Print ((CHAR16 *)L"EBX: %x ", SystemContext.SystemContextIa32->Ebx); + Print ((CHAR16 *)L"ESP: %x ", SystemContext.SystemContextIa32->Esp); + Print ((CHAR16 *)L"EBP: %x ", SystemContext.SystemContextIa32->Ebp); + Print ((CHAR16 *)L"ESI: %x ", SystemContext.SystemContextIa32->Esi); + Print ((CHAR16 *)L"EDI: %x ", SystemContext.SystemContextIa32->Edi); + Print ((CHAR16 *)L"EIP: %x\n", SystemContext.SystemContextIa32->Eip); + Print ((CHAR16 *)L"EFlags: %x\n", SystemContext.SystemContextIa32->Eflags); +} + +//Debug only.. +VOID +PrintDRreg ( + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + Print ((CHAR16 *)L"DR0: %x ", SystemContext.SystemContextIa32->Dr0); + Print ((CHAR16 *)L"DR1: %x ", SystemContext.SystemContextIa32->Dr1); + Print ((CHAR16 *)L"DR2: %x ", SystemContext.SystemContextIa32->Dr2); + Print ((CHAR16 *)L"DR3: %x ", SystemContext.SystemContextIa32->Dr3); + Print ((CHAR16 *)L"DR6: %x ", SystemContext.SystemContextIa32->Dr6); + Print ((CHAR16 *)L"DR7: %x\n", SystemContext.SystemContextIa32->Dr7); +} + + +/** + Return the number of entries in the gExceptionType[] + + @retval UINTN, the number of entries in the gExceptionType[] array. + **/ +UINTN +MaxEfiException ( + VOID + ) +{ + return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY); +} + + +/** + Check to see if the ISA is supported. + ISA = Instruction Set Architecture + + @retval TRUE if Isa is supported, + FALSE otherwise. +**/ +BOOLEAN +CheckIsa ( + IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa + ) +{ + return (BOOLEAN)(Isa == IsaIa32); +} + + +/** + This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering + It is, by default, set to find the register pointer of the IA32 member + + @param SystemContext Register content at time of the exception + @param RegNumber The register to which we want to find a pointer + @retval the pointer to the RegNumber-th pointer + **/ +UINTN * +FindPointerToRegister( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN RegNumber + ) +{ + UINT8 *TempPtr; + TempPtr = ((UINT8 *)SystemContext.SystemContextIa32) + gRegisterOffsets[RegNumber]; + return (UINTN *)TempPtr; +} + + +/** + Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr + + @param SystemContext Register content at time of the exception + @param RegNumber the number of the register that we want to read + @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on. + @retval the pointer to the next character of the output buffer that is available to be written on. + **/ +CHAR8 * +BasicReadRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN RegNumber, + IN CHAR8 *OutBufPtr + ) +{ + UINTN RegSize; + + RegSize = 0; + while (RegSize < REG_SIZE) { + *OutBufPtr++ = mHexToStr[((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)]; + *OutBufPtr++ = mHexToStr[((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)]; + RegSize = RegSize + 8; + } + return OutBufPtr; +} + + +/** ‘p n’ + Reads the n-th register's value into an output buffer and sends it as a packet + + @param SystemContext Register content at time of the exception + @param InBuffer Pointer to the input buffer received from gdb server + **/ +VOID +EFIAPI +ReadNthRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *InBuffer + ) +{ + UINTN RegNumber; + CHAR8 OutBuffer[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq) + CHAR8 *OutBufPtr; // pointer to the output buffer + + RegNumber = AsciiStrHexToUintn (&InBuffer[1]); + + if ((RegNumber < 0) || (RegNumber >= sizeof (gRegisterOffsets)/sizeof (UINTN))) { + SendError (GDB_EINVALIDREGNUM); + return; + } + + OutBufPtr = OutBuffer; + OutBufPtr = BasicReadRegister(SystemContext, RegNumber, OutBufPtr); + + *OutBufPtr = '\0'; // the end of the buffer + SendPacket(OutBuffer); +} + + +/** ‘g’ + Reads the general registers into an output buffer and sends it as a packet + + @param SystemContext Register content at time of the exception + **/ +VOID +EFIAPI +ReadGeneralRegisters ( + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN i; + CHAR8 OutBuffer[129]; // 16 regs, 8 hex chars each, and the end '\0' (escape seq) + CHAR8 *OutBufPtr; // pointer to the output buffer + + OutBufPtr = OutBuffer; + for(i = 0 ; i < sizeof (gRegisterOffsets)/sizeof (UINTN) ; i++) { // there are only 16 registers to read + OutBufPtr = BasicReadRegister(SystemContext, i, OutBufPtr); + } + + *OutBufPtr = '\0'; // the end of the buffer + SendPacket(OutBuffer); +} + + +/** + Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr + + @param SystemContext Register content at time of the exception + @param RegNumber the number of the register that we want to write + @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on. + @retval the pointer to the next character of the input buffer that can be used + **/ +CHAR8 * +BasicWriteRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN RegNumber, + IN CHAR8 *InBufPtr + ) +{ + UINTN RegSize; + UINTN TempValue; // the value transferred from a hex char + UINT32 NewValue; // the new value of the RegNumber-th Register + + NewValue = 0; + RegSize = 0; + while (RegSize < REG_SIZE) { + TempValue = HexCharToInt(*InBufPtr++); + + if (TempValue < 0) { + SendError (GDB_EBADMEMDATA); + return NULL; + } + + NewValue += (TempValue << (RegSize+4)); + TempValue = HexCharToInt(*InBufPtr++); + + if (TempValue < 0) { + SendError (GDB_EBADMEMDATA); + return NULL; + } + + NewValue += (TempValue << RegSize); + RegSize = RegSize + 8; + } + *(FindPointerToRegister(SystemContext, RegNumber)) = NewValue; + return InBufPtr; +} + + +/** ‘P n...=r...’ + Writes the new value of n-th register received into the input buffer to the n-th register + + @param SystemContext Register content at time of the exception + @param InBuffer Ponter to the input buffer received from gdb server + **/ +VOID +EFIAPI +WriteNthRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *InBuffer + ) +{ + UINTN RegNumber; + CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array + CHAR8 *RegNumBufPtr; + CHAR8 *InBufPtr; // pointer to the input buffer + + // find the register number to write + InBufPtr = &InBuffer[1]; + RegNumBufPtr = RegNumBuffer; + while (*InBufPtr != '=') { + *RegNumBufPtr++ = *InBufPtr++; + } + *RegNumBufPtr = '\0'; + RegNumber = AsciiStrHexToUintn (RegNumBuffer); + + // check if this is a valid Register Number + if ((RegNumber < 0) || (RegNumber >= sizeof (gRegisterOffsets)/sizeof (UINTN))) { + SendError (GDB_EINVALIDREGNUM); + return; + } + InBufPtr++; // skips the '=' character + BasicWriteRegister (SystemContext, RegNumber, InBufPtr); + SendSuccess(); +} + + +/** ‘G XX...’ + Writes the new values received into the input buffer to the general registers + + @param SystemContext Register content at time of the exception + @param InBuffer Pointer to the input buffer received from gdb server + **/ +VOID +EFIAPI +WriteGeneralRegisters ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *InBuffer + ) +{ + UINTN i; + CHAR8 *InBufPtr; /// pointer to the input buffer + + // check to see if the buffer is the right size which is + // 1 (for 'G') + 16 (for 16 registers) * 8 ( for 8 hex chars each) = 129 + if (AsciiStrLen(InBuffer) != 129) { // 16 regs, 8 hex chars each, and the end '\0' (escape seq) + //Bad message. Message is not the right length + SendError (GDB_EBADBUFSIZE); + return; + } + + InBufPtr = &InBuffer[1]; + + // Read the new values for the registers from the input buffer to an array, NewValueArray. + // The values in the array are in the gdb ordering + for(i=0; i < sizeof (gRegisterOffsets)/sizeof (UINTN); i++) { // there are only 16 registers to write + InBufPtr = BasicWriteRegister(SystemContext, i, InBufPtr); + } + + SendSuccess(); +} + +/** ‘c [addr ]’ + Continue. addr is Address to resume. If addr is omitted, resume at current + Address. + + @param SystemContext Register content at time of the exception + **/ +VOID +EFIAPI +ContinueAtAddress ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ) +{ + if (PacketData[1] != '\0') { + SystemContext.SystemContextIa32->Eip = AsciiStrHexToUintn (&PacketData[1]); + } +} + + +/** ‘s [addr ]’ + Single step. addr is the Address at which to resume. If addr is omitted, resume + at same Address. + + @param SystemContext Register content at time of the exception + **/ +VOID +EFIAPI +SingleStep ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ) +{ + SendNotSupported();
+} + + +/** + Returns breakpoint data address from DR0-DR3 based on the input breakpoint number + + @param SystemContext Register content at time of the exception + @param BreakpointNumber Breakpoint number + + @retval Address Data address from DR0-DR3 based on the breakpoint number. + +**/ +UINTN +GetBreakpointDataAddress ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN BreakpointNumber + ) +{ + UINTN Address; + + if (BreakpointNumber == 1) { + Address = SystemContext.SystemContextIa32->Dr0; + } else if (BreakpointNumber == 2) { + Address = SystemContext.SystemContextIa32->Dr1; + } else if (BreakpointNumber == 3) { + Address = SystemContext.SystemContextIa32->Dr2; + } else if (BreakpointNumber == 4) { + Address = SystemContext.SystemContextIa32->Dr3; + } else { + Address = 0; + } + + return Address; +} + + +/** + Returns currently detected breakpoint value based on the register DR6 B0-B3 field. + If no breakpoint is detected then it returns 0. + + @param SystemContext Register content at time of the exception + + @retval {1-4} Currently detected breakpoint value + @retval 0 No breakpoint detected. + +**/ +UINTN +GetBreakpointDetected ( + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + IA32_DR6 Dr6; + UINTN BreakpointNumber; + + Dr6.UintN = SystemContext.SystemContextIa32->Dr6; + + if (Dr6.Bits.B0 == 1) { + BreakpointNumber = 1; + } else if (Dr6.Bits.B1 == 1) { + BreakpointNumber = 2; + } else if (Dr6.Bits.B2 == 1) { + BreakpointNumber = 3; + } else if (Dr6.Bits.B3 == 1) { + BreakpointNumber = 4; + } else { + BreakpointNumber = 0; //No breakpoint detected + } + + return BreakpointNumber; +} + + +/** + Returns Breakpoint type (InstructionExecution, DataWrite, DataRead or DataReadWrite) + based on the Breakpoint number + + @param SystemContext Register content at time of the exception + @param BreakpointNumber Breakpoint number + + @retval BREAK_TYPE Breakpoint type value read from register DR7 RWn field + For unknown value, it returns NotSupported. + +**/ +BREAK_TYPE +GetBreakpointType ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN BreakpointNumber + ) +{ + IA32_DR7 Dr7; + BREAK_TYPE Type = NotSupported; //Default is NotSupported type + + Dr7.UintN = SystemContext.SystemContextIa32->Dr7; + + if (BreakpointNumber == 1) { + Type = (BREAK_TYPE) Dr7.Bits.RW0; + } else if (BreakpointNumber == 2) { + Type = (BREAK_TYPE) Dr7.Bits.RW1; + } else if (BreakpointNumber == 3) { + Type = (BREAK_TYPE) Dr7.Bits.RW2; + } else if (BreakpointNumber == 4) { + Type = (BREAK_TYPE) Dr7.Bits.RW3; + } + + return Type; +} + + +/** + Parses Length and returns the length which DR7 LENn field accepts. + For example: If we receive 1-Byte length then we should return 0. + Zero gets written to DR7 LENn field. + + @param Length Breakpoint length in Bytes (1 byte, 2 byte, 4 byte) + + @retval Length Appropriate converted values which DR7 LENn field accepts. + +**/ +UINTN +ConvertLengthData ( + IN UINTN Length + ) +{ + if (Length == 1) { //1-Byte length + return 0; + } else if (Length == 2) { //2-Byte length + return 1; + } else if (Length == 4) { //4-Byte length + return 3; + } else { //Undefined or 8-byte length + return 2; + } +} + + +/** + Finds the next free debug register. If all the registers are occupied then + EFI_OUT_OF_RESOURCES is returned. + + @param SystemContext Register content at time of the exception + @param Register Register value (0 - 3 for the first free debug register) + + @retval EFI_STATUS Appropriate status value. + +**/ +EFI_STATUS +FindNextFreeDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT UINTN *Register + ) +{ + IA32_DR7 Dr7; + + Dr7.UintN = SystemContext.SystemContextIa32->Dr7; + + if (Dr7.Bits.G0 == 0) { + *Register = 0; + } else if (Dr7.Bits.G1 == 0) { + *Register = 1; + } else if (Dr7.Bits.G2 == 0) { + *Register = 2; + } else if (Dr7.Bits.G3 == 0) { + *Register = 3; + } else { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + + +/** + Enables the debug register. Writes Address value to appropriate DR0-3 register. + Sets LENn, Gn, RWn bits in DR7 register. + + @param SystemContext Register content at time of the exception + @param Register Register value (0 - 3) + @param Address Breakpoint address value + @param Type Breakpoint type (Instruction, Data write, Data read + or write etc.) + + @retval EFI_STATUS Appropriate status value. + +**/ +EFI_STATUS +EnableDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN Register, + IN UINTN Address, + IN UINTN Length, + IN UINTN Type + ) +{ + IA32_DR7 Dr7; + + //Convert length data + Length = ConvertLengthData (Length); + + //For Instruction execution, length should be 0 + //(Ref. Intel reference manual 18.2.4) + if ((Type == 0) && (Length != 0)) { + return EFI_INVALID_PARAMETER; + } + + //Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle + //software breakpoint. We should send empty packet in both these cases. + if ((Type == (BREAK_TYPE)DataRead) || + (Type == (BREAK_TYPE)SoftwareBreakpoint)) { + return EFI_UNSUPPORTED; + } + + //Read DR7 so appropriate Gn, RWn and LENn bits can be modified. + Dr7.UintN = SystemContext.SystemContextIa32->Dr7; + + if (Register == 0) { + SystemContext.SystemContextIa32->Dr0 = Address; + Dr7.Bits.G0 = 1; + Dr7.Bits.RW0 = Type; + Dr7.Bits.LEN0 = Length; + } else if (Register == 1) { + SystemContext.SystemContextIa32->Dr1 = Address; + Dr7.Bits.G1 = 1; + Dr7.Bits.RW1 = Type; + Dr7.Bits.LEN1 = Length; + } else if (Register == 2) { + SystemContext.SystemContextIa32->Dr2 = Address; + Dr7.Bits.G2 = 1; + Dr7.Bits.RW2 = Type; + Dr7.Bits.LEN2 = Length; + } else if (Register == 3) { + SystemContext.SystemContextIa32->Dr3 = Address; + Dr7.Bits.G3 = 1; + Dr7.Bits.RW3 = Type; + Dr7.Bits.LEN3 = Length; + } else { + return EFI_INVALID_PARAMETER; + } + + //Update Dr7 with appropriate Gn, RWn and LENn bits + SystemContext.SystemContextIa32->Dr7 = Dr7.UintN; + + return EFI_SUCCESS; +} + + +/** + Returns register number 0 - 3 for the maching debug register. + This function compares incoming Address, Type, Length and + if there is a match then it returns the appropriate register number. + In case of mismatch, function returns EFI_NOT_FOUND message. + + @param SystemContext Register content at time of the exception + @param Address Breakpoint address value + @param Length Breakpoint length value + @param Type Breakpoint type (Instruction, Data write, + Data read or write etc.) + @param Register Register value to be returned + + @retval EFI_STATUS Appropriate status value. + +**/ +EFI_STATUS +FindMatchingDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN Address, + IN UINTN Length, + IN UINTN Type, + OUT UINTN *Register + ) +{ + IA32_DR7 Dr7; + + //Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle + //software breakpoint. We should send empty packet in both these cases. + if ((Type == (BREAK_TYPE)DataRead) || + (Type == (BREAK_TYPE)SoftwareBreakpoint)) { + return EFI_UNSUPPORTED; + } + + //Convert length data + Length = ConvertLengthData(Length); + + Dr7.UintN = SystemContext.SystemContextIa32->Dr7; + + if ((Dr7.Bits.G0 == 1) && + (Dr7.Bits.LEN0 == Length) && + (Dr7.Bits.RW0 == Type) && + (Address == SystemContext.SystemContextIa32->Dr0)) { + *Register = 0; + } else if ((Dr7.Bits.G1 == 1) && + (Dr7.Bits.LEN1 == Length) && + (Dr7.Bits.RW1 == Type) && + (Address == SystemContext.SystemContextIa32->Dr1)) { + *Register = 1; + } else if ((Dr7.Bits.G2 == 1) && + (Dr7.Bits.LEN2 == Length) && + (Dr7.Bits.RW2 == Type) && + (Address == SystemContext.SystemContextIa32->Dr2)) { + *Register = 2; + } else if ((Dr7.Bits.G3 == 1) && + (Dr7.Bits.LEN3 == Length) && + (Dr7.Bits.RW3 == Type) && + (Address == SystemContext.SystemContextIa32->Dr3)) { + *Register = 3; + } else { + Print ((CHAR16 *)L"No match found..\n"); + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + + +/** + Disables the particular debug register. + + @param SystemContext Register content at time of the exception + @param Register Register to be disabled + + @retval EFI_STATUS Appropriate status value. + +**/ +EFI_STATUS +DisableDebugRegister ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINTN Register + ) +{ + IA32_DR7 Dr7; + UINTN Address = 0; + + //Read DR7 register so appropriate Gn, RWn and LENn bits can be turned off. + Dr7.UintN = SystemContext.SystemContextIa32->Dr7; + + if (Register == 0) { + SystemContext.SystemContextIa32->Dr0 = Address; + Dr7.Bits.G0 = 0; + Dr7.Bits.RW0 = 0; + Dr7.Bits.LEN0 = 0; + } else if (Register == 1) { + SystemContext.SystemContextIa32->Dr1 = Address; + Dr7.Bits.G1 = 0; + Dr7.Bits.RW1 = 0; + Dr7.Bits.LEN1 = 0; + } else if (Register == 2) { + SystemContext.SystemContextIa32->Dr2 = Address; + Dr7.Bits.G2 = 0; + Dr7.Bits.RW2 = 0; + Dr7.Bits.LEN2 = 0; + } else if (Register == 3) { + SystemContext.SystemContextIa32->Dr3 = Address; + Dr7.Bits.G3 = 0; + Dr7.Bits.RW3 = 0; + Dr7.Bits.LEN3 = 0; + } else { + return EFI_INVALID_PARAMETER; + } + + //Update DR7 register so appropriate Gn, RWn and LENn bits can be turned off. + SystemContext.SystemContextIa32->Dr7 = Dr7.UintN; + + return EFI_SUCCESS; +} + + +/** + ‘Z1, [addr], [length]’ + ‘Z2, [addr], [length]’ + ‘Z3, [addr], [length]’ + ‘Z4, [addr], [length]’ + + Insert hardware breakpoint/watchpoint at address addr of size length + + @param SystemContext Register content at time of the exception + @param *PacketData Pointer to the Payload data for the packet + +**/ +VOID +EFIAPI +InsertBreakPoint ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ) +{ + UINTN Type; + UINTN Address; + UINTN Length; + UINTN Register; + EFI_STATUS Status; + BREAK_TYPE BreakType = NotSupported; + UINTN ErrorCode; + + ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length); + if (ErrorCode > 0) { + SendError ((UINT8)ErrorCode); + return; + } + + switch (Type) { + + case 0: //Software breakpoint + BreakType = SoftwareBreakpoint; + break; + + case 1: //Hardware breakpoint + BreakType = InstructionExecution; + break; + + case 2: //Write watchpoint + BreakType = DataWrite; + break; + + case 3: //Read watchpoint + BreakType = DataRead; + break; + + case 4: //Access watchpoint + BreakType = DataReadWrite; + break; + + default : + Print ((CHAR16 *)L"Insert breakpoint default: %x\n", Type); + SendError (GDB_EINVALIDBRKPOINTTYPE); + return; + } + + // Find next free debug register + Status = FindNextFreeDebugRegister (SystemContext, &Register); + if (EFI_ERROR(Status)) { + Print ((CHAR16 *)L"No space left on device\n"); + SendError (GDB_ENOSPACE); + return; + } + + // Write Address, length data at particular DR register + Status = EnableDebugRegister (SystemContext, Register, Address, Length, (UINTN)BreakType); + if (EFI_ERROR(Status)) { + if (Status == EFI_UNSUPPORTED) { + SendNotSupported(); + return; + } + + SendError (GDB_EINVALIDARG); + return; + } + + SendSuccess (); +} + + +/** + ‘z1, [addr], [length]’ + ‘z2, [addr], [length]’ + ‘z3, [addr], [length]’ + ‘z4, [addr], [length]’ + + Remove hardware breakpoint/watchpoint at address addr of size length + + @param *PacketData Pointer to the Payload data for the packet + +**/ +VOID +EFIAPI +RemoveBreakPoint ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN CHAR8 *PacketData + ) +{ + UINTN Type; + UINTN Address; + UINTN Length; + UINTN Register; + BREAK_TYPE BreakType = NotSupported; + EFI_STATUS Status; + UINTN ErrorCode; + + //Parse breakpoint packet data + ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length); + if (ErrorCode > 0) { + SendError ((UINT8)ErrorCode); + return; + } + + switch (Type) { + + case 0: //Software breakpoint + BreakType = SoftwareBreakpoint; + break; + + case 1: //Hardware breakpoint + BreakType = InstructionExecution; + break; + + case 2: //Write watchpoint + BreakType = DataWrite; + break; + + case 3: //Read watchpoint + BreakType = DataRead; + break; + + case 4: //Access watchpoint + BreakType = DataReadWrite; + break; + + default : + SendError (GDB_EINVALIDBRKPOINTTYPE); + return; + } + + //Find matching debug register + Status = FindMatchingDebugRegister (SystemContext, Address, Length, (UINTN)BreakType, &Register); + if (EFI_ERROR(Status)) { + if (Status == EFI_UNSUPPORTED) { + SendNotSupported(); + return; + } + + SendError (GDB_ENOSPACE); + return; + } + + //Remove breakpoint + Status = DisableDebugRegister(SystemContext, Register); + if (EFI_ERROR(Status)) { + SendError (GDB_EINVALIDARG); + return; + } + + SendSuccess (); +} + + +/**
+ Initialize debug agent.
+
+ This function is used to set up debug enviroment. It may enable interrupts.
+
+ @param[in] InitFlag Init flag is used to decide initialize process.
+ @param[in] Context Context needed according to InitFlag, it was optional.
+
+**/
+VOID
+EFIAPI
+InitializeDebugAgent (
+ IN UINT32 InitFlag,
+ IN VOID *Context OPTIONAL
+ )
+{
+ // BugBug: Add the code to build an GDT/IDT
+}
+
diff --git a/EmbeddedPkg/Library/GdbDebugAgent/X64/Processor.c b/EmbeddedPkg/Library/GdbDebugAgent/X64/Processor.c new file mode 100755 index 0000000000..0758bc4a84 --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/X64/Processor.c @@ -0,0 +1,963 @@ +/** @file
+ Processor specific parts of the GDB stub
+
+ Copyright (c) 2008-2009, Apple Inc. All rights reserved.
+
+ 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 <GdbStubInternal.h>
+
+//
+// Array of exception types that need to be hooked by the debugger
+//
+EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {
+ { EXCEPT_X64_DIVIDE_ERROR, GDB_SIGFPE },
+ { EXCEPT_X64_DEBUG, GDB_SIGTRAP },
+ { EXCEPT_X64_NMI, GDB_SIGEMT },
+ { EXCEPT_X64_BREAKPOINT, GDB_SIGTRAP },
+ { EXCEPT_X64_OVERFLOW, GDB_SIGSEGV },
+ { EXCEPT_X64_BOUND, GDB_SIGSEGV },
+ { EXCEPT_X64_INVALID_OPCODE, GDB_SIGILL },
+ { EXCEPT_X64_DOUBLE_FAULT, GDB_SIGEMT },
+ { EXCEPT_X64_STACK_FAULT, GDB_SIGSEGV },
+ { EXCEPT_X64_GP_FAULT, GDB_SIGSEGV },
+ { EXCEPT_X64_PAGE_FAULT, GDB_SIGSEGV },
+ { EXCEPT_X64_FP_ERROR, GDB_SIGEMT },
+ { EXCEPT_X64_ALIGNMENT_CHECK, GDB_SIGEMT },
+ { EXCEPT_X64_MACHINE_CHECK, GDB_SIGEMT }
+};
+
+
+// The offsets of registers SystemContextX64.
+// The fields in the array are in the gdb ordering.
+// HAVE TO DOUBLE-CHECK THE ORDER of the 24 regs
+//
+UINTN gRegisterOffsets[] = {
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rax),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rcx),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rdx),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rbx),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rsp),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rbp),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rsi),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rdi),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rip),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Rflags),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Cs),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Ss),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Ds),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Es),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Fs),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, Gs),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R8),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R9),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R10),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R11),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R12),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R13),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R14),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_X64, R15)
+};
+
+
+/**
+ Return the number of entries in the gExceptionType[]
+
+ @retval UINTN, the number of entries in the gExceptionType[] array.
+ **/
+UINTN
+MaxEfiException (
+ VOID
+ )
+{
+ return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY);
+}
+
+
+/**
+ Return the number of entries in the gRegisters[]
+
+ @retval UINTN, the number of entries (registers) in the gRegisters[] array.
+ **/
+UINTN
+MaxRegisterCount (
+ VOID
+ )
+{
+ return sizeof (gRegisterOffsets)/sizeof (UINTN);
+}
+
+
+/**
+ Check to see if the ISA is supported.
+ ISA = Instruction Set Architecture
+
+ @retval TRUE if Isa is supported
+**/
+BOOLEAN
+CheckIsa (
+ IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
+ )
+{
+ return (BOOLEAN)(Isa == IsaX64);
+}
+
+
+/**
+ This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
+ It is, by default, set to find the register pointer of the X64 member
+ @param SystemContext Register content at time of the exception
+ @param RegNumber The register to which we want to find a pointer
+ @retval the pointer to the RegNumber-th pointer
+ **/
+UINTN *
+FindPointerToRegister(
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber
+ )
+{
+ UINT8 *TempPtr;
+ TempPtr = ((UINT8 *)SystemContext.SystemContextX64) + gRegisterOffsets[RegNumber];
+ return (UINTN *)TempPtr;
+}
+
+
+/**
+ Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
+ @param SystemContext Register content at time of the exception
+ @param RegNumber the number of the register that we want to read
+ @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
+ @retval the pointer to the next character of the output buffer that is available to be written on.
+ **/
+CHAR8 *
+BasicReadRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber,
+ IN CHAR8 *OutBufPtr
+ )
+{
+ UINTN RegSize;
+
+ RegSize = 0;
+ while (RegSize < 64) {
+ *OutBufPtr++ = mHexToStr[((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)];
+ *OutBufPtr++ = mHexToStr[((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)];
+ RegSize = RegSize + 8;
+ }
+ return OutBufPtr;
+}
+
+
+/** ‘p n’
+ Reads the n-th register's value into an output buffer and sends it as a packet
+ @param SystemContext Register content at time of the exception
+ @param InBuffer Pointer to the input buffer received from gdb server
+ **/
+VOID
+ReadNthRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ )
+{
+ UINTN RegNumber;
+ CHAR8 OutBuffer[17]; // 1 reg=16 hex chars, and the end '\0' (escape seq)
+ CHAR8 *OutBufPtr; // pointer to the output buffer
+
+ RegNumber = AsciiStrHexToUintn (&InBuffer[1]);
+
+ if ((RegNumber < 0) || (RegNumber >= MaxRegisterCount())) {
+ SendError (GDB_EINVALIDREGNUM);
+ return;
+ }
+
+ OutBufPtr = OutBuffer;
+ OutBufPtr = BasicReadRegister(SystemContext, RegNumber, OutBufPtr);
+
+ *OutBufPtr = '\0'; // the end of the buffer
+ SendPacket (OutBuffer);
+}
+
+
+/** ‘g’
+ Reads the general registers into an output buffer and sends it as a packet
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+EFIAPI
+ReadGeneralRegisters (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ UINTN i;
+ CHAR8 OutBuffer[385]; // 24 regs, 16 hex chars each, and the end '\0' (escape seq)
+ CHAR8 *OutBufPtr; // pointer to the output buffer
+
+ OutBufPtr = OutBuffer;
+ for(i = 0 ; i < MaxRegisterCount() ; i++) { // there are only 24 registers to read
+ OutBufPtr = BasicReadRegister(SystemContext, i, OutBufPtr);
+ }
+
+ *OutBufPtr = '\0'; // the end of the buffer
+ SendPacket (OutBuffer);
+}
+
+
+/**
+ Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
+
+ @param SystemContext Register content at time of the exception
+ @param RegNumber the number of the register that we want to write
+ @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
+ @retval the pointer to the next character of the input buffer that can be used
+ **/
+CHAR8 *
+BasicWriteRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber,
+ IN CHAR8 *InBufPtr
+ )
+{
+ UINTN RegSize;
+ UINTN TempValue; // the value transferred from a hex char
+ UINT64 NewValue; // the new value of the RegNumber-th Register
+
+ NewValue = 0;
+ RegSize = 0;
+ while (RegSize < 64) {
+ TempValue = HexCharToInt(*InBufPtr++);
+
+ if (TempValue < 0) {
+ SendError (GDB_EBADMEMDATA);
+ return NULL;
+ }
+
+ NewValue += (TempValue << (RegSize+4));
+ TempValue = HexCharToInt(*InBufPtr++);
+
+ if (TempValue < 0) {
+ SendError (GDB_EBADMEMDATA);
+ return NULL;
+ }
+
+ NewValue += (TempValue << RegSize);
+ RegSize = RegSize + 8;
+ }
+ *(FindPointerToRegister(SystemContext, RegNumber)) = NewValue;
+ return InBufPtr;
+}
+
+
+/** ‘P n...=r...’
+ Writes the new value of n-th register received into the input buffer to the n-th register
+
+ @param SystemContext Register content at time of the exception
+ @param InBuffer Ponter to the input buffer received from gdb server
+ **/
+VOID
+EFIAPI
+WriteNthRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ )
+{
+ UINTN RegNumber;
+ CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array
+ CHAR8 *RegNumBufPtr;
+ CHAR8 *InBufPtr; // pointer to the input buffer
+
+ // find the register number to write
+ InBufPtr = &InBuffer[1];
+ RegNumBufPtr = RegNumBuffer;
+ while (*InBufPtr != '=') {
+ *RegNumBufPtr++ = *InBufPtr++;
+ }
+ *RegNumBufPtr = '\0';
+ RegNumber = AsciiStrHexToUintn (RegNumBuffer);
+
+ // check if this is a valid Register Number
+ if ((RegNumber < 0) || (RegNumber >= MaxRegisterCount())) {
+ SendError (GDB_EINVALIDREGNUM);
+ return;
+ }
+ InBufPtr++; // skips the '=' character
+ BasicWriteRegister (SystemContext, RegNumber, InBufPtr);
+ SendSuccess();
+}
+
+
+/** ‘G XX...’
+ Writes the new values received into the input buffer to the general registers
+
+ @param SystemContext Register content at time of the exception
+ @param InBuffer Pointer to the input buffer received from gdb server
+ **/
+VOID
+EFIAPI
+WriteGeneralRegisters (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ )
+{
+ UINTN i;
+ CHAR8 *InBufPtr; /// pointer to the input buffer
+
+ // check to see if the buffer is the right size which is
+ // 1 (for 'G') + 16 (for 16 registers) * 8 ( for 8 hex chars each) = 385
+ if (AsciiStrLen(InBuffer) != 385) { // 24 regs, 16 hex chars each, and the end '\0' (escape seq)
+ //Bad message. Message is not the right length
+ SendError (GDB_EBADBUFSIZE);
+ return;
+ }
+
+ InBufPtr = &InBuffer[1];
+
+ // Read the new values for the registers from the input buffer to an array, NewValueArray.
+ // The values in the array are in the gdb ordering
+ for(i=0; i < MaxRegisterCount(); i++) { // there are only 16 registers to write
+ InBufPtr = BasicWriteRegister(SystemContext, i, InBufPtr);
+ }
+
+ SendSuccess();
+}
+
+
+ /**
+ Insert Single Step in the SystemContext
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+AddSingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ SystemContext.SystemContextX64->Rflags |= TF_BIT; //Setting the TF bit.
+}
+
+
+
+/**
+ Remove Single Step in the SystemContext
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+RemoveSingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ SystemContext.SystemContextX64->Rflags &= ~TF_BIT; // clearing the TF bit.
+}
+
+
+
+/** ‘c [addr ]’
+ Continue. addr is Address to resume. If addr is omitted, resume at current
+ Address.
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+EFIAPI
+ContinueAtAddress (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ if (PacketData[1] != '\0') {
+ SystemContext.SystemContextX64->Rip = AsciiStrHexToUintn(&PacketData[1]);
+ }
+}
+
+
+/** ‘s [addr ]’
+ Single step. addr is the Address at which to resume. If addr is omitted, resume
+ at same Address.
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+EFIAPI
+SingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ if (PacketData[1] != '\0') {
+ SystemContext.SystemContextX64->Rip = AsciiStrHexToUintn (&PacketData[1]);
+ }
+
+ AddSingleStep (SystemContext);
+}
+
+
+/**
+ Returns breakpoint data address from DR0-DR3 based on the input breakpoint
+ number
+
+ @param SystemContext Register content at time of the exception
+ @param BreakpointNumber Breakpoint number
+
+ @retval Address Data address from DR0-DR3 based on the
+ breakpoint number.
+
+**/
+UINTN
+GetBreakpointDataAddress (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN BreakpointNumber
+ )
+{
+ UINTN Address;
+
+ if (BreakpointNumber == 1) {
+ Address = SystemContext.SystemContextIa32->Dr0;
+ } else if (BreakpointNumber == 2) {
+ Address = SystemContext.SystemContextIa32->Dr1;
+ } else if (BreakpointNumber == 3) {
+ Address = SystemContext.SystemContextIa32->Dr2;
+ } else if (BreakpointNumber == 4) {
+ Address = SystemContext.SystemContextIa32->Dr3;
+ } else {
+ Address = 0;
+ }
+
+ return Address;
+}
+
+/**
+ Returns currently detected breakpoint value based on the register
+ DR6 B0-B3 field.
+ If no breakpoint is detected then it returns 0.
+
+ @param SystemContext Register content at time of the exception
+
+ @retval {1-4} Currently detected breakpoint value
+ @retval 0 No breakpoint detected.
+
+**/
+UINTN
+GetBreakpointDetected (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ IA32_DR6 Dr6;
+ UINTN BreakpointNumber;
+
+ Dr6.UintN = SystemContext.SystemContextIa32->Dr6;
+
+ if (Dr6.Bits.B0 == 1) {
+ BreakpointNumber = 1;
+ } else if (Dr6.Bits.B1 == 1) {
+ BreakpointNumber = 2;
+ } else if (Dr6.Bits.B2 == 1) {
+ BreakpointNumber = 3;
+ } else if (Dr6.Bits.B3 == 1) {
+ BreakpointNumber = 4;
+ } else {
+ BreakpointNumber = 0; //No breakpoint detected
+ }
+
+ return BreakpointNumber;
+}
+
+/**
+ Returns Breakpoint type (InstructionExecution, DataWrite, DataRead
+ or DataReadWrite) based on the Breakpoint number
+
+ @param SystemContext Register content at time of the exception
+ @param BreakpointNumber Breakpoint number
+
+ @retval BREAK_TYPE Breakpoint type value read from register DR7 RWn
+ field. For unknown value, it returns NotSupported.
+
+**/
+BREAK_TYPE
+GetBreakpointType (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN BreakpointNumber
+ )
+{
+ IA32_DR7 Dr7;
+ BREAK_TYPE Type = NotSupported; //Default is NotSupported type
+
+ Dr7.UintN = SystemContext.SystemContextIa32->Dr7;
+
+ if (BreakpointNumber == 1) {
+ Type = (BREAK_TYPE) Dr7.Bits.RW0;
+ } else if (BreakpointNumber == 2) {
+ Type = (BREAK_TYPE) Dr7.Bits.RW1;
+ } else if (BreakpointNumber == 3) {
+ Type = (BREAK_TYPE) Dr7.Bits.RW2;
+ } else if (BreakpointNumber == 4) {
+ Type = (BREAK_TYPE) Dr7.Bits.RW3;
+ }
+
+ return Type;
+}
+
+
+/**
+ Parses Length and returns the length which DR7 LENn field accepts.
+ For example: If we receive 1-Byte length then we should return 0.
+ Zero gets written to DR7 LENn field.
+
+ @param Length Breakpoint length in Bytes (1 byte, 2 byte, 4 byte)
+
+ @retval Length Appropriate converted values which DR7 LENn field accepts.
+
+**/
+UINTN
+ConvertLengthData (
+ IN UINTN Length
+ )
+{
+ if (Length == 1) { //1-Byte length
+ return 0;
+ } else if (Length == 2) { //2-Byte length
+ return 1;
+ } else if (Length == 4) { //4-Byte length
+ return 3;
+ } else { //Undefined or 8-byte length
+ return 2;
+ }
+}
+
+
+/**
+ Finds the next free debug register. If all the registers are occupied then
+ EFI_OUT_OF_RESOURCES is returned.
+
+ @param SystemContext Register content at time of the exception
+ @param Register Register value (0 - 3 for the first free debug register)
+
+ @retval EFI_STATUS Appropriate status value.
+
+**/
+EFI_STATUS
+FindNextFreeDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ OUT UINTN *Register
+ )
+{
+ IA32_DR7 Dr7;
+
+ Dr7.UintN = SystemContext.SystemContextIa32->Dr7;
+
+ if (Dr7.Bits.G0 == 0) {
+ *Register = 0;
+ } else if (Dr7.Bits.G1 == 0) {
+ *Register = 1;
+ } else if (Dr7.Bits.G2 == 0) {
+ *Register = 2;
+ } else if (Dr7.Bits.G3 == 0) {
+ *Register = 3;
+ } else {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Enables the debug register. Writes Address value to appropriate DR0-3 register.
+ Sets LENn, Gn, RWn bits in DR7 register.
+
+ @param SystemContext Register content at time of the exception
+ @param Register Register value (0 - 3)
+ @param Address Breakpoint address value
+ @param Type Breakpoint type (Instruction, Data write,
+ Data read or write etc.)
+
+ @retval EFI_STATUS Appropriate status value.
+
+**/
+EFI_STATUS
+EnableDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN Register,
+ IN UINTN Address,
+ IN UINTN Length,
+ IN UINTN Type
+ )
+{
+ IA32_DR7 Dr7;
+
+ //Convert length data
+ Length = ConvertLengthData (Length);
+
+ //For Instruction execution, length should be 0
+ //(Ref. Intel reference manual 18.2.4)
+ if ((Type == 0) && (Length != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle
+ //software breakpoint. We should send empty packet in both these cases.
+ if ((Type == (BREAK_TYPE)DataRead) ||
+ (Type == (BREAK_TYPE)SoftwareBreakpoint)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //Read DR7 so appropriate Gn, RWn and LENn bits can be modified.
+ Dr7.UintN = SystemContext.SystemContextIa32->Dr7;
+
+ if (Register == 0) {
+ SystemContext.SystemContextIa32->Dr0 = Address;
+ Dr7.Bits.G0 = 1;
+ Dr7.Bits.RW0 = Type;
+ Dr7.Bits.LEN0 = Length;
+ } else if (Register == 1) {
+ SystemContext.SystemContextIa32->Dr1 = Address;
+ Dr7.Bits.G1 = 1;
+ Dr7.Bits.RW1 = Type;
+ Dr7.Bits.LEN1 = Length;
+ } else if (Register == 2) {
+ SystemContext.SystemContextIa32->Dr2 = Address;
+ Dr7.Bits.G2 = 1;
+ Dr7.Bits.RW2 = Type;
+ Dr7.Bits.LEN2 = Length;
+ } else if (Register == 3) {
+ SystemContext.SystemContextIa32->Dr3 = Address;
+ Dr7.Bits.G3 = 1;
+ Dr7.Bits.RW3 = Type;
+ Dr7.Bits.LEN3 = Length;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //Update Dr7 with appropriate Gn, RWn and LENn bits
+ SystemContext.SystemContextIa32->Dr7 = Dr7.UintN;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Returns register number 0 - 3 for the maching debug register.
+ This function compares incoming Address, Type, Length and
+ if there is a match then it returns the appropriate register number.
+ In case of mismatch, function returns EFI_NOT_FOUND message.
+
+ @param SystemContext Register content at time of the exception
+ @param Address Breakpoint address value
+ @param Length Breakpoint length value
+ @param Type Breakpoint type (Instruction, Data write, Data read
+ or write etc.)
+ @param Register Register value to be returned
+
+ @retval EFI_STATUS Appropriate status value.
+
+**/
+EFI_STATUS
+FindMatchingDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN Address,
+ IN UINTN Length,
+ IN UINTN Type,
+ OUT UINTN *Register
+ )
+{
+ IA32_DR7 Dr7;
+
+ //Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle
+ //software breakpoint. We should send empty packet in both these cases.
+ if ((Type == (BREAK_TYPE)DataRead) ||
+ (Type == (BREAK_TYPE)SoftwareBreakpoint)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //Convert length data
+ Length = ConvertLengthData(Length);
+
+ Dr7.UintN = SystemContext.SystemContextIa32->Dr7;
+
+ if ((Dr7.Bits.G0 == 1) &&
+ (Dr7.Bits.LEN0 == Length) &&
+ (Dr7.Bits.RW0 == Type) &&
+ (Address == SystemContext.SystemContextIa32->Dr0)) {
+ *Register = 0;
+ } else if ((Dr7.Bits.G1 == 1) &&
+ (Dr7.Bits.LEN1 == Length) &&
+ (Dr7.Bits.RW1 == Type) &&
+ (Address == SystemContext.SystemContextIa32->Dr1)) {
+ *Register = 1;
+ } else if ((Dr7.Bits.G2 == 1) &&
+ (Dr7.Bits.LEN2 == Length) &&
+ (Dr7.Bits.RW2 == Type) &&
+ (Address == SystemContext.SystemContextIa32->Dr2)) {
+ *Register = 2;
+ } else if ((Dr7.Bits.G3 == 1) &&
+ (Dr7.Bits.LEN3 == Length) &&
+ (Dr7.Bits.RW3 == Type) &&
+ (Address == SystemContext.SystemContextIa32->Dr3)) {
+ *Register = 3;
+ } else {
+ Print ((CHAR16 *)L"No match found..\n");
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Disables the particular debug register.
+
+ @param SystemContext Register content at time of the exception
+ @param Register Register to be disabled
+
+ @retval EFI_STATUS Appropriate status value.
+
+**/
+EFI_STATUS
+DisableDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN Register
+ )
+{
+ IA32_DR7 Dr7;
+ UINTN Address = 0;
+
+ //Read DR7 register so appropriate Gn, RWn and LENn bits can be turned off.
+ Dr7.UintN = SystemContext.SystemContextIa32->Dr7;
+
+ if (Register == 0) {
+ SystemContext.SystemContextIa32->Dr0 = Address;
+ Dr7.Bits.G0 = 0;
+ Dr7.Bits.RW0 = 0;
+ Dr7.Bits.LEN0 = 0;
+ } else if (Register == 1) {
+ SystemContext.SystemContextIa32->Dr1 = Address;
+ Dr7.Bits.G1 = 0;
+ Dr7.Bits.RW1 = 0;
+ Dr7.Bits.LEN1 = 0;
+ } else if (Register == 2) {
+ SystemContext.SystemContextIa32->Dr2 = Address;
+ Dr7.Bits.G2 = 0;
+ Dr7.Bits.RW2 = 0;
+ Dr7.Bits.LEN2 = 0;
+ } else if (Register == 3) {
+ SystemContext.SystemContextIa32->Dr3 = Address;
+ Dr7.Bits.G3 = 0;
+ Dr7.Bits.RW3 = 0;
+ Dr7.Bits.LEN3 = 0;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //Update DR7 register so appropriate Gn, RWn and LENn bits can be turned off.
+ SystemContext.SystemContextIa32->Dr7 = Dr7.UintN;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ ‘Z1, [addr], [length]’
+ ‘Z2, [addr], [length]’
+ ‘Z3, [addr], [length]’
+ ‘Z4, [addr], [length]’
+
+ Insert hardware breakpoint/watchpoint at address addr of size length
+
+ @param SystemContext Register content at time of the exception
+ @param *PacketData Pointer to the Payload data for the packet
+
+**/
+VOID
+EFIAPI
+InsertBreakPoint (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ UINTN Type;
+ UINTN Address;
+ UINTN Length;
+ UINTN Register;
+ EFI_STATUS Status;
+ BREAK_TYPE BreakType = NotSupported;
+ UINTN ErrorCode;
+
+ ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length);
+ if (ErrorCode > 0) {
+ SendError ((UINT8)ErrorCode);
+ return;
+ }
+
+ switch (Type) {
+
+ case 0: //Software breakpoint
+ BreakType = SoftwareBreakpoint;
+ break;
+
+ case 1: //Hardware breakpoint
+ BreakType = InstructionExecution;
+ break;
+
+ case 2: //Write watchpoint
+ BreakType = DataWrite;
+ break;
+
+ case 3: //Read watchpoint
+ BreakType = DataRead;
+ break;
+
+ case 4: //Access watchpoint
+ BreakType = DataReadWrite;
+ break;
+
+ default :
+ Print ((CHAR16 *)L"Insert breakpoint default: %x\n", Type);
+ SendError (GDB_EINVALIDBRKPOINTTYPE);
+ return;
+ }
+
+ // Find next free debug register
+ Status = FindNextFreeDebugRegister (SystemContext, &Register);
+ if (EFI_ERROR(Status)) {
+ Print ((CHAR16 *)L"No space left on device\n");
+ SendError (GDB_ENOSPACE);
+ return;
+ }
+
+ // Write Address, length data at particular DR register
+ Status = EnableDebugRegister (SystemContext, Register, Address, Length, (UINTN)BreakType);
+ if (EFI_ERROR(Status)) {
+
+ if (Status == EFI_UNSUPPORTED) {
+ Print ((CHAR16 *)L"Not supported\n");
+ SendNotSupported();
+ return;
+ }
+
+ Print ((CHAR16 *)L"Invalid argument\n");
+ SendError (GDB_EINVALIDARG);
+ return;
+ }
+
+ SendSuccess ();
+}
+
+
+/**
+ ‘z1, [addr], [length]’
+ ‘z2, [addr], [length]’
+ ‘z3, [addr], [length]’
+ ‘z4, [addr], [length]’
+
+ Remove hardware breakpoint/watchpoint at address addr of size length
+
+ @param *PacketData Pointer to the Payload data for the packet
+
+**/
+VOID
+EFIAPI
+RemoveBreakPoint (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ UINTN Type;
+ UINTN Address;
+ UINTN Length;
+ UINTN Register;
+ BREAK_TYPE BreakType = NotSupported;
+ EFI_STATUS Status;
+ UINTN ErrorCode;
+
+ //Parse breakpoint packet data
+ ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length);
+ if (ErrorCode > 0) {
+ SendError ((UINT8)ErrorCode);
+ return;
+ }
+
+ switch (Type) {
+
+ case 0: //Software breakpoint
+ BreakType = SoftwareBreakpoint;
+ break;
+
+ case 1: //Hardware breakpoint
+ BreakType = InstructionExecution;
+ break;
+
+ case 2: //Write watchpoint
+ BreakType = DataWrite;
+ break;
+
+ case 3: //Read watchpoint
+ BreakType = DataRead;
+ break;
+
+ case 4: //Access watchpoint
+ BreakType = DataReadWrite;
+ break;
+
+ default :
+ SendError (GDB_EINVALIDBRKPOINTTYPE);
+ return;
+ }
+
+ //Find matching debug register
+ Status = FindMatchingDebugRegister (SystemContext, Address, Length, (UINTN)BreakType, &Register);
+ if (EFI_ERROR(Status)) {
+
+ if (Status == EFI_UNSUPPORTED) {
+ Print ((CHAR16 *)L"Not supported.\n");
+ SendNotSupported();
+ return;
+ }
+
+ Print ((CHAR16 *)L"No matching register found.\n");
+ SendError (GDB_ENOSPACE);
+ return;
+ }
+
+ //Remove breakpoint
+ Status = DisableDebugRegister(SystemContext, Register);
+ if (EFI_ERROR(Status)) {
+ Print ((CHAR16 *)L"Invalid argument.\n");
+ SendError (GDB_EINVALIDARG);
+ return;
+ }
+
+ SendSuccess ();
+}
+
+
+VOID
+InitializeProcessor (
+ VOID
+ )
+{
+}
+
+BOOLEAN
+ValidateAddress (
+ IN VOID *Address
+ )
+{
+ return TRUE;
+}
+
+BOOLEAN
+ValidateException (
+ IN EFI_EXCEPTION_TYPE ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ return TRUE;
+}
+
diff --git a/EmbeddedPkg/Library/GdbDebugAgent/gdbnotes.txt b/EmbeddedPkg/Library/GdbDebugAgent/gdbnotes.txt new file mode 100755 index 0000000000..b5e303d953 --- /dev/null +++ b/EmbeddedPkg/Library/GdbDebugAgent/gdbnotes.txt @@ -0,0 +1,15 @@ +arm-none-eabi-gcc -march=armv7-a -mthumb t.c -Wl,-nostdlib --emit-relocs
+
+target remote com7
+set debug remote 1
+set remotetimeout 30
+set remotelogfile log.txt
+add-symbol-file c:/work/edk2/Build/BeagleBoard/DEBUG_ARMGCC/ARM/BeagleBoardPkg/Sec/Sec/DEBUG/BeagleBoardSec.dll 0x80008360
+
+
+qSupported
+Hg0
+
+Hc-1
+qC
+qAttached
\ No newline at end of file |