/** @file | |
Collect Sio information from Native EFI Drivers. | |
Sio is floppy, parallel, serial, ... hardware | |
Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "LegacyBiosInterface.h" | |
/** | |
Collect EFI Info about legacy devices through Super IO interface. | |
@param SioPtr Pointer to SIO data. | |
@retval EFI_SUCCESS When SIO data is got successfully. | |
@retval EFI_NOT_FOUND When ISA IO interface is absent. | |
**/ | |
EFI_STATUS | |
LegacyBiosBuildSioDataFromSio ( | |
IN DEVICE_PRODUCER_DATA_HEADER *SioPtr | |
) | |
{ | |
EFI_STATUS Status; | |
DEVICE_PRODUCER_SERIAL *SioSerial; | |
DEVICE_PRODUCER_PARALLEL *SioParallel; | |
DEVICE_PRODUCER_FLOPPY *SioFloppy; | |
UINTN HandleCount; | |
EFI_HANDLE *HandleBuffer; | |
UINTN Index; | |
UINTN ChildIndex; | |
EFI_SIO_PROTOCOL *Sio; | |
ACPI_RESOURCE_HEADER_PTR Resources; | |
EFI_ACPI_IO_PORT_DESCRIPTOR *IoResource; | |
EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIoResource; | |
EFI_ACPI_DMA_DESCRIPTOR *DmaResource; | |
EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR *IrqResource; | |
UINT16 Address; | |
UINT8 Dma; | |
UINT8 Irq; | |
UINTN EntryCount; | |
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
EFI_SERIAL_IO_PROTOCOL *SerialIo; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
ACPI_HID_DEVICE_PATH *Acpi; | |
// | |
// Get the list of ISA controllers in the system | |
// | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiSioProtocolGuid, | |
NULL, | |
&HandleCount, | |
&HandleBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Collect legacy information from each of the ISA controllers in the system | |
// | |
for (Index = 0; Index < HandleCount; Index++) { | |
Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSioProtocolGuid, (VOID **) &Sio); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
Address = MAX_UINT16; | |
Dma = MAX_UINT8; | |
Irq = MAX_UINT8; | |
Status = Sio->GetResources (Sio, &Resources); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Get the base address information from ACPI resource descriptor. | |
// | |
while (Resources.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) { | |
switch (Resources.SmallHeader->Byte) { | |
case ACPI_IO_PORT_DESCRIPTOR: | |
IoResource = (EFI_ACPI_IO_PORT_DESCRIPTOR *) Resources.SmallHeader; | |
Address = IoResource->BaseAddressMin; | |
break; | |
case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR: | |
FixedIoResource = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *) Resources.SmallHeader; | |
Address = FixedIoResource->BaseAddress; | |
break; | |
case ACPI_DMA_DESCRIPTOR: | |
DmaResource = (EFI_ACPI_DMA_DESCRIPTOR *) Resources.SmallHeader; | |
Dma = (UINT8) LowBitSet32 (DmaResource->ChannelMask); | |
break; | |
case ACPI_IRQ_DESCRIPTOR: | |
case ACPI_IRQ_NOFLAG_DESCRIPTOR: | |
IrqResource = (EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR *) Resources.SmallHeader; | |
Irq = (UINT8) LowBitSet32 (IrqResource->Mask); | |
break; | |
default: | |
break; | |
} | |
if (Resources.SmallHeader->Bits.Type == 0) { | |
Resources.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) Resources.SmallHeader | |
+ Resources.SmallHeader->Bits.Length | |
+ sizeof (*Resources.SmallHeader)); | |
} else { | |
Resources.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) Resources.LargeHeader | |
+ Resources.LargeHeader->Length | |
+ sizeof (*Resources.LargeHeader)); | |
} | |
} | |
} | |
DEBUG ((DEBUG_INFO, "LegacySio: Address/Dma/Irq = %x/%d/%d\n", Address, Dma, Irq)); | |
DevicePath = DevicePathFromHandle (HandleBuffer[Index]); | |
if (DevicePath == NULL) { | |
continue; | |
} | |
Acpi = NULL; | |
while (!IsDevicePathEnd (DevicePath)) { | |
Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath; | |
DevicePath = NextDevicePathNode (DevicePath); | |
} | |
if ((Acpi == NULL) || (DevicePathType (Acpi) != ACPI_DEVICE_PATH) || | |
((DevicePathSubType (Acpi) != ACPI_DP) && (DevicePathSubType (Acpi) != ACPI_EXTENDED_DP)) | |
) { | |
continue; | |
} | |
// | |
// See if this is an ISA serial port | |
// | |
// Ignore DMA resource since it is always returned NULL | |
// | |
if (Acpi->HID == EISA_PNP_ID (0x500) || Acpi->HID == EISA_PNP_ID (0x501)) { | |
if (Acpi->UID < 4 && Address != MAX_UINT16 && Irq != MAX_UINT8) { | |
// | |
// Get the handle of the child device that has opened the Super I/O Protocol | |
// | |
Status = gBS->OpenProtocolInformation ( | |
HandleBuffer[Index], | |
&gEfiSioProtocolGuid, | |
&OpenInfoBuffer, | |
&EntryCount | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
for (ChildIndex = 0; ChildIndex < EntryCount; ChildIndex++) { | |
if ((OpenInfoBuffer[ChildIndex].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { | |
Status = gBS->HandleProtocol (OpenInfoBuffer[ChildIndex].ControllerHandle, &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo); | |
if (!EFI_ERROR (Status)) { | |
SioSerial = &SioPtr->Serial[Acpi->UID]; | |
SioSerial->Address = Address; | |
SioSerial->Irq = Irq; | |
SioSerial->Mode = DEVICE_SERIAL_MODE_NORMAL | DEVICE_SERIAL_MODE_DUPLEX_HALF; | |
break; | |
} | |
} | |
} | |
FreePool (OpenInfoBuffer); | |
} | |
} | |
// | |
// See if this is an ISA parallel port | |
// | |
// Ignore DMA resource since it is always returned NULL, port | |
// only used in output mode. | |
// | |
if (Acpi->HID == EISA_PNP_ID (0x400) || Acpi->HID == EISA_PNP_ID (0x401)) { | |
if (Acpi->UID < 3 && Address != MAX_UINT16 && Irq != MAX_UINT8 && Dma != MAX_UINT8) { | |
SioParallel = &SioPtr->Parallel[Acpi->UID]; | |
SioParallel->Address = Address; | |
SioParallel->Irq = Irq; | |
SioParallel->Dma = Dma; | |
SioParallel->Mode = DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY; | |
} | |
} | |
// | |
// See if this is an ISA floppy controller | |
// | |
if (Acpi->HID == EISA_PNP_ID (0x604)) { | |
if (Address != MAX_UINT16 && Irq != MAX_UINT8 && Dma != MAX_UINT8) { | |
Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
if (!EFI_ERROR (Status)) { | |
SioFloppy = &SioPtr->Floppy; | |
SioFloppy->Address = Address; | |
SioFloppy->Irq = Irq; | |
SioFloppy->Dma = Dma; | |
SioFloppy->NumberOfFloppy++; | |
} | |
} | |
} | |
// | |
// See if this is a mouse | |
// Always set mouse found so USB hot plug will work | |
// | |
// Ignore lower byte of HID. Pnp0fxx is any type of mouse. | |
// | |
// Hid = ResourceList->Device.HID & 0xff00ffff; | |
// PnpId = EISA_PNP_ID(0x0f00); | |
// if (Hid == PnpId) { | |
// if (ResourceList->Device.UID == 1) { | |
// Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimplePointerProtocolGuid, &SimplePointer); | |
// if (!EFI_ERROR (Status)) { | |
// | |
SioPtr->MousePresent = 0x01; | |
// | |
// } | |
// } | |
// } | |
// | |
} | |
FreePool (HandleBuffer); | |
return EFI_SUCCESS; | |
} | |
/** | |
Collect EFI Info about legacy devices through ISA IO interface. | |
@param SioPtr Pointer to SIO data. | |
@retval EFI_SUCCESS When SIO data is got successfully. | |
@retval EFI_NOT_FOUND When ISA IO interface is absent. | |
**/ | |
EFI_STATUS | |
LegacyBiosBuildSioDataFromIsaIo ( | |
IN DEVICE_PRODUCER_DATA_HEADER *SioPtr | |
) | |
{ | |
EFI_STATUS Status; | |
DEVICE_PRODUCER_SERIAL *SioSerial; | |
DEVICE_PRODUCER_PARALLEL *SioParallel; | |
DEVICE_PRODUCER_FLOPPY *SioFloppy; | |
UINTN HandleCount; | |
EFI_HANDLE *HandleBuffer; | |
UINTN Index; | |
UINTN ResourceIndex; | |
UINTN ChildIndex; | |
EFI_ISA_IO_PROTOCOL *IsaIo; | |
EFI_ISA_ACPI_RESOURCE_LIST *ResourceList; | |
EFI_ISA_ACPI_RESOURCE *IoResource; | |
EFI_ISA_ACPI_RESOURCE *DmaResource; | |
EFI_ISA_ACPI_RESOURCE *InterruptResource; | |
UINTN EntryCount; | |
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
EFI_SERIAL_IO_PROTOCOL *SerialIo; | |
// | |
// Get the list of ISA controllers in the system | |
// | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiIsaIoProtocolGuid, | |
NULL, | |
&HandleCount, | |
&HandleBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Collect legacy information from each of the ISA controllers in the system | |
// | |
for (Index = 0; Index < HandleCount; Index++) { | |
Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiIsaIoProtocolGuid, (VOID **) &IsaIo); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
ResourceList = IsaIo->ResourceList; | |
if (ResourceList == NULL) { | |
continue; | |
} | |
// | |
// Collect the resource types neededto fill in the SIO data structure | |
// | |
IoResource = NULL; | |
DmaResource = NULL; | |
InterruptResource = NULL; | |
for (ResourceIndex = 0; | |
ResourceList->ResourceItem[ResourceIndex].Type != EfiIsaAcpiResourceEndOfList; | |
ResourceIndex++ | |
) { | |
switch (ResourceList->ResourceItem[ResourceIndex].Type) { | |
case EfiIsaAcpiResourceIo: | |
IoResource = &ResourceList->ResourceItem[ResourceIndex]; | |
break; | |
case EfiIsaAcpiResourceMemory: | |
break; | |
case EfiIsaAcpiResourceDma: | |
DmaResource = &ResourceList->ResourceItem[ResourceIndex]; | |
break; | |
case EfiIsaAcpiResourceInterrupt: | |
InterruptResource = &ResourceList->ResourceItem[ResourceIndex]; | |
break; | |
default: | |
break; | |
} | |
} | |
// | |
// See if this is an ISA serial port | |
// | |
// Ignore DMA resource since it is always returned NULL | |
// | |
if (ResourceList->Device.HID == EISA_PNP_ID (0x500) || ResourceList->Device.HID == EISA_PNP_ID (0x501)) { | |
if (ResourceList->Device.UID <= 3 && | |
IoResource != NULL && | |
InterruptResource != NULL | |
) { | |
// | |
// Get the handle of the child device that has opened the ISA I/O Protocol | |
// | |
Status = gBS->OpenProtocolInformation ( | |
HandleBuffer[Index], | |
&gEfiIsaIoProtocolGuid, | |
&OpenInfoBuffer, | |
&EntryCount | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// | |
// We want resource for legacy even if no 32-bit driver installed | |
// | |
for (ChildIndex = 0; ChildIndex < EntryCount; ChildIndex++) { | |
if ((OpenInfoBuffer[ChildIndex].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { | |
Status = gBS->HandleProtocol (OpenInfoBuffer[ChildIndex].ControllerHandle, &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo); | |
if (!EFI_ERROR (Status)) { | |
SioSerial = &SioPtr->Serial[ResourceList->Device.UID]; | |
SioSerial->Address = (UINT16) IoResource->StartRange; | |
SioSerial->Irq = (UINT8) InterruptResource->StartRange; | |
SioSerial->Mode = DEVICE_SERIAL_MODE_NORMAL | DEVICE_SERIAL_MODE_DUPLEX_HALF; | |
break; | |
} | |
} | |
} | |
FreePool (OpenInfoBuffer); | |
} | |
} | |
// | |
// See if this is an ISA parallel port | |
// | |
// Ignore DMA resource since it is always returned NULL, port | |
// only used in output mode. | |
// | |
if (ResourceList->Device.HID == EISA_PNP_ID (0x400) || ResourceList->Device.HID == EISA_PNP_ID (0x401)) { | |
if (ResourceList->Device.UID <= 2 && | |
IoResource != NULL && | |
InterruptResource != NULL && | |
DmaResource != NULL | |
) { | |
SioParallel = &SioPtr->Parallel[ResourceList->Device.UID]; | |
SioParallel->Address = (UINT16) IoResource->StartRange; | |
SioParallel->Irq = (UINT8) InterruptResource->StartRange; | |
SioParallel->Dma = (UINT8) DmaResource->StartRange; | |
SioParallel->Mode = DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY; | |
} | |
} | |
// | |
// See if this is an ISA floppy controller | |
// | |
if (ResourceList->Device.HID == EISA_PNP_ID (0x604)) { | |
if (IoResource != NULL && InterruptResource != NULL && DmaResource != NULL) { | |
Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
if (!EFI_ERROR (Status)) { | |
SioFloppy = &SioPtr->Floppy; | |
SioFloppy->Address = (UINT16) IoResource->StartRange; | |
SioFloppy->Irq = (UINT8) InterruptResource->StartRange; | |
SioFloppy->Dma = (UINT8) DmaResource->StartRange; | |
SioFloppy->NumberOfFloppy++; | |
} | |
} | |
} | |
// | |
// See if this is a mouse | |
// Always set mouse found so USB hot plug will work | |
// | |
// Ignore lower byte of HID. Pnp0fxx is any type of mouse. | |
// | |
// Hid = ResourceList->Device.HID & 0xff00ffff; | |
// PnpId = EISA_PNP_ID(0x0f00); | |
// if (Hid == PnpId) { | |
// if (ResourceList->Device.UID == 1) { | |
// Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimplePointerProtocolGuid, &SimplePointer); | |
// if (!EFI_ERROR (Status)) { | |
// | |
SioPtr->MousePresent = 0x01; | |
// | |
// } | |
// } | |
// } | |
// | |
} | |
FreePool (HandleBuffer); | |
return EFI_SUCCESS; | |
} | |
/** | |
Collect EFI Info about legacy devices. | |
@param Private Legacy BIOS Instance data | |
@retval EFI_SUCCESS It should always work. | |
**/ | |
EFI_STATUS | |
LegacyBiosBuildSioData ( | |
IN LEGACY_BIOS_INSTANCE *Private | |
) | |
{ | |
EFI_STATUS Status; | |
DEVICE_PRODUCER_DATA_HEADER *SioPtr; | |
EFI_HANDLE IsaBusController; | |
UINTN HandleCount; | |
EFI_HANDLE *HandleBuffer; | |
// | |
// Get the pointer to the SIO data structure | |
// | |
SioPtr = &Private->IntThunk->EfiToLegacy16BootTable.SioData; | |
// | |
// Zero the data in the SIO data structure | |
// | |
gBS->SetMem (SioPtr, sizeof (DEVICE_PRODUCER_DATA_HEADER), 0); | |
// | |
// Find the ISA Bus Controller used for legacy | |
// | |
Status = Private->LegacyBiosPlatform->GetPlatformHandle ( | |
Private->LegacyBiosPlatform, | |
EfiGetPlatformIsaBusHandle, | |
0, | |
&HandleBuffer, | |
&HandleCount, | |
NULL | |
); | |
IsaBusController = HandleBuffer[0]; | |
if (!EFI_ERROR (Status)) { | |
// | |
// Force ISA Bus Controller to produce all ISA devices | |
// | |
gBS->ConnectController (IsaBusController, NULL, NULL, TRUE); | |
} | |
Status = LegacyBiosBuildSioDataFromIsaIo (SioPtr); | |
if (EFI_ERROR (Status)) { | |
LegacyBiosBuildSioDataFromSio (SioPtr); | |
} | |
return EFI_SUCCESS; | |
} |