Skip to content

Commit

Permalink
Add support for initializing Max Payload Size during PCIe enumeration (
Browse files Browse the repository at this point in the history
…#562)

## Description

PCIe's Max Payload Size controls the maximum packet size that can be
handled by a given device. The device's maximum supported MPS is
provided by the PCIe Device Capabilities Register, and the current MPS
is programmed in the Control Register. For a given Root Port, the MPS
should be the same value for all devices and bridges under that Root
Port. When the new PCD `PcdPcieInitializeMps` is set to TRUE, the
supported MPS will be collected as part of PCI device creation and after
all devices are enumerated under the Root Port, all devices and bridges
will have their MPS set to the optimum value.

This allows platforms to ensure that the optimum MPS is set across the
PCI tree before PCI devices are started. This can significantly improve
performance for PCI devices such as NVMe.

- [x] Impacts functionality?
- [ ] Impacts security?
- [ ] Breaking change?
- [ ] Includes tests?
- [ ] Includes documentation?

## How This Was Tested

Tested on virtual and physical platforms, OS boot tested.

## Integration Instructions

N/A

 Co-authored-by: abrahamg <abrahamg@microsoft.com>

---------

Co-authored-by: abrahamg <abrahamg@microsoft.com>
  • Loading branch information
2 people authored and kenlautner committed Oct 18, 2023
1 parent f22ff93 commit 60ca383
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 4 deletions.
3 changes: 2 additions & 1 deletion MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ struct _PCI_IO_DEVICE {
UINT16 BridgeIoAlignment;
UINT32 ResizableBarOffset;
UINT32 ResizableBarNumber;
BOOLEAN IgnoreROM; // MS_CHANGE
BOOLEAN IgnoreROM; // MS_CHANGE
UINT8 MaxPayloadSize; // MU_CHANGE: Add support for initializing PCIe MPS
};

#define PCI_IO_DEVICE_FROM_PCI_IO_THIS(a) \
Expand Down
1 change: 1 addition & 0 deletions MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdPcieResizableBarSupport ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdDisableBMEonEBS ## MU_CHANGE
gEfiMdeModulePkgTokenSpaceGuid.PcdPcieInitializeMps ## MU_CHANGE: Add support for initializing PCIe MPS

[UserExtensions.TianoCore."ExtraFiles"]
PciBusDxeExtra.uni
173 changes: 173 additions & 0 deletions MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,161 @@ PciPciDeviceInfoCollector (
return EFI_SUCCESS;
}

// MU_CHANGE BEGIN: Add support for initializing PCIe MPS

/**
Searches a PCI device and it's children to find the optimum Max Payload Size
supported by the provided device and its children.
@param[in] PciIoDevice The PCI IO Device to find the optimal MPS for.
@param[out] MaxPayloadSize The optimum MPS for the device and it's children.
@retval EFI_SUCCESS Optimum MPS was found.
@retval EFI_UNSUPPORTED MPS not supported by provided device or its children.
**/
EFI_STATUS
PciGetMaxPayloadSize (
IN PCI_IO_DEVICE *PciIoDevice,
OUT UINT8 *MaxPayloadSize
)
{
LIST_ENTRY *CurrentLink;
UINT8 ChildMps;
PCI_IO_DEVICE *Child;
EFI_STATUS Status;

//
// Skip the root bridge.
//

if (PciIoDevice->Parent != NULL) {
if (!PciIoDevice->IsPciExp) {
return EFI_UNSUPPORTED;
}

*MaxPayloadSize = PciIoDevice->MaxPayloadSize;
} else {
*MaxPayloadSize = MAX_UINT8;
}

//
// Recurse into each child to find the max payload size supported.
//
CurrentLink = PciIoDevice->ChildList.ForwardLink;
while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) {
Child = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
Status = PciGetMaxPayloadSize (Child, &ChildMps);
if (!EFI_ERROR (Status) && (ChildMps < *MaxPayloadSize)) {
*MaxPayloadSize = ChildMps;
}

CurrentLink = CurrentLink->ForwardLink;
}

if (*MaxPayloadSize == MAX_UINT8) {
return EFI_UNSUPPORTED;
}

return EFI_SUCCESS;
}

/**
Sets the PCIe Max PayloadSize for the provided device and it's children.
@param[in] PciIoDevice The PCI IO Device to set the MPS for.
@param[out] MaxPayloadSize The MPS to set.
@retval EFI_SUCCESS The MPS ws set for the device and it's children.
@retval EFI_UNSUPPORTED MPS not supported by provided device or its children.
@retval Other MPS could not be read or written.
**/
EFI_STATUS
PciProgramMps (
IN PCI_IO_DEVICE *PciIoDevice,
IN UINT8 MaxPayloadSize
)
{
LIST_ENTRY *CurrentLink;
PCI_IO_DEVICE *Child;
EFI_STATUS Status;
PCI_REG_PCIE_DEVICE_CONTROL DeviceControl;

//
// Skip the root bridge.
//

if (PciIoDevice->Parent != NULL) {
if (!PciIoDevice->IsPciExp) {
return EFI_UNSUPPORTED;
}

Status = PciIoDevice->PciIo.Pci.Read (
&PciIoDevice->PciIo,
EfiPciIoWidthUint16,
PciIoDevice->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CONTROL,
1,
&DeviceControl.Uint16
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a: Failed to read MPS for device %02x %02x %02x. %r\n",
__FUNCTION__,
PciIoDevice->BusNumber,
PciIoDevice->DeviceNumber,
PciIoDevice->FunctionNumber,
Status
));
} else if (DeviceControl.Bits.MaxPayloadSize != MaxPayloadSize) {
DeviceControl.Bits.MaxPayloadSize = MaxPayloadSize;
DEBUG ((
DEBUG_VERBOSE,
"%a: %02x %02x %02x Setting MPS: %x\n",
__FUNCTION__,
PciIoDevice->BusNumber,
PciIoDevice->DeviceNumber,
PciIoDevice->FunctionNumber,
MaxPayloadSize
));

Status = PciIoDevice->PciIo.Pci.Write (
&PciIoDevice->PciIo,
EfiPciIoWidthUint16,
PciIoDevice->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CONTROL,
1,
&DeviceControl.Uint16
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a: Failed to set MPS for device %02x %02x %02x. %r\n",
__FUNCTION__,
PciIoDevice->BusNumber,
PciIoDevice->DeviceNumber,
PciIoDevice->FunctionNumber,
Status
));
}
}
}

//
// Recurse into each child to set the max payload size.
//
CurrentLink = PciIoDevice->ChildList.ForwardLink;
while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) {
Child = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
PciProgramMps (Child, MaxPayloadSize);
CurrentLink = CurrentLink->ForwardLink;
}

return EFI_SUCCESS;
}

// MU_CHANGE END: Add support for initializing PCIe MPS

/**
Search required device and create PCI device instance.
Expand Down Expand Up @@ -2535,6 +2690,24 @@ CreatePciIoDevice (
}
}

// MU_CHANGE BEGIN: Add support for initializing PCIe MPS
// Capture the maximum payload size supported for the device.
if (PcdGetBool (PcdPcieInitializeMps) && PciIoDevice->IsPciExp) {
PCI_REG_PCIE_DEVICE_CAPABILITY DeviceCapabilities;
Status = PciIoDevice->PciIo.Pci.Read (
&PciIoDevice->PciIo,
EfiPciIoWidthUint32,
PciIoDevice->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES,
1,
&DeviceCapabilities.Uint32
);

ASSERT (!EFI_ERROR (Status));
PciIoDevice->MaxPayloadSize = (UINT8)DeviceCapabilities.Bits.MaxPayloadSize;
}

// MU_CHANGE END

//
// Initialize the reserved resource list
//
Expand Down
38 changes: 38 additions & 0 deletions MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,44 @@ PciPciDeviceInfoCollector (
IN UINT8 StartBusNumber
);

// MU_CHANGE BEGIN: Add support for initializing PCIe MPS

/**
Searches a PCI device and it's children to find the optimum Max Payload Size
supported by the provided device and its children.
@param[in] PciIoDevice The PCI IO Device to find the optimal MPS for.
@param[out] MaxPayloadSize The optimum MPS for the device and it's children.
@retval EFI_SUCCESS Optimum MPS was found.
@retval EFI_UNSUPPORTED MPS not supported by provided device or its children.
**/
EFI_STATUS
PciGetMaxPayloadSize (
IN PCI_IO_DEVICE *PciIoDevice,
OUT UINT8 *MaxPayloadSize
);

/**
Sets the PCIe Max PayloadSize for the provided device and it's children.
@param[in] PciIoDevice The PCI IO Device to set the MPS for.
@param[out] MaxPayloadSize The MPS to set.
@retval EFI_SUCCESS The MPS ws set for the device and it's children.
@retval EFI_UNSUPPORTED MPS not supported by provided device or its children.
@retval Other MPS could not be read or written.
**/
EFI_STATUS
PciProgramMps (
IN PCI_IO_DEVICE *PciIoDevice,
IN UINT8 MaxPayloadSize
);

// MU_CHANGE END: Add support for initializing PCIe MPS

/**
Search required device and create PCI device instance.
Expand Down
14 changes: 14 additions & 0 deletions MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,20 @@ PciHostBridgeEnumerator (
return Status;
}

// MU_CHANGE BEGIN: Add support for initializing PCIe MPS
if (PcdGetBool (PcdPcieInitializeMps) && gFullEnumeration) {
UINT8 MaxPayloadSize;
Status = PciGetMaxPayloadSize (RootBridgeDev, &MaxPayloadSize);
if (!EFI_ERROR (Status)) {
Status = PciProgramMps (RootBridgeDev, MaxPayloadSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to set root bridge MPS to %x. %r\n", __FUNCTION__, MaxPayloadSize, Status));
}
}
}

// MU_CHANGE END: Add support for initializing PCIe MPS

InsertRootBridge (RootBridgeDev);

//
Expand Down
13 changes: 10 additions & 3 deletions MdeModulePkg/MdeModulePkg.dec
Original file line number Diff line number Diff line change
Expand Up @@ -1307,9 +1307,9 @@
# MU_CHANGE [END]

# MU_CHANGE [BEGIN] - Support indefinite boot retries
# # Some platforms require that all EfiLoadOptions are retried until one of the options
# # succeeds. When True, this Pcd will force Bds to retry all the valid EfiLoadOptions
# # indefinitely until one of the options succeeds.
# # Some platforms require that all EfiLoadOptions are retried until one of the options
# # succeeds. When True, this Pcd will force Bds to retry all the valid EfiLoadOptions
# # indefinitely until one of the options succeeds.
# # TRUE - Efi boot options will be retried indefinitely.
# # FALSE - Efi boot options will not be retried.
gEfiMdeModulePkgTokenSpaceGuid.PcdSupportInfiniteBootRetries|FALSE|BOOLEAN|0x40000152
Expand Down Expand Up @@ -2176,6 +2176,13 @@
# @Prompt Disable full PCI enumeration.
gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration|FALSE|BOOLEAN|0x10000048

# MU_CHANGE: Add support for initializing PCIe MPS
## Indicates if Max Payload Size should be set for PCIe devices
# TRUE - Initialize MPS for PCI devices.
# FALSE - Leave MPS to the default value.
# @Prompt Enable initializing PCIe Max Payload Sizes.
gEfiMdeModulePkgTokenSpaceGuid.PcdPcieInitializeMps|FALSE|BOOLEAN|0x10000049

## Disk I/O - Number of Data Buffer block.
# Define the size in block of the pre-allocated buffer. It provide better
# performance for large Disk I/O requests.
Expand Down
2 changes: 2 additions & 0 deletions MdePkg/Include/IndustryStandard/PciExpress21.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ typedef struct {
} PCI_CAPABILITY_PCIEXP;

#define EFI_PCIE_CAPABILITY_BASE_OFFSET 0x100
#define EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES 0x04 // MU_CHANGE: Add support for PCIe MPS
#define EFI_PCIE_CAPABILITY_DEVICE_CONTROL 0x08 // MU_CHANGE: Add support for PCIe MPS
#define EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL_ARI_HIERARCHY 0x10
#define EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES_2_OFFSET 0x24
#define EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES_2_ARI_FORWARDING 0x20
Expand Down

0 comments on commit 60ca383

Please sign in to comment.