/** @file | |
The AhciPei driver is used to manage ATA hard disk device working under AHCI | |
mode at PEI phase. | |
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "AhciPei.h" | |
#include <Ppi/PciDevice.h> | |
#include <Library/DevicePathLib.h> | |
#include <IndustryStandard/Pci.h> | |
EFI_PEI_PPI_DESCRIPTOR mAhciAtaPassThruPpiListTemplate = { | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEdkiiPeiAtaPassThruPpiGuid, | |
NULL | |
}; | |
EFI_PEI_PPI_DESCRIPTOR mAhciBlkIoPpiListTemplate = { | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEfiPeiVirtualBlockIoPpiGuid, | |
NULL | |
}; | |
EFI_PEI_PPI_DESCRIPTOR mAhciBlkIo2PpiListTemplate = { | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEfiPeiVirtualBlockIo2PpiGuid, | |
NULL | |
}; | |
EFI_PEI_PPI_DESCRIPTOR mAhciStorageSecurityPpiListTemplate = { | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEdkiiPeiStorageSecurityCommandPpiGuid, | |
NULL | |
}; | |
EFI_PEI_NOTIFY_DESCRIPTOR mAhciEndOfPeiNotifyListTemplate = { | |
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEfiEndOfPeiSignalPpiGuid, | |
AhciPeimEndOfPei | |
}; | |
EFI_PEI_NOTIFY_DESCRIPTOR mAtaAhciHostControllerNotify = { | |
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEdkiiPeiAtaAhciHostControllerPpiGuid, | |
AtaAhciHostControllerPpiInstallationCallback | |
}; | |
EFI_PEI_NOTIFY_DESCRIPTOR mPciDevicePpiNotify = { | |
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEdkiiPeiPciDevicePpiGuid, | |
AtaAhciPciDevicePpiInstallationCallback | |
}; | |
/** | |
Free the DMA resources allocated by an ATA AHCI controller. | |
@param[in] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA data | |
structure. | |
**/ | |
VOID | |
AhciFreeDmaResource ( | |
IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private | |
) | |
{ | |
EFI_AHCI_REGISTERS *AhciRegisters; | |
ASSERT (Private != NULL); | |
AhciRegisters = &Private->AhciRegisters; | |
if (AhciRegisters->AhciRFisMap != NULL) { | |
IoMmuFreeBuffer ( | |
EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize), | |
AhciRegisters->AhciRFis, | |
AhciRegisters->AhciRFisMap | |
); | |
} | |
if (AhciRegisters->AhciCmdListMap != NULL) { | |
IoMmuFreeBuffer ( | |
EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize), | |
AhciRegisters->AhciCmdList, | |
AhciRegisters->AhciCmdListMap | |
); | |
} | |
if (AhciRegisters->AhciCmdTableMap != NULL) { | |
IoMmuFreeBuffer ( | |
EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdTableSize), | |
AhciRegisters->AhciCmdTable, | |
AhciRegisters->AhciCmdTableMap | |
); | |
} | |
} | |
/** | |
One notified function to cleanup the allocated DMA buffers at EndOfPei. | |
@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 | |
AhciPeimEndOfPei ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, | |
IN VOID *Ppi | |
) | |
{ | |
PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private; | |
Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor); | |
AhciFreeDmaResource (Private); | |
return EFI_SUCCESS; | |
} | |
/** | |
Initialize and install PrivateData PPIs. | |
@param[in] MmioBase MMIO base address of specific AHCI controller | |
@param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL | |
structure. | |
@param[in] DevicePathLength Length of the device path. | |
@retval EFI_SUCCESS AHCI controller initialized and PPIs installed | |
@retval others Failed to initialize AHCI controller | |
**/ | |
EFI_STATUS | |
AtaAhciInitPrivateData ( | |
IN UINTN MmioBase, | |
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
IN UINTN DevicePathLength | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 PortBitMap; | |
UINT8 NumberOfPorts; | |
PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private; | |
EFI_BOOT_MODE BootMode; | |
DEBUG ((DEBUG_INFO, "Initializing private data for ATA\n")); | |
// | |
// Get the current boot mode. | |
// | |
Status = PeiServicesGetBootMode (&BootMode); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __func__)); | |
return Status; | |
} | |
// | |
// Check validity of the device path of the ATA AHCI controller. | |
// | |
Status = AhciIsHcDevicePathValid (DevicePath, DevicePathLength); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: The device path is invalid.\n", | |
__func__ | |
)); | |
return Status; | |
} | |
// | |
// For S3 resume performance consideration, not all ports on an ATA AHCI | |
// controller will be enumerated/initialized. The driver consumes the | |
// content within S3StorageDeviceInitList LockBox to get the ports that | |
// will be enumerated/initialized during S3 resume. | |
// | |
if (BootMode == BOOT_ON_S3_RESUME) { | |
NumberOfPorts = AhciS3GetEumeratePorts (DevicePath, DevicePathLength, &PortBitMap); | |
if (NumberOfPorts == 0) { | |
return EFI_SUCCESS; | |
} | |
} else { | |
PortBitMap = MAX_UINT32; | |
} | |
// | |
// Memory allocation for controller private data. | |
// | |
Private = AllocateZeroPool (sizeof (PEI_AHCI_CONTROLLER_PRIVATE_DATA)); | |
if (Private == NULL) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Fail to allocate private data.\n", | |
__func__ | |
)); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Initialize controller private data. | |
// | |
Private->Signature = AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE; | |
Private->MmioBase = MmioBase; | |
Private->DevicePathLength = DevicePathLength; | |
Private->DevicePath = DevicePath; | |
Private->PortBitMap = PortBitMap; | |
InitializeListHead (&Private->DeviceList); | |
Status = AhciModeInitialization (Private); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Private->AtaPassThruMode.Attributes = EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL | | |
EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL; | |
Private->AtaPassThruMode.IoAlign = sizeof (UINTN); | |
Private->AtaPassThruPpi.Revision = EDKII_PEI_ATA_PASS_THRU_PPI_REVISION; | |
Private->AtaPassThruPpi.Mode = &Private->AtaPassThruMode; | |
Private->AtaPassThruPpi.PassThru = AhciAtaPassThruPassThru; | |
Private->AtaPassThruPpi.GetNextPort = AhciAtaPassThruGetNextPort; | |
Private->AtaPassThruPpi.GetNextDevice = AhciAtaPassThruGetNextDevice; | |
Private->AtaPassThruPpi.GetDevicePath = AhciAtaPassThruGetDevicePath; | |
CopyMem ( | |
&Private->AtaPassThruPpiList, | |
&mAhciAtaPassThruPpiListTemplate, | |
sizeof (EFI_PEI_PPI_DESCRIPTOR) | |
); | |
Private->AtaPassThruPpiList.Ppi = &Private->AtaPassThruPpi; | |
PeiServicesInstallPpi (&Private->AtaPassThruPpiList); | |
Private->BlkIoPpi.GetNumberOfBlockDevices = AhciBlockIoGetDeviceNo; | |
Private->BlkIoPpi.GetBlockDeviceMediaInfo = AhciBlockIoGetMediaInfo; | |
Private->BlkIoPpi.ReadBlocks = AhciBlockIoReadBlocks; | |
CopyMem ( | |
&Private->BlkIoPpiList, | |
&mAhciBlkIoPpiListTemplate, | |
sizeof (EFI_PEI_PPI_DESCRIPTOR) | |
); | |
Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi; | |
PeiServicesInstallPpi (&Private->BlkIoPpiList); | |
Private->BlkIo2Ppi.Revision = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION; | |
Private->BlkIo2Ppi.GetNumberOfBlockDevices = AhciBlockIoGetDeviceNo2; | |
Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = AhciBlockIoGetMediaInfo2; | |
Private->BlkIo2Ppi.ReadBlocks = AhciBlockIoReadBlocks2; | |
CopyMem ( | |
&Private->BlkIo2PpiList, | |
&mAhciBlkIo2PpiListTemplate, | |
sizeof (EFI_PEI_PPI_DESCRIPTOR) | |
); | |
Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi; | |
PeiServicesInstallPpi (&Private->BlkIo2PpiList); | |
if (Private->TrustComputingDevices != 0) { | |
DEBUG (( | |
DEBUG_INFO, | |
"%a: Security Security Command PPI will be produced.\n", | |
__func__ | |
)); | |
Private->StorageSecurityPpi.Revision = EDKII_STORAGE_SECURITY_PPI_REVISION; | |
Private->StorageSecurityPpi.GetNumberofDevices = AhciStorageSecurityGetDeviceNo; | |
Private->StorageSecurityPpi.GetDevicePath = AhciStorageSecurityGetDevicePath; | |
Private->StorageSecurityPpi.ReceiveData = AhciStorageSecurityReceiveData; | |
Private->StorageSecurityPpi.SendData = AhciStorageSecuritySendData; | |
CopyMem ( | |
&Private->StorageSecurityPpiList, | |
&mAhciStorageSecurityPpiListTemplate, | |
sizeof (EFI_PEI_PPI_DESCRIPTOR) | |
); | |
Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi; | |
PeiServicesInstallPpi (&Private->StorageSecurityPpiList); | |
} | |
CopyMem ( | |
&Private->EndOfPeiNotifyList, | |
&mAhciEndOfPeiNotifyListTemplate, | |
sizeof (EFI_PEI_NOTIFY_DESCRIPTOR) | |
); | |
PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList); | |
return EFI_SUCCESS; | |
} | |
/** | |
Initialize AHCI controller from EDKII_ATA_AHCI_HOST_CONTROLLER_PPI instance. | |
@param[in] AhciHcPpi Pointer to the AHCI Host Controller PPI instance. | |
@retval EFI_SUCCESS PPI successfully installed. | |
**/ | |
EFI_STATUS | |
AtaAhciInitPrivateDataFromHostControllerPpi ( | |
IN EDKII_ATA_AHCI_HOST_CONTROLLER_PPI *AhciHcPpi | |
) | |
{ | |
UINT8 Controller; | |
UINTN MmioBase; | |
UINTN DevicePathLength; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
EFI_STATUS Status; | |
Controller = 0; | |
MmioBase = 0; | |
while (TRUE) { | |
Status = AhciHcPpi->GetAhciHcMmioBar ( | |
AhciHcPpi, | |
Controller, | |
&MmioBase | |
); | |
// | |
// When status is error, meant no controller is found. | |
// | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
Status = AhciHcPpi->GetAhciHcDevicePath ( | |
AhciHcPpi, | |
Controller, | |
&DevicePathLength, | |
&DevicePath | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Fail to allocate get the device path for Controller %d.\n", | |
__func__, | |
Controller | |
)); | |
return Status; | |
} | |
Status = AtaAhciInitPrivateData (MmioBase, DevicePath, DevicePathLength); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Controller initialization fail for Controller %d with Status - %r.\n", | |
__func__, | |
Controller, | |
Status | |
)); | |
} else { | |
DEBUG (( | |
DEBUG_INFO, | |
"%a: Controller %d has been successfully initialized.\n", | |
__func__, | |
Controller | |
)); | |
} | |
Controller++; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Callback for EDKII_ATA_AHCI_HOST_CONTROLLER_PPI installation. | |
@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 | |
@retval Others Cannot initialize AHCI controller from given EDKII_ATA_AHCI_HOST_CONTROLLER_PPI | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaAhciHostControllerPpiInstallationCallback ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, | |
IN VOID *Ppi | |
) | |
{ | |
EDKII_ATA_AHCI_HOST_CONTROLLER_PPI *AhciHcPpi; | |
if (Ppi == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
AhciHcPpi = (EDKII_ATA_AHCI_HOST_CONTROLLER_PPI *)Ppi; | |
return AtaAhciInitPrivateDataFromHostControllerPpi (AhciHcPpi); | |
} | |
/** | |
Initialize AHCI controller from fiven PCI_DEVICE_PPI. | |
@param[in] PciDevice Pointer to the PCI Device PPI instance. | |
@retval EFI_SUCCESS The function completes successfully | |
@retval Others Cannot initialize AHCI controller for given device | |
**/ | |
EFI_STATUS | |
AtaAhciInitPrivateDataFromPciDevice ( | |
EDKII_PCI_DEVICE_PPI *PciDevice | |
) | |
{ | |
EFI_STATUS Status; | |
PCI_TYPE00 PciData; | |
UINT32 MmioBase; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
UINTN DevicePathLength; | |
UINT64 EnabledPciAttributes; | |
// | |
// Now further check the PCI header: Base Class (offset 0x0B) and | |
// Sub Class (offset 0x0A). This controller should be an SATA controller | |
// | |
Status = PciDevice->PciIo.Pci.Read ( | |
&PciDevice->PciIo, | |
EfiPciIoWidthUint8, | |
PCI_CLASSCODE_OFFSET, | |
sizeof (PciData.Hdr.ClassCode), | |
PciData.Hdr.ClassCode | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
if (!IS_PCI_IDE (&PciData) && !IS_PCI_SATADPA (&PciData)) { | |
return EFI_UNSUPPORTED; | |
} | |
Status = PciDevice->PciIo.Attributes ( | |
&PciDevice->PciIo, | |
EfiPciIoAttributeOperationSupported, | |
0, | |
&EnabledPciAttributes | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} else { | |
EnabledPciAttributes &= (UINT64)EFI_PCI_DEVICE_ENABLE; | |
Status = PciDevice->PciIo.Attributes ( | |
&PciDevice->PciIo, | |
EfiPciIoAttributeOperationEnable, | |
EnabledPciAttributes, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
} | |
Status = PciDevice->PciIo.Pci.Read ( | |
&PciDevice->PciIo, | |
EfiPciIoWidthUint32, | |
0x24, | |
1, | |
&MmioBase | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
MmioBase &= 0xFFFFFFF0; | |
DevicePathLength = GetDevicePathSize (PciDevice->DevicePath); | |
DevicePath = PciDevice->DevicePath; | |
Status = AtaAhciInitPrivateData (MmioBase, DevicePath, DevicePathLength); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_INFO, | |
"%a: Failed to init controller, with Status - %r\n", | |
__func__, | |
Status | |
)); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Callback for EDKII_PCI_DEVICE_PPI installation. | |
@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 | |
@retval Others Cannot initialize AHCI controller from given PCI_DEVICE_PPI | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaAhciPciDevicePpiInstallationCallback ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, | |
IN VOID *Ppi | |
) | |
{ | |
EDKII_PCI_DEVICE_PPI *PciDevice; | |
PciDevice = (EDKII_PCI_DEVICE_PPI *)Ppi; | |
return AtaAhciInitPrivateDataFromPciDevice (PciDevice); | |
} | |
/** | |
Entry point of the PEIM. | |
@param[in] FileHandle Handle of the file being invoked. | |
@param[in] PeiServices Describes the list of possible PEI Services. | |
@retval EFI_SUCCESS PPI successfully installed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaAhciPeimEntry ( | |
IN EFI_PEI_FILE_HANDLE FileHandle, | |
IN CONST EFI_PEI_SERVICES **PeiServices | |
) | |
{ | |
DEBUG ((DEBUG_INFO, "%a: Enters.\n", __func__)); | |
PeiServicesNotifyPpi (&mAtaAhciHostControllerNotify); | |
PeiServicesNotifyPpi (&mPciDevicePpiNotify); | |
return EFI_SUCCESS; | |
} |