/** @file This file implement UEFI driver for IDE Bus which includes device identification, Child device(Disk, CDROM, etc) enumeration and child handler installation, and driver stop. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Revision Reference: This module is modified from DXE\IDE module for Ide Contriller Init support **/ #include "IdeBus.h" #define PCI_CLASS_MASS_STORAGE 0x01 #define PCI_SUB_CLASS_IDE 0x01 // // IDE Bus Driver Binding Protocol Instance // EFI_DRIVER_BINDING_PROTOCOL gIDEBusDriverBinding = { IDEBusDriverBindingSupported, IDEBusDriverBindingStart, IDEBusDriverBindingStop, 0xa, NULL, NULL }; /** Deregister an IDE device and free resources @param This Protocol instance pointer. @param Controller Ide device handle @param Handle Handle of device to deregister driver on @retval EFI_SUCCESS Deregiter a specific IDE device successfully **/ EFI_STATUS DeRegisterIdeDevice ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_HANDLE Handle ) { EFI_STATUS Status; EFI_BLOCK_IO_PROTOCOL *BlkIo; IDE_BLK_IO_DEV *IdeBlkIoDevice; EFI_PCI_IO_PROTOCOL *PciIo; UINTN Index; Status = gBS->OpenProtocol ( Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlkIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return Status; } IdeBlkIoDevice = IDE_BLOCK_IO_DEV_FROM_THIS (BlkIo); // // Report Status code: Device disabled // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, (EFI_IO_BUS_ATA_ATAPI | EFI_P_PC_DISABLE), IdeBlkIoDevice->DevicePath ); // // Close the child handle // Status = gBS->CloseProtocol ( Controller, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, Handle ); Status = gBS->UninstallMultipleProtocolInterfaces ( Handle, &gEfiDevicePathProtocolGuid, IdeBlkIoDevice->DevicePath, &gEfiBlockIoProtocolGuid, &IdeBlkIoDevice->BlkIo, &gEfiDiskInfoProtocolGuid, &IdeBlkIoDevice->DiskInfo, NULL ); if (EFI_ERROR (Status)) { gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); return Status; } // // Release allocated resources // Index = IdeBlkIoDevice->Channel * 2 + IdeBlkIoDevice->Device; if (Index < MAX_IDE_DEVICE) { IdeBlkIoDevice->IdeBusDriverPrivateData->HaveScannedDevice[Index] = FALSE; } ReleaseIdeResources (IdeBlkIoDevice); return EFI_SUCCESS; } /** Supported function of Driver Binding protocol for this driver. @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. @param ControllerHandle The handle of the controller to test. @param RemainingDevicePath A pointer to the remaining portion of a device path. @retval EFI_SUCCESS Driver loaded. @retval other Driver not loaded. **/ EFI_STATUS EFIAPI IDEBusDriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; EFI_DEV_PATH *Node; EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit; EFI_PCI_IO_PROTOCOL *PciIo; PCI_TYPE00 PciData; if (RemainingDevicePath != NULL) { Node = (EFI_DEV_PATH *) RemainingDevicePath; // // Check if RemainingDevicePath is the End of Device Path Node, // if yes, go on checking other conditions // if (!IsDevicePathEnd (Node)) { // // If RemainingDevicePath isn't the End of Device Path Node, // check its validation // if (Node->DevPath.Type != MESSAGING_DEVICE_PATH || Node->DevPath.SubType != MSG_ATAPI_DP || DevicePathNodeLength(&Node->DevPath) != sizeof(ATAPI_DEVICE_PATH)) { return EFI_UNSUPPORTED; } } } // // Verify the Ide Controller Init Protocol, which installed by the // IdeController module. // Status = gBS->OpenProtocol ( Controller, &gEfiIdeControllerInitProtocolGuid, (VOID **) &IdeInit, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } if (EFI_ERROR (Status)) { return Status; } // // Close the I/O Abstraction(s) used to perform the supported test // gBS->CloseProtocol ( Controller, &gEfiIdeControllerInitProtocolGuid, This->DriverBindingHandle, Controller ); // // Open the EFI Device Path protocol needed to perform the supported test // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } // // Close protocol, don't use device path protocol in the Support() function // gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); // // Get the EfiPciIoProtocol // Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return Status; } // // Now further check the PCI header: Base class (offset 0x0B) and // Sub Class (offset 0x0A). This controller should be an IDE controller // Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint8, 0, sizeof (PciData), &PciData ); if (!EFI_ERROR (Status)) { // // Examine if it is IDE mode by class code // if ((PciData.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE) || (PciData.Hdr.ClassCode[1] != PCI_SUB_CLASS_IDE)) { Status = EFI_UNSUPPORTED; } else { Status = EFI_SUCCESS; } } return Status; } /** Start function of Driver binding protocol which start this driver on Controller by detecting all disks and installing BlockIo protocol on them. @param This Protocol instance pointer. @param Controller Handle of device to bind driver to. @param RemainingDevicePath produce all possible children. @retval EFI_SUCCESS This driver is added to ControllerHandle. @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. @retval other This driver does not support this device. **/ EFI_STATUS EFIAPI IDEBusDriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_STATUS SavedStatus; EFI_PCI_IO_PROTOCOL *PciIo; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; EFI_DEV_PATH *Node; UINT8 IdeChannel; UINT8 BeginningIdeChannel; UINT8 EndIdeChannel; UINT8 IdeDevice; UINT8 BeginningIdeDevice; UINT8 EndIdeDevice; IDE_BLK_IO_DEV *IdeBlkIoDevice[IdeMaxChannel][IdeMaxDevice]; IDE_BLK_IO_DEV *IdeBlkIoDevicePtr; IDE_REGISTERS_BASE_ADDR IdeRegsBaseAddr[IdeMaxChannel]; ATA_TRANSFER_MODE TransferMode; ATA_DRIVE_PARMS DriveParameters; EFI_DEV_PATH NewNode; UINT8 ConfigurationOptions; UINT16 CommandBlockBaseAddr; UINT16 ControlBlockBaseAddr; UINTN DataSize; IDE_BUS_DRIVER_PRIVATE_DATA *IdeBusDriverPrivateData; UINT64 Supports; // // Local variables declaration for IdeControllerInit support // EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit; BOOLEAN EnumAll; BOOLEAN ChannelEnabled; UINT8 MaxDevices; EFI_IDENTIFY_DATA IdentifyData; EFI_ATA_COLLECTIVE_MODE *SupportedModes; IdeBusDriverPrivateData = NULL; SupportedModes = NULL; // // Perform IdeBus initialization // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { return Status; } // // Now open the IDE_CONTROLLER_INIT protocol. Step7.1 // Status = gBS->OpenProtocol ( Controller, &gEfiIdeControllerInitProtocolGuid, (VOID **) &IdeInit, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); // // The following OpenProtocol function with _GET_PROTOCOL attribute and // will not return EFI_ALREADY_STARTED, so save it for now // SavedStatus = Status; if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { DEBUG ((EFI_D_ERROR, "Open Init, Status=%x", Status)); // // open protocol is not SUCCESS or not ALREADY_STARTED, error exit // goto ErrorExit; } // // Save Enumall. Step7.2 // EnumAll = IdeInit->EnumAll; // // Consume PCI I/O protocol. Note that the OpenProtocol with _GET_PROTOCOL // attribute will not return EFI_ALREADY_STARTED // Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Open PciIo, Status=%x", Status)); goto ErrorExit; } // // We must check EFI_ALREADY_STARTED because many ATAPI devices are removable // if (SavedStatus != EFI_ALREADY_STARTED) { IdeBusDriverPrivateData = AllocatePool (sizeof (IDE_BUS_DRIVER_PRIVATE_DATA)); if (IdeBusDriverPrivateData == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } ZeroMem (IdeBusDriverPrivateData, sizeof (IDE_BUS_DRIVER_PRIVATE_DATA)); Status = gBS->InstallMultipleProtocolInterfaces ( &Controller, &gEfiCallerIdGuid, IdeBusDriverPrivateData, NULL ); if (EFI_ERROR (Status)) { goto ErrorExit; } } else { Status = gBS->OpenProtocol ( Controller, &gEfiCallerIdGuid, (VOID **) &IdeBusDriverPrivateData, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { IdeBusDriverPrivateData = NULL; goto ErrorExit; } } Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &Supports ); if (!EFI_ERROR (Status)) { Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, Supports, NULL ); } if (EFI_ERROR (Status)) { goto ErrorExit; } // // Read the environment variable that contains the IDEBus Driver's // Config options that were set by the Driver Configuration Protocol // DataSize = sizeof (ConfigurationOptions); Status = gRT->GetVariable ( (CHAR16 *) L"Configuration", &gEfiCallerIdGuid, NULL, &DataSize, &ConfigurationOptions ); if (EFI_ERROR (Status)) { ConfigurationOptions = 0x0f; } if (EnumAll || RemainingDevicePath == NULL) { // // If IdeInit->EnumAll is TRUE or RemainingDevicePath is NULL, // must enumerate all IDE devices anyway // BeginningIdeChannel = IdePrimary; EndIdeChannel = IdeSecondary; BeginningIdeDevice = IdeMaster; EndIdeDevice = IdeSlave; } else if (!IsDevicePathEnd (RemainingDevicePath)) { // // If RemainingDevicePath isn't the End of Device Path Node, // only scan the specified device by RemainingDevicePath // Node = (EFI_DEV_PATH *) RemainingDevicePath; BeginningIdeChannel = Node->Atapi.PrimarySecondary; EndIdeChannel = BeginningIdeChannel; BeginningIdeDevice = Node->Atapi.SlaveMaster; EndIdeDevice = BeginningIdeDevice; if (BeginningIdeChannel >= IdeMaxChannel || EndIdeChannel >= IdeMaxChannel) { Status = EFI_INVALID_PARAMETER; goto ErrorExit; } if (BeginningIdeDevice >= IdeMaxDevice|| EndIdeDevice >= IdeMaxDevice) { Status = EFI_INVALID_PARAMETER; goto ErrorExit; } } else { // // If RemainingDevicePath is the End of Device Path Node, // skip enumerate any device and return EFI_SUCESSS // BeginningIdeChannel = IdeMaxChannel; EndIdeChannel = IdeMaxChannel - 1; BeginningIdeDevice = IdeMaxDevice; EndIdeDevice = IdeMaxDevice - 1; } // // Obtain IDE IO port registers' base addresses // Status = GetIdeRegistersBaseAddr (PciIo, IdeRegsBaseAddr); if (EFI_ERROR (Status)) { goto ErrorExit; } // // Report status code: begin IdeBus initialization // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_PC_RESET), ParentDevicePath ); // // Strictly follow the enumeration based on IDE_CONTROLLER_INIT protocol // for (IdeChannel = BeginningIdeChannel; IdeChannel <= EndIdeChannel; IdeChannel++) { IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, IdeChannel); // // now obtain channel information fron IdeControllerInit protocol. Step9 // Status = IdeInit->GetChannelInfo ( IdeInit, IdeChannel, &ChannelEnabled, &MaxDevices ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "[GetChannel, Status=%x]", Status)); continue; } if (!ChannelEnabled) { continue; } EndIdeDevice = (UINT8) MIN ((MaxDevices - 1), EndIdeDevice); ASSERT (EndIdeDevice < IdeMaxDevice); // // Now inform the IDE Controller Init Module. Sept10 // IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelReset, IdeChannel); // // No reset channel function implemented. Sept11 // IdeInit->NotifyPhase (IdeInit, EfiIdeAfterChannelReset, IdeChannel); // // Step13 // IdeInit->NotifyPhase ( IdeInit, EfiIdeBusBeforeDevicePresenceDetection, IdeChannel ); // // Prepare to detect IDE device of this channel // InitializeIDEChannelData (); // // -- 1st inner loop --- Master/Slave ------------ Step14 // for (IdeDevice = BeginningIdeDevice; IdeDevice <= EndIdeDevice; IdeDevice++) { // // Check whether the configuration options allow this device // if ((ConfigurationOptions & (1 << (IdeChannel * 2 + IdeDevice))) == 0) { continue; } // // The device has been scanned in another Start(), No need to scan it again // for perf optimization. // if (IdeBusDriverPrivateData->HaveScannedDevice[IdeChannel * 2 + IdeDevice]) { continue; } // // create child handle for the detected device. // IdeBlkIoDevice[IdeChannel][IdeDevice] = AllocatePool (sizeof (IDE_BLK_IO_DEV)); if (IdeBlkIoDevice[IdeChannel][IdeDevice] == NULL) { continue; } IdeBlkIoDevicePtr = IdeBlkIoDevice[IdeChannel][IdeDevice]; ZeroMem (IdeBlkIoDevicePtr, sizeof (IDE_BLK_IO_DEV)); IdeBlkIoDevicePtr->Signature = IDE_BLK_IO_DEV_SIGNATURE; IdeBlkIoDevicePtr->Channel = (EFI_IDE_CHANNEL) IdeChannel; IdeBlkIoDevicePtr->Device = (EFI_IDE_DEVICE) IdeDevice; // // initialize Block IO interface's Media pointer // IdeBlkIoDevicePtr->BlkIo.Media = &IdeBlkIoDevicePtr->BlkMedia; // // Initialize IDE IO port addresses, including Command Block registers // and Control Block registers // IdeBlkIoDevicePtr->IoPort = AllocatePool (sizeof (IDE_BASE_REGISTERS)); if (IdeBlkIoDevicePtr->IoPort == NULL) { continue; } ZeroMem (IdeBlkIoDevicePtr->IoPort, sizeof (IDE_BASE_REGISTERS)); CommandBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].CommandBlockBaseAddr; ControlBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].ControlBlockBaseAddr; IdeBlkIoDevicePtr->IoPort->Data = CommandBlockBaseAddr; (*(UINT16 *) &IdeBlkIoDevicePtr->IoPort->Reg1) = (UINT16) (CommandBlockBaseAddr + 0x01); IdeBlkIoDevicePtr->IoPort->SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02); IdeBlkIoDevicePtr->IoPort->SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03); IdeBlkIoDevicePtr->IoPort->CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04); IdeBlkIoDevicePtr->IoPort->CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05); IdeBlkIoDevicePtr->IoPort->Head = (UINT16) (CommandBlockBaseAddr + 0x06); (*(UINT16 *) &IdeBlkIoDevicePtr->IoPort->Reg) = (UINT16) (CommandBlockBaseAddr + 0x07); (*(UINT16 *) &IdeBlkIoDevicePtr->IoPort->Alt) = ControlBlockBaseAddr; IdeBlkIoDevicePtr->IoPort->DriveAddress = (UINT16) (ControlBlockBaseAddr + 0x01); IdeBlkIoDevicePtr->IoPort->MasterSlave = (UINT16) ((IdeDevice == IdeMaster) ? 1 : 0); IdeBlkIoDevicePtr->PciIo = PciIo; IdeBlkIoDevicePtr->IdeBusDriverPrivateData = IdeBusDriverPrivateData; IdeBlkIoDevicePtr->IoPort->BusMasterBaseAddr = IdeRegsBaseAddr[IdeChannel].BusMasterBaseAddr; // // Report Status code: is about to detect IDE drive // REPORT_STATUS_CODE_EX ( EFI_PROGRESS_CODE, (EFI_IO_BUS_ATA_ATAPI | EFI_P_PC_PRESENCE_DETECT), 0, &gEfiCallerIdGuid, NULL, NULL, 0 ); // // Discover device, now! // PERF_START (NULL, "DiscoverIdeDevice", "IDE", 0); Status = DiscoverIdeDevice (IdeBlkIoDevicePtr); PERF_END (NULL, "DiscoverIdeDevice", "IDE", 0); IdeBusDriverPrivateData->HaveScannedDevice[IdeChannel * 2 + IdeDevice] = TRUE; IdeBusDriverPrivateData->DeviceProcessed[IdeChannel * 2 + IdeDevice] = FALSE; if (!EFI_ERROR (Status)) { // // Set Device Path // ZeroMem (&NewNode, sizeof (NewNode)); NewNode.DevPath.Type = MESSAGING_DEVICE_PATH; NewNode.DevPath.SubType = MSG_ATAPI_DP; SetDevicePathNodeLength (&NewNode.DevPath, sizeof (ATAPI_DEVICE_PATH)); NewNode.Atapi.PrimarySecondary = (UINT8) IdeBlkIoDevicePtr->Channel; NewNode.Atapi.SlaveMaster = (UINT8) IdeBlkIoDevicePtr->Device; NewNode.Atapi.Lun = IdeBlkIoDevicePtr->Lun; IdeBlkIoDevicePtr->DevicePath = AppendDevicePathNode ( ParentDevicePath, &NewNode.DevPath ); if (IdeBlkIoDevicePtr->DevicePath == NULL) { ReleaseIdeResources (IdeBlkIoDevicePtr); continue; } // // Submit identify data to IDE controller init driver // CopyMem (&IdentifyData, IdeBlkIoDevicePtr->IdData, sizeof (IdentifyData)); IdeBusDriverPrivateData->DeviceFound[IdeChannel * 2 + IdeDevice] = TRUE; IdeInit->SubmitData (IdeInit, IdeChannel, IdeDevice, &IdentifyData); } else { // // Device detection failed // IdeBusDriverPrivateData->DeviceFound[IdeChannel * 2 + IdeDevice] = FALSE; IdeInit->SubmitData (IdeInit, IdeChannel, IdeDevice, NULL); ReleaseIdeResources (IdeBlkIoDevicePtr); IdeBlkIoDevicePtr = NULL; } // // end of 1st inner loop --- // } // // end of 1st outer loop ========= // } // // = 2nd outer loop == Primary/Secondary ================= // for (IdeChannel = BeginningIdeChannel; IdeChannel <= EndIdeChannel; IdeChannel++) { // // -- 2nd inner loop --- Master/Slave -------- // for (IdeDevice = BeginningIdeDevice; IdeDevice <= EndIdeDevice; IdeDevice++) { ASSERT (IdeChannel * 2 + IdeDevice < MAX_IDE_DEVICE); if (IdeBusDriverPrivateData->DeviceProcessed[IdeChannel * 2 + IdeDevice]) { continue; } if (!IdeBusDriverPrivateData->DeviceFound[IdeChannel * 2 + IdeDevice]) { continue; } Status = IdeInit->CalculateMode ( IdeInit, IdeChannel, IdeDevice, &SupportedModes ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "[bStStp20S=%x]", Status)); continue; } ASSERT (IdeChannel < IdeMaxChannel && IdeDevice < IdeMaxDevice); IdeBlkIoDevicePtr = IdeBlkIoDevice[IdeChannel][IdeDevice]; // // Set best supported PIO mode on this IDE device // if (SupportedModes->PioMode.Mode <= AtaPioMode2) { TransferMode.ModeCategory = ATA_MODE_CATEGORY_DEFAULT_PIO; } else { TransferMode.ModeCategory = ATA_MODE_CATEGORY_FLOW_PIO; } TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode); if (SupportedModes->ExtModeCount == 0){ Status = SetDeviceTransferMode (IdeBlkIoDevicePtr, &TransferMode); if (EFI_ERROR (Status)) { IdeBusDriverPrivateData->DeviceFound[IdeChannel * 2 + IdeDevice] = FALSE; ReleaseIdeResources (IdeBlkIoDevicePtr); IdeBlkIoDevicePtr = NULL; continue; } } // // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann't // be set together. Only one DMA mode can be set to a device. If setting // DMA mode operation fails, we can continue moving on because we only use // PIO mode at boot time. DMA modes are used by certain kind of OS booting // if (SupportedModes->UdmaMode.Valid) { TransferMode.ModeCategory = ATA_MODE_CATEGORY_UDMA; TransferMode.ModeNumber = (UINT8) (SupportedModes->UdmaMode.Mode); Status = SetDeviceTransferMode (IdeBlkIoDevicePtr, &TransferMode); if (EFI_ERROR (Status)) { IdeBusDriverPrivateData->DeviceFound[IdeChannel * 2 + IdeDevice] = FALSE; ReleaseIdeResources (IdeBlkIoDevicePtr); IdeBlkIoDevicePtr = NULL; continue; } // // Record Udma Mode // IdeBlkIoDevicePtr->UdmaMode.Valid = TRUE; IdeBlkIoDevicePtr->UdmaMode.Mode = SupportedModes->UdmaMode.Mode; EnableInterrupt (IdeBlkIoDevicePtr); } else if (SupportedModes->MultiWordDmaMode.Valid) { TransferMode.ModeCategory = ATA_MODE_CATEGORY_MDMA; TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode; Status = SetDeviceTransferMode (IdeBlkIoDevicePtr, &TransferMode); if (EFI_ERROR (Status)) { IdeBusDriverPrivateData->DeviceFound[IdeChannel * 2 + IdeDevice] = FALSE; ReleaseIdeResources (IdeBlkIoDevicePtr); IdeBlkIoDevicePtr = NULL; continue; } EnableInterrupt (IdeBlkIoDevicePtr); } // // Init driver parameters // DriveParameters.Sector = (UINT8) ((ATA5_IDENTIFY_DATA *) IdeBlkIoDevicePtr->IdData)->sectors_per_track; DriveParameters.Heads = (UINT8) (((ATA5_IDENTIFY_DATA *) IdeBlkIoDevicePtr->IdData)->heads - 1); DriveParameters.MultipleSector = (UINT8) IdeBlkIoDevicePtr->IdData->AtaData.multi_sector_cmd_max_sct_cnt; // // Set Parameters for the device: // 1) Init // 2) Establish the block count for READ/WRITE MULTIPLE (EXT) command // if ((IdeBlkIoDevicePtr->Type == IdeHardDisk) || (IdeBlkIoDevicePtr->Type == Ide48bitAddressingHardDisk)) { Status = SetDriveParameters (IdeBlkIoDevicePtr, &DriveParameters); } // // Record PIO mode used in private data // IdeBlkIoDevicePtr->PioMode = (ATA_PIO_MODE) SupportedModes->PioMode.Mode; // // Set IDE controller Timing Blocks in the PCI Configuration Space // IdeInit->SetTiming (IdeInit, IdeChannel, IdeDevice, SupportedModes); // // Add Component Name for the IDE/ATAPI device that was discovered. // IdeBlkIoDevicePtr->ControllerNameTable = NULL; ADD_IDE_ATAPI_NAME (IdeBlkIoDevicePtr); Status = gBS->InstallMultipleProtocolInterfaces ( &IdeBlkIoDevicePtr->Handle, &gEfiDevicePathProtocolGuid, IdeBlkIoDevicePtr->DevicePath, &gEfiBlockIoProtocolGuid, &IdeBlkIoDevicePtr->BlkIo, &gEfiDiskInfoProtocolGuid, &IdeBlkIoDevicePtr->DiskInfo, NULL ); if (EFI_ERROR (Status)) { ReleaseIdeResources (IdeBlkIoDevicePtr); } gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, IdeBlkIoDevicePtr->Handle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); IdeBusDriverPrivateData->DeviceProcessed[IdeChannel * 2 + IdeDevice] = TRUE; // // Report status code: device eanbled! // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, (EFI_IO_BUS_ATA_ATAPI | EFI_P_PC_ENABLE), IdeBlkIoDevicePtr->DevicePath ); // // Create event to clear pending IDE interrupt // Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, ClearInterrupt, IdeBlkIoDevicePtr, &gEfiEventExitBootServicesGuid, &IdeBlkIoDevicePtr->ExitBootServiceEvent ); // // end of 2nd inner loop ---- // } // // end of 2nd outer loop ========== // } // // All configurations done! Notify IdeController to do post initialization // work such as saving IDE controller PCI settings for S3 resume // IdeInit->NotifyPhase (IdeInit, EfiIdeBusPhaseMaximum, 0); if (SupportedModes != NULL) { FreePool (SupportedModes); } PERF_START (NULL, "Finish IDE detection", "IDE", 1); PERF_END (NULL, "Finish IDE detection", "IDE", 0); return EFI_SUCCESS; ErrorExit: // // Report error code: controller error // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_ERROR_CODE | EFI_ERROR_MINOR, (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_EC_CONTROLLER_ERROR), ParentDevicePath ); gBS->CloseProtocol ( Controller, &gEfiIdeControllerInitProtocolGuid, This->DriverBindingHandle, Controller ); gBS->UninstallMultipleProtocolInterfaces ( Controller, &gEfiCallerIdGuid, IdeBusDriverPrivateData, NULL ); if (IdeBusDriverPrivateData != NULL) { gBS->FreePool (IdeBusDriverPrivateData); } if (SupportedModes != NULL) { gBS->FreePool (SupportedModes); } gBS->CloseProtocol ( Controller, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } /** Stop function of Driver Binding Protocol which is to stop the driver on Controller Handle and all child handle attached to the controller handle if there are. @param This Protocol instance pointer. @param Controller Handle of device to stop driver on @param NumberOfChildren Not used @param ChildHandleBuffer Not used @retval EFI_SUCCESS This driver is removed DeviceHandle @retval other This driver was not removed from this device **/ EFI_STATUS EFIAPI IDEBusDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; BOOLEAN AllChildrenStopped; UINTN Index; IDE_BUS_DRIVER_PRIVATE_DATA *IdeBusDriverPrivateData; UINT64 Supports; IdeBusDriverPrivateData = NULL; if (NumberOfChildren == 0) { Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (!EFI_ERROR (Status)) { Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &Supports ); if (!EFI_ERROR (Status)) { Supports &= (UINT64)(EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE); PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationDisable, Supports, NULL ); } } gBS->OpenProtocol ( Controller, &gEfiCallerIdGuid, (VOID **) &IdeBusDriverPrivateData, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); gBS->UninstallMultipleProtocolInterfaces ( Controller, &gEfiCallerIdGuid, IdeBusDriverPrivateData, NULL ); if (IdeBusDriverPrivateData != NULL) { gBS->FreePool (IdeBusDriverPrivateData); } // // Close the bus driver // gBS->CloseProtocol ( Controller, &gEfiIdeControllerInitProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } AllChildrenStopped = TRUE; for (Index = 0; Index < NumberOfChildren; Index++) { Status = DeRegisterIdeDevice (This, Controller, ChildHandleBuffer[Index]); if (EFI_ERROR (Status)) { AllChildrenStopped = FALSE; } } if (!AllChildrenStopped) { return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } /** issue ATA or ATAPI command to reset a block IO device. @param This Block IO protocol instance pointer. @param ExtendedVerification If FALSE,for ATAPI device, driver will only invoke ATAPI reset method If TRUE, for ATAPI device, driver need invoke ATA reset method after invoke ATAPI reset method @retval EFI_DEVICE_ERROR When the device is neighther ATA device or ATAPI device. @retval EFI_SUCCESS The device reset successfully **/ EFI_STATUS EFIAPI IDEBlkIoReset ( IN EFI_BLOCK_IO_PROTOCOL *This, IN BOOLEAN ExtendedVerification ) { IDE_BLK_IO_DEV *IdeBlkIoDevice; EFI_STATUS Status; EFI_TPL OldTpl; OldTpl = gBS->RaiseTPL (TPL_CALLBACK); IdeBlkIoDevice = IDE_BLOCK_IO_DEV_FROM_THIS (This); // // Requery IDE IO resources in case of the switch of native and legacy modes // ReassignIdeResources (IdeBlkIoDevice); // // for ATA device, using ATA reset method // if (IdeBlkIoDevice->Type == IdeHardDisk || IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) { Status = AtaSoftReset (IdeBlkIoDevice); goto Done; } if (IdeBlkIoDevice->Type == IdeUnknown) { Status = EFI_DEVICE_ERROR; goto Done; } // // for ATAPI device, using ATAPI reset method // Status = AtapiSoftReset (IdeBlkIoDevice); if (ExtendedVerification) { Status = AtaSoftReset (IdeBlkIoDevice); } Done: gBS->RestoreTPL (OldTpl); return Status; } /** Read data from a block IO device @param This Block IO protocol instance pointer. @param MediaId The media ID of the device @param Lba Starting LBA address to read data @param BufferSize The size of data to be read @param Buffer Caller supplied buffer to save data @retval EFI_DEVICE_ERROR unknown device type @retval other read data status. **/ EFI_STATUS EFIAPI IDEBlkIoReadBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, OUT VOID *Buffer ) { IDE_BLK_IO_DEV *IdeBlkIoDevice; EFI_STATUS Status; EFI_TPL OldTpl; OldTpl = gBS->RaiseTPL (TPL_CALLBACK); IdeBlkIoDevice = IDE_BLOCK_IO_DEV_FROM_THIS (This); // // Requery IDE IO resources in case of the switch of native and legacy modes // ReassignIdeResources (IdeBlkIoDevice); // // For ATA compatible device, use ATA read block's mechanism // if (IdeBlkIoDevice->Type == IdeHardDisk || IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) { Status = AtaBlkIoReadBlocks ( IdeBlkIoDevice, MediaId, Lba, BufferSize, Buffer ); goto Done; } if (IdeBlkIoDevice->Type == IdeUnknown) { Status = EFI_DEVICE_ERROR; goto Done; } // // for ATAPI device, using ATAPI read block's mechanism // Status = AtapiBlkIoReadBlocks ( IdeBlkIoDevice, MediaId, Lba, BufferSize, Buffer ); Done: gBS->RestoreTPL (OldTpl); return Status; } /** Write data to block io device. @param This Protocol instance pointer. @param MediaId The media ID of the device @param Lba Starting LBA address to write data @param BufferSize The size of data to be written @param Buffer Caller supplied buffer to save data @retval EFI_DEVICE_ERROR unknown device type @retval other write data status **/ EFI_STATUS EFIAPI IDEBlkIoWriteBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, IN VOID *Buffer ) { IDE_BLK_IO_DEV *IdeBlkIoDevice; EFI_STATUS Status; EFI_TPL OldTpl; OldTpl = gBS->RaiseTPL (TPL_CALLBACK); IdeBlkIoDevice = IDE_BLOCK_IO_DEV_FROM_THIS (This); // // Requery IDE IO resources in case of the switch of native and legacy modes // ReassignIdeResources (IdeBlkIoDevice); // // for ATA device, using ATA write block's mechanism // if (IdeBlkIoDevice->Type == IdeHardDisk || IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) { Status = AtaBlkIoWriteBlocks ( IdeBlkIoDevice, MediaId, Lba, BufferSize, Buffer ); goto Done; } if (IdeBlkIoDevice->Type == IdeUnknown) { Status = EFI_DEVICE_ERROR; goto Done; } // // for ATAPI device, using ATAPI write block's mechanism // Status = AtapiBlkIoWriteBlocks ( IdeBlkIoDevice, MediaId, Lba, BufferSize, Buffer ); Done: gBS->RestoreTPL (OldTpl); return Status; } /** Flushes all modified data to a physical block devices @param This Indicates a pointer to the calling context which to sepcify a sepcific block device @retval EFI_SUCCESS Always return success. **/ EFI_STATUS EFIAPI IDEBlkIoFlushBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This ) { // // return directly // return EFI_SUCCESS; } /** This function is used by the IDE bus driver to get inquiry data. Data format of Identify data is defined by the Interface GUID. @param This Pointer to the EFI_DISK_INFO_PROTOCOL instance. @param InquiryData Pointer to a buffer for the inquiry data. @param InquiryDataSize Pointer to the value for the inquiry data size. @retval EFI_SUCCESS The command was accepted without any errors. @retval EFI_NOT_FOUND Device does not support this data class @retval EFI_DEVICE_ERROR Error reading InquiryData from device @retval EFI_BUFFER_TOO_SMALL IntquiryDataSize not big enough **/ EFI_STATUS EFIAPI IDEDiskInfoInquiry ( IN EFI_DISK_INFO_PROTOCOL *This, IN OUT VOID *InquiryData, IN OUT UINT32 *InquiryDataSize ) { IDE_BLK_IO_DEV *IdeBlkIoDevice; IdeBlkIoDevice = IDE_BLOCK_IO_DEV_FROM_DISK_INFO_THIS (This); if (*InquiryDataSize < sizeof (ATAPI_INQUIRY_DATA)) { *InquiryDataSize = sizeof (ATAPI_INQUIRY_DATA); return EFI_BUFFER_TOO_SMALL; } if (IdeBlkIoDevice->InquiryData == NULL) { return EFI_NOT_FOUND; } gBS->CopyMem (InquiryData, IdeBlkIoDevice->InquiryData, sizeof (ATAPI_INQUIRY_DATA)); *InquiryDataSize = sizeof (ATAPI_INQUIRY_DATA); return EFI_SUCCESS; } /** This function is used by the IDE bus driver to get identify data. Data format of Identify data is defined by the Interface GUID. @param This Pointer to the EFI_DISK_INFO_PROTOCOL instance. @param IdentifyData Pointer to a buffer for the identify data. @param IdentifyDataSize Pointer to the value for the identify data size. @retval EFI_SUCCESS The command was accepted without any errors. @retval EFI_NOT_FOUND Device does not support this data class @retval EFI_DEVICE_ERROR Error reading IdentifyData from device @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough **/ EFI_STATUS EFIAPI IDEDiskInfoIdentify ( IN EFI_DISK_INFO_PROTOCOL *This, IN OUT VOID *IdentifyData, IN OUT UINT32 *IdentifyDataSize ) { IDE_BLK_IO_DEV *IdeBlkIoDevice; IdeBlkIoDevice = IDE_BLOCK_IO_DEV_FROM_DISK_INFO_THIS (This); if (*IdentifyDataSize < sizeof (EFI_IDENTIFY_DATA)) { *IdentifyDataSize = sizeof (EFI_IDENTIFY_DATA); return EFI_BUFFER_TOO_SMALL; } if (IdeBlkIoDevice->IdData == NULL) { return EFI_NOT_FOUND; } gBS->CopyMem (IdentifyData, IdeBlkIoDevice->IdData, sizeof (EFI_IDENTIFY_DATA)); *IdentifyDataSize = sizeof (EFI_IDENTIFY_DATA); return EFI_SUCCESS; } /** This function is used by the IDE bus driver to get sense data. Data format of Sense data is defined by the Interface GUID. @param This Pointer to the EFI_DISK_INFO_PROTOCOL instance. @param SenseData Pointer to the SenseData. @param SenseDataSize Size of SenseData in bytes. @param SenseDataNumber Pointer to the value for the identify data size. @retval EFI_SUCCESS The command was accepted without any errors. @retval EFI_NOT_FOUND Device does not support this data class @retval EFI_DEVICE_ERROR Error reading InquiryData from device @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough **/ EFI_STATUS EFIAPI IDEDiskInfoSenseData ( IN EFI_DISK_INFO_PROTOCOL *This, IN OUT VOID *SenseData, IN OUT UINT32 *SenseDataSize, OUT UINT8 *SenseDataNumber ) { return EFI_NOT_FOUND; } /** This function is used by the IDE bus driver to get controller information. @param This Pointer to the EFI_DISK_INFO_PROTOCOL instance. @param IdeChannel Pointer to the Ide Channel number. Primary or secondary. @param IdeDevice Pointer to the Ide Device number. Master or slave. @retval EFI_SUCCESS IdeChannel and IdeDevice are valid @retval EFI_UNSUPPORTED This is not an IDE device **/ EFI_STATUS EFIAPI IDEDiskInfoWhichIde ( IN EFI_DISK_INFO_PROTOCOL *This, OUT UINT32 *IdeChannel, OUT UINT32 *IdeDevice ) { IDE_BLK_IO_DEV *IdeBlkIoDevice; IdeBlkIoDevice = IDE_BLOCK_IO_DEV_FROM_DISK_INFO_THIS (This); *IdeChannel = IdeBlkIoDevice->Channel; *IdeDevice = IdeBlkIoDevice->Device; return EFI_SUCCESS; } /** The is an event(generally the event is exitBootService event) call back function. Clear pending IDE interrupt before OS loader/kernel take control of the IDE device. @param Event Pointer to this event @param Context Event handler private data **/ VOID EFIAPI ClearInterrupt ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; UINT64 IoPortForBmis; UINT8 RegisterValue; IDE_BLK_IO_DEV *IdeDev; // // Get our context // IdeDev = (IDE_BLK_IO_DEV *) Context; // // Obtain IDE IO port registers' base addresses // Status = ReassignIdeResources (IdeDev); if (EFI_ERROR (Status)) { return; } // // Check whether interrupt is pending // // // Reset IDE device to force it de-assert interrupt pin // Note: this will reset all devices on this IDE channel // Status = AtaSoftReset (IdeDev); if (EFI_ERROR (Status)) { return; } // // Get base address of IDE Bus Master Status Regsiter // if (IdePrimary == IdeDev->Channel) { IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET; } else { if (IdeSecondary == IdeDev->Channel) { IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET; } else { return; } } // // Read BMIS register and clear ERROR and INTR bit // IdeDev->PciIo->Io.Read ( IdeDev->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, IoPortForBmis, 1, &RegisterValue ); RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR); IdeDev->PciIo->Io.Write ( IdeDev->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, IoPortForBmis, 1, &RegisterValue ); // // Select the other device on this channel to ensure this device to release the interrupt pin // if (IdeDev->Device == 0) { RegisterValue = (1 << 4) | 0xe0; } else { RegisterValue = (0 << 4) | 0xe0; } IDEWritePortB ( IdeDev->PciIo, IdeDev->IoPort->Head, RegisterValue ); } /** The user Entry Point for module IdeBus. The user code starts with this function. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI InitializeIdeBus( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; // // Install driver model protocol(s). // Status = EfiLibInstallAllDriverProtocols2 ( ImageHandle, SystemTable, &gIDEBusDriverBinding, ImageHandle, &gIDEBusComponentName, &gIDEBusComponentName2, NULL, NULL, &gIDEBusDriverDiagnostics, &gIDEBusDriverDiagnostics2 ); ASSERT_EFI_ERROR (Status); return Status; }