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