782 lines
23 KiB
C
782 lines
23 KiB
C
/**@file
|
|
This PEIM driver initialize Nvm Express host contoller and
|
|
produce EdkiiPeiNvmExpressHostControllerPpi instance for other driver.
|
|
|
|
@copyright
|
|
INTEL CONFIDENTIAL
|
|
Copyright 2019 - 2021 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains a 'Sample Driver' and is licensed as such under the terms
|
|
of your license agreement with Intel or your vendor. This file may be modified
|
|
by the user, subject to the additional terms of the license agreement.
|
|
|
|
@par Specification Reference:
|
|
**/
|
|
|
|
#include "NvmePciHcPei.h"
|
|
|
|
EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI mNvmeHostControllerPpi = {
|
|
GetNvmeHcMmioBar,
|
|
GetNvmeHcDevicePath
|
|
};
|
|
|
|
EFI_PEI_PPI_DESCRIPTOR mPpiList = {
|
|
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
&gEdkiiPeiNvmExpressHostControllerPpiGuid,
|
|
&mNvmeHostControllerPpi
|
|
};
|
|
|
|
EFI_PEI_PPI_DESCRIPTOR mHybridPpiList = {
|
|
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
&gPeiHybridNvmExpressHostControllerPpiGuid,
|
|
&mNvmeHostControllerPpi
|
|
};
|
|
|
|
|
|
EFI_PEI_NOTIFY_DESCRIPTOR mEndOfPeiNotifyList = {
|
|
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
&gEfiEndOfPeiSignalPpiGuid,
|
|
NvmeHcEndOfPei
|
|
};
|
|
|
|
//
|
|
// silicon init done for FSP disaptch
|
|
//
|
|
EFI_PEI_NOTIFY_DESCRIPTOR mSiInitDoneNotifyList = {
|
|
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
&gEfiEndOfPeiSignal2PpiGuid,
|
|
SiInitCallBack
|
|
};
|
|
|
|
UINT32 MmioBarBase;
|
|
UINT32 MmioBarLimit;
|
|
|
|
//
|
|
// Template for a NVM Express Host controller
|
|
//
|
|
NVME_HC_DEVICE_PATH mNvmeHcDevicePathTemplate = {
|
|
{ // PciRoot
|
|
{
|
|
ACPI_DEVICE_PATH,
|
|
ACPI_DP,
|
|
{
|
|
(UINT8) (sizeof (ACPI_HID_DEVICE_PATH)),
|
|
(UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8)
|
|
}
|
|
},
|
|
0x0A0341D0,
|
|
0
|
|
},
|
|
{ // PciBridge
|
|
{
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_PCI_DP,
|
|
{
|
|
(UINT8) (sizeof (PCI_DEVICE_PATH)),
|
|
(UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8)
|
|
}
|
|
},
|
|
0,
|
|
0
|
|
},
|
|
{ // NvmeHcEp
|
|
{
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_PCI_DP,
|
|
{
|
|
(UINT8) (sizeof (PCI_DEVICE_PATH)),
|
|
(UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8)
|
|
}
|
|
},
|
|
0,
|
|
0
|
|
},
|
|
{ // End
|
|
END_DEVICE_PATH_TYPE,
|
|
END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{
|
|
(UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
|
|
(UINT8) ((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8)
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
Get the MMIO base address of NVM Express host controller.
|
|
|
|
@param[in] This The PPI instance pointer.
|
|
@param[in] ControllerId The ID of the NVM Express host controller.
|
|
@param[out] MmioBar The MMIO base address of the controller.
|
|
|
|
@retval EFI_SUCCESS The operation succeeds.
|
|
@retval EFI_INVALID_PARAMETER The parameters are invalid.
|
|
@retval EFI_NOT_FOUND The specified NVM Express host controller not
|
|
found.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetNvmeHcMmioBar (
|
|
IN EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *This,
|
|
IN UINT8 ControllerId,
|
|
OUT UINTN *MmioBar
|
|
)
|
|
{
|
|
NVME_HC_PEI_PRIVATE_DATA *Private;
|
|
|
|
if ((This == NULL) || (MmioBar == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Private = NVME_HC_PEI_PRIVATE_DATA_FROM_THIS (This);
|
|
|
|
if (ControllerId >= Private->TotalNvmeHcs) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*MmioBar = (UINTN)Private->HcInfo[ControllerId].Bar;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get the device path of NVM Express host controller.
|
|
|
|
@param[in] This The PPI instance pointer.
|
|
@param[in] ControllerId The ID of the NVM Express host controller.
|
|
@param[out] DevicePathLength The length of the device path in bytes specified
|
|
by DevicePath.
|
|
@param[out] DevicePath The device path of NVM Express host controller.
|
|
This field re-uses EFI Device Path Protocol as
|
|
defined by Section 10.2 EFI Device Path Protocol
|
|
of UEFI 2.7 Specification.
|
|
|
|
@retval EFI_SUCCESS The operation succeeds.
|
|
@retval EFI_INVALID_PARAMETER The parameters are invalid.
|
|
@retval EFI_NOT_FOUND The specified NVM Express host controller not
|
|
found.
|
|
@retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetNvmeHcDevicePath (
|
|
IN EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *This,
|
|
IN UINT8 ControllerId,
|
|
OUT UINTN *DevicePathLength,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
|
|
)
|
|
{
|
|
NVME_HC_PEI_PRIVATE_DATA *Private;
|
|
|
|
if ((This == NULL) || (DevicePath == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Private = NVME_HC_PEI_PRIVATE_DATA_FROM_THIS (This);
|
|
|
|
if (ControllerId >= Private->TotalNvmeHcs) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*DevicePathLength = Private->HcInfo[ControllerId].DevicePathLength;
|
|
*DevicePath = AllocateCopyPool (*DevicePathLength, Private->HcInfo[ControllerId].DevicePath);
|
|
if (*DevicePath == NULL) {
|
|
*DevicePathLength = 0;
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Check whether the controller is a PCI NVM Express host controller.
|
|
|
|
@param[in] PciHcBase PCI address of a specified controller.
|
|
|
|
@retval TRUE The specified controller is an NVM Express host controller.
|
|
@retval FALSE The specified controller is not an NVM Express host controller.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsPciNvmeHc (
|
|
IN UINTN PciHcBase
|
|
)
|
|
{
|
|
UINT8 SubClass;
|
|
UINT8 BaseClass;
|
|
UINT8 ProgInt;
|
|
|
|
ProgInt = PciRead8 (PciHcBase + PCI_CLASSCODE_OFFSET);
|
|
SubClass = PciRead8 (PciHcBase + PCI_CLASSCODE_OFFSET + 1);
|
|
BaseClass = PciRead8 (PciHcBase + PCI_CLASSCODE_OFFSET + 2);
|
|
|
|
if ((BaseClass != PCI_CLASS_MASS_STORAGE) ||
|
|
(SubClass != PCI_CLASS_MASS_STORAGE_SOLID_STATE) ||
|
|
(ProgInt != PCI_IF_MASS_STORAGE_SOLID_STATE_ENTERPRISE_NVMHCI)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Check whether the controller is a hybrid device.
|
|
|
|
@param[in] PciHcBase PCI address of a specified controller.
|
|
|
|
@retval TRUE The specified controller is hybrid device.
|
|
@retval FALSE The specified controller is not an hybrid device.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsIntelHybridMember (
|
|
IN UINTN PciHcBase
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Program the PCI configuration space of a PCI-PCI bridge to enable it.
|
|
|
|
@param[in, out] HcInfo A pointer to the NVME_HC_INFO structure.
|
|
|
|
**/
|
|
VOID
|
|
NvmePciBridgesConfig (
|
|
IN OUT NVME_HC_INFO *HcInfo
|
|
)
|
|
{
|
|
UINTN PpbBase;
|
|
UINT8 SecondaryBus;
|
|
|
|
PpbBase = PCI_LIB_ADDRESS (
|
|
HcInfo->Bridge.Bus,
|
|
HcInfo->Bridge.Device,
|
|
HcInfo->Bridge.Function,
|
|
0
|
|
);
|
|
SecondaryBus = HcInfo->ControllerEndPoint.Bus;
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO, "%a: Bridge @ B-D-F(0x%x-0x%x-0x%x), SecondaryBus(0x%x)\n",
|
|
__FUNCTION__,
|
|
HcInfo->Bridge.Bus,
|
|
HcInfo->Bridge.Device,
|
|
HcInfo->Bridge.Function,
|
|
SecondaryBus
|
|
));
|
|
|
|
//
|
|
// Save origin configuration space.
|
|
//
|
|
PciReadBuffer (PpbBase, PCI_BRIDGE_CONFIG_SPACE_STORE_SIZE, HcInfo->OriBridgeConfigSpace);
|
|
|
|
//
|
|
// Initialize the configuration space.
|
|
//
|
|
PciAnd16 (
|
|
PpbBase + PCI_COMMAND_OFFSET,
|
|
(UINT16)~(EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_IO_SPACE)
|
|
);
|
|
PciWrite8 (PpbBase + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET, SecondaryBus);
|
|
PciWrite8 (PpbBase + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET, SecondaryBus);
|
|
|
|
PciWrite8 (PpbBase + PCI_BRIDGE_IO_BASE, 0xFF);
|
|
PciWrite8 (PpbBase + PCI_BRIDGE_IO_LIMIT, 0x00);
|
|
PciWrite16 (PpbBase + PCI_BRIDGE_MEMORY_BASE, (UINT16) RShiftU64 (HcInfo->Bar, 16));
|
|
PciWrite16 (PpbBase + PCI_BRIDGE_MEMORY_LIMIT, (UINT16) RShiftU64 (HcInfo->Bar, 16));
|
|
PciWrite16 (PpbBase + PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE, 0xFFFF);
|
|
PciWrite16 (PpbBase + PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT, 0x0000);
|
|
PciWrite32 (PpbBase + PCI_BRIDGE_PREFETCHABLE_BASE_UPPER32, 0xFFFFFFFF);
|
|
PciWrite32 (PpbBase + PCI_BRIDGE_PREFETCHABLE_LIMIT_UPPER32, 0x00000000);
|
|
PciOr16 (PpbBase + PCI_COMMAND_OFFSET, (EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE));
|
|
|
|
}
|
|
|
|
/**
|
|
Restore the PCI configuration space of the NVM Express host controller and its
|
|
relating PCI-PCI bridge.
|
|
|
|
@param[in] HcInfo A pointer to the NVME_HC_INFO structure.
|
|
|
|
**/
|
|
VOID
|
|
NvmeRestoreHcPciConfigSpace (
|
|
IN NVME_HC_INFO *HcInfo
|
|
)
|
|
{
|
|
UINTN PciHcBase;
|
|
|
|
//
|
|
// Restore for the Controller Endpoint.
|
|
//
|
|
PciHcBase = PCI_LIB_ADDRESS (
|
|
HcInfo->ControllerEndPoint.Bus,
|
|
HcInfo->ControllerEndPoint.Device,
|
|
HcInfo->ControllerEndPoint.Function,
|
|
0
|
|
);
|
|
PciWrite8 (PciHcBase + PCI_COMMAND_OFFSET, HcInfo->OriCmdRegLow);
|
|
PciWrite32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET, HcInfo->OriBar0Reg);
|
|
PciWrite32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET + 4, HcInfo->OriBar1Reg);
|
|
|
|
//
|
|
// Restore for the Pci-Pci Bridge.
|
|
//
|
|
PciHcBase = PCI_LIB_ADDRESS (
|
|
HcInfo->Bridge.Bus,
|
|
HcInfo->Bridge.Device,
|
|
HcInfo->Bridge.Function,
|
|
0
|
|
);
|
|
PciWriteBuffer (PciHcBase, PCI_BRIDGE_CONFIG_SPACE_STORE_SIZE, HcInfo->OriBridgeConfigSpace);
|
|
|
|
}
|
|
|
|
/**
|
|
Get the assigned MMIO resource range for the NVM Express host controllers.
|
|
|
|
@param[out] Base Base of the memory-mapped I/O region.
|
|
@param[out] Limit Range limit of the memory-mapped I/O region.
|
|
|
|
@retval EFI_SUCCESS Successfully get the memory-mapped I/O region.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
NvmeGetMmio (
|
|
OUT UINT32 *Base,
|
|
OUT UINT32 *Limit
|
|
)
|
|
{
|
|
*Base = PcdGet32 (PcdNvmeHcPeiMmioBase);
|
|
*Limit = PcdGet32 (PcdNvmeHcPeiMmioLimit);
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO, "%a: BarBase = 0x%x, BarLimit = 0x%x.\n",
|
|
__FUNCTION__, *Base, *Limit
|
|
));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Enable the NVM Express host controller specified by the PCI root port index.
|
|
|
|
@param[in,out] Private A pointer to the NVME_HC_PEI_PRIVATE_DATA structure.
|
|
@param[in] RpIndex The index of the PCI root port.
|
|
|
|
@retval EFI_SUCCESS Controller enabled successfully.
|
|
@retval others Error occurs during the enabling process.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
NvmeEnableThisHc (
|
|
IN OUT NVME_HC_PEI_PRIVATE_DATA *Private,
|
|
IN UINTN RpDev,
|
|
IN UINTN RpFun
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN HcIndex;
|
|
NVME_HC_INFO *HcInfo;
|
|
UINTN PciHcBase;
|
|
UINT32 Size;
|
|
UINT64 MmioSize;
|
|
NVME_HC_DEVICE_PATH *HcDevicePath;
|
|
UINT64 RpBase;
|
|
|
|
ASSERT (Private->TotalNvmeHcs <= MAX_NVME_HCS);
|
|
|
|
//
|
|
// Check if the PCI NVM Express host controller has already been enabled.
|
|
//
|
|
for (HcIndex = 0; HcIndex < Private->TotalNvmeHcs; HcIndex++) {
|
|
HcInfo = &(Private->HcInfo[HcIndex]);
|
|
if ((HcInfo->Bridge.Device == RpDev) && (HcInfo->Bridge.Function == RpFun)) {
|
|
//
|
|
// Already started, return EFI_SUCCESS directly.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
HcIndex = Private->TotalNvmeHcs;
|
|
HcInfo = &(Private->HcInfo[HcIndex]);
|
|
|
|
//
|
|
// Check if root port exists and endpoint device is present
|
|
//
|
|
RpBase = PCI_SEGMENT_LIB_ADDRESS (
|
|
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
|
|
DEFAULT_PCI_BUS_NUMBER_PCH,
|
|
RpDev,
|
|
RpFun,
|
|
0
|
|
);
|
|
|
|
if (PciSegmentRead16 (RpBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if ((PciSegmentRead8 (RpBase + PCI_HEADER_TYPE_OFFSET) & HEADER_LAYOUT_CODE) != HEADER_TYPE_PCI_TO_PCI_BRIDGE) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
HcInfo->Bridge.Bus = (UINT8) 0;
|
|
HcInfo->Bridge.Device = (UINT8) RpDev;
|
|
HcInfo->Bridge.Function = (UINT8) RpFun;
|
|
HcInfo->ControllerEndPoint.Bus = (UINT8) (ENDPOINT_PCI_BUS_NUMBER_START + Private->TotalNvmeHcs);
|
|
HcInfo->ControllerEndPoint.Device = (UINT8) 0;
|
|
HcInfo->ControllerEndPoint.Function = (UINT8) 0;
|
|
HcInfo->Bar = MmioBarBase;
|
|
|
|
//
|
|
// Configure the Pci-Pci Bridge PCI configuration space.
|
|
//
|
|
NvmePciBridgesConfig (HcInfo);
|
|
|
|
//
|
|
// Check endpoint device type
|
|
//
|
|
|
|
PciHcBase = PCI_LIB_ADDRESS (
|
|
HcInfo->ControllerEndPoint.Bus,
|
|
HcInfo->ControllerEndPoint.Device,
|
|
HcInfo->ControllerEndPoint.Function,
|
|
0
|
|
);
|
|
|
|
|
|
|
|
HcInfo->OriCmdRegLow = PciRead8 (PciHcBase + PCI_COMMAND_OFFSET);
|
|
HcInfo->OriBar0Reg = PciRead32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET);
|
|
HcInfo->OriBar1Reg = PciRead32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET + 4);
|
|
|
|
if (!(( Private->IsHybrid && IsIntelHybridMember(PciHcBase)) ||
|
|
(!Private->IsHybrid && IsPciNvmeHc (PciHcBase)) )
|
|
) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Get the address of endpoint BAR 0
|
|
//
|
|
|
|
PciAnd8 (PciHcBase + PCI_COMMAND_OFFSET, 0);
|
|
PciWrite32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET, 0xFFFFFFFF);
|
|
Size = PciRead32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET);
|
|
if ((Size & 0x07) == 0x4) {
|
|
//
|
|
// According to Nvm Express 1.1 spec Section 2.1.10, the register is 64-bits wide.
|
|
//
|
|
MmioSize = Size & 0xFFFFFFF0;
|
|
PciWrite32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET + 4, 0xFFFFFFFF);
|
|
Size = PciRead32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET + 4);
|
|
//
|
|
// Fix the length to support some spefic 64 bit BAR
|
|
//
|
|
Size |= ((UINT32)(-1) << HighBitSet32 (Size));
|
|
//
|
|
// Calculate the size of 64bit bar
|
|
//
|
|
MmioSize |= LShiftU64 ((UINT64) Size, 32);
|
|
MmioSize = (~(MmioSize)) + 1;
|
|
//
|
|
// Clean the high 32bits of this 64bit BAR to 0 as we only allow a 32bit BAR.
|
|
//
|
|
PciWrite32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET + 4, 0);
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "%a: Unknown BAR type for an NVM Express controller!\n", __FUNCTION__));
|
|
Status = EFI_UNSUPPORTED;
|
|
goto ErrorExit;
|
|
}
|
|
//
|
|
// Roundup the MMIO size for 1MB to meet the requirement by Pci-Pci Bridge.
|
|
// Section 3.2.5.8, PCI-to-PCI Bridge Architeture Specification, Revision 1.2.
|
|
//
|
|
MmioSize = ALIGN_VALUE (MmioSize, SIZE_1MB);
|
|
//
|
|
// Check the BAR range upon the limit
|
|
//
|
|
if ((MmioSize > MmioBarLimit) ||
|
|
(MmioBarBase > MmioBarLimit - MmioSize)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
MmioBarBase += (UINT32) MmioSize;
|
|
|
|
//
|
|
// Construct the device path of the PCI NVM Express host controller.
|
|
//
|
|
HcInfo->DevicePathLength = sizeof (NVME_HC_DEVICE_PATH);
|
|
HcInfo->DevicePath = AllocateCopyPool (HcInfo->DevicePathLength, &mNvmeHcDevicePathTemplate);
|
|
if (HcInfo->DevicePath == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
HcDevicePath = (NVME_HC_DEVICE_PATH *)HcInfo->DevicePath;
|
|
HcDevicePath->PciBridge.Device = HcInfo->Bridge.Device;
|
|
HcDevicePath->PciBridge.Function = HcInfo->Bridge.Function;
|
|
HcDevicePath->NvmeHcEp.Device = HcInfo->ControllerEndPoint.Device;
|
|
HcDevicePath->NvmeHcEp.Function = HcInfo->ControllerEndPoint.Function;
|
|
|
|
PciWrite32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET, (UINT32)HcInfo->Bar);
|
|
PciWrite32 (PciHcBase + PCI_BASE_ADDRESSREG_OFFSET + 4, 0);
|
|
PciWrite8 (PciHcBase + PCI_COMMAND_OFFSET, (EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE));
|
|
Private->TotalNvmeHcs++;
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"%a: NVM Express host controller found at - Bus(0x%x), Dev(0x%x), Func(0x%x)!\n",
|
|
__FUNCTION__,
|
|
HcInfo->ControllerEndPoint.Bus,
|
|
HcInfo->ControllerEndPoint.Device,
|
|
HcInfo->ControllerEndPoint.Function
|
|
));
|
|
DEBUG ((
|
|
DEBUG_INFO, "%a: Bar = 0x%x, MmioSize = 0x%x.\n",
|
|
__FUNCTION__, HcInfo->Bar, MmioSize
|
|
));
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
ErrorExit:
|
|
NvmeRestoreHcPciConfigSpace (HcInfo);
|
|
ZeroMem (HcInfo, sizeof (NVME_HC_INFO));
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Enable the NVM Express host controllers by programing the PCI configuration space.
|
|
|
|
@param[in,out] Private A pointer to the NVME_HC_PEI_PRIVATE_DATA structure.
|
|
@param[in] IsHybrid Indicate if private structure is for hybrid storage device.
|
|
|
|
@retval EFI_SUCCESS The function completes successfully.
|
|
@retval others The function fails.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
NvmeEnableHcs (
|
|
IN OUT NVME_HC_PEI_PRIVATE_DATA *Private,
|
|
IN BOOLEAN IsHybrid
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN RpDev;
|
|
UINTN RpFun;
|
|
|
|
Private->IsHybrid = IsHybrid;
|
|
|
|
//
|
|
// Enable NVMe on PCH PCIe ports
|
|
//
|
|
for (Index = 0; Index < GetPchMaxPciePortNum (); Index++) {
|
|
//
|
|
// Get device number and function number of specific root port number
|
|
//
|
|
Status = GetPchPcieRpDevFun (Index, &RpDev, &RpFun);
|
|
Status = NvmeEnableThisHc (Private, RpDev, RpFun);
|
|
if (Status == EFI_OUT_OF_RESOURCES) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enable NVMe on CPU PCIe ports
|
|
//
|
|
for (Index = 0; Index < GetMaxCpuPciePortNum (); Index++) {
|
|
Status = GetCpuPcieRpDevFun (Index, &RpDev, &RpFun);
|
|
Status = NvmeEnableThisHc (Private, RpDev, RpFun);
|
|
if (Status == EFI_OUT_OF_RESOURCES) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO, "%a: A total of %d NVM Express host controller found. IsHybird = %d\n",
|
|
__FUNCTION__, Private->TotalNvmeHcs, IsHybrid
|
|
));
|
|
|
|
if (Private->TotalNvmeHcs == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Private->Signature = NVME_HC_PEI_SIGNATURE;
|
|
CopyMem (&Private->NvmeHostControllerPpi, &mNvmeHostControllerPpi, sizeof (EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI));
|
|
|
|
if(IsHybrid) {
|
|
CopyMem (&Private->PpiList, &mHybridPpiList, sizeof (EFI_PEI_PPI_DESCRIPTOR));
|
|
} else {
|
|
CopyMem (&Private->PpiList, &mPpiList, sizeof (EFI_PEI_PPI_DESCRIPTOR));
|
|
}
|
|
|
|
CopyMem (&Private->EndOfPeiNotifyList, &mEndOfPeiNotifyList, sizeof (EFI_PEI_NOTIFY_DESCRIPTOR));
|
|
Private->PpiList.Ppi = &Private->NvmeHostControllerPpi;
|
|
|
|
PeiServicesInstallPpi (&Private->PpiList);
|
|
PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Initialize NVM Express host controller.
|
|
|
|
@retval EFI_SUCCESS The function completes successfully.
|
|
@retval others The function fails.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
NvmePciConfigSpaceProgram (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
NVME_HC_PEI_PRIVATE_DATA *Private;
|
|
|
|
Status = NvmeGetMmio (&MmioBarBase, &MmioBarLimit);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Initialize controllers of NVME devices
|
|
//
|
|
Private = (NVME_HC_PEI_PRIVATE_DATA *) AllocateZeroPool (sizeof (NVME_HC_PEI_PRIVATE_DATA));
|
|
if (Private == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "%a: Failed to allocate memory for NVME_HC_PEI_PRIVATE_DATA.\n", __FUNCTION__));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
NvmeEnableHcs (Private, FALSE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
One notified function to cleanup the allocated resources at the end of PEI.
|
|
|
|
@param[in] PeiServices Pointer to PEI Services Table.
|
|
@param[in] NotifyDescriptor Pointer to the descriptor for the Notification
|
|
event that caused this function to execute.
|
|
@param[in] Ppi Pointer to the PPI data associated with this function.
|
|
|
|
@retval EFI_SUCCESS The function completes successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
NvmeHcEndOfPei (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
|
|
IN VOID *Ppi
|
|
)
|
|
{
|
|
NVME_HC_PEI_PRIVATE_DATA *Private;
|
|
UINTN HcIndex;
|
|
|
|
Private = NVME_HC_PEI_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);
|
|
ASSERT (Private->TotalNvmeHcs <= MAX_NVME_HCS);
|
|
|
|
for (HcIndex = 0; HcIndex < Private->TotalNvmeHcs; HcIndex++) {
|
|
NvmeRestoreHcPciConfigSpace (&(Private->HcInfo[HcIndex]));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
The callback function for SiInitDone.
|
|
It is to make sure this driver get BDF after rootport function mapping.
|
|
|
|
@param[in] PeiServices Pointer to PEI Services Table.
|
|
@param[in] NotifyDescriptor Pointer to the descriptor for the Notification
|
|
event that caused this function to execute.
|
|
@param[in] Ppi Pointer to the PPI data associated with this function.
|
|
|
|
@retval EFI_SUCCESS The function completes successfully.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SiInitCallBack (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
|
|
IN VOID *Ppi
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = NvmePciConfigSpaceProgram ();
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
The user code starts with this function.
|
|
|
|
@param FileHandle Handle of the file being invoked.
|
|
@param PeiServices Describes the list of possible PEI Services.
|
|
|
|
@retval EFI_SUCCESS The driver is successfully initialized.
|
|
@retval Others Can't initialize the driver.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
NvmePciHcPeimEntry (
|
|
IN EFI_PEI_FILE_HANDLE FileHandle,
|
|
IN CONST EFI_PEI_SERVICES **PeiServices
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_BOOT_MODE BootMode;
|
|
|
|
Status = PeiServicesGetBootMode (&BootMode);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));
|
|
return Status;
|
|
}
|
|
|
|
if ((BootMode == BOOT_ON_S3_RESUME) ||
|
|
(BootMode == BOOT_IN_RECOVERY_MODE) ||
|
|
(BootMode == BOOT_ON_FLASH_UPDATE)) {
|
|
|
|
if (PcdGet8 (PcdFspModeSelection) == 0) { // FSP dispatch mode
|
|
//
|
|
// Register a notification for SiInitDone
|
|
//
|
|
Status = PeiServicesNotifyPpi (&mSiInitDoneNotifyList);
|
|
} else {
|
|
Status = NvmePciConfigSpaceProgram ();
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|