/** @file
Main file for Mm shell Debug1 function.
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.
Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "UefiShellDebug1CommandsLib.h"
#include
#include
#include
#include
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);
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;
}