/** @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.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "AhciPei.h" #include #include #include 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; }