/** @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; }