Adding support for BeagleBoard.

ArmPkg - Supoprt for ARM specific things that can change as the architecture changes. Plus semihosting JTAG drivers.
EmbeddedPkg - Generic support for an embeddded platform. Including a light weight command line shell.
BeagleBoardPkg - Platform specifics for BeagleBoard. SD Card works, but USB has issues. Looks like a bug in the open source USB stack (Our internal stack works fine).


git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@9518 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
AJFISH
2009-12-06 01:57:05 +00:00
parent f7753a96ba
commit 2ef2b01e07
294 changed files with 47954 additions and 0 deletions

View File

@@ -0,0 +1,717 @@
/** @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.
**/
/** @file
Copyright (c) 2008, Apple, Inc
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <GdbStubInternal.h>
#include <Library/CacheMaintenanceLib.h>
#include <Library/PrintLib.h>
//
// 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_SIGEMT },
// { EXCEPT_ARM_RESERVED, GDB_SIGILL }
};
// 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
0x00000FFF,
0x00000FFF,
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);
}
/**
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
)
{
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 >= 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 Index;
CHAR8 *OutBuffer;
CHAR8 *OutBufPtr;
UINTN RegisterCount = MaxRegisterCount();
// It is not safe to allocate pool here....
OutBuffer = AllocatePool((RegisterCount * 8) + 1); // 8 bytes per register in string format plus a null to terminate
OutBufPtr = OutBuffer;
for (Index = 0; Index < RegisterCount; Index++) {
OutBufPtr = BasicReadRegister (SystemContext, Index, OutBufPtr);
}
*OutBufPtr = '\0';
SendPacket(OutBuffer);
FreePool(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 >= 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
UINTN MinLength;
UINTN RegisterCount = MaxRegisterCount();
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 ();
}
// What about Thumb?
// Use SWI 0xdbdbdb as the debug instruction
#define GDB_ARM_BKPT 0xefdbdbdb
BOOLEAN mSingleStepActive = FALSE;
UINT32 mSingleStepPC;
UINT32 mSingleStepData;
UINTN mSingleStepDataSize;
typedef struct {
LIST_ENTRY Link;
UINT64 Signature;
UINT32 Address;
UINT32 Instruction;
} ARM_SOFTWARE_BREAKPOINT;
#define ARM_SOFTWARE_BREAKPOINT_SIGNATURE SIGNATURE_64('A', 'R', 'M', 'B', 'R', 'K', 'P', 'T')
#define ARM_SOFTWARE_BREAKPOINT_FROM_LINK(a) CR(a, ARM_SOFTWARE_BREAKPOINT, Link, ARM_SOFTWARE_BREAKPOINT_SIGNATURE)
LIST_ENTRY BreakpointList;
/**
Insert Single Step in the SystemContext
@param SystemContext Register content at time of the exception
**/
VOID
AddSingleStep (
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
if (mSingleStepActive) {
// Currently don't support nesting
return;
}
mSingleStepActive = TRUE;
mSingleStepPC = SystemContext.SystemContextArm->PC;
mSingleStepDataSize = sizeof (UINT32);
mSingleStepData = (*(UINT32 *)mSingleStepPC);
*(UINT32 *)mSingleStepPC = GDB_ARM_BKPT;
if (*(UINT32 *)mSingleStepPC != GDB_ARM_BKPT) {
// For some reason our breakpoint did not take
mSingleStepActive = FALSE;
}
InvalidateInstructionCacheRange((VOID *)mSingleStepPC, mSingleStepDataSize);
//DEBUG((EFI_D_ERROR, "AddSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, mSingleStepData, *(UINT32 *)mSingleStepPC));
}
/**
Remove Single Step in the SystemContext
@param SystemContext Register content at time of the exception
**/
VOID
RemoveSingleStep (
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
if (!mSingleStepActive) {
return;
}
if (mSingleStepDataSize == sizeof (UINT16)) {
*(UINT16 *)mSingleStepPC = (UINT16)mSingleStepData;
} else {
//DEBUG((EFI_D_ERROR, "RemoveSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, *(UINT32 *)mSingleStepPC, mSingleStepData));
*(UINT32 *)mSingleStepPC = mSingleStepData;
}
InvalidateInstructionCacheRange((VOID *)mSingleStepPC, mSingleStepDataSize);
mSingleStepActive = FALSE;
}
/** 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();
}
UINTN
GetBreakpointDataAddress (
IN EFI_SYSTEM_CONTEXT SystemContext,
IN UINTN BreakpointNumber
)
{
return 0;
}
UINTN
GetBreakpointDetected (
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
return 0;
}
BREAK_TYPE
GetBreakpointType (
IN EFI_SYSTEM_CONTEXT SystemContext,
IN UINTN BreakpointNumber
)
{
return NotSupported;
}
ARM_SOFTWARE_BREAKPOINT *
SearchBreakpointList (
IN UINT32 Address
)
{
LIST_ENTRY *Current;
ARM_SOFTWARE_BREAKPOINT *Breakpoint;
Current = GetFirstNode(&BreakpointList);
while (!IsNull(&BreakpointList, Current)) {
Breakpoint = ARM_SOFTWARE_BREAKPOINT_FROM_LINK(Current);
if (Address == Breakpoint->Address) {
return Breakpoint;
}
Current = GetNextNode(&BreakpointList, Current);
}
return NULL;
}
VOID
SetBreakpoint (
IN UINT32 Address
)
{
ARM_SOFTWARE_BREAKPOINT *Breakpoint;
Breakpoint = SearchBreakpointList(Address);
if (Breakpoint != NULL) {
return;
}
// create and fill breakpoint structure
Breakpoint = AllocatePool(sizeof(ARM_SOFTWARE_BREAKPOINT));
Breakpoint->Signature = ARM_SOFTWARE_BREAKPOINT_SIGNATURE;
Breakpoint->Address = Address;
Breakpoint->Instruction = *(UINT32 *)Address;
// Add it to the list
InsertTailList(&BreakpointList, &Breakpoint->Link);
// Insert the software breakpoint
*(UINT32 *)Address = GDB_ARM_BKPT;
InvalidateInstructionCacheRange((VOID *)Address, 4);
//DEBUG((EFI_D_ERROR, "SetBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, Breakpoint->Instruction, *(UINT32 *)Address));
}
VOID
ClearBreakpoint (
IN UINT32 Address
)
{
ARM_SOFTWARE_BREAKPOINT *Breakpoint;
Breakpoint = SearchBreakpointList(Address);
if (Breakpoint == NULL) {
return;
}
// Add it to the list
RemoveEntryList(&Breakpoint->Link);
// Restore the original instruction
*(UINT32 *)Address = Breakpoint->Instruction;
InvalidateInstructionCacheRange((VOID *)Address, 4);
//DEBUG((EFI_D_ERROR, "ClearBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, GDB_ARM_BKPT, *(UINT32 *)Address));
FreePool(Breakpoint);
}
VOID
EFIAPI
InsertBreakPoint (
IN EFI_SYSTEM_CONTEXT SystemContext,
IN CHAR8 *PacketData
)
{
UINTN Type;
UINTN Address;
UINTN Length;
UINTN ErrorCode;
ErrorCode = ParseBreakpointPacket(PacketData, &Type, &Address, &Length);
if (ErrorCode > 0) {
SendError ((UINT8)ErrorCode);
return;
}
switch (Type) {
case 0: //Software breakpoint
break;
default :
DEBUG((EFI_D_ERROR, "Insert breakpoint default: %x\n", Type));
SendError (GDB_EINVALIDBRKPOINTTYPE);
return;
}
SetBreakpoint(Address);
SendSuccess ();
}
VOID
EFIAPI
RemoveBreakPoint (
IN EFI_SYSTEM_CONTEXT SystemContext,
IN CHAR8 *PacketData
)
{
UINTN Type;
UINTN Address;
UINTN Length;
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
break;
default:
SendError (GDB_EINVALIDBRKPOINTTYPE);
return;
}
ClearBreakpoint(Address);
SendSuccess ();
}
VOID
InitializeProcessor (
VOID
)
{
// Initialize breakpoint list
InitializeListHead(&BreakpointList);
}
BOOLEAN
ValidateAddress (
IN VOID *Address
)
{
if ((UINT32)Address < 0x80000000) {
return FALSE;
} else {
return TRUE;
}
}
BOOLEAN
ValidateException (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN OUT EFI_SYSTEM_CONTEXT SystemContext
)
{
UINT32 ExceptionAddress;
UINT32 Instruction;
// Is it a debugger SWI?
ExceptionAddress = SystemContext.SystemContextArm->PC -= 4;
Instruction = *(UINT32 *)ExceptionAddress;
if (Instruction != GDB_ARM_BKPT) {
return FALSE;
}
// Special for SWI-based exception handling. SWI sets up the context
// to return to the instruction following the SWI instruction - NOT what we want
// for a debugger!
SystemContext.SystemContextArm->PC = ExceptionAddress;
return TRUE;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
#%HEADER%
#/** @file
# UEFI GDB stub
#
# This is a shell application that will display Hello World.
# Copyright (c) 2008, Apple, Inc.
#
# All rights reserved. This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#
#**/
################################################################################
#
# Defines Section - statements that will be processed to create a Makefile.
#
################################################################################
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = GdbStub
FILE_GUID = 1F2CCB4F-D817-404E-98E7-80E4851FB33E
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = GdbStubEntry
[Sources.common]
GdbStub.c
SerialIo.c
[Sources.ARM]
Arm/Processor.c
[Sources.IA32]
Ia32/Processor.c
[Sources.X64]
X64/Processor.c
[Packages]
MdePkg/MdePkg.dec
EmbeddedPkg/EmbeddedPkg.dec
[LibraryClasses]
BaseLib
DebugLib
UefiLib
UefiDriverEntryPoint
UefiBootServicesTableLib
UefiRuntimeServicesTableLib
BaseMemoryLib
MemoryAllocationLib
DevicePathLib
PcdLib
GdbSerialLib
PrintLib
CacheMaintenanceLib
[Protocols]
gEfiDebugSupportProtocolGuid
gEfiDebugPortProtocolGuid
gEfiSerialIoProtocolGuid
[Guids]
gEfiDebugImageInfoTableGuid
[FeaturePcd.common]
gEmbeddedTokenSpaceGuid.PcdGdbSerial
[FixedPcd.common]
gEmbeddedTokenSpaceGuid.PcdGdbMaxPacketRetryCount

View File

@@ -0,0 +1,746 @@
/** @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 __GDB_STUB_INTERNAL__
#define __GDB_STUB_INTERNAL__
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PcdLib.h>
#include <Library/GdbSerialLib.h>
#include <Library/PrintLib.h>
#include <Protocol/DebugSupport.h>
#include <Protocol/SerialIo.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/LoadedImage.h>
#include <Guid/DebugImageInfoTable.h>
#include <IndustryStandard/PeImage.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_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
#define GDB_SERIAL_DEV_SIGNATURE SIGNATURE_32 ('g', 'd', 'b', 's')
typedef struct {
VENDOR_DEVICE_PATH VendorDevice;
UINT32 Index; // Suport more than one
EFI_DEVICE_PATH_PROTOCOL End;
} GDB_SERIAL_DEVICE_PATH;
//
// Name: SERIAL_DEV
// Purpose: To provide device specific information
// Fields:
// Signature UINTN: The identity of the serial device
// SerialIo SERIAL_IO_PROTOCOL: Serial I/O protocol interface
// SerialMode SERIAL_IO_MODE:
// DevicePath EFI_DEVICE_PATH_PROTOCOL *: Device path of the serial device
//
typedef struct {
UINTN Signature;
EFI_HANDLE Handle;
EFI_SERIAL_IO_PROTOCOL SerialIo;
EFI_SERIAL_IO_MODE SerialMode;
GDB_SERIAL_DEVICE_PATH DevicePath;
INTN InFileDescriptor;
INTN OutFileDescriptor;
} GDB_SERIAL_DEV;
#define GDB_SERIAL_DEV_FROM_THIS(a) CR (a, GDB_SERIAL_DEV, SerialIo, GDB_SERIAL_DEV_SIGNATURE)
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[];
//
// Set TRUE if F Reply package signals a ctrl-c. We can not process the Ctrl-c
// here we need to wait for the periodic callback to do this.
//
extern BOOLEAN gCtrlCBreakFlag;
//
// 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;
// The offsets of registers SystemContext.
// The fields in the array are in the gdb ordering.
//
extern UINTN gRegisterOffsets[];
/**
Return the number of entries in the gExceptionType[]
@retval UINTN, the number of entries in the gExceptionType[] array.
**/
UINTN
MaxEfiException (
VOID
);
/**
Return the number of entries in the gRegisters[]
@retval UINTN, the number of entries (registers) in the gRegisters[] array.
**/
UINTN
MaxRegisterCount (
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
);
BOOLEAN
ValidateAddress (
IN VOID *Address
);
BOOLEAN
ValidateException (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN OUT EFI_SYSTEM_CONTEXT SystemContext
);
#endif

View File

@@ -0,0 +1,993 @@
/** @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 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);
}
/**
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,
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 >= 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[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 < MaxRegisterCount() ; 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 >= 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) = 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 < 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.SystemContextIa32->Eflags |= 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.SystemContextIa32->Eflags &= ~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.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
)
{
if (PacketData[1] != '\0') {
SystemContext.SystemContextIa32->Eip = 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;
}

View File

@@ -0,0 +1,551 @@
/** @file
Serial IO Abstraction for GDB stub. This allows an EFI consoles that shows up on the system
running GDB. One consle for error information and another console for user input/output.
Basic packet format is $packet-data#checksum. So every comand has 4 bytes of overhead: $,
#, 0, 0. The 0 and 0 are the ascii characters for the checksum.
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>
//
// Set TRUE if F Reply package signals a ctrl-c. We can not process the Ctrl-c
// here we need to wait for the periodic callback to do this.
//
BOOLEAN gCtrlCBreakFlag = FALSE;
//
// 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
//
BOOLEAN gProcessingFPacket = FALSE;
/**
Process a control-C break message.
Currently a place holder, remove the ASSERT when it gets implemented.
@param ErrNo Error infomration from the F reply packet or other source
**/
VOID
GdbCtrlCBreakMessage (
IN UINTN ErrNo
)
{
// See D.10.5 of gdb.pdf
// This should look like a break message. Should look like SIGINT
/* TODO: Make sure if we should do anything with ErrNo */
//Turn on the global Ctrl-C flag.
gCtrlCBreakFlag = TRUE;
}
/**
Parse the F reply packet and extract the return value and an ErrNo if it exists.
@param Packet Packet to parse like an F reply packet
@param ErrNo Buffer to hold Count bytes that were read
@retval -1 Error, not a valid F reply packet
@retval other Return the return code from the F reply packet
**/
INTN
GdbParseFReplyPacket (
IN CHAR8 *Packet,
OUT UINTN *ErrNo
)
{
INTN RetCode;
if (Packet[0] != 'F') {
// A valid responce would be an F packet
return -1;
}
RetCode = AsciiStrHexToUintn (&Packet[1]);
// Find 1st comma
for (;*Packet != '\0' && *Packet != ','; Packet++);
if (*Packet == '\0') {
*ErrNo = 0;
return RetCode;
}
*ErrNo = AsciiStrHexToUintn (++Packet);
// Find 2nd comma
for (;*Packet != '\0' && *Packet != ','; Packet++);
if (*Packet == '\0') {
return RetCode;
}
if (*(++Packet) == 'C') {
GdbCtrlCBreakMessage (*ErrNo);
}
return RetCode;
}
/**
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
)
{
CHAR8 Packet[128];
UINTN Size;
INTN RetCode;
UINTN ErrNo;
BOOLEAN ReceiveDone = FALSE;
// Send:
// "Fread,XX,YYYYYYYY,XX
//
// XX - FileDescriptor in ASCII
// YYYYYYYY - Buffer address in ASCII
// XX - Count in ASCII
// SS - check sum
//
Size = AsciiSPrint (Packet, sizeof (Packet), "Fread,%x,%x,%x", FileDescriptor, Buffer, Count);
// Packet array is too small if you got this ASSERT
ASSERT (Size < sizeof (Packet));
gProcessingFPacket = TRUE;
SendPacket (Packet);
Print ((CHAR16 *)L"Packet sent..\n");
do {
// Reply:
ReceivePacket (Packet, sizeof (Packet));
Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);
// Process GDB commands
switch (Packet[0]) {
//Write memory command.
//M addr,length:XX...
case 'M':
WriteToMemory (Packet);
break;
//Fretcode, errno, Ctrl-C flag
//retcode - Count read
case 'F':
//Once target receives F reply packet that means the previous
//transactions are finished.
ReceiveDone = TRUE;
break;
//Send empty buffer
default :
SendNotSupported();
break;
}
} while (ReceiveDone == FALSE);
RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);
if (ErrNo > 0) {
//Send error to the host if there is any.
SendError ((UINT8)ErrNo);
}
gProcessingFPacket = FALSE;
return RetCode;
}
/**
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
)
{
CHAR8 Packet[128];
UINTN Size;
INTN RetCode;
UINTN ErrNo;
BOOLEAN ReceiveDone = FALSE;
// Send:
// #Fwrite,XX,YYYYYYYY,XX$SS
//
// XX - FileDescriptor in ASCII
// YYYYYYYY - Buffer address in ASCII
// XX - Count in ASCII
// SS - check sum
//
Size = AsciiSPrint (Packet, sizeof (Packet), "Fwrite,%x,%x,%x", FileDescriptor, Buffer, Count);
// Packet array is too small if you got this ASSERT
ASSERT (Size < sizeof (Packet));
SendPacket (Packet);
Print ((CHAR16 *)L"Packet sent..\n");
do {
// Reply:
ReceivePacket (Packet, sizeof (Packet));
Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);
// Process GDB commands
switch (Packet[0]) {
//Read memory command.
//m addr,length.
case 'm':
ReadFromMemory (Packet);
break;
//Fretcode, errno, Ctrl-C flag
//retcode - Count read
case 'F':
//Once target receives F reply packet that means the previous
//transactions are finished.
ReceiveDone = TRUE;
break;
//Send empty buffer
default :
SendNotSupported();
break;
}
} while (ReceiveDone == FALSE);
RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);
//Send error to the host if there is any.
if (ErrNo > 0) {
SendError((UINT8)ErrNo);
}
return RetCode;
}
/**
Reset the serial device.
@param This Protocol instance pointer.
@retval EFI_SUCCESS The device was reset.
@retval EFI_DEVICE_ERROR The serial device could not be reset.
**/
EFI_STATUS
EFIAPI
GdbSerialReset (
IN EFI_SERIAL_IO_PROTOCOL *This
)
{
return EFI_SUCCESS;
}
/**
Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
data buts, and stop bits on a serial device.
@param This Protocol instance pointer.
@param BaudRate The requested baud rate. A BaudRate value of 0 will use the the
device's default interface speed.
@param ReveiveFifoDepth The requested depth of the FIFO on the receive side of the
serial interface. A ReceiveFifoDepth value of 0 will use
the device's dfault FIFO depth.
@param Timeout The requested time out for a single character in microseconds.
This timeout applies to both the transmit and receive side of the
interface. A Timeout value of 0 will use the device's default time
out value.
@param Parity The type of parity to use on this serial device. A Parity value of
DefaultParity will use the device's default parity value.
@param DataBits The number of data bits to use on the serial device. A DataBits
vaule of 0 will use the device's default data bit setting.
@param StopBits The number of stop bits to use on this serial device. A StopBits
value of DefaultStopBits will use the device's default number of
stop bits.
@retval EFI_SUCCESS The device was reset.
@retval EFI_DEVICE_ERROR The serial device could not be reset.
**/
EFI_STATUS
EFIAPI
GdbSerialSetAttributes (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN UINT64 BaudRate,
IN UINT32 ReceiveFifoDepth,
IN UINT32 Timeout,
IN EFI_PARITY_TYPE Parity,
IN UINT8 DataBits,
IN EFI_STOP_BITS_TYPE StopBits
)
{
return EFI_UNSUPPORTED;
}
/**
Set the control bits on a serial device
@param This Protocol instance pointer.
@param Control Set the bits of Control that are settable.
@retval EFI_SUCCESS The new control bits were set on the serial device.
@retval EFI_UNSUPPORTED The serial device does not support this operation.
@retval EFI_DEVICE_ERROR The serial device is not functioning correctly.
**/
EFI_STATUS
EFIAPI
GdbSerialSetControl (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN UINT32 Control
)
{
return EFI_UNSUPPORTED;
}
/**
Retrieves the status of thecontrol bits on a serial device
@param This Protocol instance pointer.
@param Control A pointer to return the current Control signals from the serial device.
@retval EFI_SUCCESS The control bits were read from the serial device.
@retval EFI_DEVICE_ERROR The serial device is not functioning correctly.
**/
EFI_STATUS
EFIAPI
GdbSerialGetControl (
IN EFI_SERIAL_IO_PROTOCOL *This,
OUT UINT32 *Control
)
{
return EFI_UNSUPPORTED;
}
/**
Writes data to a serial device.
@param This Protocol instance pointer.
@param BufferSize On input, the size of the Buffer. On output, the amount of
data actually written.
@param Buffer The buffer of data to write
@retval EFI_SUCCESS The data was written.
@retval EFI_DEVICE_ERROR The device reported an error.
@retval EFI_TIMEOUT The data write was stopped due to a timeout.
**/
EFI_STATUS
EFIAPI
GdbSerialWrite (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN OUT UINTN *BufferSize,
IN VOID *Buffer
)
{
GDB_SERIAL_DEV *SerialDev;
UINTN Return;
SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
Return = GdbWrite (SerialDev->OutFileDescriptor, Buffer, *BufferSize);
if (Return == (UINTN)-1) {
return EFI_DEVICE_ERROR;
}
if (Return != *BufferSize) {
*BufferSize = Return;
}
return EFI_SUCCESS;
}
/**
Writes data to a serial device.
@param This Protocol instance pointer.
@param BufferSize On input, the size of the Buffer. On output, the amount of
data returned in Buffer.
@param Buffer The buffer to return the data into.
@retval EFI_SUCCESS The data was read.
@retval EFI_DEVICE_ERROR The device reported an error.
@retval EFI_TIMEOUT The data write was stopped due to a timeout.
**/
EFI_STATUS
EFIAPI
GdbSerialRead (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
GDB_SERIAL_DEV *SerialDev;
UINTN Return;
SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
Return = GdbRead (SerialDev->InFileDescriptor, Buffer, *BufferSize);
if (Return == (UINTN)-1) {
return EFI_DEVICE_ERROR;
}
if (Return != *BufferSize) {
*BufferSize = Return;
}
return EFI_SUCCESS;
}
//
// Template used to initailize the GDB Serial IO protocols
//
GDB_SERIAL_DEV gdbSerialDevTemplate = {
GDB_SERIAL_DEV_SIGNATURE,
NULL,
{ // SerialIo
SERIAL_IO_INTERFACE_REVISION,
GdbSerialReset,
GdbSerialSetAttributes,
GdbSerialSetControl,
GdbSerialGetControl,
GdbSerialWrite,
GdbSerialRead,
NULL
},
{ // SerialMode
0, // ControlMask
0, // Timeout
0, // BaudRate
1, // RceiveFifoDepth
0, // DataBits
0, // Parity
0 // StopBits
},
{
{
HARDWARE_DEVICE_PATH,
HW_VENDOR_DP,
{
(UINT8) (sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)),
(UINT8) ((sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)) >> 8)
},
EFI_SERIAL_IO_PROTOCOL_GUID,
},
0,
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
(UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
(UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL) >> 8)
}
},
},
GDB_STDIN,
GDB_STDOUT
};
/**
Make two serial consoles: 1) StdIn and StdOut via GDB. 2) StdErr via GDB.
These console show up on the remote system running GDB
**/
VOID
GdbInitializeSerialConsole (
VOID
)
{
EFI_STATUS Status;
GDB_SERIAL_DEV *StdOutSerialDev;
GDB_SERIAL_DEV *StdErrSerialDev;
// Use the template to make a copy of the Serial Console private data structure.
StdOutSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV), &gdbSerialDevTemplate);
ASSERT (StdOutSerialDev != NULL);
// Fixup pointer after the copy
StdOutSerialDev->SerialIo.Mode = &StdOutSerialDev->SerialMode;
StdErrSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV), &gdbSerialDevTemplate);
ASSERT (StdErrSerialDev != NULL);
// Fixup pointer and modify stuff that is different for StdError
StdErrSerialDev->SerialIo.Mode = &StdErrSerialDev->SerialMode;
StdErrSerialDev->DevicePath.Index = 1;
StdErrSerialDev->OutFileDescriptor = GDB_STDERR;
// Make a new handle with Serial IO protocol and its device path on it.
Status = gBS->InstallMultipleProtocolInterfaces (
&StdOutSerialDev->Handle,
&gEfiSerialIoProtocolGuid, &StdOutSerialDev->SerialIo,
&gEfiDevicePathProtocolGuid, &StdOutSerialDev->DevicePath,
NULL
);
ASSERT_EFI_ERROR (Status);
// Make a new handle with Serial IO protocol and its device path on it.
Status = gBS->InstallMultipleProtocolInterfaces (
&StdErrSerialDev->Handle,
&gEfiSerialIoProtocolGuid, &StdErrSerialDev->SerialIo,
&gEfiDevicePathProtocolGuid, &StdErrSerialDev->DevicePath,
NULL
);
ASSERT_EFI_ERROR (Status);
}

View 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;
}