| /** @file | |
| Collect IDE information from Native EFI Driver | |
| Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions | |
| of the BSD License which accompanies this distribution. The | |
| full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "LegacyBiosInterface.h" | |
| BOOLEAN mIdeDataBuiltFlag = FALSE; | |
| /** | |
| Collect IDE Inquiry data from the IDE disks | |
| @param Private Legacy BIOS Instance data | |
| @param HddInfo Hdd Information | |
| @param Flag Reconnect IdeController or not | |
| @retval EFI_SUCCESS It should always work. | |
| **/ | |
| EFI_STATUS | |
| LegacyBiosBuildIdeData ( | |
| IN LEGACY_BIOS_INSTANCE *Private, | |
| IN HDD_INFO **HddInfo, | |
| IN UINT16 Flag | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE IdeController; | |
| UINTN HandleCount; | |
| EFI_HANDLE *HandleBuffer; | |
| UINTN Index; | |
| EFI_DISK_INFO_PROTOCOL *DiskInfo; | |
| UINT32 IdeChannel; | |
| UINT32 IdeDevice; | |
| UINT32 Size; | |
| UINT8 *InquiryData; | |
| UINT32 InquiryDataSize; | |
| HDD_INFO *LocalHddInfo; | |
| UINT32 PciIndex; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePathNode; | |
| PCI_DEVICE_PATH *PciDevicePath; | |
| // | |
| // Only build data once | |
| // We have a problem with GetBbsInfo in that it can be invoked two | |
| // places. Once in BDS, when all EFI drivers are connected and once in | |
| // LegacyBoot after all EFI drivers are disconnected causing this routine | |
| // to hang. In LegacyBoot this function is also called before EFI drivers | |
| // are disconnected. | |
| // Cases covered | |
| // GetBbsInfo invoked in BDS. Both invocations in LegacyBoot ignored. | |
| // GetBbsInfo not invoked in BDS. First invocation of this function | |
| // proceeds normally and second via GetBbsInfo ignored. | |
| // | |
| PciDevicePath = NULL; | |
| LocalHddInfo = *HddInfo; | |
| Status = Private->LegacyBiosPlatform->GetPlatformHandle ( | |
| Private->LegacyBiosPlatform, | |
| EfiGetPlatformIdeHandle, | |
| 0, | |
| &HandleBuffer, | |
| &HandleCount, | |
| (VOID *) &LocalHddInfo | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| IdeController = HandleBuffer[0]; | |
| // | |
| // Force IDE drive spin up! | |
| // | |
| if (Flag != 0) { | |
| gBS->DisconnectController ( | |
| IdeController, | |
| NULL, | |
| NULL | |
| ); | |
| } | |
| gBS->ConnectController (IdeController, NULL, NULL, FALSE); | |
| // | |
| // Do GetIdeHandle twice since disconnect/reconnect will switch to native mode | |
| // And GetIdeHandle will switch to Legacy mode, if required. | |
| // | |
| Private->LegacyBiosPlatform->GetPlatformHandle ( | |
| Private->LegacyBiosPlatform, | |
| EfiGetPlatformIdeHandle, | |
| 0, | |
| &HandleBuffer, | |
| &HandleCount, | |
| (VOID *) &LocalHddInfo | |
| ); | |
| } | |
| mIdeDataBuiltFlag = TRUE; | |
| // | |
| // Get Identity command from all drives | |
| // | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiDiskInfoProtocolGuid, | |
| NULL, | |
| &HandleCount, | |
| &HandleBuffer | |
| ); | |
| Private->IdeDriveCount = (UINT8) HandleCount; | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[Index], | |
| &gEfiDiskInfoProtocolGuid, | |
| (VOID **) &DiskInfo | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) { | |
| // | |
| // Locate which PCI device | |
| // | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[Index], | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID *) &DevicePath | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| DevicePathNode = DevicePath; | |
| while (!IsDevicePathEnd (DevicePathNode)) { | |
| TempDevicePathNode = NextDevicePathNode (DevicePathNode); | |
| if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) && | |
| ( DevicePathSubType (DevicePathNode) == HW_PCI_DP) && | |
| ( DevicePathType(TempDevicePathNode) == MESSAGING_DEVICE_PATH) && | |
| ( DevicePathSubType(TempDevicePathNode) == MSG_ATAPI_DP) ) { | |
| PciDevicePath = (PCI_DEVICE_PATH *) DevicePathNode; | |
| break; | |
| } | |
| DevicePathNode = NextDevicePathNode (DevicePathNode); | |
| } | |
| if (PciDevicePath == NULL) { | |
| continue; | |
| } | |
| // | |
| // Find start of PCI device in HddInfo. The assumption of the data | |
| // structure is 2 controllers(channels) per PCI device and each | |
| // controller can have 2 drives(devices). | |
| // HddInfo[PciIndex+0].[0] = Channel[0].Device[0] Primary Master | |
| // HddInfo[PciIndex+0].[1] = Channel[0].Device[1] Primary Slave | |
| // HddInfo[PciIndex+1].[0] = Channel[1].Device[0] Secondary Master | |
| // HddInfo[PciIndex+1].[1] = Channel[1].Device[1] Secondary Slave | |
| // @bug eventually need to pass in max number of entries | |
| // for end of for loop | |
| // | |
| for (PciIndex = 0; PciIndex < 8; PciIndex++) { | |
| if ((PciDevicePath->Device == LocalHddInfo[PciIndex].Device) && | |
| (PciDevicePath->Function == LocalHddInfo[PciIndex].Function) | |
| ) { | |
| break; | |
| } | |
| } | |
| if (PciIndex == 8) { | |
| continue; | |
| } | |
| Status = DiskInfo->WhichIde (DiskInfo, &IdeChannel, &IdeDevice); | |
| if (!EFI_ERROR (Status)) { | |
| Size = sizeof (ATAPI_IDENTIFY); | |
| DiskInfo->Identify ( | |
| DiskInfo, | |
| &LocalHddInfo[PciIndex + IdeChannel].IdentifyDrive[IdeDevice], | |
| &Size | |
| ); | |
| if (IdeChannel == 0) { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_PRIMARY; | |
| } else if (IdeChannel == 1) { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SECONDARY; | |
| } | |
| InquiryData = NULL; | |
| InquiryDataSize = 0; | |
| Status = DiskInfo->Inquiry ( | |
| DiskInfo, | |
| NULL, | |
| &InquiryDataSize | |
| ); | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| InquiryData = (UINT8 *) AllocatePool ( | |
| InquiryDataSize | |
| ); | |
| if (InquiryData != NULL) { | |
| Status = DiskInfo->Inquiry ( | |
| DiskInfo, | |
| InquiryData, | |
| &InquiryDataSize | |
| ); | |
| } | |
| } else { | |
| Status = EFI_DEVICE_ERROR; | |
| } | |
| // | |
| // If ATAPI device then Inquiry will pass and ATA fail. | |
| // | |
| if (!EFI_ERROR (Status)) { | |
| ASSERT (InquiryData != NULL); | |
| // | |
| // If IdeDevice = 0 then set master bit, else slave bit | |
| // | |
| if (IdeDevice == 0) { | |
| if ((InquiryData[0] & 0x1f) == 0x05) { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_CDROM; | |
| } else if ((InquiryData[0] & 0x1f) == 0x00) { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_ZIPDISK; | |
| } | |
| } else { | |
| if ((InquiryData[0] & 0x1f) == 0x05) { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_CDROM; | |
| } else if ((InquiryData[0] & 0x1f) == 0x00) { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_ZIPDISK; | |
| } | |
| } | |
| FreePool (InquiryData); | |
| } else { | |
| if (IdeDevice == 0) { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_IDE; | |
| } else { | |
| LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_IDE; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (HandleBuffer != NULL) { | |
| FreePool (HandleBuffer); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| If the IDE channel is in compatibility (legacy) mode, remove all | |
| PCI I/O BAR addresses from the controller. | |
| @param IdeController The handle of target IDE controller | |
| **/ | |
| VOID | |
| InitLegacyIdeController ( | |
| IN EFI_HANDLE IdeController | |
| ) | |
| { | |
| EFI_PCI_IO_PROTOCOL *PciIo; | |
| UINT32 IOBarClear; | |
| EFI_STATUS Status; | |
| PCI_TYPE00 PciData; | |
| // | |
| // If the IDE channel is in compatibility (legacy) mode, remove all | |
| // PCI I/O BAR addresses from the controller. Some software gets | |
| // confused if an IDE controller is in compatibility (legacy) mode | |
| // and has PCI I/O resources allocated | |
| // | |
| Status = gBS->HandleProtocol ( | |
| IdeController, | |
| &gEfiPciIoProtocolGuid, | |
| (VOID **)&PciIo | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return ; | |
| } | |
| Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (PciData), &PciData); | |
| if (EFI_ERROR (Status)) { | |
| return ; | |
| } | |
| // | |
| // Check whether this is IDE | |
| // | |
| if ((PciData.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE) || | |
| (PciData.Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) { | |
| return ; | |
| } | |
| // | |
| // Clear bar for legacy IDE | |
| // | |
| IOBarClear = 0x00; | |
| if ((PciData.Hdr.ClassCode[0] & IDE_PI_REGISTER_PNE) == 0) { | |
| PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x10, 1, &IOBarClear); | |
| PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x14, 1, &IOBarClear); | |
| } | |
| if ((PciData.Hdr.ClassCode[0] & IDE_PI_REGISTER_SNE) == 0) { | |
| PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x18, 1, &IOBarClear); | |
| PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x1C, 1, &IOBarClear); | |
| } | |
| return ; | |
| } |