476 lines
13 KiB
C
476 lines
13 KiB
C
/** @file
|
|
Implementation of PchNvmePei module for Crisis Recovery
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2016, Insyde Software Corporation. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
#include <PiPei.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/PeiServicesLib.h>
|
|
#include <IndustryStandard/Pci.h>
|
|
#include <Ppi/NvmeController.h>
|
|
#include <Ppi/Stall.h>
|
|
#include <Ppi/EndOfPeiPhase.h>
|
|
|
|
#define CLASSCODE_REGISTER 0x08
|
|
#define BASE_ADDRESS_REGISTER 0x10
|
|
#define NVME_CLASSCODE 0x01080200
|
|
#define ROOT_BRIDGE_BUS_REGISTER 0x18
|
|
#define ROOT_BRIDGE_ADDRESS_REGISTER 0x20
|
|
|
|
#define PEI_NVME_SIGNATURE SIGNATURE_32 ('N', 'V', 'M', 'C')
|
|
|
|
typedef struct {
|
|
UINTN Signature;
|
|
PEI_NVME_CONTROLLER_PPI NvmeControllerPpi;
|
|
EFI_PEI_PPI_DESCRIPTOR PpiList;
|
|
EFI_PEI_NOTIFY_DESCRIPTOR NotifyList;
|
|
EFI_PEI_STALL_PPI *StallPpi;
|
|
UINTN TotalNvmeControllers;
|
|
UINTN MemBase;
|
|
UINTN RootBridge;
|
|
UINTN PciAddress;
|
|
} PEI_NVME_DEVICE;
|
|
|
|
#define PEI_NVME_DEVICE_FROM_THIS(a) CR(a, PEI_NVME_DEVICE, NvmeControllerPpi, PEI_NVME_SIGNATURE)
|
|
#define PEI_NVME_DEVICE_FROM_NOTIFY_DESC(a) CR(a, PEI_NVME_DEVICE, NotifyList, PEI_NVME_SIGNATURE)
|
|
|
|
EFI_STATUS
|
|
GetNvmeController (
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_NVME_CONTROLLER_PPI *This,
|
|
IN UINT8 NvmeControllerId,
|
|
OUT EFI_PHYSICAL_ADDRESS *BaseAddress
|
|
);
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EndOfPeiPpiNotifyCallback (
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
IN CONST EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
|
|
IN VOID *Ppi
|
|
);
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
EFI_PEI_PPI_DESCRIPTOR mPpiList = {
|
|
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
&gPeiNvmeControllerPpiGuid,
|
|
NULL
|
|
};
|
|
|
|
EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList = {
|
|
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
&gEfiEndOfPeiSignalPpiGuid,
|
|
EndOfPeiPpiNotifyCallback
|
|
};
|
|
|
|
//
|
|
// Helper function
|
|
//
|
|
EFI_STATUS
|
|
EnableNvmeController (
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_NVME_DEVICE *PeiPchNvmeDev,
|
|
IN UINT8 NvmeControllerId
|
|
);
|
|
|
|
/**
|
|
|
|
@param [in] FfsHeader
|
|
@param [in] PeiServices
|
|
|
|
|
|
**/
|
|
EFI_STATUS
|
|
InitializePchNvme (
|
|
IN EFI_PEI_FILE_HANDLE FileHandle,
|
|
IN CONST EFI_PEI_SERVICES **PeiServices
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PEI_STALL_PPI *StallPpi;
|
|
PEI_NVME_DEVICE *PeiPchNvmeDev;
|
|
|
|
if (PcdGet32 (PcdNvmeRootPortAddress) == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Get StallPpi
|
|
//
|
|
Status = (**PeiServices).LocatePpi (
|
|
PeiServices,
|
|
&gEfiPeiStallPpiGuid,
|
|
0,
|
|
NULL,
|
|
(VOID **)&StallPpi
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
PeiPchNvmeDev = (PEI_NVME_DEVICE *)AllocatePages (1);
|
|
if (PeiPchNvmeDev == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
ZeroMem (PeiPchNvmeDev, sizeof(PeiPchNvmeDev));
|
|
|
|
PeiPchNvmeDev->Signature = PEI_NVME_SIGNATURE;
|
|
PeiPchNvmeDev->NvmeControllerPpi.GetNvmeController = GetNvmeController;
|
|
PeiPchNvmeDev->PpiList = mPpiList;
|
|
PeiPchNvmeDev->PpiList.Ppi = &PeiPchNvmeDev->NvmeControllerPpi;
|
|
PeiPchNvmeDev->StallPpi = StallPpi;
|
|
PeiPchNvmeDev->NotifyList = mNotifyList;
|
|
PeiPchNvmeDev->TotalNvmeControllers = 1;
|
|
//
|
|
// Assign resources and enable NVME controllers
|
|
//
|
|
PeiPchNvmeDev->MemBase = PcdGet32 ( PcdNvmeMemBaseAddress );
|
|
Status = EnableNvmeController (PeiServices, PeiPchNvmeDev, 0);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Install NVME Controller PPI
|
|
//
|
|
Status = PeiServicesInstallPpi (
|
|
&PeiPchNvmeDev->PpiList
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Install notification in order to reset the NVME
|
|
//
|
|
Status = PeiServicesNotifyPpi (
|
|
&PeiPchNvmeDev->NotifyList
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Retrieve NVME controller information
|
|
|
|
@param [in] PeiServices Pointer to the PEI Services Table.
|
|
@param [in] This Pointer to PEI_NVME_CONTROLLER_PPI
|
|
@param [in] NvmeControllerId NVME Controller ID
|
|
@param [out] BaseAddress Result NVME base address
|
|
|
|
@retval EFI_INVALID_PARAMETER Invalid AhciControllerId is given
|
|
@retval EFI_SUCCESS NVME controller information is retrieved successfully
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetNvmeController (
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_NVME_CONTROLLER_PPI *This,
|
|
IN UINT8 NvmeControllerId,
|
|
OUT EFI_PHYSICAL_ADDRESS *BaseAddress
|
|
)
|
|
{
|
|
PEI_NVME_DEVICE *PeiPchNvmeDev;
|
|
|
|
PeiPchNvmeDev = PEI_NVME_DEVICE_FROM_THIS (This);
|
|
|
|
if (NvmeControllerId >= PeiPchNvmeDev->TotalNvmeControllers) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
*BaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiPchNvmeDev->MemBase;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Enable NVME controller
|
|
|
|
@param [in] PeiServices Pointer to the PEI Services Table.
|
|
@param [in] PeiPchNvmeDev Pointer to the PEI_NVME_DEVICE instance
|
|
@param [in] NvmeControllerId NVME Controller ID
|
|
|
|
@retval EFI_INVALID_PARAMETER Invalid AhciControllerId is given
|
|
@retval EFI_SUCCESS NVME controller information is retrieved successfully
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EnableNvmeController (
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_NVME_DEVICE *PeiPchNvmeDev,
|
|
IN UINT8 NvmeControllerId
|
|
)
|
|
{
|
|
EFI_PEI_PCI_CFG2_PPI *PciCfgPpi;
|
|
UINT32 RootPort;
|
|
UINT32 Bus;
|
|
UINT32 Register;
|
|
UINT32 Bridge;
|
|
UINT32 Device;
|
|
UINT32 OrgSecBusNum;
|
|
|
|
if (NvmeControllerId >= PeiPchNvmeDev->TotalNvmeControllers) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
PciCfgPpi = (**PeiServices).PciCfg;
|
|
//
|
|
// Setup appropriate value to PCIE bridge
|
|
//
|
|
RootPort = PcdGet32 ( PcdNvmeRootPortAddress );
|
|
Bus = RootPort >> 16;
|
|
RootPort &= 0xffff;
|
|
Bridge = RootPort << 8;
|
|
Device = Bus << 24;
|
|
|
|
//
|
|
// backup the orignal secondary and subordinary bus number
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Read (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
Bridge | ROOT_BRIDGE_BUS_REGISTER,
|
|
&Register
|
|
);
|
|
OrgSecBusNum = Register;
|
|
|
|
|
|
//
|
|
// Assign bus number to PCIE bridge
|
|
//
|
|
Register = (Bus << 8) + (Bus << 16);
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
Bridge | ROOT_BRIDGE_BUS_REGISTER,
|
|
&Register
|
|
);
|
|
//
|
|
// Discover NVME
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Read (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
Device,
|
|
&Register
|
|
);
|
|
if (Register == 0xffff) {
|
|
//
|
|
// NVME not found, clear bus number to PCIE bridge
|
|
//
|
|
goto error;
|
|
}
|
|
//
|
|
// Check the class code
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Read (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
Device | CLASSCODE_REGISTER,
|
|
&Register
|
|
);
|
|
Register &= 0xffffff00;
|
|
if (Register != NVME_CLASSCODE) {
|
|
//
|
|
// Not NVME, clear bus number to PCIE bridge
|
|
//
|
|
goto error;
|
|
}
|
|
PeiPchNvmeDev->RootBridge = Bridge;
|
|
PeiPchNvmeDev->PciAddress = Device;
|
|
//
|
|
// Assign address range for root bridge
|
|
//
|
|
Register = ((PeiPchNvmeDev->MemBase + 0x00100000) & 0xfff00000) + ((PeiPchNvmeDev->MemBase & 0xfff00000) >> 16);
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
Bridge | ROOT_BRIDGE_ADDRESS_REGISTER,
|
|
&Register
|
|
);
|
|
//
|
|
// Assign address prefetchable range for root bridge
|
|
//
|
|
Register = ((PeiPchNvmeDev->MemBase + 0x00200000) & 0xfff00000) + (((PeiPchNvmeDev->MemBase + 0x00100000) & 0xfff00000) >> 16);
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
Bridge | ROOT_BRIDGE_ADDRESS_REGISTER + 4,
|
|
&Register
|
|
);
|
|
//
|
|
// Assign base address register to NVME
|
|
//
|
|
Register = PeiPchNvmeDev->MemBase;
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
Device | BASE_ADDRESS_REGISTER,
|
|
&Register
|
|
);
|
|
//
|
|
// Enable root bridge
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Read (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
Bridge | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
Register |= 0x06;
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
Bridge | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
//
|
|
// Enable NVME
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Read (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
Device | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
Register |= 0x06;
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
Device | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
return EFI_SUCCESS;
|
|
error:
|
|
Register = OrgSecBusNum;
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
Bridge | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
Register notify ppi to reset the NVME.
|
|
|
|
|
|
@param[in] PeiServices Pointer to the PEI Services Table.
|
|
@param[in] NotifyDescriptor Pointer to the notify descriptor
|
|
|
|
@retval EFI_SUCCESS
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EndOfPeiPpiNotifyCallback (
|
|
IN CONST EFI_PEI_SERVICES **PeiServices,
|
|
IN CONST EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
|
|
IN VOID *Ppi
|
|
)
|
|
{
|
|
EFI_PEI_PCI_CFG2_PPI *PciCfgPpi;
|
|
UINT8 *BaseAddress;
|
|
UINT32 Register;
|
|
PEI_NVME_DEVICE *PeiPchNvmeDev;
|
|
|
|
PeiPchNvmeDev = PEI_NVME_DEVICE_FROM_NOTIFY_DESC (NotifyDescriptor);
|
|
//
|
|
// Disable the NVME
|
|
//
|
|
BaseAddress = (UINT8*)(UINTN)PeiPchNvmeDev->MemBase + 0x14;
|
|
Register = *(UINT32*)(UINTN)BaseAddress;
|
|
Register &= ~0x01;
|
|
*(UINT32*)(UINTN)BaseAddress = Register;
|
|
PeiPchNvmeDev->StallPpi->Stall (
|
|
PeiServices,
|
|
PeiPchNvmeDev->StallPpi,
|
|
500 * 1000
|
|
);
|
|
PciCfgPpi = (**PeiServices).PciCfg;
|
|
//
|
|
// Disable NVME
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Read (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
PeiPchNvmeDev->PciAddress | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
Register &= ~0x06;
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
PeiPchNvmeDev->PciAddress | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
//
|
|
// Disable root bridge
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Read (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
PeiPchNvmeDev->RootBridge | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
Register &= ~0x06;
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint16,
|
|
PeiPchNvmeDev->RootBridge | PCI_COMMAND_OFFSET,
|
|
&Register
|
|
);
|
|
//
|
|
// Clear bus number for root bridge
|
|
//
|
|
Register = 0;
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
PeiPchNvmeDev->RootBridge | ROOT_BRIDGE_BUS_REGISTER,
|
|
&Register
|
|
);
|
|
//
|
|
// Clear address range for root bridge
|
|
//
|
|
PciCfgPpi->Write (
|
|
PeiServices,
|
|
PciCfgPpi,
|
|
EfiPeiPciCfgWidthUint32,
|
|
PeiPchNvmeDev->RootBridge | ROOT_BRIDGE_ADDRESS_REGISTER,
|
|
&Register
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|