1 /** @file
2   Collect IDE information from Native EFI Driver
3 
4 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
5 
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution.  The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11 
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "LegacyBiosInterface.h"
18 
19 BOOLEAN mIdeDataBuiltFlag = FALSE;
20 
21 /**
22   Collect IDE Inquiry data from the IDE disks
23 
24   @param  Private        Legacy BIOS Instance data
25   @param  HddInfo        Hdd Information
26   @param  Flag           Reconnect IdeController or not
27 
28   @retval EFI_SUCCESS    It should always work.
29 
30 **/
31 EFI_STATUS
LegacyBiosBuildIdeData(IN LEGACY_BIOS_INSTANCE * Private,IN HDD_INFO ** HddInfo,IN UINT16 Flag)32 LegacyBiosBuildIdeData (
33   IN  LEGACY_BIOS_INSTANCE      *Private,
34   IN  HDD_INFO                  **HddInfo,
35   IN  UINT16                    Flag
36   )
37 {
38   EFI_STATUS                Status;
39   EFI_HANDLE                IdeController;
40   UINTN                     HandleCount;
41   EFI_HANDLE                *HandleBuffer;
42   UINTN                     Index;
43   EFI_DISK_INFO_PROTOCOL    *DiskInfo;
44   UINT32                    IdeChannel;
45   UINT32                    IdeDevice;
46   UINT32                    Size;
47   UINT8                     *InquiryData;
48   UINT32                    InquiryDataSize;
49   HDD_INFO                  *LocalHddInfo;
50   UINT32                    PciIndex;
51   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
52   EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;
53   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePathNode;
54   PCI_DEVICE_PATH           *PciDevicePath;
55 
56   //
57   // Only build data once
58   // We have a problem with GetBbsInfo in that it can be invoked two
59   // places. Once in BDS, when all EFI drivers are connected and once in
60   // LegacyBoot after all EFI drivers are disconnected causing this routine
61   // to hang. In LegacyBoot this function is also called before EFI drivers
62   // are disconnected.
63   // Cases covered
64   //    GetBbsInfo invoked in BDS. Both invocations in LegacyBoot ignored.
65   //    GetBbsInfo not invoked in BDS. First invocation of this function
66   //       proceeds normally and second via GetBbsInfo ignored.
67   //
68   PciDevicePath = NULL;
69   LocalHddInfo  = *HddInfo;
70   Status = Private->LegacyBiosPlatform->GetPlatformHandle (
71                                           Private->LegacyBiosPlatform,
72                                           EfiGetPlatformIdeHandle,
73                                           0,
74                                           &HandleBuffer,
75                                           &HandleCount,
76                                           (VOID *) &LocalHddInfo
77                                           );
78   if (!EFI_ERROR (Status)) {
79     IdeController = HandleBuffer[0];
80     //
81     // Force IDE drive spin up!
82     //
83     if (Flag != 0) {
84       gBS->DisconnectController (
85             IdeController,
86             NULL,
87             NULL
88             );
89     }
90 
91     gBS->ConnectController (IdeController, NULL, NULL, FALSE);
92 
93     //
94     // Do GetIdeHandle twice since disconnect/reconnect will switch to native mode
95     // And GetIdeHandle will switch to Legacy mode, if required.
96     //
97     Private->LegacyBiosPlatform->GetPlatformHandle (
98                                   Private->LegacyBiosPlatform,
99                                   EfiGetPlatformIdeHandle,
100                                   0,
101                                   &HandleBuffer,
102                                   &HandleCount,
103                                   (VOID *) &LocalHddInfo
104                                   );
105   }
106 
107   mIdeDataBuiltFlag = TRUE;
108 
109   //
110   // Get Identity command from all drives
111   //
112   gBS->LocateHandleBuffer (
113         ByProtocol,
114         &gEfiDiskInfoProtocolGuid,
115         NULL,
116         &HandleCount,
117         &HandleBuffer
118         );
119 
120   Private->IdeDriveCount = (UINT8) HandleCount;
121   for (Index = 0; Index < HandleCount; Index++) {
122     Status = gBS->HandleProtocol (
123                     HandleBuffer[Index],
124                     &gEfiDiskInfoProtocolGuid,
125                     (VOID **) &DiskInfo
126                     );
127     ASSERT_EFI_ERROR (Status);
128 
129     if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
130       //
131       //  Locate which PCI device
132       //
133       Status = gBS->HandleProtocol (
134                       HandleBuffer[Index],
135                       &gEfiDevicePathProtocolGuid,
136                       (VOID *) &DevicePath
137                       );
138       ASSERT_EFI_ERROR (Status);
139 
140       DevicePathNode = DevicePath;
141       while (!IsDevicePathEnd (DevicePathNode)) {
142         TempDevicePathNode = NextDevicePathNode (DevicePathNode);
143         if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&
144               ( DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&
145               ( DevicePathType(TempDevicePathNode) == MESSAGING_DEVICE_PATH) &&
146               ( DevicePathSubType(TempDevicePathNode) == MSG_ATAPI_DP) ) {
147           PciDevicePath = (PCI_DEVICE_PATH *) DevicePathNode;
148           break;
149         }
150         DevicePathNode = NextDevicePathNode (DevicePathNode);
151       }
152 
153       if (PciDevicePath == NULL) {
154         continue;
155       }
156 
157       //
158       // Find start of PCI device in HddInfo. The assumption of the data
159       // structure is 2 controllers(channels) per PCI device and each
160       // controller can have 2 drives(devices).
161       // HddInfo[PciIndex+0].[0] = Channel[0].Device[0] Primary Master
162       // HddInfo[PciIndex+0].[1] = Channel[0].Device[1] Primary Slave
163       // HddInfo[PciIndex+1].[0] = Channel[1].Device[0] Secondary Master
164       // HddInfo[PciIndex+1].[1] = Channel[1].Device[1] Secondary Slave
165       // @bug eventually need to pass in max number of entries
166       // for end of for loop
167       //
168       for (PciIndex = 0; PciIndex < 8; PciIndex++) {
169         if ((PciDevicePath->Device == LocalHddInfo[PciIndex].Device) &&
170             (PciDevicePath->Function == LocalHddInfo[PciIndex].Function)
171             ) {
172           break;
173         }
174       }
175 
176       if (PciIndex == 8) {
177         continue;
178       }
179 
180       Status = DiskInfo->WhichIde (DiskInfo, &IdeChannel, &IdeDevice);
181       if (!EFI_ERROR (Status)) {
182         Size = sizeof (ATAPI_IDENTIFY);
183         DiskInfo->Identify (
184                     DiskInfo,
185                     &LocalHddInfo[PciIndex + IdeChannel].IdentifyDrive[IdeDevice],
186                     &Size
187                     );
188         if (IdeChannel == 0) {
189           LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_PRIMARY;
190         } else if (IdeChannel == 1) {
191           LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SECONDARY;
192         }
193 
194         InquiryData     = NULL;
195         InquiryDataSize = 0;
196         Status = DiskInfo->Inquiry (
197                              DiskInfo,
198                              NULL,
199                              &InquiryDataSize
200                              );
201         if (Status == EFI_BUFFER_TOO_SMALL) {
202           InquiryData = (UINT8 *) AllocatePool (
203                                   InquiryDataSize
204                                   );
205           if (InquiryData != NULL) {
206             Status = DiskInfo->Inquiry (
207                                  DiskInfo,
208                                  InquiryData,
209                                  &InquiryDataSize
210                                  );
211           }
212         } else {
213           Status = EFI_DEVICE_ERROR;
214         }
215 
216         //
217         // If ATAPI device then Inquiry will pass and ATA fail.
218         //
219         if (!EFI_ERROR (Status)) {
220           ASSERT (InquiryData != NULL);
221           //
222           // If IdeDevice = 0 then set master bit, else slave bit
223           //
224           if (IdeDevice == 0) {
225             if ((InquiryData[0] & 0x1f) == 0x05) {
226               LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_CDROM;
227             } else if ((InquiryData[0] & 0x1f) == 0x00) {
228               LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_ZIPDISK;
229             }
230           } else {
231             if ((InquiryData[0] & 0x1f) == 0x05) {
232               LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_CDROM;
233             } else if ((InquiryData[0] & 0x1f) == 0x00) {
234               LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_ZIPDISK;
235             }
236           }
237           FreePool (InquiryData);
238         } else {
239           if (IdeDevice == 0) {
240             LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_IDE;
241           } else {
242             LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_IDE;
243           }
244         }
245       }
246     }
247   }
248 
249   if (HandleBuffer != NULL) {
250     FreePool (HandleBuffer);
251   }
252 
253   return EFI_SUCCESS;
254 }
255 
256 
257 /**
258   If the IDE channel is in compatibility (legacy) mode, remove all
259   PCI I/O BAR addresses from the controller.
260 
261   @param  IdeController  The handle of target IDE controller
262 
263 
264 **/
265 VOID
InitLegacyIdeController(IN EFI_HANDLE IdeController)266 InitLegacyIdeController (
267   IN EFI_HANDLE                        IdeController
268   )
269 {
270   EFI_PCI_IO_PROTOCOL               *PciIo;
271   UINT32                            IOBarClear;
272   EFI_STATUS                        Status;
273   PCI_TYPE00                        PciData;
274 
275   //
276   // If the IDE channel is in compatibility (legacy) mode, remove all
277   // PCI I/O BAR addresses from the controller.  Some software gets
278   // confused if an IDE controller is in compatibility (legacy) mode
279   // and has PCI I/O resources allocated
280   //
281   Status = gBS->HandleProtocol (
282                   IdeController,
283                   &gEfiPciIoProtocolGuid,
284                   (VOID **)&PciIo
285                   );
286   if (EFI_ERROR (Status)) {
287     return ;
288   }
289 
290   Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (PciData), &PciData);
291   if (EFI_ERROR (Status)) {
292     return ;
293   }
294 
295   //
296   // Check whether this is IDE
297   //
298   if ((PciData.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE) ||
299       (PciData.Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) {
300     return ;
301   }
302 
303   //
304   // Clear bar for legacy IDE
305   //
306   IOBarClear = 0x00;
307   if ((PciData.Hdr.ClassCode[0] & IDE_PI_REGISTER_PNE) == 0) {
308     PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x10, 1, &IOBarClear);
309     PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x14, 1, &IOBarClear);
310   }
311   if ((PciData.Hdr.ClassCode[0] & IDE_PI_REGISTER_SNE) == 0) {
312     PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x18, 1, &IOBarClear);
313     PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x1C, 1, &IOBarClear);
314   }
315 
316   return ;
317 }
318