1 /** @file
2   Collect Sio information from Native EFI Drivers.
3   Sio is floppy, parallel, serial, ... hardware
4 
5 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
6 
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8 
9 **/
10 
11 #include "LegacyBiosInterface.h"
12 
13 /**
14   Collect EFI Info about legacy devices through Super IO interface.
15 
16   @param  SioPtr       Pointer to SIO data.
17 
18   @retval EFI_SUCCESS   When SIO data is got successfully.
19   @retval EFI_NOT_FOUND When ISA IO interface is absent.
20 
21 **/
22 EFI_STATUS
LegacyBiosBuildSioDataFromSio(IN DEVICE_PRODUCER_DATA_HEADER * SioPtr)23 LegacyBiosBuildSioDataFromSio (
24   IN DEVICE_PRODUCER_DATA_HEADER             *SioPtr
25   )
26 {
27   EFI_STATUS                                 Status;
28   DEVICE_PRODUCER_SERIAL                     *SioSerial;
29   DEVICE_PRODUCER_PARALLEL                   *SioParallel;
30   DEVICE_PRODUCER_FLOPPY                     *SioFloppy;
31   UINTN                                      HandleCount;
32   EFI_HANDLE                                 *HandleBuffer;
33   UINTN                                      Index;
34   UINTN                                      ChildIndex;
35   EFI_SIO_PROTOCOL                           *Sio;
36   ACPI_RESOURCE_HEADER_PTR                   Resources;
37   EFI_ACPI_IO_PORT_DESCRIPTOR                *IoResource;
38   EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIoResource;
39   EFI_ACPI_DMA_DESCRIPTOR                    *DmaResource;
40   EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR             *IrqResource;
41   UINT16                                     Address;
42   UINT8                                      Dma;
43   UINT8                                      Irq;
44   UINTN                                      EntryCount;
45   EFI_OPEN_PROTOCOL_INFORMATION_ENTRY        *OpenInfoBuffer;
46   EFI_BLOCK_IO_PROTOCOL                      *BlockIo;
47   EFI_SERIAL_IO_PROTOCOL                     *SerialIo;
48   EFI_DEVICE_PATH_PROTOCOL                   *DevicePath;
49   ACPI_HID_DEVICE_PATH                       *Acpi;
50 
51   //
52   // Get the list of ISA controllers in the system
53   //
54   Status = gBS->LocateHandleBuffer (
55                   ByProtocol,
56                   &gEfiSioProtocolGuid,
57                   NULL,
58                   &HandleCount,
59                   &HandleBuffer
60                   );
61   if (EFI_ERROR (Status)) {
62     return EFI_NOT_FOUND;
63   }
64   //
65   // Collect legacy information from each of the ISA controllers in the system
66   //
67   for (Index = 0; Index < HandleCount; Index++) {
68     Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSioProtocolGuid, (VOID **) &Sio);
69     if (EFI_ERROR (Status)) {
70       continue;
71     }
72 
73     Address = MAX_UINT16;
74     Dma     = MAX_UINT8;
75     Irq     = MAX_UINT8;
76     Status = Sio->GetResources (Sio, &Resources);
77     if (!EFI_ERROR (Status)) {
78       //
79       // Get the base address information from ACPI resource descriptor.
80       //
81       while (Resources.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) {
82         switch (Resources.SmallHeader->Byte) {
83         case ACPI_IO_PORT_DESCRIPTOR:
84           IoResource = (EFI_ACPI_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;
85           Address = IoResource->BaseAddressMin;
86           break;
87 
88         case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR:
89           FixedIoResource = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;
90           Address = FixedIoResource->BaseAddress;
91           break;
92 
93         case ACPI_DMA_DESCRIPTOR:
94           DmaResource = (EFI_ACPI_DMA_DESCRIPTOR *) Resources.SmallHeader;
95           Dma = (UINT8) LowBitSet32 (DmaResource->ChannelMask);
96           break;
97 
98         case ACPI_IRQ_DESCRIPTOR:
99         case ACPI_IRQ_NOFLAG_DESCRIPTOR:
100           IrqResource = (EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR *) Resources.SmallHeader;
101           Irq = (UINT8) LowBitSet32 (IrqResource->Mask);
102           break;
103 
104         default:
105           break;
106         }
107 
108         if (Resources.SmallHeader->Bits.Type == 0) {
109           Resources.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) Resources.SmallHeader
110                                                                   + Resources.SmallHeader->Bits.Length
111                                                                   + sizeof (*Resources.SmallHeader));
112         } else {
113           Resources.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) Resources.LargeHeader
114                                                                   + Resources.LargeHeader->Length
115                                                                   + sizeof (*Resources.LargeHeader));
116         }
117       }
118     }
119 
120     DEBUG ((EFI_D_INFO, "LegacySio: Address/Dma/Irq = %x/%d/%d\n", Address, Dma, Irq));
121 
122     DevicePath = DevicePathFromHandle (HandleBuffer[Index]);
123     if (DevicePath == NULL) {
124       continue;
125     }
126 
127     Acpi = NULL;
128     while (!IsDevicePathEnd (DevicePath)) {
129       Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath;
130       DevicePath = NextDevicePathNode (DevicePath);
131     }
132 
133     if ((Acpi == NULL) || (DevicePathType (Acpi) != ACPI_DEVICE_PATH) ||
134         ((DevicePathSubType (Acpi) != ACPI_DP) && (DevicePathSubType (Acpi) != ACPI_EXTENDED_DP))
135         ) {
136       continue;
137     }
138 
139     //
140     // See if this is an ISA serial port
141     //
142     // Ignore DMA resource since it is always returned NULL
143     //
144     if (Acpi->HID == EISA_PNP_ID (0x500) || Acpi->HID == EISA_PNP_ID (0x501)) {
145 
146       if (Acpi->UID < 4 && Address != MAX_UINT16 && Irq != MAX_UINT8) {
147         //
148         // Get the handle of the child device that has opened the Super I/O Protocol
149         //
150         Status = gBS->OpenProtocolInformation (
151                         HandleBuffer[Index],
152                         &gEfiSioProtocolGuid,
153                         &OpenInfoBuffer,
154                         &EntryCount
155                         );
156         if (EFI_ERROR (Status)) {
157           continue;
158         }
159         for (ChildIndex = 0; ChildIndex < EntryCount; ChildIndex++) {
160           if ((OpenInfoBuffer[ChildIndex].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
161             Status = gBS->HandleProtocol (OpenInfoBuffer[ChildIndex].ControllerHandle, &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo);
162             if (!EFI_ERROR (Status)) {
163               SioSerial           = &SioPtr->Serial[Acpi->UID];
164               SioSerial->Address  = Address;
165               SioSerial->Irq      = Irq;
166               SioSerial->Mode     = DEVICE_SERIAL_MODE_NORMAL | DEVICE_SERIAL_MODE_DUPLEX_HALF;
167               break;
168             }
169           }
170         }
171 
172         FreePool (OpenInfoBuffer);
173       }
174     }
175     //
176     // See if this is an ISA parallel port
177     //
178     // Ignore DMA resource since it is always returned NULL, port
179     // only used in output mode.
180     //
181     if (Acpi->HID == EISA_PNP_ID (0x400) || Acpi->HID == EISA_PNP_ID (0x401)) {
182       if (Acpi->UID < 3 && Address != MAX_UINT16 && Irq != MAX_UINT8 && Dma != MAX_UINT8) {
183         SioParallel           = &SioPtr->Parallel[Acpi->UID];
184         SioParallel->Address  = Address;
185         SioParallel->Irq      = Irq;
186         SioParallel->Dma      = Dma;
187         SioParallel->Mode     = DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY;
188       }
189     }
190     //
191     // See if this is an ISA floppy controller
192     //
193     if (Acpi->HID == EISA_PNP_ID (0x604)) {
194       if (Address != MAX_UINT16 && Irq != MAX_UINT8 && Dma != MAX_UINT8) {
195         Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
196         if (!EFI_ERROR (Status)) {
197           SioFloppy           = &SioPtr->Floppy;
198           SioFloppy->Address  = Address;
199           SioFloppy->Irq      = Irq;
200           SioFloppy->Dma      = Dma;
201           SioFloppy->NumberOfFloppy++;
202         }
203       }
204     }
205     //
206     // See if this is a mouse
207     // Always set mouse found so USB hot plug will work
208     //
209     // Ignore lower byte of HID. Pnp0fxx is any type of mouse.
210     //
211     //    Hid = ResourceList->Device.HID & 0xff00ffff;
212     //    PnpId = EISA_PNP_ID(0x0f00);
213     //    if (Hid == PnpId) {
214     //      if (ResourceList->Device.UID == 1) {
215     //        Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimplePointerProtocolGuid, &SimplePointer);
216     //      if (!EFI_ERROR (Status)) {
217     //
218     SioPtr->MousePresent = 0x01;
219     //
220     //        }
221     //      }
222     //    }
223     //
224   }
225 
226   FreePool (HandleBuffer);
227   return EFI_SUCCESS;
228 
229 }
230 
231 /**
232   Collect EFI Info about legacy devices through ISA IO interface.
233 
234   @param  SioPtr       Pointer to SIO data.
235 
236   @retval EFI_SUCCESS   When SIO data is got successfully.
237   @retval EFI_NOT_FOUND When ISA IO interface is absent.
238 
239 **/
240 EFI_STATUS
LegacyBiosBuildSioDataFromIsaIo(IN DEVICE_PRODUCER_DATA_HEADER * SioPtr)241 LegacyBiosBuildSioDataFromIsaIo (
242   IN DEVICE_PRODUCER_DATA_HEADER      *SioPtr
243   )
244 {
245   EFI_STATUS                          Status;
246   DEVICE_PRODUCER_SERIAL              *SioSerial;
247   DEVICE_PRODUCER_PARALLEL            *SioParallel;
248   DEVICE_PRODUCER_FLOPPY              *SioFloppy;
249   UINTN                               HandleCount;
250   EFI_HANDLE                          *HandleBuffer;
251   UINTN                               Index;
252   UINTN                               ResourceIndex;
253   UINTN                               ChildIndex;
254   EFI_ISA_IO_PROTOCOL                 *IsaIo;
255   EFI_ISA_ACPI_RESOURCE_LIST          *ResourceList;
256   EFI_ISA_ACPI_RESOURCE               *IoResource;
257   EFI_ISA_ACPI_RESOURCE               *DmaResource;
258   EFI_ISA_ACPI_RESOURCE               *InterruptResource;
259   UINTN                               EntryCount;
260   EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
261   EFI_BLOCK_IO_PROTOCOL               *BlockIo;
262   EFI_SERIAL_IO_PROTOCOL              *SerialIo;
263 
264   //
265   // Get the list of ISA controllers in the system
266   //
267   Status = gBS->LocateHandleBuffer (
268                   ByProtocol,
269                   &gEfiIsaIoProtocolGuid,
270                   NULL,
271                   &HandleCount,
272                   &HandleBuffer
273                   );
274   if (EFI_ERROR (Status)) {
275     return EFI_NOT_FOUND;
276   }
277   //
278   // Collect legacy information from each of the ISA controllers in the system
279   //
280   for (Index = 0; Index < HandleCount; Index++) {
281 
282     Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiIsaIoProtocolGuid, (VOID **) &IsaIo);
283     if (EFI_ERROR (Status)) {
284       continue;
285     }
286 
287     ResourceList = IsaIo->ResourceList;
288 
289     if (ResourceList == NULL) {
290       continue;
291     }
292     //
293     // Collect the resource types neededto fill in the SIO data structure
294     //
295     IoResource        = NULL;
296     DmaResource       = NULL;
297     InterruptResource = NULL;
298     for (ResourceIndex = 0;
299          ResourceList->ResourceItem[ResourceIndex].Type != EfiIsaAcpiResourceEndOfList;
300          ResourceIndex++
301         ) {
302       switch (ResourceList->ResourceItem[ResourceIndex].Type) {
303       case EfiIsaAcpiResourceIo:
304         IoResource = &ResourceList->ResourceItem[ResourceIndex];
305         break;
306 
307       case EfiIsaAcpiResourceMemory:
308         break;
309 
310       case EfiIsaAcpiResourceDma:
311         DmaResource = &ResourceList->ResourceItem[ResourceIndex];
312         break;
313 
314       case EfiIsaAcpiResourceInterrupt:
315         InterruptResource = &ResourceList->ResourceItem[ResourceIndex];
316         break;
317 
318       default:
319         break;
320       }
321     }
322     //
323     // See if this is an ISA serial port
324     //
325     // Ignore DMA resource since it is always returned NULL
326     //
327     if (ResourceList->Device.HID == EISA_PNP_ID (0x500) || ResourceList->Device.HID == EISA_PNP_ID (0x501)) {
328 
329       if (ResourceList->Device.UID <= 3 &&
330           IoResource != NULL &&
331           InterruptResource != NULL
332           ) {
333         //
334         // Get the handle of the child device that has opened the ISA I/O Protocol
335         //
336         Status = gBS->OpenProtocolInformation (
337                         HandleBuffer[Index],
338                         &gEfiIsaIoProtocolGuid,
339                         &OpenInfoBuffer,
340                         &EntryCount
341                         );
342         if (EFI_ERROR (Status)) {
343           continue;
344         }
345         //
346         // We want resource for legacy even if no 32-bit driver installed
347         //
348         for (ChildIndex = 0; ChildIndex < EntryCount; ChildIndex++) {
349           if ((OpenInfoBuffer[ChildIndex].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
350             Status = gBS->HandleProtocol (OpenInfoBuffer[ChildIndex].ControllerHandle, &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo);
351             if (!EFI_ERROR (Status)) {
352               SioSerial           = &SioPtr->Serial[ResourceList->Device.UID];
353               SioSerial->Address  = (UINT16) IoResource->StartRange;
354               SioSerial->Irq      = (UINT8) InterruptResource->StartRange;
355               SioSerial->Mode     = DEVICE_SERIAL_MODE_NORMAL | DEVICE_SERIAL_MODE_DUPLEX_HALF;
356               break;
357             }
358           }
359         }
360 
361         FreePool (OpenInfoBuffer);
362       }
363     }
364     //
365     // See if this is an ISA parallel port
366     //
367     // Ignore DMA resource since it is always returned NULL, port
368     // only used in output mode.
369     //
370     if (ResourceList->Device.HID == EISA_PNP_ID (0x400) || ResourceList->Device.HID == EISA_PNP_ID (0x401)) {
371       if (ResourceList->Device.UID <= 2 &&
372           IoResource != NULL &&
373           InterruptResource != NULL &&
374           DmaResource != NULL
375           ) {
376         SioParallel           = &SioPtr->Parallel[ResourceList->Device.UID];
377         SioParallel->Address  = (UINT16) IoResource->StartRange;
378         SioParallel->Irq      = (UINT8) InterruptResource->StartRange;
379         SioParallel->Dma      = (UINT8) DmaResource->StartRange;
380         SioParallel->Mode     = DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY;
381       }
382     }
383     //
384     // See if this is an ISA floppy controller
385     //
386     if (ResourceList->Device.HID == EISA_PNP_ID (0x604)) {
387       if (IoResource != NULL && InterruptResource != NULL && DmaResource != NULL) {
388         Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
389         if (!EFI_ERROR (Status)) {
390           SioFloppy           = &SioPtr->Floppy;
391           SioFloppy->Address  = (UINT16) IoResource->StartRange;
392           SioFloppy->Irq      = (UINT8) InterruptResource->StartRange;
393           SioFloppy->Dma      = (UINT8) DmaResource->StartRange;
394           SioFloppy->NumberOfFloppy++;
395         }
396       }
397     }
398     //
399     // See if this is a mouse
400     // Always set mouse found so USB hot plug will work
401     //
402     // Ignore lower byte of HID. Pnp0fxx is any type of mouse.
403     //
404     //    Hid = ResourceList->Device.HID & 0xff00ffff;
405     //    PnpId = EISA_PNP_ID(0x0f00);
406     //    if (Hid == PnpId) {
407     //      if (ResourceList->Device.UID == 1) {
408     //        Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimplePointerProtocolGuid, &SimplePointer);
409     //      if (!EFI_ERROR (Status)) {
410     //
411     SioPtr->MousePresent = 0x01;
412     //
413     //        }
414     //      }
415     //    }
416     //
417   }
418 
419   FreePool (HandleBuffer);
420   return EFI_SUCCESS;
421 }
422 
423 /**
424   Collect EFI Info about legacy devices.
425 
426   @param  Private      Legacy BIOS Instance data
427 
428   @retval EFI_SUCCESS  It should always work.
429 
430 **/
431 EFI_STATUS
LegacyBiosBuildSioData(IN LEGACY_BIOS_INSTANCE * Private)432 LegacyBiosBuildSioData (
433   IN  LEGACY_BIOS_INSTANCE      *Private
434   )
435 {
436   EFI_STATUS                          Status;
437   DEVICE_PRODUCER_DATA_HEADER         *SioPtr;
438   EFI_HANDLE                          IsaBusController;
439   UINTN                               HandleCount;
440   EFI_HANDLE                          *HandleBuffer;
441 
442   //
443   // Get the pointer to the SIO data structure
444   //
445   SioPtr = &Private->IntThunk->EfiToLegacy16BootTable.SioData;
446 
447   //
448   // Zero the data in the SIO data structure
449   //
450   gBS->SetMem (SioPtr, sizeof (DEVICE_PRODUCER_DATA_HEADER), 0);
451 
452   //
453   // Find the ISA Bus Controller used for legacy
454   //
455   Status = Private->LegacyBiosPlatform->GetPlatformHandle (
456                                           Private->LegacyBiosPlatform,
457                                           EfiGetPlatformIsaBusHandle,
458                                           0,
459                                           &HandleBuffer,
460                                           &HandleCount,
461                                           NULL
462                                           );
463   IsaBusController = HandleBuffer[0];
464   if (!EFI_ERROR (Status)) {
465     //
466     // Force ISA Bus Controller to produce all ISA devices
467     //
468     gBS->ConnectController (IsaBusController, NULL, NULL, TRUE);
469   }
470 
471   Status = LegacyBiosBuildSioDataFromIsaIo (SioPtr);
472   if (EFI_ERROR (Status)) {
473     LegacyBiosBuildSioDataFromSio (SioPtr);
474   }
475 
476   return EFI_SUCCESS;
477 }
478