/** @file
  Main file for Mm shell Debug1 function.

  (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
  Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "UefiShellDebug1CommandsLib.h"
#include <Library/ShellLib.h>
#include <Library/IoLib.h>
#include <Protocol/PciRootBridgeIo.h>
#include <Protocol/DeviceIo.h>

typedef enum {
  ShellMmMemory,
  ShellMmMemoryMappedIo,
  ShellMmIo,
  ShellMmPci,
  ShellMmPciExpress
} SHELL_MM_ACCESS_TYPE;

CONST UINT16  mShellMmAccessTypeStr[] = {
  STRING_TOKEN (STR_MM_MEM),
  STRING_TOKEN (STR_MM_MMIO),
  STRING_TOKEN (STR_MM_IO),
  STRING_TOKEN (STR_MM_PCI),
  STRING_TOKEN (STR_MM_PCIE)
};

STATIC CONST SHELL_PARAM_ITEM  ParamList[] = {
  { L"-mmio", TypeFlag  },
  { L"-mem",  TypeFlag  },
  { L"-io",   TypeFlag  },
  { L"-pci",  TypeFlag  },
  { L"-pcie", TypeFlag  },
  { L"-n",    TypeFlag  },
  { L"-w",    TypeValue },
  { NULL,     TypeMax   }
};

CONST UINT64                                 mShellMmMaxNumber[] = {
  0, MAX_UINT8, MAX_UINT16, 0, MAX_UINT32, 0, 0, 0, MAX_UINT64
};
CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH  mShellMmRootBridgeIoWidth[] = {
  0, EfiPciWidthUint8, EfiPciWidthUint16, 0, EfiPciWidthUint32, 0, 0, 0, EfiPciWidthUint64
};
CONST EFI_CPU_IO_PROTOCOL_WIDTH              mShellMmCpuIoWidth[] = {
  0, EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, 0, EfiCpuIoWidthUint32, 0, 0, 0, EfiCpuIoWidthUint64
};

/**
  Extract the PCI segment, bus, device, function, register from
  from a PCI or PCIE format of address..

  @param[in]  PciFormat      Whether the address is of PCI format of PCIE format.
  @param[in]  Address        PCI or PCIE address.
  @param[out] Segment        PCI segment number.
  @param[out] Bus            PCI bus number.
  @param[out] Device         PCI device number.
  @param[out] Function       PCI function number.
  @param[out] Register       PCI register offset.
**/
VOID
ShellMmDecodePciAddress (
  IN BOOLEAN  PciFormat,
  IN UINT64   Address,
  OUT UINT32  *Segment,
  OUT UINT8   *Bus,
  OUT UINT8   *Device    OPTIONAL,
  OUT UINT8   *Function  OPTIONAL,
  OUT UINT32  *Register  OPTIONAL
  )
{
  if (PciFormat) {
    //
    // PCI Configuration Space.The address will have the format ssssbbddffrr,
    // where ssss = Segment, bb = Bus, dd = Device, ff = Function and rr = Register.
    //
    *Segment = (UINT32)(RShiftU64 (Address, 32) & 0xFFFF);
    *Bus     = (UINT8)(((UINT32)Address) >> 24);

    if (Device != NULL) {
      *Device = (UINT8)(((UINT32)Address) >> 16);
    }

    if (Function != NULL) {
      *Function = (UINT8)(((UINT32)Address) >> 8);
    }

    if (Register != NULL) {
      *Register = (UINT8)Address;
    }
  } else {
    //
    // PCI Express Configuration Space.The address will have the format ssssssbbddffrrr,
    // where ssss = Segment, bb = Bus, dd = Device, ff = Function and rrr = Register.
    //
    *Segment = (UINT32)(RShiftU64 (Address, 36) & 0xFFFF);
    *Bus     = (UINT8)RShiftU64 (Address, 28);
    if (Device != NULL) {
      *Device = (UINT8)(((UINT32)Address) >> 20);
    }

    if (Function != NULL) {
      *Function = (UINT8)(((UINT32)Address) >> 12);
    }

    if (Register != NULL) {
      *Register = (UINT32)(Address & 0xFFF);
    }
  }
}

/**
  Read or write some data from or into the Address.

  @param[in]      AccessType      Access type.
  @param[in]      PciRootBridgeIo PciRootBridgeIo instance.
  @param[in]      CpuIo           CpuIo instance.
  @param[in]      Read            TRUE for read, FALSE for write.
  @param[in]      Addresss        The memory location to access.
  @param[in]      Size            The size of Buffer in Width sized units.
  @param[in, out] Buffer          The buffer to read into or write from.
**/
VOID
ShellMmAccess (
  IN     SHELL_MM_ACCESS_TYPE             AccessType,
  IN     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *PciRootBridgeIo,
  IN     EFI_CPU_IO2_PROTOCOL             *CpuIo,
  IN     BOOLEAN                          Read,
  IN     UINT64                           Address,
  IN     UINTN                            Size,
  IN OUT VOID                             *Buffer
  )
{
  EFI_STATUS                              Status;
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM  RootBridgeIoMem;
  EFI_CPU_IO_PROTOCOL_IO_MEM              CpuIoMem;
  UINT32                                  Segment;
  UINT8                                   Bus;
  UINT8                                   Device;
  UINT8                                   Function;
  UINT32                                  Register;

  if (AccessType == ShellMmMemory) {
    if (Read) {
      CopyMem (Buffer, (VOID *)(UINTN)Address, Size);
    } else {
      CopyMem ((VOID *)(UINTN)Address, Buffer, Size);
    }
  } else {
    RootBridgeIoMem = NULL;
    CpuIoMem        = NULL;
    switch (AccessType) {
      case ShellMmPci:
      case ShellMmPciExpress:
        ASSERT (PciRootBridgeIo != NULL);
        ShellMmDecodePciAddress ((BOOLEAN)(AccessType == ShellMmPci), Address, &Segment, &Bus, &Device, &Function, &Register);
        if (Read) {
          Status = PciRootBridgeIo->Pci.Read (
                                          PciRootBridgeIo,
                                          mShellMmRootBridgeIoWidth[Size],
                                          EFI_PCI_ADDRESS (Bus, Device, Function, Register),
                                          1,
                                          Buffer
                                          );
        } else {
          Status = PciRootBridgeIo->Pci.Write (
                                          PciRootBridgeIo,
                                          mShellMmRootBridgeIoWidth[Size],
                                          EFI_PCI_ADDRESS (Bus, Device, Function, Register),
                                          1,
                                          Buffer
                                          );
        }

        ASSERT_EFI_ERROR (Status);
        return;

      case ShellMmMemoryMappedIo:
        if (PciRootBridgeIo != NULL) {
          RootBridgeIoMem = Read ? PciRootBridgeIo->Mem.Read : PciRootBridgeIo->Mem.Write;
        }

        if (CpuIo != NULL) {
          CpuIoMem = Read ? CpuIo->Mem.Read : CpuIo->Mem.Write;
        }

        break;

      case ShellMmIo:
        if (PciRootBridgeIo != NULL) {
          RootBridgeIoMem = Read ? PciRootBridgeIo->Io.Read : PciRootBridgeIo->Io.Write;
        }

        if (CpuIo != NULL) {
          CpuIoMem = Read ? CpuIo->Io.Read : CpuIo->Io.Write;
        }

        break;
      default:
        ASSERT (FALSE);
        break;
    }

    Status = EFI_UNSUPPORTED;
    if (RootBridgeIoMem != NULL) {
      Status = RootBridgeIoMem (PciRootBridgeIo, mShellMmRootBridgeIoWidth[Size], Address, 1, Buffer);
    }

    if (EFI_ERROR (Status) && (CpuIoMem != NULL)) {
      Status = CpuIoMem (CpuIo, mShellMmCpuIoWidth[Size], Address, 1, Buffer);
    }

    if (EFI_ERROR (Status)) {
      if (AccessType == ShellMmIo) {
        switch (Size) {
          case 1:
            if (Read) {
              *(UINT8 *)Buffer = IoRead8 ((UINTN)Address);
            } else {
              IoWrite8 ((UINTN)Address, *(UINT8 *)Buffer);
            }

            break;
          case 2:
            if (Read) {
              *(UINT16 *)Buffer = IoRead16 ((UINTN)Address);
            } else {
              IoWrite16 ((UINTN)Address, *(UINT16 *)Buffer);
            }

            break;
          case 4:
            if (Read) {
              *(UINT32 *)Buffer = IoRead32 ((UINTN)Address);
            } else {
              IoWrite32 ((UINTN)Address, *(UINT32 *)Buffer);
            }

            break;
          case 8:
            if (Read) {
              *(UINT64 *)Buffer = IoRead64 ((UINTN)Address);
            } else {
              IoWrite64 ((UINTN)Address, *(UINT64 *)Buffer);
            }

            break;
          default:
            ASSERT (FALSE);
            break;
        }
      } else {
        switch (Size) {
          case 1:
            if (Read) {
              *(UINT8 *)Buffer = MmioRead8 ((UINTN)Address);
            } else {
              MmioWrite8 ((UINTN)Address, *(UINT8 *)Buffer);
            }

            break;
          case 2:
            if (Read) {
              *(UINT16 *)Buffer = MmioRead16 ((UINTN)Address);
            } else {
              MmioWrite16 ((UINTN)Address, *(UINT16 *)Buffer);
            }

            break;
          case 4:
            if (Read) {
              *(UINT32 *)Buffer = MmioRead32 ((UINTN)Address);
            } else {
              MmioWrite32 ((UINTN)Address, *(UINT32 *)Buffer);
            }

            break;
          case 8:
            if (Read) {
              *(UINT64 *)Buffer = MmioRead64 ((UINTN)Address);
            } else {
              MmioWrite64 ((UINTN)Address, *(UINT64 *)Buffer);
            }

            break;
          default:
            ASSERT (FALSE);
            break;
        }
      }
    }
  }
}

/**
  Find the CpuIo instance and PciRootBridgeIo instance in the platform.
  If there are multiple PciRootBridgeIo instances, the instance which manages
  the Address is returned.

  @param[in]  AccessType      Access type.
  @param[in]  Address         Address to access.
  @param[out] CpuIo           Return the CpuIo instance.
  @param[out] PciRootBridgeIo Return the proper PciRootBridgeIo instance.

  @retval TRUE  There are PciRootBridgeIo instances in the platform.
  @retval FALSE There isn't PciRootBridgeIo instance in the platform.
**/
BOOLEAN
ShellMmLocateIoProtocol (
  IN SHELL_MM_ACCESS_TYPE              AccessType,
  IN UINT64                            Address,
  OUT EFI_CPU_IO2_PROTOCOL             **CpuIo,
  OUT EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  **PciRootBridgeIo
  )
{
  EFI_STATUS                         Status;
  UINTN                              Index;
  UINTN                              HandleCount;
  EFI_HANDLE                         *HandleBuffer;
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL    *Io;
  UINT32                             Segment;
  UINT8                              Bus;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR  *Descriptors;

  Status = gBS->LocateProtocol (&gEfiCpuIo2ProtocolGuid, NULL, (VOID **)CpuIo);
  if (EFI_ERROR (Status)) {
    *CpuIo = NULL;
  }

  *PciRootBridgeIo = NULL;
  HandleBuffer     = NULL;
  Status           = gBS->LocateHandleBuffer (
                            ByProtocol,
                            &gEfiPciRootBridgeIoProtocolGuid,
                            NULL,
                            &HandleCount,
                            &HandleBuffer
                            );
  if (EFI_ERROR (Status) || (HandleCount == 0) || (HandleBuffer == NULL)) {
    return FALSE;
  }

  Segment = 0;
  Bus     = 0;
  if ((AccessType == ShellMmPci) || (AccessType == ShellMmPciExpress)) {
    ShellMmDecodePciAddress ((BOOLEAN)(AccessType == ShellMmPci), Address, &Segment, &Bus, NULL, NULL, NULL);
  }

  //
  // Find the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL of the specified segment & bus number
  //
  for (Index = 0; (Index < HandleCount) && (*PciRootBridgeIo == NULL); Index++) {
    Status = gBS->HandleProtocol (
                    HandleBuffer[Index],
                    &gEfiPciRootBridgeIoProtocolGuid,
                    (VOID *)&Io
                    );
    if (EFI_ERROR (Status)) {
      continue;
    }

    if ((((AccessType == ShellMmPci) || (AccessType == ShellMmPciExpress)) && (Io->SegmentNumber == Segment)) ||
        ((AccessType == ShellMmIo) || (AccessType == ShellMmMemoryMappedIo))
        )
    {
      Status = Io->Configuration (Io, (VOID **)&Descriptors);
      if (!EFI_ERROR (Status)) {
        while (Descriptors->Desc != ACPI_END_TAG_DESCRIPTOR) {
          //
          // Compare the segment and bus range for PCI/PCIE access
          //
          if ((Descriptors->ResType == ACPI_ADDRESS_SPACE_TYPE_BUS) &&
              ((AccessType == ShellMmPci) || (AccessType == ShellMmPciExpress)) &&
              ((Bus >= Descriptors->AddrRangeMin) && (Bus <= Descriptors->AddrRangeMax))
              )
          {
            *PciRootBridgeIo = Io;
            break;

            //
            // Compare the address range for MMIO/IO access
            //
          } else if ((((Descriptors->ResType == ACPI_ADDRESS_SPACE_TYPE_IO) && (AccessType == ShellMmIo)) ||
                      ((Descriptors->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) && (AccessType == ShellMmMemoryMappedIo))
                      ) && ((Address >= Descriptors->AddrRangeMin) && (Address <= Descriptors->AddrRangeMax))
                     )
          {
            *PciRootBridgeIo = Io;
            break;
          }

          Descriptors++;
        }
      }
    }
  }

  if (HandleBuffer != NULL) {
    FreePool (HandleBuffer);
  }

  return TRUE;
}

/**
  Function for 'mm' command.

  @param[in] ImageHandle  Handle to the Image (NULL if Internal).
  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
**/
SHELL_STATUS
EFIAPI
ShellCommandRunMm (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                       Status;
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *PciRootBridgeIo;
  EFI_CPU_IO2_PROTOCOL             *CpuIo;
  UINT64                           Address;
  UINT64                           Value;
  SHELL_MM_ACCESS_TYPE             AccessType;
  UINT64                           Buffer;
  UINTN                            Index;
  UINTN                            Size;
  BOOLEAN                          Complete;
  CHAR16                           *InputStr;
  BOOLEAN                          Interactive;
  LIST_ENTRY                       *Package;
  CHAR16                           *ProblemParam;
  SHELL_STATUS                     ShellStatus;
  CONST CHAR16                     *Temp;
  BOOLEAN                          HasPciRootBridgeIo;

  Value       = 0;
  Address     = 0;
  ShellStatus = SHELL_SUCCESS;
  InputStr    = NULL;
  Size        = 1;
  AccessType  = ShellMmMemory;

  //
  // Parse arguments
  //
  Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
  if (EFI_ERROR (Status)) {
    if ((Status == EFI_VOLUME_CORRUPTED) && (ProblemParam != NULL)) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellDebug1HiiHandle, L"mm", ProblemParam);
      FreePool (ProblemParam);
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    } else {
      ASSERT (FALSE);
    }
  } else {
    if (ShellCommandLineGetCount (Package) < 2) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellDebug1HiiHandle, L"mm");
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    } else if (ShellCommandLineGetCount (Package) > 3) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellDebug1HiiHandle, L"mm");
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    } else if (ShellCommandLineGetFlag (Package, L"-w") && (ShellCommandLineGetValue (Package, L"-w") == NULL)) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_VALUE), gShellDebug1HiiHandle, L"mm", L"-w");
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    } else {
      if (ShellCommandLineGetFlag (Package, L"-mmio")) {
        AccessType = ShellMmMemoryMappedIo;
        if (  ShellCommandLineGetFlag (Package, L"-mem")
           || ShellCommandLineGetFlag (Package, L"-io")
           || ShellCommandLineGetFlag (Package, L"-pci")
           || ShellCommandLineGetFlag (Package, L"-pcie")
              )
        {
          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellDebug1HiiHandle, L"mm");
          ShellStatus = SHELL_INVALID_PARAMETER;
          goto Done;
        }
      } else if (ShellCommandLineGetFlag (Package, L"-mem")) {
        AccessType = ShellMmMemory;
        if (  ShellCommandLineGetFlag (Package, L"-io")
           || ShellCommandLineGetFlag (Package, L"-pci")
           || ShellCommandLineGetFlag (Package, L"-pcie")
              )
        {
          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellDebug1HiiHandle, L"mm");
          ShellStatus = SHELL_INVALID_PARAMETER;
          goto Done;
        }
      } else if (ShellCommandLineGetFlag (Package, L"-io")) {
        AccessType = ShellMmIo;
        if (  ShellCommandLineGetFlag (Package, L"-pci")
           || ShellCommandLineGetFlag (Package, L"-pcie")
              )
        {
          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellDebug1HiiHandle, L"mm");
          ShellStatus = SHELL_INVALID_PARAMETER;
          goto Done;
        }
      } else if (ShellCommandLineGetFlag (Package, L"-pci")) {
        AccessType = ShellMmPci;
        if (ShellCommandLineGetFlag (Package, L"-pcie")
            )
        {
          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellDebug1HiiHandle, L"mm");
          ShellStatus = SHELL_INVALID_PARAMETER;
          goto Done;
        }
      } else if (ShellCommandLineGetFlag (Package, L"-pcie")) {
        AccessType = ShellMmPciExpress;
      }
    }

    //
    // Non interactive for a script file or for the specific parameter
    //
    Interactive = TRUE;
    if (gEfiShellProtocol->BatchIsActive () || ShellCommandLineGetFlag (Package, L"-n")) {
      Interactive = FALSE;
    }

    Temp = ShellCommandLineGetValue (Package, L"-w");
    if (Temp != NULL) {
      Size = ShellStrToUintn (Temp);
    }

    if ((Size != 1) && (Size != 2) && (Size != 4) && (Size != 8)) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM_VAL), gShellDebug1HiiHandle, L"mm", Temp, L"-w");
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    }

    Temp = ShellCommandLineGetRawValue (Package, 1);
    if (Temp == NULL) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellDebug1HiiHandle, L"mm", L"NULL");
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    }

    Status = ShellConvertStringToUint64 (Temp, &Address, TRUE, FALSE);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellDebug1HiiHandle, L"mm", Temp);
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    }

    if ((Address & (Size - 1)) != 0) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_MM_NOT_ALIGNED), gShellDebug1HiiHandle, L"mm", Address);
      ShellStatus = SHELL_INVALID_PARAMETER;
      goto Done;
    }

    //
    // locate IO protocol interface
    //
    HasPciRootBridgeIo = ShellMmLocateIoProtocol (AccessType, Address, &CpuIo, &PciRootBridgeIo);
    if ((AccessType == ShellMmPci) || (AccessType == ShellMmPciExpress)) {
      if (!HasPciRootBridgeIo) {
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PCIRBIO_NF), gShellDebug1HiiHandle, L"mm");
        ShellStatus = SHELL_NOT_FOUND;
        goto Done;
      }

      if (PciRootBridgeIo == NULL) {
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_MM_PCIE_ADDRESS_RANGE), gShellDebug1HiiHandle, L"mm", Address);
        ShellStatus = SHELL_INVALID_PARAMETER;
        goto Done;
      }
    }

    //
    // Mode 1: Directly set a value
    //
    Temp = ShellCommandLineGetRawValue (Package, 2);
    if (Temp != NULL) {
      Status = ShellConvertStringToUint64 (Temp, &Value, TRUE, FALSE);
      if (EFI_ERROR (Status)) {
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellDebug1HiiHandle, L"mm", Temp);
        ShellStatus = SHELL_INVALID_PARAMETER;
        goto Done;
      }

      if (Value > mShellMmMaxNumber[Size]) {
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellDebug1HiiHandle, L"mm", Temp);
        ShellStatus = SHELL_INVALID_PARAMETER;
        goto Done;
      }

      ShellMmAccess (AccessType, PciRootBridgeIo, CpuIo, FALSE, Address, Size, &Value);
      goto Done;
    }

    //
    // Mode 2: Directly show a value
    //
    if (!Interactive) {
      if (!gEfiShellProtocol->BatchIsActive ()) {
        ShellPrintHiiEx (-1, -1, NULL, mShellMmAccessTypeStr[AccessType], gShellDebug1HiiHandle);
      }

      ShellMmAccess (AccessType, PciRootBridgeIo, CpuIo, TRUE, Address, Size, &Buffer);

      if (!gEfiShellProtocol->BatchIsActive ()) {
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_MM_ADDRESS), gShellDebug1HiiHandle, Address);
      }

      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_MM_BUF), gShellDebug1HiiHandle, Size * 2, Buffer & mShellMmMaxNumber[Size]);
      ShellPrintEx (-1, -1, L"\r\n");
      goto Done;
    }

    //
    // Mode 3: Show or set values in interactive mode
    //
    Complete = FALSE;
    do {
      ShellMmAccess (AccessType, PciRootBridgeIo, CpuIo, TRUE, Address, Size, &Buffer);
      ShellPrintHiiEx (-1, -1, NULL, mShellMmAccessTypeStr[AccessType], gShellDebug1HiiHandle);
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_MM_ADDRESS), gShellDebug1HiiHandle, Address);
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_MM_BUF), gShellDebug1HiiHandle, Size * 2, Buffer & mShellMmMaxNumber[Size]);
      ShellPrintEx (-1, -1, L" > ");
      //
      // wait user input to modify
      //
      if (InputStr != NULL) {
        FreePool (InputStr);
        InputStr = NULL;
      }

      ShellPromptForResponse (ShellPromptResponseTypeFreeform, NULL, (VOID **)&InputStr);

      if (InputStr != NULL) {
        //
        // skip space characters
        //
        for (Index = 0; InputStr[Index] == ' '; Index++) {
        }

        if (InputStr[Index] != CHAR_NULL) {
          if ((InputStr[Index] == '.') || (InputStr[Index] == 'q') || (InputStr[Index] == 'Q')) {
            Complete = TRUE;
          } else if (!EFI_ERROR (ShellConvertStringToUint64 (InputStr + Index, &Buffer, TRUE, TRUE)) &&
                     (Buffer <= mShellMmMaxNumber[Size])
                     )
          {
            ShellMmAccess (AccessType, PciRootBridgeIo, CpuIo, FALSE, Address, Size, &Buffer);
          } else {
            ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_MM_ERROR), gShellDebug1HiiHandle, L"mm");
            continue;
          }
        }
      }

      Address += Size;
      ShellPrintEx (-1, -1, L"\r\n");
    } while (!Complete);
  }

  ASSERT (ShellStatus == SHELL_SUCCESS);

Done:
  if (InputStr != NULL) {
    FreePool (InputStr);
  }

  if (Package != NULL) {
    ShellCommandLineFreeVarList (Package);
  }

  return ShellStatus;
}
