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
This commit is contained in:
@ -40,9 +40,10 @@
|
|||||||
RealTimeClockLib|Include/Library/RealTimeClockLib.h
|
RealTimeClockLib|Include/Library/RealTimeClockLib.h
|
||||||
EfiResetSystemLib|Include/Library/EfiResetSystemLib.h
|
EfiResetSystemLib|Include/Library/EfiResetSystemLib.h
|
||||||
EblCmdLib|Include/Library/EblCmdLib.h
|
EblCmdLib|Include/Library/EblCmdLib.h
|
||||||
EblAddExternalCommandLib|Library/EblAddExternalCommandLib.h
|
EblAddExternalCommandLib|Include/Library/EblAddExternalCommandLib.h
|
||||||
EblNetworkLib|Library/EblNetworkLib.h
|
EblNetworkLib|Include/Library/EblNetworkLib.h
|
||||||
GdbSerialLib|Include/Library/GdbSerialLib.h
|
GdbSerialLib|Include/Library/GdbSerialLib.h
|
||||||
|
DebugAgentTimerLib|Include/Library/DebugAgentTimerLib.h
|
||||||
|
|
||||||
|
|
||||||
[Guids.common]
|
[Guids.common]
|
||||||
|
@ -88,8 +88,6 @@ UINTN gRegisterOffsets[] = {
|
|||||||
0x00000F72,
|
0x00000F72,
|
||||||
0x00000F73,
|
0x00000F73,
|
||||||
0x00000FFF, // fps
|
0x00000FFF, // fps
|
||||||
0x00000FFF,
|
|
||||||
0x00000FFF,
|
|
||||||
OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)
|
OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
62
EmbeddedPkg/Include/Library/DebugAgentTimerLib.h
Executable file
62
EmbeddedPkg/Include/Library/DebugAgentTimerLib.h
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
/** @file
|
||||||
|
Platform specific Debug Agent abstraction for timer used by the agent.
|
||||||
|
|
||||||
|
The timer is used by the debugger to break into a running program.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef __GDB_TIMER_LIB__
|
||||||
|
#define __GDB_TIMER_LIB__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
64
EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLib.c
Executable file
64
EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLib.c
Executable file
@ -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
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
37
EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLibNull.inf
Executable file
37
EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLibNull.inf
Executable file
@ -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
|
||||||
|
|
258
EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.S
Executable file
258
EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.S
Executable file
@ -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
|
||||||
|
|
259
EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.asm
Executable file
259
EmbeddedPkg/Library/GdbDebugAgent/Arm/ExceptionSupport.ARMv6.asm
Executable file
@ -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
|
||||||
|
|
||||||
|
|
630
EmbeddedPkg/Library/GdbDebugAgent/Arm/Processor.c
Executable file
630
EmbeddedPkg/Library/GdbDebugAgent/Arm/Processor.c
Executable file
@ -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;
|
||||||
|
}
|
||||||
|
|
816
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.c
Executable file
816
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.c
Executable file
@ -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) {
|
||||||
|
// <20>qXfer:libraries:read::offset,length
|
||||||
|
// gInBuffer[22] is offset string, ++Ptr is length string<6E>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
727
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.h
Executable file
727
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.h
Executable file
@ -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
|
69
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.inf
Executable file
69
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.inf
Executable file
@ -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
|
933
EmbeddedPkg/Library/GdbDebugAgent/Ia32/Processor.c
Executable file
933
EmbeddedPkg/Library/GdbDebugAgent/Ia32/Processor.c
Executable file
@ -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
|
||||||
|
}
|
||||||
|
|
963
EmbeddedPkg/Library/GdbDebugAgent/X64/Processor.c
Executable file
963
EmbeddedPkg/Library/GdbDebugAgent/X64/Processor.c
Executable file
@ -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;
|
||||||
|
}
|
||||||
|
|
15
EmbeddedPkg/Library/GdbDebugAgent/gdbnotes.txt
Executable file
15
EmbeddedPkg/Library/GdbDebugAgent/gdbnotes.txt
Executable file
@ -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
|
Reference in New Issue
Block a user