/** @file

Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent


**/

#include "Edb.h"

/**

  Check whether current IP is EBC BREAK3 instruction.

  @param  Address    EBC IP address.

  @retval TRUE       Current IP is EBC BREAK3 instruction
  @retval FALSE      Current IP is not EBC BREAK3 instruction

**/
BOOLEAN
IsEBCBREAK3 (
  IN UINTN            Address
  )
{
  if (GET_OPCODE(Address) != OPCODE_BREAK) {
    return FALSE;
  }

  if (GET_OPERANDS (Address) != 3) {
    return FALSE;
  } else {
    return TRUE;
  }
}

/**

  Check whether the Address is already set in breakpoint.

  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  Address           Breakpoint Address

  @retval TRUE              breakpoint is found
  @retval FALSE             breakpoint is not found

**/
BOOLEAN
DebuggerBreakpointIsDuplicated (
  IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN UINTN                     Address
  )
{
  UINTN  Index;

  //
  // Go through each breakpoint context
  //
  for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) {
    if (DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress == Address) {
      //
      // Found it
      //
      return TRUE;
    }
  }

  //
  // Not found
  //
  return FALSE;
}

/**

  Add this breakpoint.

  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  Address           Breakpoint Address

  @retval EFI_SUCCESS            breakpoint added successfully
  @retval EFI_ALREADY_STARTED    breakpoint is already added
  @retval EFI_OUT_OF_RESOURCES   all the breakpoint entries are used

**/
EFI_STATUS
DebuggerBreakpointAdd (
  IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN UINTN                     Address
  )
{
  //
  // Check duplicated breakpoint
  //
  if (DebuggerBreakpointIsDuplicated (DebuggerPrivate, Address)) {
    EDBPrint (L"Breakpoint duplicated!\n");
    return EFI_ALREADY_STARTED;
  }

  //
  // Check whether the address is a breakpoint 3 instruction
  //
  if (IsEBCBREAK3 (Address)) {
    EDBPrint (L"Breakpoint can not be set on BREAK 3 instruction!\n");
    return EFI_ALREADY_STARTED;
  }

  if (DebuggerPrivate->DebuggerBreakpointCount >= EFI_DEBUGGER_BREAKPOINT_MAX) {
    EDBPrint (L"Breakpoint out of resource!\n");
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Set the breakpoint
  //
  DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].BreakpointAddress = Address;
  DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].State = TRUE;
  DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].OldInstruction = 0;
  CopyMem (
    &DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].OldInstruction,
    (VOID *)Address,
    sizeof(UINT16)
    );

  DebuggerPrivate->DebuggerBreakpointCount ++;

  //
  // Done
  //
  return EFI_SUCCESS;
}

/**

  Delete this breakpoint.

  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  Index             Breakpoint Index

  @retval EFI_SUCCESS     breakpoint deleted successfully
  @retval EFI_NOT_FOUND   breakpoint not found

**/
EFI_STATUS
DebuggerBreakpointDel (
  IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN UINTN                     Index
  )
{
  UINTN    BpIndex;

  if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) ||
      (Index >= DebuggerPrivate->DebuggerBreakpointCount)) {
    return EFI_NOT_FOUND;
  }

  //
  // Delete this breakpoint
  //
  for (BpIndex = Index; BpIndex < DebuggerPrivate->DebuggerBreakpointCount - 1; BpIndex++) {
    DebuggerPrivate->DebuggerBreakpointContext[BpIndex] = DebuggerPrivate->DebuggerBreakpointContext[BpIndex + 1];
  }
  ZeroMem (
    &DebuggerPrivate->DebuggerBreakpointContext[BpIndex],
    sizeof(DebuggerPrivate->DebuggerBreakpointContext[BpIndex])
    );

  DebuggerPrivate->DebuggerBreakpointCount --;

  //
  // Done
  //
  return EFI_SUCCESS;
}

/**

  Disable this breakpoint.

  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  Index             Breakpoint Index

  @retval EFI_SUCCESS     breakpoint disabled successfully
  @retval EFI_NOT_FOUND   breakpoint not found

**/
EFI_STATUS
DebuggerBreakpointDis (
  IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN UINTN                     Index
  )
{
  if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) ||
      (Index >= DebuggerPrivate->DebuggerBreakpointCount)) {
    return EFI_NOT_FOUND;
  }

  //
  // Disable this breakpoint
  //
  DebuggerPrivate->DebuggerBreakpointContext[Index].State = FALSE;

  return EFI_SUCCESS;
}

/**

  Enable this breakpoint.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  Index           - Breakpoint Index

  @retval EFI_SUCCESS   - breakpoint enabled successfully
  @retval EFI_NOT_FOUND - breakpoint not found

**/
EFI_STATUS
DebuggerBreakpointEn (
  IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN UINTN                     Index
  )
{
  if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) ||
      (Index >= DebuggerPrivate->DebuggerBreakpointCount)) {
    return EFI_NOT_FOUND;
  }

  //
  // Enable this breakpoint
  //
  DebuggerPrivate->DebuggerBreakpointContext[Index].State = TRUE;

  return EFI_SUCCESS;
}

/**

  DebuggerCommand - BreakpointList.

  @param  CommandArg      - The argument for this command
  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  ExceptionType   - Exception type.
  @param  SystemContext   - EBC system context.

  @retval EFI_DEBUG_CONTINUE - formal return value

**/
EFI_DEBUG_STATUS
DebuggerBreakpointList (
  IN     CHAR16                    *CommandArg,
  IN     EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN     EFI_EXCEPTION_TYPE        ExceptionType,
  IN OUT EFI_SYSTEM_CONTEXT        SystemContext
  )
{
  UINTN Index;

  //
  // Check breakpoint cound
  //
  if (DebuggerPrivate->DebuggerBreakpointCount == 0) {
    EDBPrint (L"No Breakpoint\n");
    return EFI_DEBUG_CONTINUE;
  } else if (DebuggerPrivate->DebuggerBreakpointCount > EFI_DEBUGGER_BREAKPOINT_MAX) {
    EDBPrint (L"Breakpoint too many!\n");
    DebuggerPrivate->DebuggerBreakpointCount = 0;
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Go through each breakpoint
  //
  EDBPrint (L"Breakpoint :\n");
  EDBPrint (L" Index   Address            Status\n");
  EDBPrint (L"======= ================== ========\n");
//EDBPrint (L"   1    0xFFFFFFFF00000000    *\n");
//EDBPrint (L"  12    0x00000000FFFFFFFF\n");
  for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) {
    //
    // Print the breakpoint
    //
    EDBPrint (L"  %2d    0x%016lx", Index, DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress);
    if (DebuggerPrivate->DebuggerBreakpointContext[Index].State) {
      EDBPrint (L"    *\n");
    } else {
      EDBPrint (L"\n");
    }
  }

  //
  // Done
  //
  return EFI_DEBUG_CONTINUE;
}

/**

  DebuggerCommand - BreakpointSet.

  @param  CommandArg        The argument for this command
  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  ExceptionType     Exception type.
  @param  SystemContext     EBC system context.

  @retval EFI_DEBUG_CONTINUE - formal return value

**/
EFI_DEBUG_STATUS
DebuggerBreakpointSet (
  IN     CHAR16                    *CommandArg,
  IN     EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN     EFI_EXCEPTION_TYPE        ExceptionType,
  IN OUT EFI_SYSTEM_CONTEXT        SystemContext
  )
{
  UINTN      Address;
  EFI_STATUS Status;

  if (CommandArg == NULL) {
    EDBPrint (L"BreakpointSet Argument error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Get breakpoint address
  //
  Status = Symboltoi (CommandArg, &Address);
  if (EFI_ERROR (Status)) {
    if (Status == EFI_NOT_FOUND) {
      Address = Xtoi(CommandArg);
    } else {
      //
      // Something wrong, let Symboltoi print error info.
      //
      EDBPrint (L"Command Argument error!\n");
      return EFI_DEBUG_CONTINUE;
    }
  }

  //
  // Add breakpoint
  //
  Status = DebuggerBreakpointAdd (DebuggerPrivate, Address);
  if (EFI_ERROR(Status)) {
    EDBPrint (L"BreakpointSet error!\n");
  }

  //
  // Done
  //
  return EFI_DEBUG_CONTINUE;
}

/**

  DebuggerCommand - BreakpointClear

  @param  CommandArg        The argument for this command
  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  ExceptionType     Exception type.
  @param  SystemContext     EBC system context.

  @retval EFI_DEBUG_CONTINUE   formal return value

**/
EFI_DEBUG_STATUS
DebuggerBreakpointClear (
  IN     CHAR16                    *CommandArg,
  IN     EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN     EFI_EXCEPTION_TYPE        ExceptionType,
  IN OUT EFI_SYSTEM_CONTEXT        SystemContext
  )
{
  UINTN      Index;
  EFI_STATUS Status;

  if (CommandArg == NULL) {
    EDBPrint (L"BreakpointClear Argument error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  if (StriCmp (CommandArg, L"*") == 0) {
    //
    // delete all breakpoint
    //
    DebuggerPrivate->DebuggerBreakpointCount = 0;
    ZeroMem (DebuggerPrivate->DebuggerBreakpointContext, sizeof(DebuggerPrivate->DebuggerBreakpointContext));
    EDBPrint (L"All the Breakpoint is cleared\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Get breakpoint index
  //
  Index = Atoi(CommandArg);
  if (Index == (UINTN) -1) {
    EDBPrint (L"BreakpointClear Argument error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) ||
      (Index >= DebuggerPrivate->DebuggerBreakpointCount)) {
    EDBPrint (L"BreakpointClear error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Delete breakpoint
  //
  Status = DebuggerBreakpointDel (DebuggerPrivate, Index);
  if (EFI_ERROR(Status)) {
    EDBPrint (L"BreakpointClear error!\n");
  }

  //
  // Done
  //
  return EFI_DEBUG_CONTINUE;
}

/**

  DebuggerCommand - BreakpointDisable

  @param  CommandArg        The argument for this command
  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  ExceptionType     Exception type.
  @param  SystemContext     EBC system context.

  @retval EFI_DEBUG_CONTINUE   formal return value

**/
EFI_DEBUG_STATUS
DebuggerBreakpointDisable (
  IN     CHAR16                    *CommandArg,
  IN     EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN     EFI_EXCEPTION_TYPE        ExceptionType,
  IN OUT EFI_SYSTEM_CONTEXT        SystemContext
  )
{
  UINTN      Index;
  EFI_STATUS Status;

  if (CommandArg == NULL) {
    EDBPrint (L"BreakpointDisable Argument error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  if (StriCmp (CommandArg, L"*") == 0) {
    //
    // disable all breakpoint
    //
    for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) {
      Status = DebuggerBreakpointDis (DebuggerPrivate, Index);
    }
    EDBPrint (L"All the Breakpoint is disabled\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Get breakpoint index
  //
  Index = Atoi(CommandArg);
  if (Index == (UINTN) -1) {
    EDBPrint (L"BreakpointDisable Argument error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Disable breakpoint
  //
  Status = DebuggerBreakpointDis (DebuggerPrivate, Index);
  if (EFI_ERROR(Status)) {
    EDBPrint (L"BreakpointDisable error!\n");
  }

  //
  // Done
  //
  return EFI_DEBUG_CONTINUE;
}

/**
  DebuggerCommand - BreakpointEnable.

  @param  CommandArg        The argument for this command
  @param  DebuggerPrivate   EBC Debugger private data structure
  @param  ExceptionType     Exception type.
  @param  SystemContext     EBC system context.

  @retval EFI_DEBUG_CONTINUE   formal return value

**/
EFI_DEBUG_STATUS
DebuggerBreakpointEnable (
  IN     CHAR16                    *CommandArg,
  IN     EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate,
  IN     EFI_EXCEPTION_TYPE        ExceptionType,
  IN OUT EFI_SYSTEM_CONTEXT        SystemContext
  )
{
  UINTN      Index;
  EFI_STATUS Status;

  if (CommandArg == NULL) {
    EDBPrint (L"BreakpointEnable Argument error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  if (StriCmp (CommandArg, L"*") == 0) {
    //
    // enable all breakpoint
    //
    for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) {
      Status = DebuggerBreakpointEn (DebuggerPrivate, Index);
    }
    EDBPrint (L"All the Breakpoint is enabled\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Get breakpoint index
  //
  Index = Atoi(CommandArg);
  if (Index == (UINTN) -1) {
    EDBPrint (L"BreakpointEnable Argument error!\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Enable breakpoint
  //
  Status = DebuggerBreakpointEn (DebuggerPrivate, Index);
  if (EFI_ERROR(Status)) {
    EDBPrint (L"BreakpointEnable error!\n");
  }

  //
  // Done
  //
  return EFI_DEBUG_CONTINUE;
}
